Compare commits

...

2 Commits

19 changed files with 181 additions and 79 deletions

View File

@ -2,14 +2,13 @@ CPPFLAGS=-g
LDFLAGS=-lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
DEBUGFLAGS=-DDEBUG -fsanitize=address
GDBFLAGS=
SRC = $(shell find . -name *.cpp)
SRC = $(shell find . -name "*.cpp")
SHDRSRC = $(shell find . -name "*.frag" -o -name "*vert")
SPV = $(SHDRSRC:%.vert=%.spv) $(SHDRSRC:%.frag=%.spv)
OBJ = $(SRC:%.cpp=%.o)
VERTEX = $(src/shaders/%.vert=%.spv)
FRAGMENT = $(src/shaders/%.frag=%.spv)
MAKEFLAGS += -j16
BIN=build/agnosiaengine
.PHONY: all
all: $(BIN)
@ -28,25 +27,30 @@ debug: $(BIN)
.PHONY: dep
dep:
sudo pacman -S gcc glfw glm shaderc libxi libxxf86vm gdb glslc
sudo pacman -S gcc glfw glm shaderc libxi libxxf86vm gdb shaderc
.PHONY: info
info:
@echo "make: Build executable"
@echo "make dep: Make all required dependencies"
@echo "make debug: Make with Debug hooked in"
@echo "make gdb: Make with GDB hooked in"
@echo "make clean: Clean all files"
@echo "make run: Run the executable after building"
$(BIN): $(OBJ) $(VERTEX) $(FRAGMENT)
$(BIN): $(OBJ) $(SPV)
mkdir -p build
g++ $(CPPFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS)
%.o: %.cpp
g++ -c -g $< -o $@ $(LDFLAGS)
%.spv: %.vert %.frag
%.spv: %.frag
glslc $< -o $@
%.spv: %.vert
glslc $< -o $@
.PHONY: clean
clean:
rm -rf build
find . -name "*.o" -type f -delete
find . -name "*.spv" -type f -delete

View File

@ -1,12 +1,6 @@
#include "vulkandebuglibs.h"
#include "../global.h"
using namespace Debug;
#include <cstdint>
#include <stdexcept>
#include <vulkan/vk_platform.h>
#include <cstring>
// This is our messenger object! It handles passing along debug messages to the debug callback we will also set.
VkDebugUtilsMessengerEXT debugMessenger;
// This is the set of "layers" to hook into. Basically, layers are used to tell the messenger what data we want, its a filter. *validation* is the general blanket layer to cover incorrect usage.

View File

