diff --git a/Makefile b/Makefile index 2de88c1..252461d 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ SRC = $(shell find . -name "*.cpp") SHDRSRC = $(shell find . -name "*.frag" -o -name "*vert") SPV = $(SHDRSRC:%.vert=%.spv) $(SHDRSRC:%.frag=%.spv) OBJ = $(SRC:%.cpp=%.o) - +MAKEFLAGS += -j16 BIN=build/agnosiaengine .PHONY: all diff --git a/src/devicelibrary.cpp b/src/devicelibrary.cpp index 19e8e0e..395020f 100644 --- a/src/devicelibrary.cpp +++ b/src/devicelibrary.cpp @@ -264,7 +264,7 @@ namespace DeviceControl { // Do NOT blend with other windows on the system. createSwapChainInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; createSwapChainInfo.presentMode = presentMode; - // This is interesting, clip pixels that are obscured for performance, but that means you wont be able to reaf them reliably.. + // This is interesting, clip pixels that are obscured for performance, but that means you wont be able to read them reliably.. // I am curious if this would affect screen-space rendering techniques, may be something to note. createSwapChainInfo.clipped = VK_TRUE; // This is something that needs to be implemented later, operations like resizing the window invalidate the swap chain and diff --git a/src/entrypoint.cpp b/src/entrypoint.cpp index 7d28ace..fb9e3cd 100644 --- a/src/entrypoint.cpp +++ b/src/entrypoint.cpp @@ -1,8 +1,10 @@ #include "entrypoint.h" +#include "graphics/buffers.h" DeviceControl::devicelibrary deviceLibs; Debug::vulkandebuglibs debugController; Graphics::graphicspipeline graphicsPipeline; RenderPresent::render renderPresentation; +Buffers::bufferslibrary buffers; VkInstance vulkaninstance; // Getters and Setters! @@ -40,7 +42,7 @@ void createInstance() { appInfo.applicationVersion = VK_MAKE_VERSION(1,0,0); // Create a Major Minor Patch version number for the application! appInfo.pEngineName = "Agnosia Engine"; // Give an internal name for the engine running appInfo.engineVersion = VK_MAKE_VERSION(1,0,0); // Similar to the App version, give vulkan an *engine* version - appInfo.apiVersion = VK_API_VERSION_1_1; // Tell vulkan what the highest API version we will allow this program to run on + appInfo.apiVersion = VK_API_VERSION_1_3; // Tell vulkan what the highest API version we will allow this program to run on VkInstanceCreateInfo createInfo{}; // Define parameters of new vulkan instance createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; // Tell vulkan this is a info structure @@ -52,7 +54,7 @@ void createInstance() { void initVulkan() { createInstance(); - debugController.setupDebugMessenger(vulkaninstance); // The debug messenger is out holy grail, it gives us Vulkan related debug info when built with the -DNDEBUG flag (as per the makefile) + debugController.setupDebugMessenger(vulkaninstance); // The debug messenger is out holy grail, it gives us Vulkan related debug info when built with the -DDEBUG flag (as per the makefile) deviceLibs.createSurface(vulkaninstance, Global::window); deviceLibs.pickPhysicalDevice(vulkaninstance); deviceLibs.createLogicalDevice(); @@ -62,6 +64,7 @@ void initVulkan() { graphicsPipeline.createGraphicsPipeline(); graphicsPipeline.createFramebuffers(); graphicsPipeline.createCommandPool(); + buffers.createVertexBuffer(); graphicsPipeline.createCommandBuffer(); renderPresentation.createSyncObject(); } @@ -76,6 +79,7 @@ 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(); renderPresentation.destroyFenceSemaphores(); diff --git a/src/global.cpp b/src/global.cpp index 7e72065..4acd9ef 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -21,6 +21,7 @@ namespace Global { VkQueue graphicsQueue; VkQueue presentQueue; GLFWwindow* window; + Global::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { // First we feed in a integer we want to use to hold the number of queued items, that fills it, then we create that amount of default constructed *VkQueueFamilyProperties* structs. diff --git a/src/global.h b/src/global.h index 03e5849..a878962 100644 --- a/src/global.h +++ b/src/global.h @@ -1,10 +1,13 @@ #pragma once +#include +#include #include #include #include #include #include "debug/vulkandebuglibs.h" +#include #define GLFW_INCLUDE_VULKAN #include @@ -21,6 +24,34 @@ namespace Global { const int MAX_FRAMES_IN_FLIGHT = 2; extern GLFWwindow* window; + struct Vertex { + glm::vec2 pos; + glm::vec3 color; + + static VkVertexInputBindingDescription getBindingDescription() { + VkVertexInputBindingDescription bindingDescription{}; + bindingDescription.binding = 0; + bindingDescription.stride = sizeof(Vertex); + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + return bindingDescription; + } + static std::array getAttributeDescriptions() { + std::array attributeDescriptions{}; + + attributeDescriptions[0].binding = 0; + attributeDescriptions[0].location = 0; + attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT; + attributeDescriptions[0].offset = offsetof(Vertex, pos); + + attributeDescriptions[1].binding = 0; + attributeDescriptions[1].location = 1; + attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; + attributeDescriptions[1].offset = offsetof(Vertex, color); + return attributeDescriptions; + } + }; + const uint32_t WIDTH = 800; const uint32_t HEIGHT = 600; diff --git a/src/graphics/buffers.cpp b/src/graphics/buffers.cpp new file mode 100644 index 0000000..940b84c --- /dev/null +++ b/src/graphics/buffers.cpp @@ -0,0 +1,71 @@ +#include "buffers.h" +#include +#include + +VkBuffer vertexBuffer; +VkDeviceMemory vertexBufferMemory; +namespace Buffers { + + const std::vector 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}} + }; + 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. + VkPhysicalDeviceMemoryProperties memProperties; + vkGetPhysicalDeviceMemoryProperties(Global::physicalDevice, &memProperties); + // iterate over and see if any of the memory types match our needs, in this case, HOST_VISIBLE and HOST_COHERENT. These will be explained shortly. + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { + if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { + return i; + } + } + 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!"); + } + // Query the memory requirements of the buffer. + VkMemoryRequirements memRequirements; + vkGetBufferMemoryRequirements(Global::device, vertexBuffer, &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); + + if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &vertexBufferMemory) != VK_SUCCESS) { + throw std::runtime_error("failed to allocate vertex buffer memory!"); + } + vkBindBufferMemory(Global::device, vertexBuffer, vertexBufferMemory, 0); + + void* data; + vkMapMemory(Global::device, vertexBufferMemory, 0, bufferInfo.size, 0, &data); + memcpy(data, vertices.data(), (size_t) bufferInfo.size); + vkUnmapMemory(Global::device, vertexBufferMemory); + } + void bufferslibrary::destroyVertexBuffer() { + vkDestroyBuffer(Global::device, vertexBuffer, nullptr); + vkFreeMemory(Global::device, vertexBufferMemory, nullptr); + } + VkBuffer bufferslibrary::getVertexBuffer() { + return vertexBuffer; + } + std::vector bufferslibrary::getVertices() { + return vertices; + } +} diff --git a/src/graphics/buffers.h b/src/graphics/buffers.h new file mode 100644 index 0000000..6fa0d46 --- /dev/null +++ b/src/graphics/buffers.h @@ -0,0 +1,12 @@ +#pragma once +#include "../global.h" + +namespace Buffers { + class bufferslibrary { + public: + void createVertexBuffer(); + void destroyVertexBuffer(); + VkBuffer getVertexBuffer(); + std::vector getVertices(); + }; +} diff --git a/src/graphics/graphicspipeline.cpp b/src/graphics/graphicspipeline.cpp index b0bc62b..62c018c 100644 --- a/src/graphics/graphicspipeline.cpp +++ b/src/graphics/graphicspipeline.cpp @@ -1,5 +1,7 @@ #include "graphicspipeline.h" +#include "buffers.h" +#include namespace Graphics { std::vector dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, @@ -11,6 +13,7 @@ namespace Graphics { VkPipelineLayout pipelineLayout; VkPipeline graphicsPipeline; DeviceControl::devicelibrary deviceLibs; + Buffers::bufferslibrary buffers; std::vector swapChainFramebuffers; @@ -75,10 +78,14 @@ namespace Graphics { vertShaderStageInfo.pName = "main"; VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; - vertexInputInfo.vertexBindingDescriptionCount = 0; - vertexInputInfo.pVertexBindingDescriptions = nullptr; - vertexInputInfo.vertexAttributeDescriptionCount = 0; - vertexInputInfo.pVertexAttributeDescriptions = nullptr; + + auto bindingDescription = Global::Vertex::getBindingDescription(); + auto attributeDescriptions = Global::Vertex::getAttributeDescriptions(); + + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); // ------------------- STAGE 5 - RASTERIZATION ----------------- // // Take Vertex shader vertices and fragmentize them for the frament shader. The rasterizer also can perform depth testing, face culling, and scissor testing. @@ -265,8 +272,6 @@ namespace Graphics { void graphicspipeline::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { VkCommandBufferBeginInfo beginInfo{}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.flags = 0; // Optional - beginInfo.pInheritanceInfo = nullptr; // Optional if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { throw std::runtime_error("failed to begin recording command buffer!"); @@ -286,7 +291,6 @@ namespace Graphics { vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); - VkViewport viewport{}; viewport.x = 0.0f; viewport.y = 0.0f; @@ -299,10 +303,13 @@ namespace Graphics { VkRect2D scissor{}; scissor.offset = {0, 0}; scissor.extent = deviceLibs.getSwapChainExtent(); - vkCmdSetScissor(commandBuffer, 0, 1, &scissor); + vkCmdSetScissor(commandBuffer, 0, 1, &scissor); - vkCmdDraw(commandBuffer, 3, 1, 0, 0); + VkBuffer vertexBuffers[] = {buffers.getVertexBuffer()}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); + vkCmdDraw(commandBuffer, static_cast(buffers.getVertices().size()), 1, 0, 0); vkCmdEndRenderPass(commandBuffer); if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { diff --git a/src/graphics/graphicspipeline.h b/src/graphics/graphicspipeline.h index 258628c..b5d74a3 100644 --- a/src/graphics/graphicspipeline.h +++ b/src/graphics/graphicspipeline.h @@ -1,6 +1,7 @@ #pragma once #include "../global.h" #include "../devicelibrary.h" +#include "buffers.h" #include namespace Graphics { class graphicspipeline { diff --git a/src/shaders/vertex.vert b/src/shaders/vertex.vert index f5b2f8d..a32c132 100644 --- a/src/shaders/vertex.vert +++ b/src/shaders/vertex.vert @@ -1,20 +1,12 @@ #version 450 +// inPosition and inColor are vertex attributes, per-vertex properties defined in the vertex buffer! +// Layout assigns indices to access these inputs, dvec3 takes 2 slots so we must index it at 2. https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL) +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec3 inColor; layout(location = 0) out vec3 fragColor; -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); - -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); - void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; + gl_Position = vec4(inPosition, 0.0, 1.0); + fragColor = inColor; }