Implement volk meta loader and ImGui

This commit is contained in:
2024-11-11 21:43:56 -06:00
parent 4a8f6909a8
commit 2631bbbaba
66 changed files with 69886 additions and 2424 deletions

View File

@@ -1,5 +1,4 @@
#include "devicelibrary.h"
#include <vulkan/vulkan_core.h>
namespace device_libs {
@@ -15,7 +14,9 @@ struct SwapChainSupportDetails {
std::vector<VkPresentModeKHR> presentModes;
};
const std::vector<const char *> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME};
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
};
SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
/* Swap chains are weird ngl, it's another one of those Vulkan platform

View File

@@ -74,11 +74,15 @@ void createInstance() {
}
void initVulkan() {
// Initialize volk and continue if successful.
volkInitialize();
// Initialize vulkan and set up pipeline.
createInstance();
volkLoadInstance(vulkaninstance);
device_libs::DeviceControl::createSurface(vulkaninstance, Global::window);
device_libs::DeviceControl::pickPhysicalDevice(vulkaninstance);
device_libs::DeviceControl::createLogicalDevice();
volkLoadDevice(Global::device);
device_libs::DeviceControl::createSwapChain(Global::window);
device_libs::DeviceControl::createImageViews();
buffers_libs::Buffers::createDescriptorSetLayout();
@@ -96,11 +100,14 @@ void initVulkan() {
buffers_libs::Buffers::createDescriptorSets();
graphics_pipeline::Graphics::createCommandBuffer();
render_present::Render::createSyncObject();
render_present::Render::init_imgui(vulkaninstance);
}
void mainLoop() {
while (!glfwWindowShouldClose(Global::window)) {
glfwPollEvents();
render_present::Render::drawImGui();
render_present::Render::drawFrame();
}
vkDeviceWaitIdle(Global::device);

View File

@@ -1,23 +1,25 @@
#include "graphics/graphicspipeline.h"
#include "graphics/render.h"
#pragma once
#include "global.h"
#include "graphics/graphicspipeline.h"
#include "graphics/model.h"
#include "graphics/render.h"
#include "graphics/texture.h"
class EntryApp {
public:
static EntryApp& getInstance();
void initialize();
bool isInitialized() const;
void run();
void setFramebufferResized(bool frame);
bool getFramebufferResized() const;
private:
EntryApp();
EntryApp(const EntryApp&) = delete;
void operator=(const EntryApp&) = delete;
public:
static EntryApp &getInstance();
void initialize();
bool isInitialized() const;
void run();
void setFramebufferResized(bool frame);
bool getFramebufferResized() const;
bool framebufferResized;
bool initialized;
private:
EntryApp();
EntryApp(const EntryApp &) = delete;
void operator=(const EntryApp &) = delete;
bool framebufferResized;
bool initialized;
};

View File

@@ -1,5 +1,7 @@
#pragma once
#include <vulkan/vulkan_core.h>
#define VK_NO_PROTOTYPES
#include "volk.h"
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#include <glm/glm.hpp>

View File

@@ -340,4 +340,5 @@ void Buffers::createDescriptorSets() {
void Buffers::destroyDescriptorPool() {
vkDestroyDescriptorPool(Global::device, descriptorPool, nullptr);
}
VkDescriptorPool Buffers::getDescriptorPool() { return descriptorPool; }
} // namespace buffers_libs

View File

@@ -1,24 +1,28 @@
#pragma once
#include "../devicelibrary.h"
#include <chrono>
#include <cstring>
#include "../devicelibrary.h"
namespace buffers_libs {
class Buffers {
public:
static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags props, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
static uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags flags);
static void createIndexBuffer();
static void createVertexBuffer();
static void destroyBuffers();
static VkBuffer getVertexBuffer();
static VkBuffer getIndexBuffer();
static void createDescriptorSetLayout();
static void createUniformBuffers();
static void updateUniformBuffer(uint32_t currentImage);
static void destroyUniformBuffer();
static void createDescriptorPool();
static void createDescriptorSets();
static void destroyDescriptorPool();
};
}
class Buffers {
public:
static void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
VkMemoryPropertyFlags props, VkBuffer &buffer,
VkDeviceMemory &bufferMemory);
static uint32_t findMemoryType(uint32_t typeFilter,
VkMemoryPropertyFlags flags);
static void createIndexBuffer();
static void createVertexBuffer();
static void destroyBuffers();
static VkBuffer getVertexBuffer();
static VkBuffer getIndexBuffer();
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();
};
} // namespace buffers_libs

View File

@@ -1,6 +1,7 @@
#include "graphicspipeline.h"
#include "imgui.h"
#include "imgui_impl_vulkan.h"
#include "texture.h"
#include <vulkan/vulkan_core.h>
namespace graphics_pipeline {
@@ -353,6 +354,8 @@ void Graphics::recordCommandBuffer(VkCommandBuffer commandBuffer,
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(Global::indices.size()),
1, 0, 0, 0);
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer);
vkCmdEndRendering(commandBuffer);
const VkImageMemoryBarrier2 prePresentImageBarrier{

View File

@@ -1,3 +1,4 @@
#pragma once
#include "../global.h"
#define TINY_OBJ_IMPLEMENTATION
@@ -7,8 +8,8 @@
#include <glm/gtx/hash.hpp>
namespace modellib {
class Model {
public:
static void loadModel();
};
}
class Model {
public:
static void loadModel();
};
} // namespace modellib

View File

@@ -1,14 +1,23 @@
#include "../devicelibrary.h"
#include "../entrypoint.h"
#include "buffers.h"
#include "graphicspipeline.h"
#include "render.h"
#include "texture.h"
#include <cstdint>
#include <stdexcept>
#include <vulkan/vulkan_core.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_vulkan.h"
namespace render_present {
std::vector<VkSemaphore> imageAvailableSemaphores;
std::vector<VkSemaphore> renderFinishedSemaphores;
std::vector<VkFence> inFlightFences;
VkDescriptorPool imGuiDescriptorPool;
void recreateSwapChain() {
int width = 0, height = 0;
@@ -59,7 +68,8 @@ void Render::drawFrame() {
/*VkCommandBufferResetFlagBits*/ 0);
graphics_pipeline::Graphics::recordCommandBuffer(
Global::commandBuffers[Global::currentFrame], imageIndex);
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(),
Global::commandBuffers[Global::currentFrame]);
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
@@ -106,6 +116,37 @@ void Render::drawFrame() {
Global::currentFrame =
(Global::currentFrame + 1) % Global::MAX_FRAMES_IN_FLIGHT;
}
void Render::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.
{
static float f = 0.0f;
static int counter = 0;
ImGui::Begin("Agnosia Debug"); // Create a window called "Hello, world!" and
// append into it.
ImGui::Text("This is some useful text."); // Display some text (you can use
// a format strings too)
ImGui::SliderFloat("float", &f, 0.0f,
1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
if (ImGui::Button("Button")) // Buttons return true when clicked (most
// widgets return true when edited/activated)
counter++;
ImGui::SameLine();
ImGui::Text("counter = %d", counter);
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
}
ImGui::Render();
}
#pragma info
// SEMAPHORES
// Synchronization of execution on the GPU in Vulkan is *explicit* The Order of
@@ -171,4 +212,67 @@ void Render::cleanupSwapChain() {
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
}
void Render::init_imgui(VkInstance instance) {
auto load_vk_func = [&](const char *fn) {
if (auto proc = vkGetDeviceProcAddr(Global::device, fn))
return proc;
return vkGetInstanceProcAddr(instance, fn);
};
ImGui_ImplVulkan_LoadFunctions(
[](const char *fn, void *data) {
return (*(decltype(load_vk_func) *)data)(fn);
},
&load_vk_func);
IMGUI_CHECKVERSION();
ImGui::CreateContext();
// TODO
ImGuiIO &io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
ImGui::StyleColorsDark();
ImGui_ImplGlfw_InitForVulkan(Global::window, true);
VkDescriptorPoolSize ImGuiPoolSizes[]{
{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1},
};
VkDescriptorPoolCreateInfo ImGuiPoolInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
.maxSets = 1,
.poolSizeCount = 1,
.pPoolSizes = ImGuiPoolSizes,
};
if (vkCreateDescriptorPool(Global::device, &ImGuiPoolInfo, nullptr,
&imGuiDescriptorPool) != VK_SUCCESS) {
throw std::runtime_error("Failed to create ImGui descriptor pool!");
}
VkPipelineRenderingCreateInfo pipelineRenderingCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = device_libs::DeviceControl::getImageFormat(),
.depthAttachmentFormat = texture_libs::Texture::findDepthFormat(),
};
ImGui_ImplVulkan_InitInfo initInfo{
.Instance = instance,
.PhysicalDevice = Global::physicalDevice,
.Device = Global::device,
.QueueFamily = Global::findQueueFamilies(Global::physicalDevice)
.graphicsFamily.value(),
.Queue = Global::graphicsQueue,
.DescriptorPool = imGuiDescriptorPool,
.MinImageCount = Global::MAX_FRAMES_IN_FLIGHT,
.ImageCount = Global::MAX_FRAMES_IN_FLIGHT,
.MSAASamples = VK_SAMPLE_COUNT_1_BIT,
.UseDynamicRendering = true,
.PipelineRenderingCreateInfo = pipelineRenderingCreateInfo,
};
ImGui_ImplVulkan_Init(&initInfo);
} // namespace render_present
} // namespace render_present