@ -1,6 +1,8 @@
#pragma once
#include <vector>
#include <vulkan/vulkan_core.h>
#include <cstdint>
#include <cstring>
#include "../global.h"
namespace Debug {
class vulkandebuglibs {

View File

@ -1,16 +1,6 @@
#include "devicelibrary.h"
#include "global.h"
#include <algorithm>
#include <cstdint>
#include <limits>
#include <optional>
#include <ostream>
#include <set>
#include <stdexcept>
#include <string>
#include <vector>
#include <vulkan/vulkan_core.h>
namespace DeviceControl {
@ -274,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

View File

@ -1,7 +1,12 @@
#pragma once
#include "global.h"
#include <optional>
#include <vulkan/vulkan_core.h>
#include <algorithm>
#include <limits>
#include <ostream>
#include <set>
#include <string>
#include <vector>
namespace DeviceControl {
class devicelibrary {
public:

View File

@ -1,11 +1,13 @@
#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!
void EntryApp::setFramebufferResized(bool setter) {
framebufferResized = setter;
}
@ -28,8 +30,6 @@ void initWindow() {
glfwSetWindowUserPointer(Global::window, &EntryApp::getInstance());
glfwSetFramebufferSizeCallback(Global::window, framebufferResizeCallback);
}
void createInstance() {
debugController.checkUnavailableValidationLayers(); // Check if there is a mistake with our Validation Layers.
@ -42,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
@ -54,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();
@ -64,6 +64,7 @@ void initVulkan() {
graphicsPipeline.createGraphicsPipeline();
graphicsPipeline.createFramebuffers();
graphicsPipeline.createCommandPool();
buffers.createVertexBuffer();
graphicsPipeline.createCommandBuffer();
renderPresentation.createSyncObject();
}
@ -78,14 +79,12 @@ 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();
graphicsPipeline.destroyCommandPool();
deviceLibs.destroyImageViews();
deviceLibs.destroySwapChain();
vkDestroyDevice(Global::device, nullptr);
if(Global::enableValidationLayers) {
debugController.DestroyDebugUtilsMessengerEXT(vulkaninstance, nullptr);
@ -96,7 +95,7 @@ void cleanup() { // Similar to th
glfwDestroyWindow(Global::window);
glfwTerminate();
}
// External Functions
EntryApp& EntryApp::getInstance() {
static EntryApp instance;
return instance;

View File

@ -19,6 +19,4 @@ class EntryApp {
bool framebufferResized;
bool initialized;
};

View File

@ -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.

View File

@ -1,10 +1,13 @@
#pragma once
#include "debug/vulkandebuglibs.h"
#include <glm/ext/vector_float2.hpp>
#include <glm/ext/vector_float3.hpp>
#include <iostream>
#include <vector>
#include <optional>
#include <vulkan/vulkan_core.h>
#include "debug/vulkandebuglibs.h"
#include <array>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
@ -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<VkVertexInputAttributeDescription, 2> getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, 2> 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;

71
src/graphics/buffers.cpp Normal file
View File

@ -0,0 +1,71 @@
#include "buffers.h"
#include <iostream>
#include <vulkan/vulkan_core.h>
VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
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}}
};
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<Global::Vertex> bufferslibrary::getVertices() {
return vertices;
}
}

12
src/graphics/buffers.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "../global.h"
namespace Buffers {
class bufferslibrary {
public:
void createVertexBuffer();
void destroyVertexBuffer();
VkBuffer getVertexBuffer();
std::vector<Global::Vertex> getVertices();
};
}

View File

@ -1,9 +1,7 @@
#include "graphicspipeline.h"
#include "../devicelibrary.h"
#include <cstdint>
#include <fstream>
#include <stdexcept>
#include <vulkan/vulkan_core.h>
#include "buffers.h"
#include <vector>
namespace Graphics {
std::vector<VkDynamicState> dynamicStates = {
VK_DYNAMIC_STATE_VIEWPORT,
@ -15,6 +13,7 @@ namespace Graphics {
VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline;
DeviceControl::devicelibrary deviceLibs;
Buffers::bufferslibrary buffers;
std::vector<VkFramebuffer> swapChainFramebuffers;
@ -57,8 +56,8 @@ namespace Graphics {
void graphicspipeline::createGraphicsPipeline() {
// Note to self, for some reason the working directory is not where a read file is called from, but the project folder!
auto vertShaderCode = readFile("src/shaders/vert.spv");
auto fragShaderCode = readFile("src/shaders/frag.spv");
auto vertShaderCode = readFile("src/shaders/vertex.spv");
auto fragShaderCode = readFile("src/shaders/fragment.spv");
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode, Global::device);
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode, Global::device);
@ -79,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<uint32_t>(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.
@ -208,7 +211,7 @@ namespace Graphics {
}
void graphicspipeline::destroyRenderPass() {
vkDestroyRenderPass(Global::device, renderPass, nullptr);
std::cout << "Destroyed Render Pass Safely\n" << std::endl;
if(Global::enableValidationLayers) std::cout << "Destroyed Render Pass Safely\n" << std::endl;
}
void graphicspipeline::createFramebuffers() {
// Resize the container to hold all the framebuffers.
@ -269,13 +272,10 @@ 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!");
}
if(Global::enableValidationLayers) std::cout << "Recording command buffer...\n" << std::endl;
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
@ -291,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;
@ -304,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<uint32_t>(buffers.getVertices().size()), 1, 0, 0);
vkCmdEndRenderPass(commandBuffer);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {

View File

@ -1,6 +1,8 @@
#pragma once
#include "../global.h"
#include "../devicelibrary.h"
#include "buffers.h"
#include <fstream>
namespace Graphics {
class graphicspipeline {
public:

View File

@ -1,7 +1,6 @@
#include "entrypoint.h"
int main() {
EntryApp::getInstance().initialize();
try {
EntryApp::getInstance().run();
} catch (const std::exception &e) {

Binary file not shown.

View File

@ -1,20 +0,0 @@
#version 450
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];
}

Binary file not shown.

12
src/shaders/vertex.vert Normal file
View File

@ -0,0 +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;
void main() {
gl_Position = vec4(inPosition, 0.0, 1.0);
fragColor = inColor;
}