Compare commits

...

2 Commits

18 changed files with 425 additions and 87 deletions

View File

@@ -5,7 +5,7 @@ caption
//**This dictates basic flow of the vulkan boilerplate system. **// //**This dictates basic flow of the vulkan boilerplate system. **//
endcaption endcaption
:main; ://**main()**//; <<procedure>>
://**run()**//; <<procedure>> ://**run()**//; <<procedure>>
split split
://**initWindow()**//; <<procedure>> ://**initWindow()**//; <<procedure>>
@@ -109,6 +109,29 @@ partition "**Graphics**" {
buffer objects, pools manage the memory used for buffers. buffer objects, pools manage the memory used for buffers.
end note end note
} }
partition "**TextureLibraries**" {
:createTextureImage();
note right
This function imports the pixels from an image, puts them
into a buffer, and copies them from memory into a texture!
A bit complicated because we are moving and freeing lots of
memory, but quite useful.
end note
:createTextureImageView();
note right
This function creates a image view for the texture, just
builds a struct holding information about the texture, like
layers, mip levels, and etc.
end note
:createTextureSampler();
note right
This function is **incredibly** important. This builds a
texture sampler, information about what to do with the
texture once its created. This defines settings like
//UVW mode//, //Filtering//, //Anisotropy//, and
//Mipmap modes//
end note
}
partition "**Buffers**" { partition "**Buffers**" {
:createVertexBuffer(); :createVertexBuffer();
note right note right
@@ -146,13 +169,13 @@ partition "**Buffers**" {
therefore necessary. (see **createDescriptorSets()**) therefore necessary. (see **createDescriptorSets()**)
end note end note
} }
:Graphics::createCommandBuffer(); :**Graphics**::createCommandBuffer();
note right note right
This is the partner to the commandPool creator, This is the partner to the commandPool creator,
storing the commands we wish to perform whilst storing the commands we wish to perform whilst
waiting in a queue. These are very efficient. waiting in a queue. These are very efficient.
end note end note
:RenderPresent::createSyncObject(); :**RenderPresent**::createSyncObject();
note right note right
This is **HEAVILY** documented, create Semaphores This is **HEAVILY** documented, create Semaphores
and Fences, for halting and starting execution, basically and Fences, for halting and starting execution, basically

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -27,7 +27,7 @@ debug: $(BIN)
.PHONY: dep .PHONY: dep
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 .PHONY: info
info: info:
@echo "make: Build executable" @echo "make: Build executable"

BIN
assets/textures/test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,12 +1,12 @@
#include "devicelibrary.h" #include "devicelibrary.h"
#include "global.h" #include "global.h"
#include <vulkan/vulkan_core.h>
namespace DeviceControl { namespace DeviceControl {
VkPhysicalDeviceProperties deviceProperties; VkPhysicalDeviceProperties deviceProperties;
VkPhysicalDeviceFeatures deviceFeatures;
std::vector<VkImage> swapChainImages; std::vector<VkImage> swapChainImages;
VkFormat swapChainImageFormat; VkFormat swapChainImageFormat;
@@ -74,7 +74,8 @@ namespace DeviceControl {
vkGetPhysicalDeviceProperties(device, &deviceProperties); 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. // 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. // 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 // 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! // is a queue family with the VK_QUEUE_GRAPHICS_BIT flipped!
Global::QueueFamilyIndices indices = Global::findQueueFamilies(device); Global::QueueFamilyIndices indices = Global::findQueueFamilies(device);
@@ -87,7 +88,7 @@ namespace DeviceControl {
} }
return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU return deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU
&& deviceFeatures.multiViewport && supportedFeatures.samplerAnisotropy
&& indices.isComplete() && indices.isComplete()
&& extensionSupported && extensionSupported
&& swapChainAdequate; && swapChainAdequate;
@@ -195,6 +196,9 @@ namespace DeviceControl {
queueCreateSingularInfo.pQueuePriorities = &queuePriority; queueCreateSingularInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateSingularInfo); queueCreateInfos.push_back(queueCreateSingularInfo);
} }
VkPhysicalDeviceFeatures deviceFeatures{};
deviceFeatures.samplerAnisotropy = VK_TRUE;
VkDeviceCreateInfo createDeviceInfo = {}; VkDeviceCreateInfo createDeviceInfo = {};
createDeviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; createDeviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createDeviceInfo.pQueueCreateInfos = queueCreateInfos.data(); createDeviceInfo.pQueueCreateInfos = queueCreateInfos.data();
@@ -287,33 +291,31 @@ namespace DeviceControl {
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr); vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl; if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl;
} }
VkImageView devicelibrary::createImageView(VkImage image, VkFormat format) {
// This defines the parameters of a newly created image object!
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() { void devicelibrary::createImageViews() {
swapChainImageViews.resize(swapChainImages.size()); 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; for (uint32_t i = 0; i < swapChainImages.size(); i++) {
createImageViewInfo.subresourceRange.baseMipLevel = 0; swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);
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;
} }
} }
void devicelibrary::destroyImageViews() { void devicelibrary::destroyImageViews() {

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,21 +16,21 @@ std::vector<VkDeviceMemory> uniformBuffersMemory;
std::vector<void*> uniformBuffersMapped; std::vector<void*> uniformBuffersMapped;
namespace Buffers { namespace BuffersLibraries {
const std::vector<Global::Vertex> vertices = { const std::vector<Global::Vertex> vertices = {
{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.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.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.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}} {{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}
}; };
// Index buffer definition, showing which points to reuse. // Index buffer definition, showing which points to reuse.
const std::vector<uint16_t> indices = { const std::vector<uint16_t> indices = {
0, 1, 2, 2, 3, 0 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. // 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. // Query the available types of memory to iterate over.
VkPhysicalDeviceMemoryProperties memProperties; VkPhysicalDeviceMemoryProperties memProperties;
@@ -77,7 +77,7 @@ namespace Buffers {
vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer); 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{}; VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = size; bufferInfo.size = size;
@@ -103,7 +103,7 @@ namespace Buffers {
vkBindBufferMemory(Global::device, buffer, bufferMemory, 0); vkBindBufferMemory(Global::device, buffer, bufferMemory, 0);
} }
void bufferslibrary::createIndexBuffer() { void buffers::createIndexBuffer() {
VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();
VkBuffer stagingBuffer; VkBuffer stagingBuffer;
@@ -123,7 +123,7 @@ namespace Buffers {
vkDestroyBuffer(Global::device, stagingBuffer, nullptr); vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, 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! // 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. // 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. // 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); vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
} }
void bufferslibrary::destroyBuffers() { void buffers::destroyBuffers() {
vkDestroyBuffer(Global::device, indexBuffer, nullptr); vkDestroyBuffer(Global::device, indexBuffer, nullptr);
vkFreeMemory(Global::device, indexBufferMemory, nullptr); vkFreeMemory(Global::device, indexBufferMemory, nullptr);
vkDestroyBuffer(Global::device, vertexBuffer, nullptr); vkDestroyBuffer(Global::device, vertexBuffer, nullptr);
vkFreeMemory(Global::device, vertexBufferMemory, nullptr); vkFreeMemory(Global::device, vertexBufferMemory, nullptr);
} }
VkBuffer bufferslibrary::getVertexBuffer() { VkBuffer buffers::getVertexBuffer() {
return vertexBuffer; return vertexBuffer;
} }
VkBuffer bufferslibrary::getIndexBuffer() { VkBuffer buffers::getIndexBuffer() {
return indexBuffer; return indexBuffer;
} }
std::vector<Global::Vertex> bufferslibrary::getVertices() { std::vector<Global::Vertex> buffers::getVertices() {
return vertices; return vertices;
} }
std::vector<uint16_t> bufferslibrary::getIndices() { std::vector<uint16_t> buffers::getIndices() {
return indices; return indices;
} }
// ------------------------------ Uniform Buffer Setup -------------------------------- // // ------------------------------ Uniform Buffer Setup -------------------------------- //
void bufferslibrary::createDescriptorSetLayout() { void buffers::createDescriptorSetLayout() {
// Create a table of pointers to data, a Descriptor Set! // Create a table of pointers to data, a Descriptor Set!
// --------------------- UBO Layout --------------------- //
VkDescriptorSetLayoutBinding uboLayoutBinding{}; VkDescriptorSetLayoutBinding uboLayoutBinding{};
uboLayoutBinding.binding = 0; uboLayoutBinding.binding = 0;
uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
@@ -178,16 +179,25 @@ namespace Buffers {
// Immutable Samplers is relevant for image sampling. // Immutable Samplers is relevant for image sampling.
uboLayoutBinding.pImmutableSamplers = nullptr; 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{}; VkDescriptorSetLayoutCreateInfo layoutInfo{};
layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutInfo.bindingCount = 1; layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
layoutInfo.pBindings = &uboLayoutBinding; layoutInfo.pBindings = bindings.data();
if(vkCreateDescriptorSetLayout(Global::device, &layoutInfo, nullptr, &Global::descriptorSetLayout) != VK_SUCCESS) { if(vkCreateDescriptorSetLayout(Global::device, &layoutInfo, nullptr, &Global::descriptorSetLayout) != VK_SUCCESS) {
throw std::runtime_error("Failed to create descriptor set layout!"); 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. // 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 // 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); VkDeviceSize bufferSize = sizeof(Global::UniformBufferObject);
@@ -201,7 +211,7 @@ namespace Buffers {
vkMapMemory(Global::device, uniformBuffersMemory[i], 0, bufferSize, 0, &uniformBuffersMapped[i]); 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 // 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. // so we aren't locked to the framerate as the world time.
@@ -225,29 +235,31 @@ namespace Buffers {
memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo));
} }
void bufferslibrary::destroyUniformBuffer() { void buffers::destroyUniformBuffer() {
for(size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) { for(size_t i = 0; i < Global::MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroyBuffer(Global::device, uniformBuffers[i],nullptr); vkDestroyBuffer(Global::device, uniformBuffers[i],nullptr);
vkFreeMemory(Global::device, uniformBuffersMemory[i], nullptr); vkFreeMemory(Global::device, uniformBuffersMemory[i], nullptr);
} }
} }
void bufferslibrary::createDescriptorPool() { void buffers::createDescriptorPool() {
// Create a pool to be used to allocate descriptor sets. // Create a pool to be used to allocate descriptor sets.
VkDescriptorPoolSize poolSize{}; std::array<VkDescriptorPoolSize, 2> poolSizes{};
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSize.descriptorCount = static_cast<uint32_t>(Global::MAX_FRAMES_IN_FLIGHT); 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{}; VkDescriptorPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 1; poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
poolInfo.pPoolSizes = &poolSize; poolInfo.pPoolSizes = poolSizes.data();
poolInfo.maxSets = static_cast<uint32_t>(Global::MAX_FRAMES_IN_FLIGHT); poolInfo.maxSets = static_cast<uint32_t>(Global::MAX_FRAMES_IN_FLIGHT);
if (vkCreateDescriptorPool(Global::device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) { if (vkCreateDescriptorPool(Global::device, &poolInfo, nullptr, &descriptorPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create descriptor pool!"); 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); std::vector<VkDescriptorSetLayout> layouts(Global::MAX_FRAMES_IN_FLIGHT, Global::descriptorSetLayout);
VkDescriptorSetAllocateInfo allocInfo{}; VkDescriptorSetAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; 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) { if (vkAllocateDescriptorSets(Global::device, &allocInfo, Global::descriptorSets.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate descriptor sets!"); 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{}; VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = uniformBuffers[i]; bufferInfo.buffer = uniformBuffers[i];
bufferInfo.offset = 0; bufferInfo.offset = 0;
bufferInfo.range = sizeof(Global::UniformBufferObject); bufferInfo.range = sizeof(Global::UniformBufferObject);
VkWriteDescriptorSet descriptorWrite{}; VkDescriptorImageInfo imageInfo{};
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
descriptorWrite.dstSet = Global::descriptorSets[i]; imageInfo.imageView = Global::textureImageView;
descriptorWrite.dstBinding = 0; imageInfo.sampler = Global::textureSampler;
descriptorWrite.dstArrayElement = 0;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; std::array<VkWriteDescriptorSet, 2> descriptorWrites{};
descriptorWrite.descriptorCount = 1;
descriptorWrite.pBufferInfo = &bufferInfo; descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrite.pImageInfo = nullptr; // Optional descriptorWrites[0].dstSet = Global::descriptorSets[i];
descriptorWrite.pTexelBufferView = nullptr; // Optional descriptorWrites[0].dstBinding = 0;
descriptorWrites[0].dstArrayElement = 0;
vkUpdateDescriptorSets(Global::device, 1, &descriptorWrite, 0, nullptr); 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 = 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); vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr);
} }
} }

View File

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

View File

@@ -13,7 +13,7 @@ namespace Graphics {
VkPipelineLayout pipelineLayout; VkPipelineLayout pipelineLayout;
VkPipeline graphicsPipeline; VkPipeline graphicsPipeline;
DeviceControl::devicelibrary deviceLibs; DeviceControl::devicelibrary deviceLibs;
Buffers::bufferslibrary buffers; BuffersLibraries::buffers buffers;
std::vector<VkFramebuffer> swapChainFramebuffers; std::vector<VkFramebuffer> swapChainFramebuffers;
@@ -97,7 +97,7 @@ namespace Graphics {
rasterizer.rasterizerDiscardEnable = 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* // 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.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. // 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.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;

View File

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

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

@@ -0,0 +1,254 @@
#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) {
// This function specifies all the data in an image object, this is called directly after the creation of an image object.
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);
}
// -------------------------------- Image Libraries ------------------------------- //
void texture::createTextureImage() {
// 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("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() {
// Create a texture image view, which is a struct of information about the image.
Global::textureImageView = deviceLibraries.createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB);
}
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
// 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(Global::physicalDevice, &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 = 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 #version 450
layout(binding = 1) uniform sampler2D texSampler;
layout(location = 0) in vec3 fragColor; layout(location = 0) in vec3 fragColor;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 0) out vec4 outColor; layout(location = 0) out vec4 outColor;
void main() { 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 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 = 0) in vec2 inPosition;
layout(location = 1) in vec3 inColor; layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
layout(location = 0) out vec3 fragColor; layout(location = 0) out vec3 fragColor;
layout(location = 1) out vec2 fragTexCoord;
void main() { void main() {
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
fragColor = inColor + sin(ubo.time*2); fragColor = inColor;
fragTexCoord = inTexCoord;
} }