Multiple Object renderinggit add .git add . Completely revamped the model loading system using Model class instances, uses a Material class to build textures, clean slated descriptor sets for bindless rendering, significantly shortening the amount of code needed to load VkImage, VkImageView, and VkSampler. Added multiple texture rendering, abstracted out model loading process to automatically collect all Model instances and render for each object.

This commit is contained in:
Lillian Salehi 2024-12-03 01:57:55 -06:00
parent e2ef2e4134
commit d862068c6e
34 changed files with 662 additions and 615 deletions

Binary file not shown.

View File

@ -4,7 +4,7 @@ LDFLAGS=-lglfw -Ilib -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi -ltinyobjloade
MAKEFLAGS += -j16
SRC = $(shell find . -name "*.cpp")
CSRC = $(shell find . -name "*.c")
SHDRSRC = $(shell find . -name "*.frag" -o -name "*vert")
SHDRSRC = $(shell find . -name "*.frag" -o -name "*.vert")
SPV = $(SHDRSRC:%.vert=%.spv) $(SHDRSRC:%.frag=%.spv)
OBJ = $(SRC:%.cpp=%.o)
COBJ=$(CSRC:%.c=%.o)
@ -48,6 +48,8 @@ $(BIN): $(OBJ) $(COBJ) $(SPV)
glslc $< -o $@
%.spv: %.vert
glslc $< -o $@
%.spv: %.glsl
glslc $< -o $@
.PHONY: clean
clean:

View File

@ -3,6 +3,6 @@ Pos=60,60
Size=400,400
[Window][Agnosia Debug]
Pos=522,0
Size=583,202
Pos=40,377
Size=623,438

View File