View File

@@ -1,13 +1,14 @@
#pragma once
#include "../global.h"
namespace render_present {
class Render {
public:
static void drawFrame();
static void createSyncObject();
static void destroyFenceSemaphores();
static void cleanupSwapChain();
};
}
public:
static void drawFrame();
static void createSyncObject();
static void destroyFenceSemaphores();
static void cleanupSwapChain();
static void init_imgui(VkInstance instance);
static void drawImGui();
};
} // namespace render_present

View File

@@ -1,8 +1,7 @@
#include <cstdint>
#include <vulkan/vulkan_core.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#include "texture.h"
#include <stb/stb_image.h>
uint32_t mipLevels;
@@ -12,369 +11,404 @@ VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
namespace texture_libs {
void createImage(uint32_t width, uint32_t height, uint32_t mipLevels, 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;
imageInfo.mipLevels = mipLevels;
void createImage(uint32_t width, uint32_t height, uint32_t mipLevels,
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;
imageInfo.mipLevels = mipLevels;
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 = buffers_libs::Buffers::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);
if (vkCreateImage(Global::device, &imageInfo, nullptr, &image) !=
VK_SUCCESS) {
throw std::runtime_error("failed to create image!");
}
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;
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(Global::device, image, &memRequirements);
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = buffers_libs::Buffers::findMemoryType(
memRequirements.memoryTypeBits, properties);
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;
if (vkAllocateMemory(Global::device, &allocInfo, nullptr, &imageMemory) !=
VK_SUCCESS) {
throw std::runtime_error("failed to allocate image memory!");
}
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;
vkBindImageMemory(Global::device, image, imageMemory, 0);
}
vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(Global::graphicsQueue);
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;
vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer);
}
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
// Copy 1 buffer to another.
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(Global::device, &allocInfo, &commandBuffer);
VkBufferCopy copyRegion{};
copyRegion.size = size;
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
endSingleTimeCommands(commandBuffer);
}
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels) {
// This function handles transitioning image layout data from one layout to another.
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
vkBeginCommandBuffer(commandBuffer, &beginInfo);
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;
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);
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = mipLevels;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkQueueSubmit(Global::graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(Global::graphicsQueue);
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;
vkFreeCommandBuffers(Global::device, Global::commandPool, 1, &commandBuffer);
}
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
// Copy 1 buffer to another.
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
throw std::invalid_argument("unsupported layout transition!");
}
VkBufferCopy copyRegion{};
copyRegion.size = size;
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
vkCmdPipelineBarrier(
commandBuffer,
sourceStage, destinationStage,
0,
0, nullptr,
0, nullptr,
1, &barrier
);
endSingleTimeCommands(commandBuffer);
}
void transitionImageLayout(VkImage image, VkFormat format,
VkImageLayout oldLayout, VkImageLayout newLayout,
uint32_t mipLevels) {
// This function handles transitioning image layout data from one layout to
// another.
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
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();
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;
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = mipLevels;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.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;
region.imageOffset = {0, 0, 0};
region.imageExtent = {
width,
height,
1
};
vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
endSingleTimeCommands(commandBuffer);
}
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
for(VkFormat format : candidates) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, format, &props);
// Do we support linear tiling?
if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
return format;
// Or do we support optimal tiling?
} else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
return format;
}
}
throw std::runtime_error("failed to find supported depth buffering format!");
}
bool hasStencilComponent(VkFormat format) {
return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
}
void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t textureWidth, int32_t textureHeight, uint32_t mipLevels) {
// Check if image format supports linear blitting
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, imageFormat, &formatProperties);
if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
throw std::runtime_error("texture image format does not support linear blitting!");
}
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
// Specify the parameters of an image memory barrier
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.image = image;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
int32_t mipWidth = textureWidth;
int32_t mipHeight = textureHeight;
for(uint32_t mip = 1; mip < mipLevels; mip++) {
barrier.subresourceRange.baseMipLevel = mip - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
VkImageBlit blit{};
blit.srcOffsets[0] = { 0, 0, 0 };
blit.srcOffsets[1] = { mipWidth, mipHeight, 1 };
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = mip - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = 1;
blit.dstOffsets[0] = { 0, 0, 0 };
blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 };
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = mip;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = 1;
vkCmdBlitImage(commandBuffer,
image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &blit, VK_FILTER_LINEAR);
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
if (mipWidth > 1) mipWidth /= 2;
if (mipHeight > 1) mipHeight /= 2;
}
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
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;
vkCmdPipelineBarrier(commandBuffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0,
0, nullptr,
0, nullptr,
1, &barrier);
endSingleTimeCommands(commandBuffer);
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else {
throw std::invalid_argument("unsupported layout transition!");
}
// -------------------------------- 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(Global::TEXTURE_PATH.c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha);
mipLevels = static_cast<uint32_t>(std::floor(std::log2(std::max(textureWidth, textureHeight)))) + 1;
VkDeviceSize imageSize = textureWidth * textureHeight * 4;
vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0,
nullptr, 0, nullptr, 1, &barrier);
if(!pixels) {
throw std::runtime_error("Failed to load texture!");
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);
}
VkFormat findSupportedFormat(const std::vector<VkFormat> &candidates,
VkImageTiling tiling,
VkFormatFeatureFlags features) {
for (VkFormat format : candidates) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, format, &props);
// Do we support linear tiling?
if (tiling == VK_IMAGE_TILING_LINEAR &&
(props.linearTilingFeatures & features) == features) {
return format;
// Or do we support optimal tiling?
} else if (tiling == VK_IMAGE_TILING_OPTIMAL &&
(props.optimalTilingFeatures & features) == features) {
return format;
}
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
buffers_libs::Buffers::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, mipLevels, 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);
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));
vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
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.
Global::textureImageView = device_libs::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
// 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;
throw std::runtime_error("failed to find supported depth buffering format!");
}
bool hasStencilComponent(VkFormat format) {
return format == VK_FORMAT_D32_SFLOAT_S8_UINT ||
format == VK_FORMAT_D24_UNORM_S8_UINT;
}
void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t textureWidth,
int32_t textureHeight, uint32_t mipLevels) {
// Check if image format supports linear blitting
VkFormatProperties formatProperties;
vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, imageFormat,
&formatProperties);
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 = VK_LOD_CLAMP_NONE;
if (!(formatProperties.optimalTilingFeatures &
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
throw std::runtime_error(
"texture image format does not support linear blitting!");
}
if (vkCreateSampler(Global::device, &samplerInfo, nullptr, &Global::textureSampler) != VK_SUCCESS) {
throw std::runtime_error("failed to create texture sampler!");
}
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
// Specify the parameters of an image memory barrier
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.image = image;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
int32_t mipWidth = textureWidth;
int32_t mipHeight = textureHeight;
for (uint32_t mip = 1; mip < mipLevels; mip++) {
barrier.subresourceRange.baseMipLevel = mip - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
nullptr, 1, &barrier);
VkImageBlit blit{};
blit.srcOffsets[0] = {0, 0, 0};
blit.srcOffsets[1] = {mipWidth, mipHeight, 1};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = mip - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = 1;
blit.dstOffsets[0] = {0, 0, 0};
blit.dstOffsets[1] = {mipWidth > 1 ? mipWidth / 2 : 1,
mipHeight > 1 ? mipHeight / 2 : 1, 1};
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = mip;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = 1;
vkCmdBlitImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
VK_FILTER_LINEAR);
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr,
0, nullptr, 1, &barrier);
if (mipWidth > 1)
mipWidth /= 2;
if (mipHeight > 1)
mipHeight /= 2;
}
void Texture::destroyTextureSampler() {
vkDestroySampler(Global::device, Global::textureSampler, nullptr);
vkDestroyImageView(Global::device, Global::textureImageView, nullptr);
barrier.subresourceRange.baseMipLevel = mipLevels - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0,
nullptr, 1, &barrier);
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(Global::TEXTURE_PATH.c_str(), &textureWidth,
&textureHeight, &textureChannels, STBI_rgb_alpha);
mipLevels = static_cast<uint32_t>(std::floor(
std::log2(std::max(textureWidth, textureHeight)))) +
1;
VkDeviceSize imageSize = textureWidth * textureHeight * 4;
if (!pixels) {
throw std::runtime_error("Failed to load texture!");
}
void Texture::destroyTextureImage() {
vkDestroyImage(Global::device, textureImage, nullptr);
vkFreeMemory(Global::device, textureImageMemory, nullptr);
}
VkFormat Texture::findDepthFormat() {
return findSupportedFormat(
{VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT},
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
);
}
void Texture::createDepthResources() {
VkFormat depthFormat = findDepthFormat();
VkExtent2D swapChainExtent = device_libs::DeviceControl::getSwapChainExtent();
createImage(swapChainExtent.width, swapChainExtent.height, 1, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, Global::depthImage, Global::depthImageMemory);
Global::depthImageView = device_libs::DeviceControl::createImageView(Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
// Explicit transition from the layout of the image to the depth attachment is unnecessary here, since that will be handled in the render pass!
}
// ---------------------------- Getters & Setters ---------------------------------//
uint32_t Texture::getMipLevels() {
return mipLevels;
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
buffers_libs::Buffers::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, mipLevels, 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);
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));
vkDestroyBuffer(Global::device, stagingBuffer, nullptr);
vkFreeMemory(Global::device, stagingBufferMemory, nullptr);
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.
Global::textureImageView = device_libs::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
// 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 = VK_LOD_CLAMP_NONE;
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);
}
VkFormat Texture::findDepthFormat() {
return findSupportedFormat(
{VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT},
VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
}
void Texture::createDepthResources() {
VkFormat depthFormat = findDepthFormat();
VkExtent2D swapChainExtent = device_libs::DeviceControl::getSwapChainExtent();
createImage(swapChainExtent.width, swapChainExtent.height, 1, depthFormat,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, Global::depthImage,
Global::depthImageMemory);
Global::depthImageView = device_libs::DeviceControl::createImageView(
Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1);
// Explicit transition from the layout of the image to the depth attachment is
// unnecessary here, since that will be handled in the render pass!
}
// ---------------------------- Getters & Setters
// ---------------------------------//
uint32_t Texture::getMipLevels() { return mipLevels; }
} // namespace texture_libs

View File

@@ -1,5 +1,6 @@
#include "entrypoint.h"
#define VOLK_IMPLEMENTATION
int main() {
EntryApp::getInstance().initialize();
try {