Fixed Function pipeline settings, prepping for Render passes then rendering to the screen

This commit is contained in:
Lillian Salehi 2024-10-07 17:02:47 -05:00
parent fa14b3fd8f
commit a8cbb97fd0
9 changed files with 197 additions and 6 deletions

View File

@ -2,8 +2,10 @@ CPPFLAGS=-g
LDFLAGS=-lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi LDFLAGS=-lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
DEBUGFLAGS=-DDEBUG -fsanitize=address DEBUGFLAGS=-DDEBUG -fsanitize=address
GDBFLAGS= GDBFLAGS=
SRC=$(shell find . -name *.cpp) SRC = $(shell find . -name *.cpp)
OBJ=$(SRC:%.cpp=%.o) OBJ = $(SRC:%.cpp=%.o)
VERTEX = $(src/shaders/%.vert=%.spv)
FRAGMENT = $(src/shaders/%.frag=%.spv)
BIN=build/agnosiaengine BIN=build/agnosiaengine
@ -26,7 +28,7 @@ debug: $(BIN)
.PHONY: dep .PHONY: dep
dep: dep:
sudo pacman -S gcc glfw glm shaderc libxi libxxf86vm gdb sudo pacman -S gcc glfw glm shaderc libxi libxxf86vm gdb glslc
.PHONY: info .PHONY: info
info: info:
@echo "make: Build executable" @echo "make: Build executable"
@ -35,12 +37,14 @@ info:
@echo "make clean: Clean all files" @echo "make clean: Clean all files"
@echo "make run: Run the executable after building" @echo "make run: Run the executable after building"
$(BIN): $(OBJ) $(BIN): $(OBJ) $(VERTEX) $(FRAGMENT)
mkdir -p build mkdir -p build
g++ $(CPPFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS) g++ $(CPPFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS)
%.o: %.cpp %.o: %.cpp
g++ -c -g $< -o $@ $(LDFLAGS) g++ -c -g $< -o $@ $(LDFLAGS)
%.spv: %.vert %.frag
glslc $< -o $@
.PHONY: clean .PHONY: clean
clean: clean:

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "global.h" #include "global.h"
namespace DeviceControl { namespace DeviceControl {
class devicelibrary { class devicelibrary {
public: public:
void pickPhysicalDevice(VkInstance& instance); void pickPhysicalDevice(VkInstance& instance);

View File

@ -0,0 +1,145 @@
#include "graphicspipeline.h"
#include <fstream>
namespace Graphics {
std::vector<VkDynamicState> dynamicStates = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineLayout pipelineLayout;
static std::vector<char> readFile(const std::string& filename) {
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error("failed to open file!");
}
size_t fileSize = (size_t) file.tellg();
std::vector<char> buffer(fileSize);
file.seekg(0);
file.read(buffer.data(), fileSize);
file.close();
return buffer;
}
VkShaderModule createShaderModule(const std::vector<char>& code, VkDevice& device) {
VkShaderModuleCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.codeSize = code.size();
createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
VkShaderModule shaderModule;
if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
throw std::runtime_error("failed to create shader module!");
}
return shaderModule;
}
void graphicspipeline::destroyGraphicsPipeline(VkDevice& device) {
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
if(Global::enableValidationLayers) std::cout << "Destroyed Layout Pipeline safely\n" << std::endl;
}
void graphicspipeline::createGraphicsPipeline(VkDevice& device) {
// 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");
VkShaderModule vertShaderModule = createShaderModule(vertShaderCode, device);
VkShaderModule fragShaderModule = createShaderModule(fragShaderCode, device);
// ------------------ STAGE 1 - INPUT ASSEMBLER ---------------- //
// This can get a little complicated, normally, vertices are loaded in sequential order, with an element buffer however, you can specify the indices yourself!
// Using an element buffer means you can reuse vertices, which can lead to optimizations. If you set PrimRestart to TRUE, you can utilize the _STRIP modes with special indices
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
// ------------------ STAGE 2 - VERTEX SHADER ------------------ //
// this will be revisited, right now we are hardcoding shader data, so we tell it to not load anything, but that will change.
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
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;
// ------------------- 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.
// In addition, it can also be configured for wireframe rendering.
VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
// Render regardless of the near and far planes, useful for shadow maps, requires GPU feature *depthClamp*
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;
// 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;
// Whether or not to add depth values. e.x. for shadow maps.
rasterizer.depthBiasEnable = VK_FALSE;
// ------------------ STAGE 6 - FRAGMENT SHADER ---------------- //
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
// ------------------ STAGE 7 - COLOR BLENDING ----------------- //
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
// ---------------------- STATE CONTROLS ---------------------- //
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
// Again, this will be revisited, multisampling can be very useful for anti-aliasing, since it is fast, but we won't implement it for now.
// Requires GPU feature UNKNOWN eanbled.
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
// Most of the graphics pipeline is set in stone, some of the pipeline state can be modified without recreating it at runtime though!
// There are TONS of settings, this would be another TODO to see what else we can mess with dynamically, but right now we just allow dynamic size of the viewport
// and dynamic scissor states. Scissors are pretty straightforward, they are basically pixel masks for the rasterizer.
// Scissors describe what regions pixels will be stored, it doesnt cut them after being rendered, it stops them from ever being rendered in that area in the first place.
VkPipelineDynamicStateCreateInfo dynamicState{};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
dynamicState.pDynamicStates = dynamicStates.data();
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0;
pipelineLayoutInfo.pushConstantRangeCount = 0;
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("failed to create pipeline layout!");
}
vkDestroyShaderModule(device, fragShaderModule, nullptr);
vkDestroyShaderModule(device, vertShaderModule, nullptr);
if(Global::enableValidationLayers) std::cout << "Pipeline Layout created successfully\n" << std::endl;
}
}

View File

@ -0,0 +1,10 @@
#pragma once
#include "../global.h"
namespace Graphics {
class graphicspipeline {
public:
void createGraphicsPipeline(VkDevice& device);
void destroyGraphicsPipeline(VkDevice& device);
};
}

View File

@ -1,5 +1,6 @@
#include "devicelibrary.h" // Device Library includes global, redundant to include with it here #include "devicelibrary.h" // Device Library includes global, redundant to include with it here
#include "debug/vulkandebuglibs.h" #include "debug/vulkandebuglibs.h"
#include "graphics/graphicspipeline.h"
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
@ -23,7 +24,7 @@ public:
private: private:
DeviceControl::devicelibrary deviceLibs; DeviceControl::devicelibrary deviceLibs;
Debug::vulkandebuglibs debugController; Debug::vulkandebuglibs debugController;
Graphics::graphicspipeline graphicsPipeline;
GLFWwindow* window; GLFWwindow* window;
VkInstance instance; VkInstance instance;
VkDevice device; VkDevice device;
@ -47,6 +48,7 @@ private:
deviceLibs.createLogicalDevice(device); deviceLibs.createLogicalDevice(device);
deviceLibs.createSwapChain(window, device); deviceLibs.createSwapChain(window, device);
deviceLibs.createImageViews(device); deviceLibs.createImageViews(device);
graphicsPipeline.createGraphicsPipeline(device);
} }
void createInstance() { void createInstance() {
@ -76,6 +78,7 @@ private:
} }
void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library! void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library!
graphicsPipeline.destroyGraphicsPipeline(device);
deviceLibs.destroyImageViews(device); deviceLibs.destroyImageViews(device);
deviceLibs.destroySwapChain(device); deviceLibs.destroySwapChain(device);
vkDestroyDevice(device, nullptr); vkDestroyDevice(device, nullptr);

BIN
src/shaders/frag.spv Normal file

Binary file not shown.

9
src/shaders/shader.frag Normal file
View File

@ -0,0 +1,9 @@
#version 450
layout(location = 0) in vec3 fragColor;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
}

20
src/shaders/shader.vert Normal file
View File

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

BIN
src/shaders/vert.spv Normal file

Binary file not shown.