Add Texture loading and extend original functionality! this needs HEAVY documentation, which I will do tomorrow.

This commit is contained in:
Lillian Salehi 2024-10-13 04:56:45 -05:00
parent e2a732c98c
commit 9878070b4c
19 changed files with 8373 additions and 85 deletions

View File

@ -5,7 +5,7 @@ caption
//**This dictates basic flow of the vulkan boilerplate system. **//
endcaption
:main;
://**main()**//; <<procedure>>
://**run()**//; <<procedure>>
split
://**initWindow()**//; <<procedure>>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -27,7 +27,7 @@ debug: $(BIN)
.PHONY: dep
dep:
sudo pacman -S gcc glfw glm shaderc libxi libxxf86vm gdb shaderc
sudo pacman -S gcc glfw glm shaderc libxi libxxf86vm gdb shaderc stb
.PHONY: info
info:
@echo "make: Build executable"

BIN
assets/textures/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

7988
lib/stb_image.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
#include "devicelibrary.h"
#include "global.h"
#include <vulkan/vulkan_core.h>
namespace DeviceControl {
VkPhysicalDeviceProperties deviceProperties;
VkPhysicalDeviceFeatures deviceFeatures;
std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat;
@ -74,7 +74,8 @@ namespace DeviceControl {
vkGetPhysicalDeviceProperties(device, &deviceProperties);
// Similarly, we can pass in the device and a deviceFeatures struct, this is quite special, it holds a struct of optional features the GPU can perform.
// Some, like a geometry shader, and stereoscopic rendering (multiViewport) we want, so we dont return true without them.
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
VkPhysicalDeviceFeatures supportedFeatures;
vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
// We need to find a device that supports graphical operations, or else we cant do much with it! This function just runs over all the queueFamilies and sees if there
// is a queue family with the VK_QUEUE_GRAPHICS_BIT flipped!
Global::QueueFamilyIndices indices = Global::findQueueFamilies(device);
@ -87,7 +88,7 @@ namespace DeviceControl {
}
return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
&& deviceFeatures.multiViewport
&& supportedFeatures.samplerAnisotropy
&& indices.isComplete()
&& extensionSupported
&& swapChainAdequate;
@ -195,6 +196,9 @@ namespace DeviceControl {
queueCreateSingularInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateSingularInfo);
}
VkPhysicalDeviceFeatures deviceFeatures{};
deviceFeatures.samplerAnisotropy = VK_TRUE;
VkDeviceCreateInfo createDeviceInfo = {};
createDeviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createDeviceInfo.pQueueCreateInfos = queueCreateInfos.data();
@ -287,33 +291,30 @@ namespace DeviceControl {
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl;
}
VkImageView devicelibrary::createImageView(VkImage image, VkFormat format) {
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkImageView imageView;
if (vkCreateImageView(Global::device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
throw std::runtime_error("failed to create image view!");
}
return imageView;
}
void devicelibrary::createImageViews() {
swapChainImageViews.resize(swapChainImages.size());
for(size_t i = 0; i < swapChainImages.size(); i++) {
VkImageViewCreateInfo createImageViewInfo{};
createImageViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createImageViewInfo.image = swapChainImages[i];
// Are we treating images as 1D, 2D or 3D?
createImageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createImageViewInfo.format = swapChainImageFormat;
// Allow us to swizzle color channels
createImageViewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createImageViewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createImageViewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createImageViewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createImageViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createImageViewInfo.subresourceRange.baseMipLevel = 0;
createImageViewInfo.subresourceRange.levelCount = 1;
createImageViewInfo.subresourceRange.baseArrayLayer = 0;
// Yet another setting we would increase for VR applications, and specifically create a swap chain with more layers as well. The other layers would be the eye outputs.
createImageViewInfo.subresourceRange.layerCount = 1;
if(vkCreateImageView(Global::device, &createImageViewInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create image views!");
}
if(Global::enableValidationLayers) std::cout << "Image views created successfully\n" << std::endl;
for (uint32_t i = 0; i < swapChainImages.size(); i++) {
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);
}
}
void devicelibrary::destroyImageViews() {

View File

@ -16,6 +16,7 @@ class devicelibrary {
void destroySurface(VkInstance& instance);
void createSwapChain(GLFWwindow* window);
void destroySwapChain();
VkImageView createImageView(VkImage image, VkFormat format);
void createImageViews();
void destroyImageViews();
void createCommandPool();

View File

@ -3,7 +3,8 @@ DeviceControl::devicelibrary deviceLibs;
Debug::vulkandebuglibs debugController;
Graphics::graphicspipeline graphicsPipeline;
RenderPresent::render renderPresentation;
Buffers::bufferslibrary buffers;
BuffersLibraries::buffers buffers;
TextureLibraries::texture texture;
VkInstance vulkaninstance;
// Getters and Setters!
@ -48,7 +49,6 @@ void createInstance() {
debugController.vulkanDebugSetup(createInfo, vulkaninstance); // Handoff to the debug library to wrap the validation libs in! (And set the window up!)
}
void initVulkan() {
createInstance();
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)
@ -62,6 +62,9 @@ void initVulkan() {
graphicsPipeline.createGraphicsPipeline();
graphicsPipeline.createFramebuffers();
graphicsPipeline.createCommandPool();
texture.createTextureImage();
texture.createTextureImageView();
texture.createTextureSampler();
buffers.createVertexBuffer();
buffers.createIndexBuffer();
buffers.createUniformBuffers();
@ -81,6 +84,8 @@ void mainLoop() { // This loop jus
void cleanup() { // Similar to the last handoff, destroy the utils in a safe manner in the library!
renderPresentation.cleanupSwapChain();
texture.createTextureSampler();
texture.destroyTextureImage();
buffers.destroyUniformBuffer();
buffers.destroyDescriptorPool();
vkDestroyDescriptorSetLayout(Global::device, Global::descriptorSetLayout, nullptr);
@ -95,7 +100,6 @@ void cleanup() { // Similar to th
if(Global::enableValidationLayers) {
debugController.DestroyDebugUtilsMessengerEXT(vulkaninstance, nullptr);
}
deviceLibs.destroySurface(vulkaninstance);
vkDestroyInstance(vulkaninstance, nullptr);
glfwDestroyWindow(Global::window);

View File

@ -3,6 +3,7 @@
#include "debug/vulkandebuglibs.h"
#include "graphics/graphicspipeline.h"
#include "graphics/render.h"
#include "graphics/texture.h"
#include "global.h"
class EntryApp {
public:

View File

@ -24,6 +24,8 @@ namespace Global {
VkDescriptorSetLayout descriptorSetLayout;
std::vector<VkDescriptorSet> descriptorSets;
uint32_t currentFrame = 0;
VkImageView textureImageView;
VkSampler textureSampler;
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

@ -30,6 +30,9 @@ namespace Global {
extern VkDescriptorSetLayout descriptorSetLayout;
extern uint32_t currentFrame;
extern std::vector<VkDescriptorSet> descriptorSets;
extern VkImageView textureImageView;
extern VkSampler textureSampler;
struct UniformBufferObject {
float time;
alignas(16) glm::mat4 model;
@ -39,6 +42,7 @@ namespace Global {
struct Vertex {
glm::vec2 pos;
glm::vec3 color;
glm::vec2 texCoord;
static VkVertexInputBindingDescription getBindingDescription() {
VkVertexInputBindingDescription bindingDescription{};
@ -48,8 +52,8 @@ namespace Global {
return bindingDescription;
}
static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions{};
static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions() {
std::array<VkVertexInputAttributeDescription, 3> attributeDescriptions{};
attributeDescriptions[0].binding = 0;
attributeDescriptions[0].location = 0;
@ -60,6 +64,11 @@ namespace Global {
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;
}
};

View File

@ -16,21 +16,21 @@ std::vector<VkDeviceMemory> uniformBuffersMemory;
std::vector<void*> uniformBuffersMapped;
namespace Buffers {
namespace BuffersLibraries {
const std::vector<Global::Vertex> vertices = {
{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}},
{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}},
{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}}
{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}},
{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}
};
// Index buffer definition, showing which points to reuse.
const std::vector<uint16_t> indices = {
0, 1, 2, 2, 3, 0
};
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
uint32_t buffers::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;
@ -77,7 +77,7 @@ namespace Buffers {
vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer);
}
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
void buffers::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size;
@ -103,7 +103,7 @@ namespace Buffers {
vkBindBufferMemory(Global::device, buffer, bufferMemory, 0);
}
void bufferslibrary::createIndexBuffer() {
void buffers::createIndexBuffer() {
VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();
VkBuffer stagingBuffer;
@ -123,7 +123,7 @@ namespace Buffers {
vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
}
void bufferslibrary::createVertexBuffer() {
void buffers::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.
@ -146,28 +146,29 @@ namespace Buffers {
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
}
void bufferslibrary::destroyBuffers() {
void buffers::destroyBuffers() {
vkDestroyBuffer(Global::device, indexBuffer, nullptr);
vkFreeMemory(Global::device, indexBufferMemory, nullptr);
vkDestroyBuffer(Global::device, vertexBuffer, nullptr);
vkFreeMemory(Global::device, vertexBufferMemory, nullptr);
}
VkBuffer bufferslibrary::getVertexBuffer() {
VkBuffer buffers::getVertexBuffer() {
return vertexBuffer;
}
VkBuffer bufferslibrary::getIndexBuffer() {
VkBuffer buffers::getIndexBuffer() {
return indexBuffer;
}
std::vector<Global::Vertex> bufferslibrary::getVertices() {
std::vector<Global::Vertex> buffers::getVertices() {
return vertices;
}
std::vector<uint16_t> bufferslibrary::getIndices() {
std::vector<uint16_t> buffers::getIndices() {
return indices;
}
// ------------------------------ Uniform Buffer Setup -------------------------------- //
void bufferslibrary::createDescriptorSetLayout() {
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;
@ -178,16 +179,25 @@ namespace Buffers {
// 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 = 1;
layoutInfo.pBindings = &uboLayoutBinding;
layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = bindings.data();
if(vkCreateDescriptorSetLayout(Global::device, &layoutInfo, nullptr, &Global::descriptorSetLayout) != VK_SUCCESS) {
throw std::runtime_error("Failed to create descriptor set layout!");
}
}
void bufferslibrary::createUniformBuffers() {
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(Global::UniformBufferObject);
@ -201,7 +211,7 @@ namespace Buffers {
vkMapMemory(Global::device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]);
}
}
void bufferslibrary::updateUniformBuffer(uint32_t currentImage) {
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.
@ -225,29 +235,31 @@ namespace Buffers {
memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo));
}
void bufferslibrary::destroyUniformBuffer() {
void buffers::destroyUniformBuffer() {
for(size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroyBuffer(Global::device, uniformBuffers[i],nullptr);
vkFreeMemory(Global::device, uniformBuffersMemory[i], nullptr);
}
}
void bufferslibrary::createDescriptorPool() {
void buffers::createDescriptorPool() {
// Create a pool to be used to allocate descriptor sets.
VkDescriptorPoolSize poolSize{};
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSize.descriptorCount = static_cast<uint32_t>(Global::MAX_FRAMES_IN_FLIGHT);
std::array<VkDescriptorPoolSize, 2> poolSizes{};
poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[0].descriptorCount = static_cast<uint32_t>(Global::MAX_FRAMES_IN_FLIGHT);
poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[1].descriptorCount = static_cast<uint32_t>(Global::MAX_FRAMES_IN_FLIGHT);
VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;
poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
poolInfo.pPoolSizes = poolSizes.data();
poolInfo.maxSets = static_cast<uint32_t>(Global::MAX_FRAMES_IN_FLIGHT);
if (vkCreateDescriptorPool(Global::device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor pool!");
}
}
void bufferslibrary::createDescriptorSets() {
void buffers::createDescriptorSets() {
std::vector<VkDescriptorSetLayout> layouts(Global::MAX_FRAMES_IN_FLIGHT, Global::descriptorSetLayout);
VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
@ -259,29 +271,40 @@ namespace Buffers {
if (vkAllocateDescriptorSets(Global::device, &allocInfo, Global::descriptorSets.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate descriptor sets!");
}
for(size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
for (size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = uniformBuffers[i];
bufferInfo.offset = 0;
bufferInfo.range = sizeof(Global::UniformBufferObject);
VkWriteDescriptorSet descriptorWrite{};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.dstSet = Global::descriptorSets[i];
descriptorWrite.dstBinding = 0;
descriptorWrite.dstArrayElement = 0;
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = Global::textureImageView;
imageInfo.sampler = Global::textureSampler;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descriptorWrite.descriptorCount = 1;
std::array<VkWriteDescriptorSet, 2> descriptorWrites{};
descriptorWrite.pBufferInfo = &bufferInfo;
descriptorWrite.pImageInfo = nullptr; // Optional
descriptorWrite.pTexelBufferView = nullptr; // Optional
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = Global::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;
vkUpdateDescriptorSets(Global::device, 1, &descriptorWrite, 0, nullptr);
descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[1].dstSet = Global::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(Global::device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
}
}
void bufferslibrary::destroyDescriptorPool() {
void buffers::destroyDescriptorPool() {
vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr);
}
}

View File

@ -1,10 +1,11 @@
#pragma once
#include "../global.h"
#include <cstdint>
namespace Buffers {
class bufferslibrary {
namespace BuffersLibraries {
class buffers {
public:
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags);
void createIndexBuffer();
void createVertexBuffer();
void destroyBuffers();

View File

@ -13,7 +13,7 @@ namespace Graphics {
VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline;
DeviceControl::devicelibrary deviceLibs;
Buffers::bufferslibrary buffers;
BuffersLibraries::buffers buffers;
std::vector<VkFramebuffer> swapChainFramebuffers;
@ -97,7 +97,7 @@ namespace Graphics {
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 = 2.0f;
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_COUNTER_CLOCKWISE;

View File

@ -9,7 +9,7 @@ namespace RenderPresent {
std::vector<VkFence> inFlightFences;
Graphics::graphicspipeline pipeline;
DeviceControl::devicelibrary deviceLibs;
Buffers::bufferslibrary buffers;
BuffersLibraries::buffers buffers;
void recreateSwapChain() {
int width = 0, height = 0;

240
src/graphics/texture.cpp Normal file
View File

@ -0,0 +1,240 @@
#include <vulkan/vulkan_core.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#include "texture.h"
#include "buffers.h"
#include "../devicelibrary.h"
BuffersLibraries::buffers buffer;
DeviceControl::devicelibrary deviceLibraries;
VkImage textureImage;
VkDeviceMemory textureImageMemory;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
namespace TextureLibraries {
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = tiling;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = usage;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateImage(Global::device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
throw std::runtime_error("failed to create image!");
}
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(Global::device, image, &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = buffer.findMemoryType(memRequirements.memoryTypeBits, properties);
if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate image memory!");
}
vkBindImageMemory(Global::device, image, imageMemory, 0);
}
VkCommandBuffer beginSingleTimeCommands() {
// This is a neat function! This sets up a command buffer using our previously set command pool
// to return a command buffer to execute commands!
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = Global::commandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
return commandBuffer;
}
void endSingleTimeCommands(VkCommandBuffer commandBuffer) {
// This function takes a command buffer with the data we wish to execute and submits it to the graphics queue.
// Afterwards, it purges the command buffer.
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(Global::graphicsQueue);
vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer);
}
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
// Copy 1 buffer to another.
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
VkBufferCopy copyRegion{};
copyRegion.size = size;
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
endSingleTimeCommands(commandBuffer);
}
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
// This function handles transitioning image layout data from one layout to another.
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
} else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
throw std::invalid_argument("unsupported layout transition!");
}
vkCmdPipelineBarrier(
commandBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
endSingleTimeCommands(commandBuffer);
}
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
//This handles copying from the buffer to the image, specifically what *parts* to copy to the image.
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0};
region.imageExtent = {
width,
height,
1
};
vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
endSingleTimeCommands(commandBuffer);
}
void texture::createTextureImage() {
int textureWidth, textureHeight, textureChannels;
stbi_uc* pixels = stbi_load("assets/textures/test.png", &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha);
VkDeviceSize imageSize = textureWidth * textureHeight * 4;
if(!pixels) {
throw std::runtime_error("Failed to load texture!");
}
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
buffer.createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
void* data;
vkMapMemory(Global::device, stagingBufferMemory, 0, imageSize, 0, &data);
memcpy(data, pixels, static_cast<size_t>(imageSize));
vkUnmapMemory(Global::device, stagingBufferMemory);
stbi_image_free(pixels);
createImage(textureWidth, textureHeight, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory);
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyBufferToImage(stagingBuffer, textureImage, static_cast<uint32_t>(textureWidth), static_cast<uint32_t>(textureHeight));
transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
}
void texture::createTextureImageView() {
Global::textureImageView = deviceLibraries.createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB);
}
void texture::createTextureSampler() {
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR; // TODO: CUBIC
samplerInfo.minFilter = VK_FILTER_LINEAR; // TODO: CUBIC
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(Global::physicalDevice, &properties);
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
if (vkCreateSampler(Global::device, &samplerInfo, nullptr, &Global::textureSampler) != VK_SUCCESS) {
throw std::runtime_error("failed to create texture sampler!");
}
}
void texture::destroyTextureSampler() {
vkDestroySampler(Global::device, Global::textureSampler, nullptr);
vkDestroyImageView(Global::device, Global::textureImageView, nullptr);
}
void texture::destroyTextureImage() {
vkDestroyImage(Global::device, textureImage, nullptr);
vkFreeMemory(Global::device, textureImageMemory, nullptr);
}
}

13
src/graphics/texture.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "../global.h"
namespace TextureLibraries {
class texture {
public:
void createTextureImage();
void createTextureImageView();
void createTextureSampler();
void destroyTextureImage();
void destroyTextureSampler();
};
}

View File

@ -1,9 +1,11 @@
#version 450
layout(binding = 1) uniform sampler2D texSampler;
layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor;
void main() {
outColor = vec4(fragColor, 1.0);
outColor = vec4(texture(texSampler, fragTexCoord).rgb, 1.0);
}

View File

@ -10,10 +10,13 @@ layout(binding = 0) uniform UniformBufferObject {
// 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 = 2) in vec2 inTexCoord;
layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;
void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
fragColor = inColor + sin(ubo.time*2);
fragColor = inColor;
fragTexCoord = inTexCoord;
}