@ -2,23 +2,32 @@
#include "devicelibrary.h"
#include "entrypoint.h"
#include "graphics/buffers.h"
#include "graphics/graphicspipeline.h"
#include "graphics/texture.h"
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_vulkan.h"
#include <glm/gtc/type_ptr.hpp>
#include <stdexcept>
VkDescriptorPool imGuiDescriptorPool;
void initImGuiWindow() {
if (ImGui::TreeNode("Model Transforms")) {
for (Model *model : Model::getInstances()) {
ImGui::DragFloat3("Object Position", Buffers::getObjPos());
ImGui::DragFloat3("Camera Position", Buffers::getCamPos());
ImGui::DragFloat3("Center Position", Buffers::getCenterPos());
ImGui::DragFloat3("Up Direction", Buffers::getUpDir());
ImGui::DragFloat("Depth of Field", &Buffers::getDepthField(), 0.1f, 1.0f,
ImGui::DragFloat3(model->getID().c_str(),
const_cast<float *>(glm::value_ptr(model->getPos())));
}
ImGui::TreePop();
}
ImGui::DragFloat3("Camera Position", Graphics::getCamPos());
ImGui::DragFloat3("Center Position", Graphics::getCenterPos());
ImGui::DragFloat3("Up Direction", Graphics::getUpDir());
ImGui::DragFloat("Depth of Field", &Graphics::getDepthField(), 0.1f, 1.0f,
180.0f, NULL, ImGuiSliderFlags_AlwaysClamp);
ImGui::DragFloat2("Near and Far fields", Buffers::getDistanceField());
ImGui::DragFloat2("Near and Far fields", Graphics::getDistanceField());
}
void drawTabs() {
@ -37,8 +46,6 @@ void Gui::drawImGui() {
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// 2. Show a simple window that we create ourselves. We use a Begin/End pair
// to create a named window.
ImGui::Begin("Agnosia Debug");

View File

@ -4,7 +4,6 @@
#include <set>
#include <stdexcept>
#include <string>
#include <vulkan/vulkan_core.h>
VkPhysicalDeviceProperties deviceProperties;
VkDevice device;
@ -303,18 +302,31 @@ void DeviceControl::createLogicalDevice() {
VkPhysicalDeviceVulkan12Features features12{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = nullptr,
.shaderSampledImageArrayNonUniformIndexing = true,
.shaderStorageBufferArrayNonUniformIndexing = true,
.shaderStorageImageArrayNonUniformIndexing = true,
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingStorageBufferUpdateAfterBind = true,
.descriptorBindingUpdateUnusedWhilePending = true,
.descriptorBindingPartiallyBound = true,
.runtimeDescriptorArray = true,
.scalarBlockLayout = true,
.bufferDeviceAddress = true,
};
VkPhysicalDeviceVulkan13Features features13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.pNext = &features12,
.synchronization2 = true,
.dynamicRendering = true,
};
VkPhysicalDeviceFeatures featuresBase{
.robustBufferAccess = true,
.sampleRateShading = true,
.samplerAnisotropy = true,
};
VkPhysicalDeviceFeatures2 deviceFeatures{

View File

@ -3,6 +3,7 @@
#include "entrypoint.h"
#include "graphics/buffers.h"
#include "graphics/graphicspipeline.h"
#include "graphics/model.h"
#include "graphics/render.h"
#include "graphics/texture.h"
@ -88,6 +89,22 @@ void createInstance() {
}
void initVulkan() {
Material *vikingRoomMaterial =
new Material("vikingRoomMaterial", "assets/textures/viking_room.png");
Material *stanfordDragonMaterial =
new Material("stanfordDragonMaterial", "assets/textures/checkermap.png");
Material *teapotMaterial =
new Material("teapotMaterial", "assets/textures/checkermap.png");
Model *vikingRoom =
new Model("vikingRoom", *vikingRoomMaterial,
"assets/models/viking_room.obj", glm::vec3(0.0f, 0.0f, 0.0f));
Model *stanfordDragon = new Model("stanfordDragon", *stanfordDragonMaterial,
"assets/models/StanfordDragon800k.obj",
glm::vec3(0.0f, 2.0f, 0.0f));
Model *teapot =
new Model("teapot", *teapotMaterial, "assets/models/teapot.obj",
glm::vec3(1.0f, -3.0f, -1.0f));
// Initialize volk and continue if successful.
volkInitialize();
// Initialize vulkan and set up pipeline.
@ -98,20 +115,22 @@ void initVulkan() {
DeviceControl::createLogicalDevice();
volkLoadDevice(DeviceControl::getDevice());
DeviceControl::createSwapChain(window);
Buffers::createMemoryAllocator(vulkaninstance);
Model::createMemoryAllocator(vulkaninstance);
DeviceControl::createImageViews();
Buffers::createDescriptorSetLayout();
Graphics::createGraphicsPipeline();
Graphics::createCommandPool();
// Image creation MUST be after command pool, because command
// buffers.
vikingRoom->populateData();
stanfordDragon->populateData();
teapot->populateData();
Texture::createMaterialTextures(Model::getInstances());
Texture::createColorResources();
Texture::createDepthResources();
Texture::createTextureImage();
Texture::createTextureImageView();
Texture::createTextureSampler();
Model::loadModel(glm::vec3(0.0, 0.0, 0.0));
Buffers::createUniformBuffers();
Buffers::createDescriptorPool();
Buffers::createDescriptorSets();
Buffers::createDescriptorSet(Model::getInstances());
Graphics::createCommandBuffer();
Render::createSyncObject();
@ -131,12 +150,7 @@ void mainLoop() {
void cleanup() {
Render::cleanupSwapChain();
Graphics::destroyGraphicsPipeline();
Buffers::destroyUniformBuffer();
Buffers::destroyDescriptorPool();
Texture::destroyTextureSampler();
Texture::destroyTextureImage();
vkDestroyDescriptorSetLayout(DeviceControl::getDevice(),
Buffers::getDescriptorSetLayout(), nullptr);
Buffers::destroyBuffers();
Render::destroyFenceSemaphores();
Graphics::destroyCommandPool();
@ -163,6 +177,7 @@ void EntryApp::initialize() { initialized = true; }
bool EntryApp::isInitialized() const { return initialized; }
GLFWwindow *EntryApp::getWindow() { return window; }
void EntryApp::run() {
initWindow();
initVulkan();
mainLoop();

View File

@ -1,58 +1,165 @@
#include "../devicelibrary.h"
#include "../types.h"
#include "buffers.h"
#include "texture.h"
#include <chrono>
#include "buffers.h"
#include "model.h"
#include <cstdint>
#include <cstring>
#include <functional>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <stdexcept>
#include <vulkan/vulkan_core.h>
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#define VMA_IMPLEMENTATION
#include "vk_mem_alloc.h"
std::vector<VkDescriptorSetLayout> modelSetLayouts;
VkBuffer vertexBuffer;
VkDeviceMemory vertexBufferMemory;
VkBuffer indexBuffer;
VkDeviceMemory indexBufferMemory;
std::vector<Agnosia_T::Vertex> vertices;
// Index buffer definition, showing which points to reuse.
std::vector<uint32_t> indices;
uint32_t indicesSize;
VmaAllocator _allocator;
Agnosia_T::GPUPushConstants pushConstants;
// Select a binding for each descriptor type
constexpr int STORAGE_BINDING = 0;
constexpr int SAMPLER_BINDING = 1;
constexpr int IMAGE_BINDING = 2;
// Max count of each descriptor type
// You can query the max values for these with
// physicalDevice.getProperties().limits.maxDescriptrorSet*******
constexpr int STORAGE_COUNT = 65536;
constexpr int SAMPLER_COUNT = 65536;
constexpr int IMAGE_COUNT = 65536;
// Create descriptor pool
VkDescriptorPool descriptorPool;
VkDescriptorSetLayout descriptorSetLayout;
std::vector<VkDescriptorSet> descriptorSets;
VkDescriptorSet descriptorSet;
VkCommandPool commandPool;
std::vector<VkCommandBuffer> commandBuffers;
std::vector<VkBuffer> uniformBuffers;
std::vector<VkDeviceMemory> uniformBuffersMemory;
std::vector<void *> uniformBuffersMapped;
float objPos[4] = {0.0f, 0.0f, 0.0f, 0.44f};
float camPos[4] = {2.0f, 2.0f, 2.0f, 0.44f};
float centerPos[4] = {0.0f, 0.0f, 0.0f, 0.44f};
float upDir[4] = {0.0f, 0.0f, 1.0f, 0.44f};
float depthField = 45.0f;
float distanceField[2] = {0.1f, 100.0f};
const int MAX_FRAMES_IN_FLIGHT = 2;
struct UniformBufferObject {
float time;
alignas(16) glm::mat4 model;
alignas(16) glm::mat4 view;
alignas(16) glm::mat4 proj;
};
void Buffers::createDescriptorSetLayout() {
// Create a table of pointers to data, a Descriptor Set!
VkDescriptorSetLayoutBinding storageLayoutBinding = {
.binding = STORAGE_BINDING,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = STORAGE_COUNT,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = nullptr,
};
VkDescriptorSetLayoutBinding samplerLayoutBinding = {
.binding = SAMPLER_BINDING,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = SAMPLER_COUNT,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = nullptr,
};
VkDescriptorSetLayoutBinding imageLayoutBinding = {
.binding = IMAGE_BINDING,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.descriptorCount = IMAGE_COUNT,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = nullptr,
};
std::vector<VkDescriptorSetLayoutBinding> bindings = {
storageLayoutBinding, imageLayoutBinding, samplerLayoutBinding};
std::vector<VkDescriptorBindingFlags> bindingFlags = {
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT |
VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT |
VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT |
VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT,
};
VkDescriptorSetLayoutBindingFlagsCreateInfo setLayoutBindingsFlags = {
.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.bindingCount = 3,
.pBindingFlags = bindingFlags.data(),
};
VkDescriptorSetLayoutCreateInfo layoutInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = &setLayoutBindingsFlags,
.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
.bindingCount = static_cast<uint32_t>(bindings.size()),
.pBindings = bindings.data(),
};
if (vkCreateDescriptorSetLayout(DeviceControl::getDevice(), &layoutInfo,
nullptr,
&descriptorSetLayout) != VK_SUCCESS) {
throw std::runtime_error("Failed to create descriptor set layout!");
}
}
void Buffers::createDescriptorPool() {
std::vector<VkDescriptorPoolSize> poolSizes = {
{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, STORAGE_COUNT},
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, SAMPLER_COUNT},
{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, IMAGE_COUNT},
};
VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
poolInfo.pPoolSizes = poolSizes.data();
poolInfo.maxSets = 1;
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT;
if (vkCreateDescriptorPool(DeviceControl::getDevice(), &poolInfo, nullptr,
&descriptorPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor pool!");
}
}
void Buffers::createDescriptorSet(std::vector<Model *> models) {
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &descriptorSetLayout;
if (vkAllocateDescriptorSets(DeviceControl::getDevice(), &allocInfo,
&descriptorSet) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate descriptor sets!");
}
std::vector<VkDescriptorImageInfo> imageInfoSet;
imageInfoSet.resize(models.size());
for (int i = 0; i < models.size(); i++) {
imageInfoSet[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfoSet[i].imageView = models[i]->getMaterial().getTextureView();
imageInfoSet[i].sampler = models[i]->getMaterial().getTextureSampler();
}
std::vector<VkWriteDescriptorSet> descriptorWrites{};
descriptorWrites.resize(models.size());
for (int i = 0; i < models.size(); i++) {
descriptorWrites[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[i].dstSet = descriptorSet;
descriptorWrites[i].dstBinding = SAMPLER_BINDING;
descriptorWrites[i].dstArrayElement = i;
descriptorWrites[i].descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrites[i].descriptorCount = 1;
descriptorWrites[i].pImageInfo = &imageInfoSet[i];
}
vkUpdateDescriptorSets(DeviceControl::getDevice(),
static_cast<uint32_t>(descriptorWrites.size()),
descriptorWrites.data(), 0, nullptr);
}
uint32_t Buffers::findMemoryType(uint32_t typeFilter,
VkMemoryPropertyFlags properties) {
@ -73,40 +180,6 @@ uint32_t Buffers::findMemoryType(uint32_t typeFilter,
throw std::runtime_error("failed to find suitable memory type!");
}
void immediate_submit(std::function<void(VkCommandBuffer cmd)> &&function) {
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = commandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(DeviceControl::getDevice(), &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);
function(commandBuffer);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(DeviceControl::getGraphicsQueue(), 1, &submitInfo,
VK_NULL_HANDLE);
vkQueueWaitIdle(DeviceControl::getGraphicsQueue());
vkFreeCommandBuffers(DeviceControl::getDevice(), commandPool, 1,
&commandBuffer);
}
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
@ -142,101 +215,6 @@ void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
vkFreeCommandBuffers(DeviceControl::getDevice(), commandPool, 1,
&commandBuffer);
}
void Buffers::createMemoryAllocator(VkInstance vkInstance) {
VmaVulkanFunctions vulkanFuncs{
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
};
VmaAllocatorCreateInfo allocInfo{
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
.physicalDevice = DeviceControl::getPhysicalDevice(),
.device = DeviceControl::getDevice(),
.pVulkanFunctions = &vulkanFuncs,
.instance = vkInstance,
.vulkanApiVersion = VK_API_VERSION_1_3,
};
vmaCreateAllocator(&allocInfo, &_allocator);
}
Agnosia_T::AllocatedBuffer Buffers::createBuffer(size_t allocSize,
VkBufferUsageFlags usage,
VmaMemoryUsage memUsage) {
// Allocate the buffer we will use for Device Addresses
VkBufferCreateInfo bufferInfo{.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.size = allocSize,
.usage = usage};
VmaAllocationCreateInfo vmaAllocInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, .usage = memUsage};
Agnosia_T::AllocatedBuffer newBuffer;
if (vmaCreateBuffer(_allocator, &bufferInfo, &vmaAllocInfo, &newBuffer.buffer,
&newBuffer.allocation, &newBuffer.info) != VK_SUCCESS) {
throw std::runtime_error("Failed to allocate a buffer using VMA!");
}
return newBuffer;
}
Agnosia_T::GPUMeshBuffers
Buffers::sendMesh(std::span<uint32_t> indices,
std::span<Agnosia_T::Vertex> vertices) {
const size_t vertexBufferSize = vertices.size() * sizeof(Agnosia_T::Vertex);
const size_t indexBufferSize = indices.size() * sizeof(uint32_t);
Agnosia_T::GPUMeshBuffers newSurface;
// Create a Vertex Buffer here, infinitely easier than the old Vulkan method!
newSurface.vertexBuffer = createBuffer(
vertexBufferSize,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VMA_MEMORY_USAGE_GPU_ONLY);
// Find the address of the vertex buffer!
VkBufferDeviceAddressInfo deviceAddressInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = newSurface.vertexBuffer.buffer,
};
newSurface.vertexBufferAddress =
vkGetBufferDeviceAddress(DeviceControl::getDevice(), &deviceAddressInfo);
// Create the index buffer to iterate over and check for duplicate vertices
newSurface.indexBuffer = createBuffer(indexBufferSize,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VMA_MEMORY_USAGE_GPU_ONLY);
Agnosia_T::AllocatedBuffer stagingBuffer =
createBuffer(vertexBufferSize + indexBufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
void *data = stagingBuffer.allocation->GetMappedData();
// Copy the vertex buffer
memcpy(data, vertices.data(), vertexBufferSize);
// Copy the index buffer
memcpy((char *)data + vertexBufferSize, indices.data(), indexBufferSize);
immediate_submit([&](VkCommandBuffer cmd) {
VkBufferCopy vertexCopy{0};
vertexCopy.dstOffset = 0;
vertexCopy.srcOffset = 0;
vertexCopy.size = vertexBufferSize;
vkCmdCopyBuffer(cmd, stagingBuffer.buffer, newSurface.vertexBuffer.buffer,
1, &vertexCopy);
VkBufferCopy indexCopy{0};
indexCopy.dstOffset = 0;
indexCopy.srcOffset = vertexBufferSize;
indexCopy.size = indexBufferSize;
vkCmdCopyBuffer(cmd, stagingBuffer.buffer, newSurface.indexBuffer.buffer, 1,
&indexCopy);
});
vmaDestroyBuffer(_allocator, stagingBuffer.buffer, stagingBuffer.allocation);
return newSurface;
}
void Buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
VkMemoryPropertyFlags properties, VkBuffer &buffer,
@ -269,6 +247,11 @@ void Buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
vkBindBufferMemory(DeviceControl::getDevice(), buffer, bufferMemory, 0);
}
VkDescriptorPool &Buffers::getDescriptorPool() { return descriptorPool; }
VkDescriptorSet &Buffers::getDescriptorSet() { return descriptorSet; }
VkDescriptorSetLayout &Buffers::getDescriptorSetLayout() {
return descriptorSetLayout;
}
void Buffers::destroyBuffers() {
vkDestroyBuffer(DeviceControl::getDevice(), indexBuffer, nullptr);
@ -278,202 +261,10 @@ void Buffers::destroyBuffers() {
vkFreeMemory(DeviceControl::getDevice(), vertexBufferMemory, nullptr);
}
// ------------------------------ Uniform Buffer Setup
// -------------------------------- //
void Buffers::createDescriptorSetLayout() {
// Create a table of pointers to data, a Descriptor Set!
// --------------------- UBO Layout --------------------- //
VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
// Model-View-Projection matrix is in a single uniform buffer, so just 1
// descriptor.
uboLayoutBinding.descriptorCount = 1;
// We are only using this buffer in the vertex shader, so set the flags thus.
uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
// Immutable Samplers is relevant for image sampling.
uboLayoutBinding.pImmutableSamplers = nullptr;
// --------------- Texture Sampler Layout --------------- //
VkDescriptorSetLayoutBinding samplerLayoutBinding{};
samplerLayoutBinding.binding = 1;
samplerLayoutBinding.descriptorCount = 1;
samplerLayoutBinding.descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
samplerLayoutBinding.pImmutableSamplers = nullptr;
samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
std::array<VkDescriptorSetLayoutBinding, 2> bindings = {uboLayoutBinding,
samplerLayoutBinding};
VkDescriptorSetLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = bindings.data();
if (vkCreateDescriptorSetLayout(DeviceControl::getDevice(), &layoutInfo,
nullptr,
&descriptorSetLayout) != VK_SUCCESS) {
throw std::runtime_error("Failed to create descriptor set layout!");
}
}
void Buffers::createUniformBuffers() {
// Map the uniform buffer to memory as a pointer we can use to write data to
// later. This stays mapped to memory for the applications lifetime. This
// technique is called "persistent mapping", not having to map the buffer
// every time we need to update it increases performance, though not free
VkDeviceSize bufferSize = sizeof(UniformBufferObject);
uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT);
uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
uniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
uniformBuffers[i], uniformBuffersMemory[i]);
vkMapMemory(DeviceControl::getDevice(), uniformBuffersMemory[i], 0,
bufferSize, 0, &uniformBuffersMapped[i]);
}
}
void Buffers::updateUniformBuffer(uint32_t currentImage) {
// Update the uniform buffer every frame to change the position, but notably,
// use chrono to know exactly how much to move so we aren't locked to the
// framerate as the world time.
static auto startTime = std::chrono::high_resolution_clock::now();
// Calculate the time in seconds since rendering has began to floating point
// precision.
auto currentTime = std::chrono::high_resolution_clock::now();
float time = std::chrono::duration<float, std::chrono::seconds::period>(
currentTime - startTime)
.count();
UniformBufferObject ubo{};
ubo.time = time;
// Modify the model projection transformation to rotate around the Z over
// time.
ubo.model = glm::translate(glm::mat4(1.0f),
glm::vec3(objPos[0], objPos[1], objPos[2]));
// ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(30.0f),
// glm::vec3(0.0f, 0.0f, 1.0f));
// Modify the view transformation to look at the object from above at a 45
// degree angle. This takes the eye position, center position, and the up
// direction.
ubo.view = glm::lookAt(glm::vec3(camPos[0], camPos[1], camPos[2]),
glm::vec3(centerPos[0], centerPos[1], centerPos[2]),
glm::vec3(upDir[0], upDir[1], upDir[2]));
// 45 degree field of view, set aspect ratio, and near and far clipping range.
ubo.proj =
glm::perspective(glm::radians(depthField),
DeviceControl::getSwapChainExtent().width /
(float)DeviceControl::getSwapChainExtent().height,
distanceField[0], distanceField[1]);
// GLM was created for OpenGL, where the Y coordinate was inverted. This
// simply flips the sign.
ubo.proj[1][1] *= -1;
memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo));
}
void Buffers::destroyUniformBuffer() {
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroyBuffer(DeviceControl::getDevice(), uniformBuffers[i], nullptr);
vkFreeMemory(DeviceControl::getDevice(), uniformBuffersMemory[i], nullptr);
}
}
void Buffers::createDescriptorPool() {
// Create a pool to be used to allocate descriptor sets.
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 poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
poolInfo.pPoolSizes = poolSizes.data();
poolInfo.maxSets = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
if (vkCreateDescriptorPool(DeviceControl::getDevice(), &poolInfo, nullptr,
&descriptorPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor pool!");
}
}
void Buffers::createDescriptorSets() {
std::vector<VkDescriptorSetLayout> layouts(MAX_FRAMES_IN_FLIGHT,
descriptorSetLayout);
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
allocInfo.descriptorPool = descriptorPool;
allocInfo.descriptorSetCount = static_cast<uint32_t>(MAX_FRAMES_IN_FLIGHT);
allocInfo.pSetLayouts = layouts.data();
descriptorSets.resize(MAX_FRAMES_IN_FLIGHT);
if (vkAllocateDescriptorSets(DeviceControl::getDevice(), &allocInfo,
descriptorSets.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate descriptor sets!");
}
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = uniformBuffers[i];
bufferInfo.offset = 0;
bufferInfo.range = sizeof(UniformBufferObject);
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = Texture::getTextureImageView();
imageInfo.sampler = Texture::getTextureSampler();
std::array<VkWriteDescriptorSet, 2> descriptorWrites{};
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = descriptorSets[i];
descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrites[0].descriptorCount = 1;
descriptorWrites[0].pBufferInfo = &bufferInfo;
descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = descriptorSets[i];
descriptorWrites[1].dstBinding = 1;
descriptorWrites[1].dstArrayElement = 0;
descriptorWrites[1].descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrites[1].descriptorCount = 1;
descriptorWrites[1].pImageInfo = &imageInfo;
vkUpdateDescriptorSets(DeviceControl::getDevice(),
static_cast<uint32_t>(descriptorWrites.size()),
descriptorWrites.data(), 0, nullptr);
}
}
void Buffers::destroyDescriptorPool() {
vkDestroyDescriptorPool(DeviceControl::getDevice(), descriptorPool, nullptr);
}
VkDescriptorPool &Buffers::getDescriptorPool() { return descriptorPool; }
std::vector<VkDescriptorSet> &Buffers::getDescriptorSets() {
return descriptorSets;
}
float *Buffers::getObjPos() { return objPos; }
float *Buffers::getCamPos() { return camPos; }
float *Buffers::getCenterPos() { return centerPos; }
float *Buffers::getUpDir() { return upDir; }
float &Buffers::getDepthField() { return depthField; }
float *Buffers::getDistanceField() { return distanceField; }
uint32_t Buffers::getMaxFramesInFlight() { return MAX_FRAMES_IN_FLIGHT; }
std::vector<VkCommandBuffer> &Buffers::getCommandBuffers() {
return commandBuffers;
}
std::vector<VkBuffer> &Buffers::getUniformBuffers() { return uniformBuffers; }
std::vector<VkDeviceMemory> &Buffers::getUniformBuffersMemory() {
return uniformBuffersMemory;
}
VkCommandPool &Buffers::getCommandPool() { return commandPool; }
VkDescriptorSetLayout &Buffers::getDescriptorSetLayout() {
return descriptorSetLayout;
}
std::vector<Agnosia_T::Vertex> &Buffers::getVertices() { return vertices; }
std::vector<uint32_t> &Buffers::getIndices() { return indices; }
uint32_t Buffers::getIndicesSize() { return indicesSize; }

View File

@ -1,51 +1,37 @@
#pragma once
#include <cstdint>
#include <span>
#define VK_NO_PROTOTYPES
#include "../types.h"
#include "model.h"
#include "volk.h"
#include <vector>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
class Buffers {
public:
static Agnosia_T::AllocatedBuffer createBuffer(size_t allocSize,
VkBufferUsageFlags usage,
VmaMemoryUsage memUsage);
static void createMemoryAllocator(VkInstance vkInstance);
static Agnosia_T::GPUMeshBuffers
sendMesh(std::span<uint32_t> indices, std::span<Agnosia_T::Vertex> vertices);
static void createDescriptorSetLayout();
static void createDescriptorSet(std::vector<Model *> models);
static void createDescriptorPool();
static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
VkMemoryPropertyFlags props, VkBuffer &buffer,
VkDeviceMemory &bufferMemory);
static uint32_t findMemoryType(uint32_t typeFilter,
VkMemoryPropertyFlags flags);
static void destroyBuffers();
static void createDescriptorSetLayout();
static void createUniformBuffers();
static void updateUniformBuffer(uint32_t currentImage);
static void destroyUniformBuffer();
static void createDescriptorPool();
static void createDescriptorSets();
static void destroyDescriptorPool();
static VkDescriptorPool &getDescriptorPool();
static VkDescriptorSetLayout &getDescriptorSetLayout();
static std::vector<VkDescriptorSet> &getDescriptorSets();
static VkDescriptorSet &getDescriptorSet();
static float *getObjPos();
static float *getCamPos();
static float *getCenterPos();
static float *getUpDir();
static float &getDepthField();
static float *getDistanceField();
static VkDescriptorSetLayout &getDescriptorSetLayout();
static uint32_t getMaxFramesInFlight();
static std::vector<VkCommandBuffer> &getCommandBuffers();
static std::vector<VkBuffer> &getUniformBuffers();
static std::vector<VkDeviceMemory> &getUniformBuffersMemory();
static VkCommandPool &getCommandPool();
static std::vector<Agnosia_T::Vertex> &getVertices();
static std::vector<uint32_t> &getIndices();
static uint32_t getIndicesSize();
};

View File

@ -6,7 +6,18 @@
#include "render.h"
#include "texture.h"
#include <fstream>
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <iostream>
#include <vulkan/vulkan_core.h>
float camPos[4] = {3.0f, 3.0f, 3.0f, 0.44f};
float centerPos[4] = {0.0f, 0.0f, 0.0f, 0.44f};
float upDir[4] = {0.0f, 0.0f, 1.0f, 0.44f};
float depthField = 45.0f;
float distanceField[2] = {0.1f, 100.0f};
std::vector<VkDynamicState> dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR};
@ -84,15 +95,6 @@ void Graphics::createGraphicsPipeline() {
vertexInputInfo.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
auto bindingDescription = Agnosia_T::Vertex::getBindingDescription();
auto attributeDescriptions = Agnosia_T::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
@ -176,7 +178,7 @@ void Graphics::createGraphicsPipeline() {
dynamicState.pDynamicStates = dynamicStates.data();
VkPushConstantRange pushConstant{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.stageFlags = VK_SHADER_STAGE_ALL,
.offset = 0,
.size = sizeof(Agnosia_T::GPUPushConstants),
};
@ -351,26 +353,45 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer,
scissor.offset = {0, 0};
scissor.extent = DeviceControl::getSwapChainExtent();
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
int texID = 0;
for (Model *model : Model::getInstances()) {
Agnosia_T::GPUMeshBuffers Model =
Buffers::sendMesh(Buffers::getIndices(), Buffers::getVertices());
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout, 0, 1, &Buffers::getDescriptorSet(),
0, nullptr);
Agnosia_T::GPUPushConstants pushConsts;
pushConsts.vertexBuffer = Model.vertexBufferAddress;
Agnosia_T::GPUPushConstants pushConsts;
pushConsts.vertexBuffer = model->getBuffers().vertexBufferAddress;
pushConsts.objPosition = model->getPos();
pushConsts.textureID = texID;
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT,
0, sizeof(Agnosia_T::GPUPushConstants), &pushConsts);
vkCmdBindIndexBuffer(commandBuffer, Model.indexBuffer.buffer, 0,
VK_INDEX_TYPE_UINT32);
pushConsts.model =
glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
vkCmdBindDescriptorSets(
commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1,
&Buffers::getDescriptorSets()[Render::getCurrentFrame()], 0, nullptr);
pushConsts.view =
glm::lookAt(glm::vec3(camPos[0], camPos[1], camPos[2]),
glm::vec3(centerPos[0], centerPos[1], centerPos[2]),
glm::vec3(upDir[0], upDir[1], upDir[2]));
vkCmdDrawIndexed(commandBuffer,
static_cast<uint32_t>(Buffers::getIndices().size()), 1, 0, 0,
0);
pushConsts.proj =
glm::perspective(glm::radians(depthField),
DeviceControl::getSwapChainExtent().width /
(float)DeviceControl::getSwapChainExtent().height,
distanceField[0], distanceField[1]);
// GLM was created for OpenGL, where the Y coordinate was inverted. This
// simply flips the sign.
pushConsts.proj[1][1] *= -1;
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_ALL, 0,
sizeof(Agnosia_T::GPUPushConstants), &pushConsts);
vkCmdBindIndexBuffer(commandBuffer, model->getBuffers().indexBuffer.buffer,
0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(model->getIndices()),
1, 0, 0, 0);
texID++;
}
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer);
vkCmdEndRendering(commandBuffer);
@ -403,10 +424,16 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer,
.pImageMemoryBarriers = &prePresentImageBarrier,
};
vkCmdPipelineBarrier2(
Buffers ::getCommandBuffers()[Render::getCurrentFrame()], &depInfo);
vkCmdPipelineBarrier2(Buffers::getCommandBuffers()[Render::getCurrentFrame()],
&depInfo);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
throw std::runtime_error("failed to record command buffer!");
}
}
float *Graphics::getCamPos() { return camPos; }
float *Graphics::getCenterPos() { return centerPos; }
float *Graphics::getUpDir() { return upDir; }
float &Graphics::getDepthField() { return depthField; }
float *Graphics::getDistanceField() { return distanceField; }

View File

@ -13,4 +13,10 @@ public:
static void createCommandBuffer();
static void recordCommandBuffer(VkCommandBuffer cmndBuffer,
uint32_t imageIndex);
static float *getCamPos();
static float *getCenterPos();
static float *getUpDir();
static float &getDepthField();
static float *getDistanceField();
};

19
src/graphics/material.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "material.h"
Material::Material(const std::string &matID, const std::string &texPath)
: ID(matID), texturePath(texPath) {}
std::string Material::getID() const { return ID; }
std::string Material::getTexturePath() const { return texturePath; }
VkImage &Material::getTextureImage() { return this->textureImage; }
VkImageView &Material::getTextureView() { return this->textureImageView; }
VkSampler &Material::getTextureSampler() { return this->textureSampler; }
void Material::setTextureImage(VkImage image) { this->textureImage = image; }
void Material::setTextureView(VkImageView imageView) {
this->textureImageView = imageView;
}
void Material::setTextureSampler(VkSampler sampler) {
this->textureSampler = sampler;
}

26
src/graphics/material.h Normal file
View File

@ -0,0 +1,26 @@
#define VK_NO_PROTOTYPES
#include "volk.h"
#include <string>
class Material {
protected:
std::string ID;
std::string texturePath;
VkImage textureImage;
VkImageView textureImageView;
VkSampler textureSampler;
public:
Material(const std::string &matID, const std::string &texPath);
std::string getID() const;
std::string getTexturePath() const;
VkImage &getTextureImage();
VkImageView &getTextureView();
VkSampler &getTextureSampler();
void setTextureImage(VkImage image);
void setTextureView(VkImageView imageView);
void setTextureSampler(VkSampler sampler);
};

View File

@ -1,13 +1,25 @@
#include "buffers.h"
#include "model.h"
#include <stdexcept>
#include <cstdint>
#include <cstring>
#include <unordered_map>
#define TINY_OBJ_IMPLEMENTATION
#include <stdexcept>
#include <tiny_obj_loader.h>
#define GLM_ENABLE_EXPERIMENTAL
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include "../devicelibrary.h"
#include <glm/glm.hpp>
#include <glm/gtx/hash.hpp>
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#define VMA_IMPLEMENTATION
#include "vk_mem_alloc.h"
std::vector<Model *> Model::instances;
VmaAllocator _allocator;
namespace std {
template <> struct hash<Agnosia_T::Vertex> {
size_t operator()(Agnosia_T::Vertex const &vertex) const {
@ -18,15 +30,90 @@ template <> struct hash<Agnosia_T::Vertex> {
}
};
} // namespace std
void Model::createMemoryAllocator(VkInstance vkInstance) {
VmaVulkanFunctions vulkanFuncs{
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
};
VmaAllocatorCreateInfo allocInfo{
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
.physicalDevice = DeviceControl::getPhysicalDevice(),
.device = DeviceControl::getDevice(),
.pVulkanFunctions = &vulkanFuncs,
.instance = vkInstance,
.vulkanApiVersion = VK_API_VERSION_1_3,
const std::string MODEL_PATH = "assets/models/viking_room.obj";
};
vmaCreateAllocator(&allocInfo, &_allocator);
}
Agnosia_T::AllocatedBuffer createBuffer(size_t allocSize,
VkBufferUsageFlags usage,
VmaMemoryUsage memUsage) {
// Allocate the buffer we will use for Device Addresses
VkBufferCreateInfo bufferInfo{.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
.size = allocSize,
.usage = usage};
VmaAllocationCreateInfo vmaAllocInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, .usage = memUsage};
void Model::loadModel(glm::vec3 position) {
Agnosia_T::AllocatedBuffer newBuffer;
if (vmaCreateBuffer(_allocator, &bufferInfo, &vmaAllocInfo, &newBuffer.buffer,
&newBuffer.allocation, &newBuffer.info) != VK_SUCCESS) {
throw std::runtime_error("Failed to allocate a buffer using VMA!");
}
return newBuffer;
}
void immediate_submit(std::function<void(VkCommandBuffer cmd)> &&function) {
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = Buffers::getCommandPool();
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(DeviceControl::getDevice(), &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);
function(commandBuffer);
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(DeviceControl::getGraphicsQueue(), 1, &submitInfo,
VK_NULL_HANDLE);
vkQueueWaitIdle(DeviceControl::getGraphicsQueue());
vkFreeCommandBuffers(DeviceControl::getDevice(), Buffers::getCommandPool(), 1,
&commandBuffer);
}
Model::Model(const std::string &modelID, const Material &material,
const std::string &modelPath, const glm::vec3 &objPos)
: ID(modelID), material(material), objPosition(objPos),
modelPath(modelPath) {
instances.push_back(this);
}
void Model::populateData() {
std::vector<Agnosia_T::Vertex> vertices;
// Index buffer definition, showing which points to reuse.
std::vector<uint32_t> indices;
tinyobj::ObjReaderConfig readerConfig;
tinyobj::ObjReader reader;
if (!reader.ParseFromFile(MODEL_PATH, readerConfig)) {
if (!reader.ParseFromFile(modelPath, readerConfig)) {
if (!reader.Error().empty()) {
throw std::runtime_error(reader.Error());
}
@ -38,15 +125,16 @@ void Model::loadModel(glm::vec3 position) {
auto &attrib = reader.GetAttrib();
auto &shapes = reader.GetShapes();
auto &materials = reader.GetMaterials();
std::unordered_map<Agnosia_T::Vertex, uint32_t> uniqueVertices{};
for (const auto &shape : shapes) {
for (const auto &index : shape.mesh.indices) {
Agnosia_T::Vertex vertex{};
vertex.pos = {attrib.vertices[3 * index.vertex_index + 0] + position.x,
attrib.vertices[3 * index.vertex_index + 1] + position.y,
attrib.vertices[3 * index.vertex_index + 2] + position.z};
vertex.pos = {attrib.vertices[3 * index.vertex_index + 0],
attrib.vertices[3 * index.vertex_index + 1],
attrib.vertices[3 * index.vertex_index + 2]};
// TODO: Small fix here, handle if there are no UV's unwrapped for the
// model.
@ -58,11 +146,76 @@ void Model::loadModel(glm::vec3 position) {
vertex.color = {1.0f, 1.0f, 1.0f};
if (uniqueVertices.count(vertex) == 0) {
uniqueVertices[vertex] =
static_cast<uint32_t>(Buffers::getVertices().size());
Buffers::getVertices().push_back(vertex);
uniqueVertices[vertex] = static_cast<uint32_t>(vertices.size());
vertices.push_back(vertex);
}
Buffers::getIndices().push_back(uniqueVertices[vertex]);
indices.push_back(uniqueVertices[vertex]);
}
}
const size_t vertexBufferSize = vertices.size() * sizeof(Agnosia_T::Vertex);
const size_t indexBufferSize = indices.size() * sizeof(uint32_t);
Agnosia_T::GPUMeshBuffers newSurface;
// Create a Vertex Buffer here, infinitely easier than the old Vulkan method!
newSurface.vertexBuffer = createBuffer(
vertexBufferSize,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VMA_MEMORY_USAGE_GPU_ONLY);
// Find the address of the vertex buffer!
VkBufferDeviceAddressInfo deviceAddressInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = newSurface.vertexBuffer.buffer,
};
newSurface.vertexBufferAddress =
vkGetBufferDeviceAddress(DeviceControl::getDevice(), &deviceAddressInfo);
// Create the index buffer to iterate over and check for duplicate vertices
newSurface.indexBuffer = createBuffer(indexBufferSize,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VMA_MEMORY_USAGE_GPU_ONLY);
Agnosia_T::AllocatedBuffer stagingBuffer =
createBuffer(vertexBufferSize + indexBufferSize,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
void *data = stagingBuffer.allocation->GetMappedData();
// Copy the vertex buffer
memcpy(data, vertices.data(), vertexBufferSize);
// Copy the index buffer
memcpy((char *)data + vertexBufferSize, indices.data(), indexBufferSize);
immediate_submit([&](VkCommandBuffer cmd) {
VkBufferCopy vertexCopy{0};
vertexCopy.dstOffset = 0;
vertexCopy.srcOffset = 0;
vertexCopy.size = vertexBufferSize;
vkCmdCopyBuffer(cmd, stagingBuffer.buffer, newSurface.vertexBuffer.buffer,
1, &vertexCopy);
VkBufferCopy indexCopy{0};
indexCopy.dstOffset = 0;
indexCopy.srcOffset = vertexBufferSize;
indexCopy.size = indexBufferSize;
vkCmdCopyBuffer(cmd, stagingBuffer.buffer, newSurface.indexBuffer.buffer, 1,
&indexCopy);
});
vmaDestroyBuffer(_allocator, stagingBuffer.buffer, stagingBuffer.allocation);
this->buffers = newSurface;
this->objPosition = objPosition;
this->indiceCount = indices.size();
}
std::string Model::getID() { return this->ID; }
glm::vec3 &Model::getPos() { return this->objPosition; }
Material &Model::getMaterial() { return this->material; }
Agnosia_T::GPUMeshBuffers Model::getBuffers() { return this->buffers; }
uint32_t Model::getIndices() { return this->indiceCount; }
const std::vector<Model *> &Model::getInstances() { return instances; }

View File

@ -1,8 +1,39 @@
#pragma once
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <cstdint>
#include <vector>
#define VK_NO_PROTOTYPES
#include "../types.h"
#include "material.h"
#include "volk.h"
#include <glm/glm.hpp>
#include <string>
class Model {
protected:
std::string ID;
Agnosia_T::GPUMeshBuffers buffers;
Material material;
glm::vec3 objPosition;
uint32_t indiceCount;
std::string modelPath;
static std::vector<Model *> instances;
public:
static void loadModel(glm::vec3 position);
Model(const std::string &modelID, const Material &material,
const std::string &modelPath, const glm::vec3 &opjPos);
static void createMemoryAllocator(VkInstance instance);
static const std::vector<Model *> &getInstances();
void populateData();
Agnosia_T::GPUMeshBuffers getBuffers();
std::string getID();
glm::vec3 &getPos();
Material &getMaterial();
std::string getModelPath();
uint32_t getIndices();
};

View File

@ -59,16 +59,12 @@ void Render::drawFrame() {
throw std::runtime_error("failed to acquire swap chain image!");
}
Buffers::updateUniformBuffer(currentFrame);
vkResetFences(DeviceControl::getDevice(), 1, &inFlightFences[currentFrame]);
vkResetCommandBuffer(Buffers::getCommandBuffers()[currentFrame],
/*VkCommandBufferResetFlagBits*/ 0);
Graphics::recordCommandBuffer(Buffers::getCommandBuffers()[currentFrame],
imageIndex);
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(),
Buffers::getCommandBuffers()[currentFrame]);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;

View File

@ -1,6 +1,7 @@
#include "../devicelibrary.h"
#include "buffers.h"
#include "texture.h"
#include <iostream>
#include <stdexcept>
#include <string>
#define STB_IMAGE_IMPLEMENTATION
@ -8,14 +9,10 @@
uint32_t mipLevels;
VkImage textureImage;
VkDeviceMemory textureImageMemory;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
VkImageView textureImageView;
VkSampler textureSampler;
VkImage colorImage;
VkImageView colorImageView;
VkDeviceMemory colorImageMemory;
@ -24,8 +21,6 @@ VkImage depthImage;
VkImageView depthImageView;
VkDeviceMemory depthImageMemory;
std::string TEXTURE_PATH = "assets/textures/viking_room.png";
void createImage(uint32_t width, uint32_t height, uint32_t mipLevels,
VkSampleCountFlagBits sampleNum, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage,
@ -290,118 +285,119 @@ void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t textureWidth,
}
// -------------------------------- Image Libraries
// ------------------------------- //
void Texture::createTextureImage() {
void Texture::createMaterialTextures(std::vector<Model *> models) {
// Import pixels from image with data on color channels, width and height, and
// colorspace! Its a lot of kind of complicated memory calls to bring it from
// a file -> to a buffer -> to a image object.
int textureWidth, textureHeight, textureChannels;
stbi_uc *pixels = stbi_load(TEXTURE_PATH.c_str(), &textureWidth,
&textureHeight, &textureChannels, STBI_rgb_alpha);
mipLevels = static_cast<uint32_t>(std::floor(
std::log2(std::max(textureWidth, textureHeight)))) +
1;
for (Model *model : models) {
VkDeviceSize imageSize = textureWidth * textureHeight * 4;
int textureWidth, textureHeight, textureChannels;
if (!pixels) {
throw std::runtime_error("Failed to load texture!");
}
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
Buffers::createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer, stagingBufferMemory);
stbi_uc *pixels =
stbi_load(model->getMaterial().getTexturePath().c_str(), &textureWidth,
&textureHeight, &textureChannels, STBI_rgb_alpha);
void *data;
vkMapMemory(DeviceControl::getDevice(), stagingBufferMemory, 0, imageSize, 0,
&data);
memcpy(data, pixels, static_cast<size_t>(imageSize));
vkUnmapMemory(DeviceControl::getDevice(), stagingBufferMemory);
mipLevels = static_cast<uint32_t>(std::floor(
std::log2(std::max(textureWidth, textureHeight)))) +
1;
stbi_image_free(pixels);
VkDeviceSize imageSize = textureWidth * textureHeight * 4;
createImage(textureWidth, textureHeight, mipLevels, VK_SAMPLE_COUNT_1_BIT,
VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage,
textureImageMemory);
if (!pixels) {
throw std::runtime_error("Failed to load texture!");
}
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
Buffers::createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
stagingBuffer, stagingBufferMemory);
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels);
copyBufferToImage(stagingBuffer, textureImage,
static_cast<uint32_t>(textureWidth),
static_cast<uint32_t>(textureHeight));
void *data;
vkMapMemory(DeviceControl::getDevice(), stagingBufferMemory, 0, imageSize,
0, &data);
memcpy(data, pixels, static_cast<size_t>(imageSize));
vkUnmapMemory(DeviceControl::getDevice(), stagingBufferMemory);
vkDestroyBuffer(DeviceControl::getDevice(), stagingBuffer, nullptr);
vkFreeMemory(DeviceControl::getDevice(), stagingBufferMemory, nullptr);
stbi_image_free(pixels);
generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, textureWidth,
textureHeight, mipLevels);
}
void Texture::createTextureImageView() {
// Create a texture image view, which is a struct of information about the
// image.
textureImageView =
DeviceControl::createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT, mipLevels);
}
void Texture::createTextureSampler() {
// Create a sampler to access and parse the texture object.
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
// These two options define the filtering method when sampling the texture.
// It also handles zooming in versus out, min vs mag!
samplerInfo.magFilter = VK_FILTER_LINEAR; // TODO: CUBIC
samplerInfo.minFilter = VK_FILTER_LINEAR; // TODO: CUBIC
createImage(textureWidth, textureHeight, mipLevels, VK_SAMPLE_COUNT_1_BIT,
VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
model->getMaterial().getTextureImage(), textureImageMemory);
// These options define UVW edge modes, ClampToEdge extends the last pixels to
// the edges when larger than the UVW.
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
transitionImageLayout(model->getMaterial().getTextureImage(),
VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels);
copyBufferToImage(stagingBuffer, model->getMaterial().getTextureImage(),
static_cast<uint32_t>(textureWidth),
static_cast<uint32_t>(textureHeight));
VkPhysicalDeviceProperties properties{};
vkGetPhysicalDeviceProperties(DeviceControl::getPhysicalDevice(),
&properties);
// Enable or Disable Anisotropy, and set the amount.
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
vkDestroyBuffer(DeviceControl::getDevice(), stagingBuffer, nullptr);
vkFreeMemory(DeviceControl::getDevice(), stagingBufferMemory, nullptr);
// When sampling with Clamp to Border, the border color is defined here.
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
// Normalizing coordinates changes texCoords from [0, texWidth] to [0, 1].
// This is what should ALWAYS be used, because it means you can use varying
// texture sizes. Another TODO: Normalizing
samplerInfo.unnormalizedCoordinates = VK_FALSE;
// Compare texels to a value and use the output in filtering!
// This is mainly used in percentage-closer filtering on shadow maps, this
// will be revisted eventually...
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
generateMipmaps(model->getMaterial().getTextureImage(),
VK_FORMAT_R8G8B8A8_SRGB, textureWidth, textureHeight,
mipLevels);
// Create a texture image view, which is a struct of information about the
// image.
model->getMaterial().setTextureView(DeviceControl::createImageView(
model->getMaterial().getTextureImage(), VK_FORMAT_R8G8B8A8_SRGB,
VK_IMAGE_ASPECT_COLOR_BIT, mipLevels));
// Mipmaps are basically LoD's for textures, different resolutions to load
// based on distance. These settings simply describe how to apply mipmapping.
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = VK_LOD_CLAMP_NONE;
// Create a sampler to access and parse the texture object.
if (vkCreateSampler(DeviceControl::getDevice(), &samplerInfo, nullptr,
&textureSampler) != VK_SUCCESS) {
throw std::runtime_error("failed to create texture sampler!");
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
// These two options define the filtering method when sampling the texture.
// It also handles zooming in versus out, min vs mag!
samplerInfo.magFilter = VK_FILTER_LINEAR; // TODO: CUBIC
samplerInfo.minFilter = VK_FILTER_LINEAR; // TODO: CUBIC
// These options define UVW edge modes, ClampToEdge extends the last pixels
// to the edges when larger than the UVW.
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
VkPhysicalDeviceProperties properties{};
vkGetPhysicalDeviceProperties(DeviceControl::getPhysicalDevice(),
&properties);
// Enable or Disable Anisotropy, and set the amount.
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
// When sampling with Clamp to Border, the border color is defined here.
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
// Normalizing coordinates changes texCoords from [0, texWidth] to [0, 1].
// This is what should ALWAYS be used, because it means you can use varying
// texture sizes. Another TODO: Normalizing
samplerInfo.unnormalizedCoordinates = VK_FALSE;
// Compare texels to a value and use the output in filtering!
// This is mainly used in percentage-closer filtering on shadow maps, this
// will be revisted eventually...
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
// Mipmaps are basically LoD's for textures, different resolutions to load
// based on distance. These settings simply describe how to apply
// mipmapping.
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = VK_LOD_CLAMP_NONE;
if (vkCreateSampler(DeviceControl::getDevice(), &samplerInfo, nullptr,
&model->getMaterial().getTextureSampler()) !=
VK_SUCCESS) {
throw std::runtime_error("failed to create texture sampler!");
}
}
}
void Texture::destroyTextureSampler() {
vkDestroySampler(DeviceControl::getDevice(), textureSampler, nullptr);
vkDestroyImageView(DeviceControl::getDevice(), textureImageView, nullptr);
}
void Texture::destroyTextureImage() {
vkDestroyImage(DeviceControl::getDevice(), textureImage, nullptr);
vkFreeMemory(DeviceControl::getDevice(), textureImageMemory, nullptr);
}
VkFormat Texture::findDepthFormat() {
return findSupportedFormat(
{VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT,
@ -438,8 +434,6 @@ void Texture::createDepthResources() {
// ---------------------------- Getters & Setters
// ---------------------------------//
uint32_t Texture::getMipLevels() { return mipLevels; }
VkImageView &Texture::getTextureImageView() { return textureImageView; }
VkSampler &Texture::getTextureSampler() { return textureSampler; }
VkImage &Texture::getColorImage() { return colorImage; }
VkImageView &Texture::getColorImageView() { return colorImageView; }

View File

@ -1,12 +1,14 @@
#pragma once
#include "model.h"
#define VK_NO_PROTOTYPES
#include "volk.h"
#include <cstdint>
class Texture {
public:
static void createTextureImage();
static void createTextureImageView();
static void createTextureSampler();
static const uint32_t TEXTURE_COUNT = 2;
static void createMaterialTextures(std::vector<Model *> models);
static void destroyTextureImage();
static void destroyTextureSampler();
static VkFormat findDepthFormat();
@ -14,8 +16,6 @@ public:
static void createColorResources();
// ------------ Getters & Setters ------------ //
static uint32_t getMipLevels();
static VkImageView &getTextureImageView();
static VkSampler &getTextureSampler();
static VkImage &getColorImage();
static VkImageView &getColorImageView();

21
src/shaders/common.glsl Normal file
View File

@ -0,0 +1,21 @@
#extension GL_EXT_buffer_reference : require
#extension GL_EXT_scalar_block_layout : require
#extension GL_EXT_nonuniform_qualifier : require
struct Vertex {
vec3 pos;
vec3 color;
vec2 texCoord;
};
layout(buffer_reference, scalar) readonly buffer VertexBuffer{
Vertex vertices[];
};
layout( push_constant, scalar ) uniform constants {
VertexBuffer vertBuffer;
vec3 objPos;
int textureID;
mat4 model;
mat4 view;
mat4 proj;
} PushConstants;

View File

@ -1,5 +1,9 @@
#version 450
layout(binding = 1) uniform sampler2D texSampler;
#extension GL_GOOGLE_include_directive : enable
#include "common.glsl"
layout(binding = 1) uniform sampler2D texSampler[];
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
@ -7,6 +11,5 @@ layout(location = 1) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(texture(texSampler, fragTexCoord).rgb, 1.0);
outColor = texture(texSampler[PushConstants.textureID], fragTexCoord);
}

View File

@ -1,32 +1,15 @@
#version 450
#extension GL_EXT_buffer_reference : require
layout(binding = 0) uniform UniformBufferObject {
float time;
mat4 model;
mat4 view;
mat4 proj;
} ubo;
struct Vertex {
vec3 pos;
vec3 color;
vec2 texCoord;
};
layout(buffer_reference, std430) readonly buffer VertexBuffer{
Vertex vertices[];
};
layout( push_constant ) uniform constants {
VertexBuffer vertBuffer;
}PushConstants;
#extension GL_GOOGLE_include_directive : enable
#include "common.glsl"
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;
void main() {
Vertex v = PushConstants.vertBuffer.vertices[gl_VertexIndex];
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(v.pos, 1.0f);
fragColor = v.color.xyz;
fragTexCoord = v.texCoord;
Vertex vertex = PushConstants.vertBuffer.vertices[gl_VertexIndex];
gl_Position = PushConstants.proj * PushConstants.view * PushConstants.model *
vec4(vertex.pos + PushConstants.objPos, 1.0f);
fragColor = vertex.color.rgb;
fragTexCoord = vertex.texCoord;
}

View File

@ -1,9 +1,7 @@
#pragma once
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include "vk_mem_alloc.h"
#include <array>
#include <glm/glm.hpp>
class Agnosia_T {
@ -11,38 +9,10 @@ public:
struct Vertex {
// This defines what a vertex is!
// We control the position, color and texture coordinate here!
alignas(16) glm::vec3 pos;
alignas(16) glm::vec3 color;
alignas(8) glm::vec2 texCoord;
glm::vec3 pos;
glm::vec3 color;
glm::vec2 texCoord;
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, 3>
getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions{};
attributeDescriptions[0].binding = 0;
attributeDescriptions[0].location = 0;
attributeDescriptions[0].format = VK_FORMAT_R32G32B32_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);
attributeDescriptions[2].binding = 0;
attributeDescriptions[2].location = 2;
attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
attributeDescriptions[2].offset = offsetof(Vertex, texCoord);
return attributeDescriptions;
}
bool operator==(const Vertex &other) const {
return pos == other.pos && color == other.color &&
texCoord == other.texCoord;
@ -60,5 +30,10 @@ public:
};
struct GPUPushConstants {
VkDeviceAddress vertexBuffer;
glm::vec3 objPosition;
int textureID;
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;
};
};