initial commit
This commit is contained in:
commit
db90bb2c31
3
.clangd
Normal file
3
.clangd
Normal file
@ -0,0 +1,3 @@
|
||||
CompileFlags: # Tweak the parse settings
|
||||
Add: [-std=c++2a, -Wall, -I/usr/include/freetype2, -I.., -I.]
|
||||
# https://clangd.llvm.org/config
|
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
build
|
||||
fonts
|
||||
models
|
||||
old
|
||||
shaders
|
||||
stb_image.h
|
||||
textures
|
||||
tiny_obj_loader.h
|
||||
vk_enum_string.h
|
||||
vulkan.log
|
||||
vulkan_specs.html
|
||||
vulkan_test
|
||||
Vulkan Tutorial en.pdf
|
||||
|
22
.vimspector.json
Normal file
22
.vimspector.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"configurations": {
|
||||
"gze": {
|
||||
"adapter": "vscode-cpptools",
|
||||
"configuration": {
|
||||
"name": "vulkan (cpp)",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"externalConsole": true,
|
||||
"logging": {
|
||||
"engineLogging": true
|
||||
},
|
||||
"stopOnEntry": true,
|
||||
"stopAtEntry": true,
|
||||
"debugOptions": [],
|
||||
"MIMode": "gdb",
|
||||
"cwd": "~/c++/vulkan",
|
||||
"program": "vulkan_test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
Makefile
Executable file
65
Makefile
Executable file
@ -0,0 +1,65 @@
|
||||
CXX = /usr/bin/g++
|
||||
CXXFLAGS = -std=c++20 -MMD -MP -Wextra #-O3: optimierungsstufe 3, -MMD .d files, -MP leeres Target, Wextra alle Warnungen
|
||||
# CXXFLAGS += -ftemplate-backtrace-limit=4 #-fno-pretty-templates
|
||||
# most stuff glfw deps
|
||||
LDFLAGS = -lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi -lgzutil -lfreetype
|
||||
IFLAGS = -I/usr/include/freetype2
|
||||
|
||||
OBJECT_DIR = build
|
||||
EXEC = vulkan_test
|
||||
|
||||
SRC = $(wildcard *.cpp)
|
||||
OBJECTS = $($(notdir SRC):%.cpp=$(OBJECT_DIR)/%.o)
|
||||
DEPENDS = ${OBJECTS:.o=.d}
|
||||
|
||||
CXXFLAGS += $(IFLAGS)
|
||||
|
||||
|
||||
default: $(EXEC)
|
||||
echo $(OBJECTS)
|
||||
|
||||
release: CXXFLAGS += -O3
|
||||
release : default
|
||||
|
||||
# rule for the executable
|
||||
$(EXEC): $(OBJECTS)
|
||||
$(CXX) $(OBJECTS) -o $@ $(CXXFLAGS) $(LDFLAGS)
|
||||
# include the makefiles generated by the -M flag
|
||||
-include $(DEPENDS)
|
||||
|
||||
# rule for all ../build/*.o files
|
||||
$(OBJECT_DIR)/%.o: %.cpp $(OBJECT_DIR)/.dirstamp
|
||||
$(CXX) -c $< -o $@ $(CXXFLAGS) $(LDFLAGS)
|
||||
|
||||
# if build dir does not exist, create and put stamp inside
|
||||
$(OBJECT_DIR)/.dirstamp:
|
||||
mkdir -p $(OBJECT_DIR)
|
||||
touch $@
|
||||
|
||||
#
|
||||
# Extras Options
|
||||
#
|
||||
# with debug flags
|
||||
debug: CXXFLAGS += -g # -DDEBUG
|
||||
debug: default
|
||||
|
||||
# make with debug flags and run afterwards
|
||||
run: CXXFLAGS += -g
|
||||
run: default
|
||||
$(CXX) $(OBJECTS) -o $(EXEC) $(CXXFLAGS) $(LDFLAGS)
|
||||
./$(EXEC)
|
||||
|
||||
# with debug flags and run gnu debugger
|
||||
gdb: CXXFLAGS += -g
|
||||
gdb: default
|
||||
gdb $(EXEC) -ex "layout src"
|
||||
|
||||
# build pch
|
||||
pch:
|
||||
$(CXX) pch.hpp -std=c++20 -O3 -g $(IFLAGS)
|
||||
|
||||
# remove all object and dependecy files
|
||||
clean:
|
||||
# -rm -f $(OBJECT_DIR)/*.o
|
||||
# -rm -f $(OBJECT_DIR)/*.d
|
||||
-rm -r $(OBJECT_DIR)
|
12
compile.sh
Executable file
12
compile.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
WDIR=$HOME/c++/vulkan
|
||||
|
||||
mkdir -p $WDIR/shaders
|
||||
|
||||
glslc $WDIR/shader.vert -o $WDIR/shaders/vert.spv
|
||||
glslc $WDIR/shader.frag -o $WDIR/shaders/frag.spv
|
||||
|
||||
glslc $WDIR/shader2D.vert -o $WDIR/shaders/vert2D.spv
|
||||
glslc $WDIR/shader2D.frag -o $WDIR/shaders/frag2D.spv
|
||||
|
||||
|
21
exceptions.cpp
Normal file
21
exceptions.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "exceptions.hpp"
|
||||
|
||||
#include "vk_enum_string.h"
|
||||
|
||||
namespace gz {
|
||||
VkException getVkException(VkResult result, std::string&& what, std::string&& functionName) {
|
||||
std::string whatStr;
|
||||
if (!functionName.empty()) {
|
||||
whatStr += "Error in function: " + functionName + ": ";
|
||||
}
|
||||
else {
|
||||
whatStr += "Error: ";
|
||||
}
|
||||
if (!what.empty()) {
|
||||
whatStr += what + ": ";
|
||||
}
|
||||
whatStr += "VkResult=";
|
||||
whatStr += STR_VK_RESULT(result);
|
||||
return VkException(whatStr);
|
||||
}
|
||||
}
|
36
exceptions.hpp
Normal file
36
exceptions.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <gz-util/exceptions.hpp>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace gz {
|
||||
/**
|
||||
* @brief An error that has something to with vulkan
|
||||
*/
|
||||
class VkException : public Exception {
|
||||
public:
|
||||
VkException(const std::string& what) : Exception(what) {};
|
||||
VkException(const std::string&& what, const std::string&& functionName)
|
||||
: Exception(std::move(what), std::move(functionName)) {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A user error that has something to do with vulkan
|
||||
* @details
|
||||
* This error comes may come from bad function parameters
|
||||
*/
|
||||
class VkUserError : public VkException {
|
||||
public:
|
||||
VkUserError(const std::string&& what, const std::string&& functionName)
|
||||
: VkException(std::move(what), std::move(functionName)) {};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return a VkException with a formatted string
|
||||
*/
|
||||
VkException getVkException(VkResult result, std::string&& what="", std::string&& functionName="");
|
||||
}
|
28
font.cpp
Normal file
28
font.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "font.hpp"
|
||||
|
||||
#include <gz-util/exceptions.hpp>
|
||||
|
||||
namespace gz {
|
||||
|
||||
FontManager::FontManager(const std::string&& fontDir)
|
||||
: fontDir(fontDir) {
|
||||
FT_Error error = FT_Init_FreeType(&freetype);
|
||||
if (error != FT_Err_Ok) {
|
||||
throw Exception("Could not load freetype library", "FontManager");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void FontManager::loadFont(const std::string& font) {
|
||||
FT_Error error = FT_New_Face(freetype, (fontDir + "/" + font).c_str(), 0, &faces[font]);
|
||||
if (error != FT_Err_Ok) {
|
||||
throw Exception("Could not load font.", "FontManager::loadFont");
|
||||
}
|
||||
FT_Set_Pixel_Sizes(faces[font], 0, 48);
|
||||
if (FT_Load_Char(faces[font], 'X', FT_LOAD_RENDER)) {
|
||||
throw Exception("Could not load char.", "FontManager::loadFont");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
20
font.hpp
Normal file
20
font.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
namespace gz {
|
||||
class FontManager {
|
||||
public:
|
||||
FontManager(const std::string&& fontDir);
|
||||
void loadFont(const std::string& font);
|
||||
const std::unordered_map<std::string, FT_Face>& getFaces() const { return faces; }
|
||||
private:
|
||||
FT_Library freetype;
|
||||
std::string fontDir;
|
||||
std::unordered_map<std::string, FT_Face> faces;
|
||||
};
|
||||
}
|
60
main.cpp
Normal file
60
main.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "main.hpp"
|
||||
|
||||
#include "vulkan_instance.hpp"
|
||||
#include "renderer2D.hpp"
|
||||
#include <thread>
|
||||
|
||||
namespace gz::vk {
|
||||
int mainLoop() {
|
||||
gz::SettingsManagerCreateInfo<SettingsTypes> smCI{};
|
||||
smCI.filepath = gz::vk::CONFIG_FILE;
|
||||
smCI.readFileOnCreation = true;
|
||||
smCI.writeFileOnExit = true;
|
||||
smCI.initialValues = gz::vk::INITIAL_SETTINGS;
|
||||
smCI.throwExceptionWhenNewValueNotAllowed = true;
|
||||
|
||||
VulkanInstance vulkanInstance(smCI);
|
||||
vulkanInstance.init();
|
||||
Renderer2D r2D(vulkanInstance);
|
||||
|
||||
Rectangle rect1( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f});
|
||||
Rectangle rect2(190, 191, 192, 193, { 0.7f, 0.6f, 0.5f});
|
||||
Rectangle rect3(420, 64, 512, 64, { 0.0f, 1.0f, 0.0f});
|
||||
Rectangle rect4( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f});
|
||||
Rectangle rect5( -600, -600, 800, 400, { 1.0f, 0.0f, 0.0f});
|
||||
r2D.drawShape(rect1);
|
||||
r2D.drawShape(rect2);
|
||||
r2D.drawShape(rect3);
|
||||
r2D.drawShape(rect4);
|
||||
r2D.drawShape(rect5);
|
||||
r2D.fillVertexBufferWithShapes();
|
||||
r2D.fillIndexBufferWithShapes();
|
||||
/* gz::FontManager fm("fonts"); */
|
||||
/* fm.loadFont("menu.ttf"); */
|
||||
/* /1* fm.getFaces().at("menu.ttf"). *1/ */
|
||||
try {
|
||||
uint32_t imageIndex;
|
||||
while (! glfwWindowShouldClose(vulkanInstance.window)) {
|
||||
glfwPollEvents();
|
||||
imageIndex = vulkanInstance.beginFrameDraw();
|
||||
r2D.drawFrame(imageIndex);
|
||||
vulkanInstance.endFrameDraw(imageIndex);
|
||||
auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.settings.get<uint32_t>("framerate"));
|
||||
std::this_thread::sleep_for(SLEEP_TIME);
|
||||
}
|
||||
r2D.cleanup();
|
||||
vulkanInstance.cleanup();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
return gz::vk::mainLoop();
|
||||
}
|
19
main.hpp
Normal file
19
main.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace gz::vk {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace gz::vk
|
17
renderer.cpp
Normal file
17
renderer.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "renderer.hpp"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace gz::vk {
|
||||
void Renderer::cleanup_(){
|
||||
vkFreeCommandBuffers(vk.device, vk.commandPoolGraphics, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
|
||||
|
||||
vkDestroyBuffer(vk.device, indexBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, indexBufferMemory, nullptr);
|
||||
|
||||
vkDestroyBuffer(vk.device, vertexBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, vertexBufferMemory, nullptr);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
19
renderer.hpp
Normal file
19
renderer.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#include "vulkan_instance.hpp"
|
||||
|
||||
namespace gz::vk {
|
||||
class Renderer {
|
||||
public:
|
||||
Renderer(VulkanInstance& instance) : vk(instance) {};
|
||||
protected:
|
||||
void cleanup_();
|
||||
VulkanInstance& vk;
|
||||
std::vector<VkCommandBuffer> commandBuffers;
|
||||
/// On device local memory
|
||||
VkBuffer vertexBuffer;
|
||||
VkDeviceMemory vertexBufferMemory;
|
||||
VkDeviceSize vertexBufferSize;
|
||||
VkBuffer indexBuffer;
|
||||
VkDeviceMemory indexBufferMemory;
|
||||
VkDeviceSize indexBufferSize;
|
||||
}; // class RendererBase
|
||||
} // namespace gz::vk
|
247
renderer2D.cpp
Normal file
247
renderer2D.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
#include "renderer2D.hpp"
|
||||
|
||||
#include "exceptions.hpp"
|
||||
#include "vk_enum_string.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
|
||||
namespace gz::vk {
|
||||
|
||||
Renderer2D::Renderer2D(VulkanInstance& instance) :
|
||||
Renderer(instance),
|
||||
rLog("renderer2D.log", true, false, "2D-Renderer", Color::BMAGENTA, true, 100) {
|
||||
vk.createCommandBuffers(commandBuffers);
|
||||
const size_t vertexCount = 500;
|
||||
const size_t indexCount = 1000;
|
||||
vk.createVertexBuffer<Vertex2D>(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize);
|
||||
vk.createIndexBuffer<uint32_t>(indexCount, indexBuffer, indexBufferMemory, indexBufferSize);
|
||||
createRenderPass();
|
||||
createImages();
|
||||
renderPassID = vk.createFramebuffers(imageViews, renderPass);
|
||||
rLog("Created Renderer2D");
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::cleanup() {
|
||||
cleanup_();
|
||||
}
|
||||
|
||||
void Renderer2D::createImages() {
|
||||
images.resize(vk.scImages.size());
|
||||
imageMemory.resize(vk.scImages.size());
|
||||
imageViews.resize(vk.scImages.size());
|
||||
VkImageUsageFlags usage= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
vk.createImage(vk.scExtent.width, vk.scExtent.height, vk.scImageFormat, VK_IMAGE_TILING_OPTIMAL, usage, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, images[i], imageMemory[i]);
|
||||
vk.createImageView(vk.scImageFormat, images[i], imageViews[i], VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Renderer2D::createRenderPass() {
|
||||
VkAttachmentDescription colorBlendAttachment{};
|
||||
colorBlendAttachment.format = vk.scImageFormat;
|
||||
colorBlendAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorBlendAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorBlendAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorBlendAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
colorBlendAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
colorBlendAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
colorBlendAttachment.finalLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
|
||||
VkAttachmentReference colorAttachmentRef{};
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
/* VkAttachmentDescription depthAttachment{}; */
|
||||
/* depthAttachment.format = findDepthFormat(); */
|
||||
/* depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT; */
|
||||
/* depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; */
|
||||
/* depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
|
||||
/* depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; */
|
||||
/* depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; */
|
||||
/* depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; */
|
||||
/* depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
|
||||
|
||||
/* VkAttachmentReference depthAttachmentRef{}; */
|
||||
/* depthAttachmentRef.attachment = 1; */
|
||||
/* depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; */
|
||||
|
||||
VkSubpassDescription subpass{};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef;
|
||||
/* subpass.pDepthStencilAttachment = &depthAttachmentRef; */
|
||||
|
||||
VkSubpassDependency dependency{};
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.srcAccessMask = 0;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
/* VkSubpassDependency dependency{}; */
|
||||
/* dependency.srcSubpass = VK_SUBPASS_EXTERNAL; */
|
||||
/* dependency.dstSubpass = 0; */
|
||||
/* dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
|
||||
/* dependency.srcAccessMask = 0; */
|
||||
/* dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; */
|
||||
/* dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; */
|
||||
|
||||
/* std::array<VkAttachmentDescription, 2> attachments = { colorBlendAttachment, depthAttachment }; */
|
||||
std::vector<VkAttachmentDescription> attachments = { colorBlendAttachment };
|
||||
VkRenderPassCreateInfo renderPassCI{};
|
||||
renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassCI.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||
renderPassCI.pAttachments = attachments.data();
|
||||
renderPassCI.subpassCount = 1;
|
||||
renderPassCI.pSubpasses = &subpass;
|
||||
renderPassCI.dependencyCount = 1;
|
||||
renderPassCI.pDependencies = &dependency;
|
||||
/* renderPassCI.dependencyCount = 0; */
|
||||
/* renderPassCI.pDependencies = nullptr; */
|
||||
/* renderPassCI.correlatedViewMaskCount = 0; */
|
||||
/* renderPassCI.pCorrelatedViewMasks = nullptr; */
|
||||
VkResult result = vkCreateRenderPass(vk.device, &renderPassCI, nullptr, &renderPass);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Could not create render pass", "Renderer2D::createRenderPass");
|
||||
}
|
||||
rLog("createRenderPass: Created render pass.");
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) {
|
||||
VkCommandBufferBeginInfo commandBufferBI{};
|
||||
commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
/* commandBufferBI.flags = 0; */
|
||||
/* commandBufferBI.pInheritanceInfo = nullptr; */
|
||||
VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to begin 2D command buffer", "Renderer2D::recordCommandBuffer");
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo renderPassBI{};
|
||||
renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassBI.renderPass = renderPass;
|
||||
renderPassBI.framebuffer = vk.getFramebuffers(renderPassID)[imageIndex];
|
||||
renderPassBI.renderArea.offset = { 0, 0 };
|
||||
renderPassBI.renderArea.extent = vk.scExtent;
|
||||
// clear
|
||||
std::array<VkClearValue, 2> clearValues{};
|
||||
clearValues[0].color = {{1.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clearValues[1].depthStencil = {1.0f, 0};
|
||||
renderPassBI.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||
renderPassBI.pClearValues = clearValues.data();
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_2D].pipeline);
|
||||
VkBuffer vertexBuffers[] = { vertexBuffer };
|
||||
VkDeviceSize offsets[] = {0};
|
||||
uint32_t bindingCount = 1;
|
||||
vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets);
|
||||
// TODO use correct index type!
|
||||
vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
/* uint32_t descriptorCount = 1; */
|
||||
/* uint32_t firstSet = 0; */
|
||||
/* uint32_t dynamicOffsetCount = 0; */
|
||||
/* uint32_t* dynamicOffsets = nullptr; */
|
||||
/* vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[PL_2D].layout, firstSet, descriptorCount, &descriptorSets[currentFrame], dynamicOffsetCount, dynamicOffsets); */
|
||||
|
||||
int instanceCount = 1;
|
||||
int firstIndex = 0;
|
||||
int firstInstance = 0;
|
||||
vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast<uint32_t>(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance);
|
||||
|
||||
vkCmdEndRenderPass(commandBuffers[currentFrame]);
|
||||
vk.copyImageToImage(commandBuffers[currentFrame], images[imageIndex], vk.scImages[imageIndex], vk.scExtent.width, vk.scExtent.height);
|
||||
result = vkEndCommandBuffer(commandBuffers[currentFrame]);
|
||||
if (result != VK_SUCCESS) {
|
||||
rLog.error("Failed to record 2D - command buffer", "VkResult:", STR_VK_RESULT(result));
|
||||
throw getVkException(result, "Failed to record 2D - command buffer", "Renderer2D::recordCommandBuffer");
|
||||
}
|
||||
vk.commandBuffersToSubmitThisFrame.push_back(commandBuffers[currentFrame]);
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::fillVertexBufferWithShapes() {
|
||||
rLog("fillVertexBufferWithShapes");
|
||||
if (vertexBufferSize < shapesVerticesCount * sizeof(Vertex2D)) {
|
||||
throw VkException("vertex buffer too small! vertexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes");
|
||||
}
|
||||
|
||||
// create staging buffer
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
vk.createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
|
||||
|
||||
// fill staging buffer
|
||||
void* data;
|
||||
vkMapMemory(vk.device, stagingBufferMemory, NO_OFFSET, vertexBufferSize, NO_FLAGS, &data);
|
||||
Vertex2D* vdata = reinterpret_cast<Vertex2D*>(data);
|
||||
size_t offset = 0;
|
||||
for (auto it = shapes.begin(); it != shapes.end(); it++) {
|
||||
rLog("fillVertexBufferWithShapes: copying vertex buffer nr", it - shapes.begin(), "-", it->getVertices(), "to address:", long(vdata + offset), " offset:", offset);
|
||||
memcpy(vdata+offset, it->getVertices().data(), it->getVertices().size() * sizeof(Vertex2D));
|
||||
offset += it->getVertices().size();
|
||||
}
|
||||
vkUnmapMemory(vk.device, stagingBufferMemory);
|
||||
// fill vertex buffer
|
||||
vk.copyBuffer(stagingBuffer, vertexBuffer, vertexBufferSize);
|
||||
vkDestroyBuffer(vk.device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, stagingBufferMemory, nullptr);
|
||||
}
|
||||
void Renderer2D::fillIndexBufferWithShapes() {
|
||||
rLog("fillIndexBufferWithShapes");
|
||||
if (indexBufferSize < shapesIndicesCount * sizeof(uint32_t)) {
|
||||
throw VkException("index buffer too small! indexBufferSize: " + std::to_string(vertexBufferSize) + ", required size: " + std::to_string(shapesVerticesCount), "fillVertexBufferWithShapes");
|
||||
}
|
||||
|
||||
// create staging buffer
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
vk.createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
|
||||
|
||||
// fill staging buffer
|
||||
void* data;
|
||||
vkMapMemory(vk.device, stagingBufferMemory, NO_OFFSET, indexBufferSize, NO_FLAGS, &data);
|
||||
uint32_t* idata = reinterpret_cast<uint32_t*>(data);
|
||||
size_t offset = 0;
|
||||
for (auto it = shapes.begin(); it != shapes.end(); it++) {
|
||||
rLog("fillIndexBufferWithShapes: copying index buffer nr", it - shapes.begin(), "-", it->getIndices(), "to address:", long(idata + offset), " offset:", offset);
|
||||
memcpy(idata+offset, it->getIndices().data(), it->getIndices().size() * sizeof(uint32_t));
|
||||
offset += it->getIndices().size();
|
||||
}
|
||||
rLog("fillIndexBufferWithShapes: indices count:", shapesIndicesCount);
|
||||
vkUnmapMemory(vk.device, stagingBufferMemory);
|
||||
|
||||
// fill index buffer
|
||||
vk.copyBuffer(stagingBuffer, indexBuffer, indexBufferSize);
|
||||
vkDestroyBuffer(vk.device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(vk.device, stagingBufferMemory, nullptr);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::drawShape(const Shape& shape) {
|
||||
shapes.emplace_back(shape);
|
||||
// make indices valid
|
||||
shapes.rbegin()->setIndexOffset(shapesVerticesCount);
|
||||
shapes.rbegin()->setNormalize(vk.scExtent.width, vk.scExtent.height);
|
||||
shapesVerticesCount += shape.getVertices().size();
|
||||
shapesIndicesCount += shape.getIndices().size();
|
||||
}
|
||||
|
||||
|
||||
void Renderer2D::drawFrame(uint32_t imageIndex) {
|
||||
vkResetCommandBuffer(commandBuffers[vk.currentFrame], NO_FLAGS);
|
||||
recordCommandBuffer(imageIndex, vk.currentFrame);
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace gz::vk
|
62
renderer2D.hpp
Normal file
62
renderer2D.hpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include "renderer.hpp"
|
||||
|
||||
namespace gz::vk {
|
||||
class Renderer2D : public Renderer {
|
||||
public:
|
||||
Renderer2D(VulkanInstance& instance);
|
||||
void drawShape(const Shape& shape);
|
||||
/**
|
||||
* @brief Copies the vertices from shapes into the vertex buffer, using a staging buffer
|
||||
*/
|
||||
void fillVertexBufferWithShapes();
|
||||
void fillIndexBufferWithShapes();
|
||||
void drawFrame(uint32_t imageIndex);
|
||||
void cleanup();
|
||||
/**
|
||||
* @brief Create a render pass
|
||||
* @details
|
||||
* Attachments:
|
||||
* - color blend:
|
||||
* - loadOp = VK_ATTACHMENT_LOAD_OP_LOAD (not clear!)
|
||||
* - storeOp = VK_ATTACHMENT_STORE_OP_STORE
|
||||
* - initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - stencil load/store = dont care
|
||||
*/
|
||||
void createRenderPass();
|
||||
|
||||
private:
|
||||
std::vector<Shape> shapes;
|
||||
size_t shapesVerticesCount = 0;
|
||||
size_t shapesIndicesCount = 0;
|
||||
/**
|
||||
* @brief Render everything
|
||||
* @details
|
||||
* As of now, this does (in the same command buffer from graphicsPool)
|
||||
* -# begin render pass
|
||||
* - image layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
||||
* - clear image
|
||||
* -# bind 2d pipeline, vertex and index buffer
|
||||
* -# draw indexed: draw the shapes from shapes vector
|
||||
* -# end render pass
|
||||
* - image layout: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL
|
||||
* -# copy image to swapChain image with same imageIndex
|
||||
*
|
||||
*/
|
||||
void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame);
|
||||
// IMAGE
|
||||
std::vector<VkImage> images;
|
||||
std::vector<VkDeviceMemory> imageMemory;
|
||||
std::vector<VkImageView> imageViews;
|
||||
/**
|
||||
* @brief Creates the images and imageViews with the format of the VulkanInstance::swapChain images
|
||||
*/
|
||||
void createImages();
|
||||
VkRenderPass renderPass;
|
||||
int renderPassID;
|
||||
|
||||
Log rLog;
|
||||
|
||||
};
|
||||
|
||||
} // namespace gz::vk
|
105
renderer3D.cpp
Normal file
105
renderer3D.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
#include "renderer3D.hpp"
|
||||
|
||||
#include "exceptions.hpp"
|
||||
#include "vk_enum_string.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace gz::vk {
|
||||
|
||||
Renderer3D::Renderer3D(VulkanInstance& instance) :
|
||||
Renderer(instance),
|
||||
rLog("renderer3D.log", true, false, "3D-Renderer", Color::BCYAN, true, 100) {
|
||||
vk.createCommandBuffers(commandBuffers);
|
||||
const size_t vertexCount = 500;
|
||||
const size_t indexCount = 1000;
|
||||
vk.createVertexBuffer<Vertex2D>(vertexCount, vertexBuffer, vertexBufferMemory, vertexBufferSize);
|
||||
vk.createIndexBuffer<uint32_t>(indexCount, indexBuffer, indexBufferMemory, indexBufferSize);
|
||||
rLog("Created Renderer3D");
|
||||
}
|
||||
|
||||
void Renderer3D::cleanup() {
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
vkDestroyBuffer(vk.device, uniformBuffers[i], nullptr);
|
||||
vkFreeMemory(vk.device, uniformBuffersMemory[i], nullptr);
|
||||
}
|
||||
cleanup_();
|
||||
}
|
||||
|
||||
|
||||
void Renderer3D::updateUniformBuffer() {
|
||||
static auto startTime = std::chrono::high_resolution_clock::now();
|
||||
auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();
|
||||
|
||||
// TODO use push constant instead of ubo
|
||||
UniformBufferObject ubo{};
|
||||
/* ubo.model = glm::rotate(glm::mat4(1.0f), time * std::numbers::pi_v<float> / 2, glm::vec3(0.0f, 0.0f, 1.0f)); */
|
||||
/* ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)); */
|
||||
/* ubo.projection = glm::perspective(glm::radians(45.0f), static_cast<float>(scExtent.width) / scExtent.height, 1.0f, 10.0f); */
|
||||
ubo.model = glm::mat4(1);
|
||||
ubo.view = glm::mat4(1);
|
||||
ubo.projection = glm::mat4(1);
|
||||
/* ubo.projection[1][1] *= -1; // y coordinate inverted in opengl */
|
||||
void* data;
|
||||
vkMapMemory(vk.device, uniformBuffersMemory[vk.currentFrame], NO_OFFSET, sizeof(ubo), NO_FLAGS, &data);
|
||||
memcpy(data, &ubo, sizeof(ubo));
|
||||
vkUnmapMemory(vk.device, uniformBuffersMemory[vk.currentFrame]);
|
||||
}
|
||||
|
||||
|
||||
void Renderer3D::recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame) {
|
||||
VkCommandBufferBeginInfo commandBufferBI{};
|
||||
commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
/* commandBufferBI.flags = 0; */
|
||||
/* commandBufferBI.pInheritanceInfo = nullptr; */
|
||||
VkResult result = vkBeginCommandBuffer(commandBuffers[currentFrame], &commandBufferBI);
|
||||
if (result != VK_SUCCESS) {
|
||||
throw getVkException(result, "Failed to begin 3D - command buffer", "recordCommandBuffer");
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo renderPassBI{};
|
||||
renderPassBI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassBI.renderPass = vk.renderPassBegin;
|
||||
// TODO
|
||||
/* renderPassBI.framebuffer = vk.scFramebuffers[0][imageIndex]; */
|
||||
renderPassBI.renderArea.offset = { 0, 0 };
|
||||
renderPassBI.renderArea.extent = vk.scExtent;
|
||||
std::array<VkClearValue, 2> clearValues{};
|
||||
clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clearValues[1].depthStencil = {1.0f, 0};
|
||||
renderPassBI.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||
renderPassBI.pClearValues = clearValues.data();
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffers[currentFrame], &renderPassBI, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
vkCmdBindPipeline(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_3D].pipeline);
|
||||
VkBuffer vertexBuffers[] = { vertexBuffer };
|
||||
VkDeviceSize offsets[] = {0};
|
||||
uint32_t bindingCount = 1;
|
||||
vkCmdBindVertexBuffers(commandBuffers[currentFrame], BINDING, bindingCount, vertexBuffers, offsets);
|
||||
// TODO use correct index type!
|
||||
vkCmdBindIndexBuffer(commandBuffers[currentFrame], indexBuffer, NO_OFFSET, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
uint32_t descriptorCount = 1;
|
||||
uint32_t firstSet = 0;
|
||||
uint32_t dynamicOffsetCount = 0;
|
||||
uint32_t* dynamicOffsets = nullptr;
|
||||
vkCmdBindDescriptorSets(commandBuffers[currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, vk.pipelines[PL_3D].layout, firstSet, descriptorCount, &vk.descriptorSets[currentFrame], dynamicOffsetCount, dynamicOffsets);
|
||||
|
||||
int instanceCount = 1;
|
||||
int firstIndex = 0;
|
||||
int firstInstance = 0;
|
||||
/* vkCmdDrawIndexed(commandBuffers[currentFrame], static_cast<uint32_t>(shapesIndicesCount), instanceCount, firstIndex, NO_OFFSET, firstInstance); */
|
||||
|
||||
vkCmdEndRenderPass(commandBuffers[currentFrame]);
|
||||
result = vkEndCommandBuffer(commandBuffers[currentFrame]);
|
||||
if (result != VK_SUCCESS) {
|
||||
rLog.error("Failed to record command buffer", "VkResult:", STR_VK_RESULT(result));
|
||||
throw getVkException(result, "Failed to record command buffer");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace gz::vk
|
50
renderer3D.hpp
Normal file
50
renderer3D.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "renderer.hpp"
|
||||
|
||||
namespace gz::vk {
|
||||
struct UniformBufferObject {
|
||||
alignas(16) glm::mat4 model;
|
||||
alignas(16) glm::mat4 view;
|
||||
alignas(16) glm::mat4 projection;
|
||||
};
|
||||
|
||||
|
||||
const std::vector<Vertex3D> vertices2 = {
|
||||
{{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}},
|
||||
{{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
|
||||
{{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
|
||||
{{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
|
||||
|
||||
{{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}},
|
||||
{{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}},
|
||||
{{ 0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}},
|
||||
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
|
||||
|
||||
};
|
||||
/* const std::vector<Vertex> vertices = { */
|
||||
/* {{-1.0f, -1.0f}, {0.0f, 0.0f, 1.0f}}, */
|
||||
/* {{0.5f, 0.5f}, {0.0, 1.0f, 1.0f}}, */
|
||||
/* {{-1.0, 0.0f}, {1.0f, 0.0f, 0.0f}}, */
|
||||
/* {{1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}}, */
|
||||
/* {{-0.5f, -0.5f}, {0.0, 1.0f, 1.0f}}, */
|
||||
/* {{1.0, 0.0f}, {1.0f, 0.0f, 1.0f}}, */
|
||||
/* }; */
|
||||
|
||||
|
||||
const std::vector<uint16_t> indices2 = {
|
||||
0, 1, 2, 2, 3, 0,
|
||||
4, 5, 6, 6, 7, 4
|
||||
};
|
||||
|
||||
class Renderer3D : public Renderer {
|
||||
public:
|
||||
Renderer3D(VulkanInstance& instance);
|
||||
void cleanup();
|
||||
protected:
|
||||
void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame);
|
||||
std::vector<VkBuffer> uniformBuffers;
|
||||
std::vector<VkDeviceMemory> uniformBuffersMemory;
|
||||
void updateUniformBuffer();
|
||||
Log rLog;
|
||||
};
|
||||
|
||||
}
|
16
shader.frag
Normal file
16
shader.frag
Normal file
@ -0,0 +1,16 @@
|
||||
#version 450
|
||||
#extension GL_EXT_debug_printf : enable
|
||||
|
||||
layout(binding = 1) uniform sampler2D textureSampler;
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 1) in vec2 fragTextureCoordinate;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
/* outColor = vec4(fragColor, 1.0); */
|
||||
/* debugPrintfEXT("outColor %v3f", fragColor); */
|
||||
outColor = vec4(fragTextureCoordinate, 0.0, 1.0);
|
||||
/* outColor = vec4(fragColor * texture(textureSampler, fragTextureCoordinate).rgb, 1.0); */
|
||||
}
|
25
shader.vert
Normal file
25
shader.vert
Normal file
@ -0,0 +1,25 @@
|
||||
#version 450
|
||||
#extension GL_EXT_debug_printf : enable
|
||||
|
||||
layout(binding = 0) uniform UniformBufferObject {
|
||||
mat4 model;
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
} ubo;
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
layout(location = 2) in vec2 inTextureCoordinate;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec2 fragTextureCoordinate;
|
||||
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
||||
/* gl_Position = vec4(inPosition, 1.0); */
|
||||
|
||||
debugPrintfEXT("inPosition %v3f, inColor %v3f", inPosition, inColor);
|
||||
fragColor = inColor;
|
||||
fragTextureCoordinate = inTextureCoordinate;
|
||||
}
|
16
shader2D.frag
Normal file
16
shader2D.frag
Normal file
@ -0,0 +1,16 @@
|
||||
#version 450
|
||||
#extension GL_EXT_debug_printf : enable
|
||||
|
||||
/* layout(binding = 1) uniform sampler2D textureSampler; */
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
layout(location = 1) in vec2 fragTextureCoordinate;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
/* debugPrintfEXT("outColor %v3f", fragColor); */
|
||||
/* outColor = vec4(fragTextureCoordinate, 0.0, 1.0); */
|
||||
/* outColor = vec4(fragColor * texture(textureSampler, fragTextureCoordinate).rgb, 1.0); */
|
||||
}
|
25
shader2D.vert
Normal file
25
shader2D.vert
Normal file
@ -0,0 +1,25 @@
|
||||
#version 450
|
||||
#extension GL_EXT_debug_printf : enable
|
||||
|
||||
/* layout(binding = 0) uniform UniformBufferObject { */
|
||||
/* mat4 model; */
|
||||
/* mat4 view; */
|
||||
/* mat4 proj; */
|
||||
/* } ubo; */
|
||||
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
layout(location = 2) in vec2 inTextureCoordinate;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec2 fragTextureCoordinate;
|
||||
|
||||
|
||||
void main() {
|
||||
/* gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); */
|
||||
gl_Position = vec4(inPosition, 0.0, 1.0);
|
||||
|
||||
debugPrintfEXT("inPosition %v2f, inColor %v3f", inPosition, inColor);
|
||||
fragColor = inColor;
|
||||
fragTextureCoordinate = inTextureCoordinate;
|
||||
}
|
34
shape.cpp
Normal file
34
shape.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "shape.hpp"
|
||||
|
||||
namespace gz::vk {
|
||||
Rectangle::Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color)
|
||||
: top(top), left(left), width(width), height(height), color(color) {
|
||||
generateVertices();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Rectangle::generateVertices() {
|
||||
vertices.clear();
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top, left), color, glm::vec2(0, 0)});
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top, left + width), color, glm::vec2(1, 0)});
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top + height, left + width), color, glm::vec2(1, 1)});
|
||||
vertices.emplace_back(Vertex2D{glm::vec2(top + height, left), color, glm::vec2(0, 1)});
|
||||
indices = { 0, 1, 2, 2, 3, 0 };
|
||||
/* indices = { 2, 1, 0, 2, 3, 0 }; */
|
||||
}
|
||||
|
||||
void Shape::setIndexOffset(uint32_t offset) {
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
indices[i] += offset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Shape::setNormalize(float width, float height) {
|
||||
for (size_t i = 0; i < vertices.size(); i++) {
|
||||
vertices[i].pos.x /= width;
|
||||
vertices[i].pos.y /= height;
|
||||
}
|
||||
}
|
||||
} // namespaec gz::vk
|
29
shape.hpp
Normal file
29
shape.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "vertex.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace gz::vk {
|
||||
class Shape {
|
||||
public:
|
||||
const std::vector<Vertex2D>& getVertices() const { return vertices; }
|
||||
const std::vector<uint32_t>& getIndices() const { return indices; }
|
||||
void setIndexOffset(uint32_t offset);
|
||||
void setNormalize(float width, float height);
|
||||
|
||||
protected:
|
||||
std::vector<Vertex2D> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
|
||||
};
|
||||
|
||||
class Rectangle : public Shape {
|
||||
public:
|
||||
Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color);
|
||||
private:
|
||||
float top, left;
|
||||
uint32_t width, height;
|
||||
glm::vec3 color;
|
||||
void generateVertices();
|
||||
};
|
||||
} // namespace gz::vk
|
73
vertex.cpp
Normal file
73
vertex.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#include "vertex.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <gz-util/util/string_conversion.hpp>
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
namespace gz::vk {
|
||||
const uint32_t BINDING = 0;
|
||||
|
||||
template<GLM_vec2_or_3 PosVec>
|
||||
VkFormat getVkFormat() {
|
||||
if (std::same_as<PosVec, glm::vec3>) {
|
||||
return VK_FORMAT_R32G32B32_SFLOAT;
|
||||
}
|
||||
else if (std::same_as<PosVec, glm::vec2>) {
|
||||
return VK_FORMAT_R32G32_SFLOAT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 3D VERTEX
|
||||
//
|
||||
template<GLM_vec2_or_3 PosVec>
|
||||
std::string Vertex<PosVec>::toString() const {
|
||||
return "pos: " + gz::toString(pos) + ", color: " + gz::toString(color) + ", texCoords: <TODO>"; // + gz::toString(texCoord);
|
||||
};
|
||||
|
||||
|
||||
template<GLM_vec2_or_3 PosVec>
|
||||
VkVertexInputBindingDescription Vertex<PosVec>::getBindingDescription() {
|
||||
VkVertexInputBindingDescription bindingD{};
|
||||
bindingD.binding = BINDING;
|
||||
bindingD.stride = sizeof(Vertex<PosVec>);
|
||||
bindingD.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
return bindingD;
|
||||
}
|
||||
|
||||
template<GLM_vec2_or_3 PosVec>
|
||||
std::array<VkVertexInputAttributeDescription, 3> Vertex<PosVec>::getAttributeDescriptions() {
|
||||
std::array<VkVertexInputAttributeDescription, 3> inputAttributeD{};
|
||||
inputAttributeD[0].binding = BINDING;
|
||||
inputAttributeD[0].location = 0;
|
||||
inputAttributeD[0].format = getVkFormat<PosVec>();
|
||||
inputAttributeD[0].offset = offsetof(Vertex, pos);
|
||||
inputAttributeD[1].binding = BINDING;
|
||||
inputAttributeD[1].location = 1;
|
||||
inputAttributeD[1].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
inputAttributeD[1].offset = offsetof(Vertex, color);
|
||||
inputAttributeD[2].binding = BINDING;
|
||||
inputAttributeD[2].location = 2;
|
||||
inputAttributeD[2].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
inputAttributeD[2].offset = offsetof(Vertex, texCoord);
|
||||
|
||||
return inputAttributeD;
|
||||
}
|
||||
|
||||
template<GLM_vec2_or_3 PosVec>
|
||||
bool Vertex<PosVec>::operator==(const Vertex<PosVec>& other) const {
|
||||
return pos == other.pos and color == other.color and texCoord == other.texCoord;
|
||||
}
|
||||
|
||||
template std::string Vertex<glm::vec2>::toString() const;
|
||||
template std::string Vertex<glm::vec3>::toString() const;
|
||||
template VkVertexInputBindingDescription Vertex<glm::vec2>::getBindingDescription();
|
||||
template VkVertexInputBindingDescription Vertex<glm::vec3>::getBindingDescription();
|
||||
template std::array<VkVertexInputAttributeDescription, 3> Vertex<glm::vec2>::getAttributeDescriptions();
|
||||
template std::array<VkVertexInputAttributeDescription, 3> Vertex<glm::vec3>::getAttributeDescriptions();
|
||||
template bool Vertex<glm::vec2>::operator==(const Vertex<glm::vec2>& other) const;
|
||||
template bool Vertex<glm::vec3>::operator==(const Vertex<glm::vec3>& other) const;
|
||||
|
||||
} // namespace gz::vk
|
59
vertex.hpp
Normal file
59
vertex.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
/* #include <bits/ranges_base.h> */
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/hash.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
/* #include <vulkan/vulkan_core.h> */
|
||||
|
||||
struct VkVertexInputBindingDescription;
|
||||
struct VkVertexInputAttributeDescription;
|
||||
|
||||
namespace gz::vk {
|
||||
template<typename T>
|
||||
concept VertexType = requires {
|
||||
requires std::same_as<decltype(T::getBindingDescription()), VkVertexInputBindingDescription>;
|
||||
requires std::ranges::forward_range<decltype(T::getAttributeDescriptions())>;
|
||||
requires std::same_as<std::ranges::range_value_t<decltype(T::getAttributeDescriptions())>, VkVertexInputAttributeDescription>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept GLM_vec2_or_3 = std::same_as<glm::vec3, T> or std::same_as<glm::vec2, T>;
|
||||
|
||||
//
|
||||
// VERTEX
|
||||
//
|
||||
/**
|
||||
* @brief Base for 2D and 3D vertices with texture and/or color
|
||||
*/
|
||||
template<GLM_vec2_or_3 PosVec>
|
||||
struct Vertex {
|
||||
PosVec pos;
|
||||
glm::vec3 color;
|
||||
glm::vec2 texCoord;
|
||||
std::string toString() const;
|
||||
bool operator==(const Vertex& other) const;
|
||||
static VkVertexInputBindingDescription getBindingDescription();
|
||||
static std::array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions();
|
||||
}; // struct Vertex
|
||||
using Vertex3D = Vertex<glm::vec3>;
|
||||
using Vertex2D = Vertex<glm::vec2>;
|
||||
} // namespace gz::vk
|
||||
|
||||
|
||||
|
||||
//
|
||||
// HASHES
|
||||
//
|
||||
namespace std {
|
||||
template<gz::vk::GLM_vec2_or_3 PosVec>
|
||||
struct hash<gz::vk::Vertex<PosVec>> {
|
||||
size_t operator()(gz::vk::Vertex<PosVec> const& vertex) const {
|
||||
return ((hash<PosVec>()(vertex.pos)) ^
|
||||
(hash<glm::vec3>()(vertex.color) << 1) >> 1 ) ^
|
||||
(hash<glm::vec2>()(vertex.texCoord) << 1);
|
||||
}
|
||||
};
|
||||
}
|
37
vk_convert.cpp
Normal file
37
vk_convert.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include "vk_convert.hpp"
|
||||
|
||||
bool vkBool2Bool(const VkBool32& b) {
|
||||
if (b == VK_TRUE) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
VkBool32 bool2VkBool(const bool& b) {
|
||||
if (b) {
|
||||
return VK_TRUE;
|
||||
}
|
||||
else {
|
||||
return VK_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
std::string vkBool2String(const VkBool32& b) {
|
||||
if (b == VK_TRUE) {
|
||||
return "true";
|
||||
}
|
||||
else {
|
||||
return "false";
|
||||
}
|
||||
};
|
||||
|
||||
VkBool32 string2VkBool(const std::string& s) {
|
||||
if (s == "true") {
|
||||
return VK_TRUE;
|
||||
}
|
||||
else {
|
||||
return VK_FALSE;
|
||||
}
|
||||
}
|
9
vk_convert.hpp
Normal file
9
vk_convert.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <string>
|
||||
|
||||
VkBool32 bool2VkBool(const bool& b);
|
||||
bool vkBool2Bool(const VkBool32& b);
|
||||
|
||||
VkBool32 string2VkBool(const std::string& s);
|
||||
std::string vkBool2String(const VkBool32& b);
|
||||
|
2
vk_layer_settings.txt
Normal file
2
vk_layer_settings.txt
Normal file
@ -0,0 +1,2 @@
|
||||
# https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/master/layers/vk_layer_settings.txt
|
||||
khronos_validation.enables = VK_LAYER_ENABLES=VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT;VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD
|
4
vulkan.conf
Normal file
4
vulkan.conf
Normal file
@ -0,0 +1,4 @@
|
||||
# Written by writeKeyValueFile
|
||||
max_anisotropy = 1
|
||||
anisotropy_enable = false
|
||||
framerate = 60
|
2200
vulkan_instance.cpp
Normal file
2200
vulkan_instance.cpp
Normal file
File diff suppressed because it is too large
Load Diff
487
vulkan_instance.hpp
Normal file
487
vulkan_instance.hpp
Normal file
@ -0,0 +1,487 @@
|
||||
#pragma once
|
||||
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "vk_convert.hpp"
|
||||
|
||||
#include "vertex.hpp"
|
||||
#include "shape.hpp"
|
||||
#include "vulkan_util.hpp"
|
||||
|
||||
|
||||
#include <gz-util/log.hpp>
|
||||
#include <gz-util/settings_manager.hpp>
|
||||
#include <gz-util/util/string.hpp>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace gz::vk {
|
||||
const std::string CONFIG_FILE = "vulkan.conf";
|
||||
const int MAX_FRAMES_IN_FLIGHT = 3;
|
||||
#define SettingsTypes uint32_t, bool, float
|
||||
|
||||
/* const std::string MODEL_PATH = "models/gazebo-3d-model/gazebo.obj"; */
|
||||
/* const std::string TEXTURE_PATH = "models/gazebo-3d-model/gazebo_diffuse.png"; */
|
||||
|
||||
const std::string MODEL_PATH = "models/armoire-3d-model/Armoire.obj";
|
||||
const std::string TEXTURE_PATH = "models/armoire-3d-model/Armoire_diffuse.png";
|
||||
|
||||
const gz::util::unordered_string_map<std::string> INITIAL_SETTINGS = {
|
||||
{ "framerate", "60" },
|
||||
{ "anisotropy_enable", "false" },
|
||||
{ "max_anisotropy", "1" },
|
||||
/* { "", "" } */
|
||||
};
|
||||
|
||||
|
||||
const int BINDING = 0;
|
||||
const uint32_t NO_FLAGS = 0;
|
||||
const uint32_t NO_OFFSET = 0;
|
||||
const size_t VERTEX_BUFFER_SIZE = 512;
|
||||
const size_t INDEX_BUFFER_SIZE = 512;
|
||||
class VulkanInstance {
|
||||
friend class Renderer;
|
||||
friend class Renderer2D;
|
||||
friend class Renderer3D;
|
||||
public:
|
||||
VulkanInstance(gz::SettingsManagerCreateInfo<SettingsTypes>smCI) : settings(smCI) {};
|
||||
void init();
|
||||
uint32_t beginFrameDraw();
|
||||
void submitThisFrame(VkCommandBuffer);
|
||||
void endFrameDraw(uint32_t imageIndex);
|
||||
void deInit();
|
||||
GLFWwindow* window;
|
||||
void cleanup();
|
||||
/// The frame in the swap chain that is currently drawn to
|
||||
uint32_t currentFrame = 0;
|
||||
//
|
||||
// SETTINGS
|
||||
//
|
||||
gz::SettingsManager<SettingsTypes> settings;
|
||||
private:
|
||||
void mainLoop();
|
||||
std::vector<VkCommandBuffer> commandBuffersToSubmitThisFrame;
|
||||
|
||||
void createWindow();
|
||||
|
||||
|
||||
//
|
||||
// INSTANCE
|
||||
//
|
||||
VkInstance instance;
|
||||
/**
|
||||
* @brief Create the vulkan instance
|
||||
* @details
|
||||
* - check if validationLayers are available (if enabled)
|
||||
* - create instance with info
|
||||
* - check if all extensions required by glfw are available
|
||||
*/
|
||||
void createInstance();
|
||||
|
||||
//
|
||||
// PHYSICAL DEVICE
|
||||
//
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||
VkPhysicalDeviceProperties phDevProperties;
|
||||
VkPhysicalDeviceFeatures phDevFeatures;
|
||||
/**
|
||||
* @brief Assign the physicalDevice handle to the @ref rateDevice "best rated" GPU
|
||||
* @details
|
||||
* After this method, physicalDevice, phDevProperties and phDevFeatures will be initialized
|
||||
*/
|
||||
void selectPhysicalDevice();
|
||||
/**
|
||||
* @brief Assigns a score to a physical device.
|
||||
* @details
|
||||
* score = GPU_TYPE_FACTOR + VRAM_SIZE (in MB) + SUPPORTED_QUEUES_FACTOR + FEATURES_FACTORS
|
||||
* A GPU is unsuitable and gets score 0 if it does not:
|
||||
* - have the needed queue families
|
||||
* - support all necessary extensions
|
||||
* - have swap chain support
|
||||
*/
|
||||
unsigned int rateDevice(VkPhysicalDevice device);
|
||||
/**
|
||||
* @brief Set valid values for the SettingsManager according to phDevFeatures and phDevProperties
|
||||
* @details
|
||||
* Must be called after selectPhysicalDevice
|
||||
* Sets valid values for:
|
||||
* - anisotropy_enable
|
||||
* - max_anisotropy
|
||||
*/
|
||||
void setValidSettings();
|
||||
/**
|
||||
* @brief Find the best of the supported formats
|
||||
* @param candidates Candidate format, from best to worst
|
||||
* @returns The first format from candidates that is supported
|
||||
*/
|
||||
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features);
|
||||
|
||||
//
|
||||
// QUEUE
|
||||
//
|
||||
QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
|
||||
QueueFamilyIndices qFamilyIndices;
|
||||
VkQueue graphicsQ;
|
||||
VkQueue presentQ;
|
||||
// if possible, use dedicated transferQ. if not available, transferQ = graphicsQ
|
||||
VkQueue transferQ;
|
||||
//
|
||||
// LOGICAL DEVICE
|
||||
//
|
||||
VkDevice device;
|
||||
/**
|
||||
* @details
|
||||
* request anisotropic sampling feature
|
||||
*/
|
||||
void createLogicalDevice();
|
||||
|
||||
//
|
||||
// SURFACE
|
||||
//
|
||||
VkSurfaceKHR surface;
|
||||
void createSurface();
|
||||
//
|
||||
// SWAP CHAIN
|
||||
//
|
||||
VkSwapchainKHR swapChain;
|
||||
std::vector<VkImage> scImages;
|
||||
VkFormat scImageFormat;
|
||||
VkExtent2D scExtent;
|
||||
|
||||
SwapChainSupport querySwapChainSupport(VkPhysicalDevice device);
|
||||
/**
|
||||
* @todo Rate formats if preferred is not available
|
||||
*/
|
||||
VkSurfaceFormatKHR selectSwapChainSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
|
||||
/**
|
||||
* @todo Check settings for preferred mode
|
||||
*/
|
||||
VkPresentModeKHR selectSwapChainPresentMode(const std::vector<VkPresentModeKHR>& availableModes);
|
||||
VkExtent2D selectSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
|
||||
void createSwapChain();
|
||||
void recreateSwapChain();
|
||||
void cleanupSwapChain();
|
||||
//
|
||||
// IMAGE VIEW
|
||||
//
|
||||
std::vector<VkImageView> scImageViews;
|
||||
void createImageViews();
|
||||
/**
|
||||
* @brief Create a 2D imageView with format for image.
|
||||
*/
|
||||
void createImageView(VkFormat format, VkImage& image, VkImageView& imageView, VkImageAspectFlags aspectFlags);
|
||||
//
|
||||
// RENDER PASS
|
||||
//
|
||||
VkRenderPass renderPassBegin;
|
||||
VkRenderPass renderPassEnd;
|
||||
/**
|
||||
* @brief Create the render pass
|
||||
* @details
|
||||
* The subpass will contain the following attachments
|
||||
* -# color attachment
|
||||
* -# depth stencil attachment
|
||||
*/
|
||||
void createRenderPassBegin();
|
||||
void createRenderPassEnd();
|
||||
//
|
||||
// DESCRIPTORS
|
||||
//
|
||||
/**
|
||||
* @name Create desciptors
|
||||
* @details These functions create a desciptor with bindings for
|
||||
* -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
* -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
* @{
|
||||
*/
|
||||
VkDescriptorSetLayout descriptorSetLayout;
|
||||
/**
|
||||
* @brief Create a descriptor layout binding for the MVP uniform buffer object
|
||||
*/
|
||||
void createDescriptorSetLayout();
|
||||
VkDescriptorPool descriptorPool;
|
||||
/**
|
||||
* @brief Create a descriptor layout binding for the MVP uniform buffer object
|
||||
* @details Create a desciptor set layout with bindings for
|
||||
* -# UniformBufferObject (DESCRIPTOR_TYPE_UNIFORM_BUFFER)
|
||||
* -# Combined Image Sampler (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
*/
|
||||
void createDescriptorPool();
|
||||
std::vector<VkDescriptorSet> descriptorSets;
|
||||
/*
|
||||
* @bried Create one descriptor set with layout descriptorSetLayout for each frame
|
||||
*/
|
||||
void createDescriptorSets();
|
||||
const uint32_t bindingUniformBuffer = 0;
|
||||
const uint32_t bindingCombinedImageSampler = 1;
|
||||
/* const uint32_t bindingCombinedImageSampler = 1; */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
//
|
||||
// PIPELINE
|
||||
//
|
||||
|
||||
PipelineContainer pipelines;
|
||||
/* void createGraphicsPipeline(); */
|
||||
VkShaderModule createShaderModule(const std::vector<char>& code);
|
||||
/**
|
||||
* @brief Create a new graphics pipeline
|
||||
* @param vertexShader Path to the SPIR-V vertex shader
|
||||
* @param fragmentShader Path to the SPIR-V fragment shader
|
||||
* @param useDepthStencil Wether to use depth // TODO
|
||||
* @param renderPass The (already created) render pass
|
||||
* @param pipelineLayout Pipeline layout handle to bind
|
||||
* @param pipeline Pipeline handle to bind
|
||||
* @details
|
||||
* Create a pipeline with:
|
||||
* - 2 shader stages: vertex and fragment shader
|
||||
* - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, vertices are triangles
|
||||
* - viewport viewing the whole image as described by scExtent
|
||||
* - scissor with offset (0, 0)
|
||||
* - rasterizer:
|
||||
* - triangles are filled with the colors from the vertex (VK_POLYGON_FILL)
|
||||
* - counter clockwise front face (VK_FRONT_FACE_COUNTER_CLOCKWISE)
|
||||
*
|
||||
*
|
||||
*/
|
||||
template<VertexType T>
|
||||
void createGraphicsPipeline(const std::string& vertexShader, const std::string& fragmentShader, bool useDepthStencil, VkRenderPass& renderPass, Pipeline& pipeline);
|
||||
//
|
||||
// FONT
|
||||
//
|
||||
// TODO
|
||||
/* VkPipeline fontPipeline; */
|
||||
/* VkPipelineLayout fontPipelineLayout; */
|
||||
/* void createFontPipeline(); */
|
||||
//
|
||||
// BUFFERS
|
||||
//
|
||||
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory, VkSharingMode sharingMode=VK_SHARING_MODE_EXCLUSIVE, std::vector<uint32_t>* qFamiliesWithAccess=nullptr);
|
||||
uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties);
|
||||
/**
|
||||
* @brief Begin a command buffer that is going to be used once
|
||||
* @param commandPool: The command pool from which the buffer should be allocated
|
||||
*/
|
||||
VkCommandBuffer beginSingleTimeCommands(VkCommandPool commandPool);
|
||||
/**
|
||||
* @brief Submit cmdBuffer on the queue and free it afterwards
|
||||
* @param cmdBuffer: Command buffer returned by beginSingleTimeCommands()
|
||||
* @param q: The queue belonging to the commandPool parameter from beginSingleTimeCommands()
|
||||
*/
|
||||
void endSingleTimeCommands(VkCommandBuffer cmdBuffer, VkCommandPool commandPool, VkQueue q);
|
||||
/**
|
||||
* @brief Copy from srcBuffer to dstBuffer
|
||||
*/
|
||||
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
|
||||
//
|
||||
// VERTEX BUFFER
|
||||
//
|
||||
/**
|
||||
* @brief Create a vertex buffer
|
||||
* @param vertexCount Number of vertices the buffer should contain
|
||||
* @todo Basically the same as createIndexBuffer
|
||||
*/
|
||||
template<VertexType VertexT>
|
||||
void createVertexBuffer(size_t vertexCount, VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory, VkDeviceSize& vertexBufferSize);
|
||||
//
|
||||
// INDEX BUFFER
|
||||
//
|
||||
template<SupportedIndexType T>
|
||||
void createIndexBuffer(size_t indexCount, VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory, VkDeviceSize& indexBufferSize);
|
||||
//
|
||||
// UNIFORM BUFFER
|
||||
//
|
||||
template<typename T>
|
||||
void createUniformBuffers(std::vector<VkBuffer>& uniformBuffers, std::vector<VkDeviceMemory>& uniformBuffersMemory);
|
||||
/* void createUniformBuffers(); */
|
||||
//
|
||||
// FRAMEBUFFERS
|
||||
//
|
||||
/* std::vector<VkFramebuffer> scFramebuffers; */
|
||||
std::set<int> framebufferIDs;
|
||||
// render pass is need for recreation of framebuffer
|
||||
std::unordered_map<int, std::pair<std::vector<VkFramebuffer>, VkRenderPass>> scFramebuffers;
|
||||
/**
|
||||
* @brief Create a vector of framebuffers and return the id as a handle
|
||||
* @returns id that can be used with getFramebuffers and destroyFramebuffers
|
||||
*/
|
||||
int createFramebuffers(std::vector<VkImageView>& imageViews, VkRenderPass& renderPass);
|
||||
/**
|
||||
* @brief Destroy the framebuffers from id
|
||||
*/
|
||||
void destroyFramebuffers(int id);
|
||||
void createFramebuffers_(std::vector<VkFramebuffer>& framebuffers, std::vector<VkImageView>& imageViews, VkRenderPass& renderPass);
|
||||
/**
|
||||
* @brief Recreate all framebuffers in scFramebuffers
|
||||
*/
|
||||
void recreateFramebuffers();
|
||||
/**
|
||||
* @brief Get the framebuffer vector to id
|
||||
*/
|
||||
std::vector<VkFramebuffer>& getFramebuffers(int id);
|
||||
bool frameBufferResized = false;
|
||||
static void frameBufferResizedCallback(GLFWwindow* window, int width, int height);
|
||||
//
|
||||
// COMMAND POOL
|
||||
//
|
||||
VkCommandPool commandPoolGraphics;
|
||||
VkCommandPool commandPoolTransfer;
|
||||
void createCommandPools();
|
||||
//
|
||||
// COMMAND BUFFER
|
||||
//
|
||||
void createCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers);
|
||||
void destroyCommandBuffers(std::vector<VkCommandBuffer>& commandBuffers);
|
||||
std::vector<VkCommandBuffer> commandBuffersBegin;
|
||||
std::vector<VkCommandBuffer> commandBuffersEnd;
|
||||
//
|
||||
// IMAGE UTILITY
|
||||
//
|
||||
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags memoryProperties, VkImage& image, VkDeviceMemory& imageMemory);
|
||||
void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
|
||||
/**
|
||||
* @todo make a version using vkCmdResolveImage for multisampled images
|
||||
* @brief Copy srcImage to dstImage
|
||||
* @details
|
||||
* Both images must have:
|
||||
* - same extent
|
||||
* - mipLevel 0
|
||||
* - layerCount 1
|
||||
*
|
||||
* Calls vkCmdBlitImage, but does NOT submit the commandBuffer
|
||||
* @param cmdBuffer The command buffer where the command will be recorded to
|
||||
* @param srcImage Image with layout TRANSFER_SRC_OPTIMAL
|
||||
* @param dstImage Image with layout TRANSFER_DST_OPTIMAL
|
||||
*/
|
||||
void copyImageToImage(VkCommandBuffer& cmdBuffer, VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height); //, VkCommandPool& commandPool=commandPoolTransfer);
|
||||
/**
|
||||
* @brief Transition the layout of image from oldLayout to newLayout
|
||||
* @details
|
||||
* Supported transitions:
|
||||
* - undefined -> depth stencil attachment (graphics q)
|
||||
* - undefined -> transfer dst optimal (transfer q)
|
||||
* - transfer dst optimal -> shader read only optimal (graphics q)
|
||||
* - transfer dst optimal -> present src (graphics q)
|
||||
*
|
||||
* If you do not provide a command buffer, a command buffer from the indicated queue will be created and submitted.
|
||||
* If you do provide a command buffer, the command is recorded but NOT submitted.
|
||||
* @param cmdBuffer [Optional] The command buffer where the command will be recorded to
|
||||
* @throws VkUserError if the transition is not supported.
|
||||
*/
|
||||
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout, VkCommandBuffer* cmdBuffer=nullptr);
|
||||
|
||||
//
|
||||
// DEPTH
|
||||
//
|
||||
VkImage depthImage;
|
||||
VkDeviceMemory depthImageMemory;
|
||||
VkImageView depthImageView;
|
||||
VkFormat findDepthFormat();
|
||||
void createDepthImageAndView();
|
||||
//
|
||||
// TEXTURES
|
||||
//
|
||||
VkImage textureImage;
|
||||
VkDeviceMemory textureImageMemory;
|
||||
VkImageView textureImageView;
|
||||
VkSampler textureSampler;
|
||||
/// Load textures/image.png
|
||||
void createTextureImageAndView();
|
||||
void createTextureSampler();
|
||||
|
||||
//
|
||||
// SYNCHRONIZATION
|
||||
//
|
||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||
std::vector<VkFence> inFlightFences;
|
||||
void createSyncObjects();
|
||||
//
|
||||
// SHADERS
|
||||
//
|
||||
static std::vector<char> readFile(const std::string& filename);
|
||||
//
|
||||
// MODELS
|
||||
//
|
||||
void loadModel();
|
||||
VerticesAndIndices<uint32_t> model;
|
||||
//
|
||||
// DEBUG
|
||||
//
|
||||
void setupDebugMessenger();
|
||||
void cleanupDebugMessenger();
|
||||
VkDebugUtilsMessengerEXT debugMessenger;
|
||||
static gz::Log vLog;
|
||||
public:
|
||||
//
|
||||
// UTILITY
|
||||
//
|
||||
/**
|
||||
* @brief Log messages from validation layers with the Apps logger
|
||||
* @details
|
||||
* Using the static vLog to log vulkan messages with a prefix dependant on the messageType.
|
||||
*/
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL debugLog(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverety,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||
void* pUserData);
|
||||
|
||||
|
||||
template<typename T, typename... Args>
|
||||
VkResult runVkResultFunction(const char* name, Args&&... args) {
|
||||
auto f = reinterpret_cast<T>(vkGetInstanceProcAddr(instance, name));
|
||||
if (f == nullptr) {
|
||||
vLog.error("getVkFunction: Could not get function:", name);
|
||||
throw std::runtime_error("getVkFunction: Could not get function.");
|
||||
}
|
||||
else {
|
||||
return f(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
template<typename T, typename... Args>
|
||||
void runVkVoidFunction(const char* name, Args&&... args) {
|
||||
auto f = reinterpret_cast<T>(vkGetInstanceProcAddr(instance, name));
|
||||
if (f == nullptr) {
|
||||
vLog.error("getVkFunction: Could not get function:", name);
|
||||
throw std::runtime_error("getVkFunction: Could not get function.");
|
||||
}
|
||||
else {
|
||||
f(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}; // VulkanInstance
|
||||
|
||||
//
|
||||
// IMPLEMENTATIONS
|
||||
//
|
||||
//
|
||||
// UNIFORM BUFFERS
|
||||
//
|
||||
template<typename T>
|
||||
void VulkanInstance::createUniformBuffers(std::vector<VkBuffer>& uniformBuffers, std::vector<VkDeviceMemory>& uniformBuffersMemory) {
|
||||
VkDeviceSize bufferSize = sizeof(T);
|
||||
|
||||
uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers[i], uniformBuffersMemory[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* @file Creating a vulkan instance
|
||||
* @todo Write/get allocator for buffers
|
||||
*/
|
||||
|
21
vulkan_util.cpp
Normal file
21
vulkan_util.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "vulkan_util.hpp"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
|
||||
namespace gz::vk {
|
||||
void PipelineContainer::erase(const PipelineT& key, VkDevice& device, const VkAllocationCallbacks* pAllocator) {
|
||||
vkDestroyPipeline(device, pipelines[key].pipeline, pAllocator);
|
||||
vkDestroyPipelineLayout(device, pipelines[key].layout, pAllocator);
|
||||
pipelines.erase(pipelines.find(key));
|
||||
}
|
||||
PipelineContainer::iterator PipelineContainer::erase(const PipelineContainer::iterator& it, VkDevice& device, const VkAllocationCallbacks* pAllocator) {
|
||||
vkDestroyPipeline(device, it->second.pipeline, pAllocator);
|
||||
vkDestroyPipelineLayout(device, it->second.layout, pAllocator);
|
||||
return pipelines.erase(it);
|
||||
|
||||
}
|
||||
|
||||
} // namespace gz::vk
|
||||
|
||||
|
||||
|
89
vulkan_util.hpp
Normal file
89
vulkan_util.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "vertex.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace gz::vk {
|
||||
enum PipelineT {
|
||||
PL_3D, PL_2D
|
||||
};
|
||||
struct Pipeline {
|
||||
VkPipeline pipeline;
|
||||
VkPipelineLayout layout;
|
||||
void operator=(const Pipeline& other) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Map holding pipelines
|
||||
*/
|
||||
class PipelineContainer {
|
||||
public:
|
||||
using iterator = std::unordered_map<PipelineT, Pipeline>::iterator;
|
||||
Pipeline& operator[](const PipelineT& key) { return pipelines[key]; }
|
||||
/**
|
||||
* @brief Destroy the pipeline+layout and then remove the handles
|
||||
*/
|
||||
void erase(const PipelineT& key, VkDevice& device, const VkAllocationCallbacks* pAllocator=nullptr);
|
||||
iterator erase(const iterator& it, VkDevice& device, const VkAllocationCallbacks* pAllocator=nullptr);
|
||||
iterator begin() { return pipelines.begin(); }
|
||||
iterator end() { return pipelines.end(); }
|
||||
size_t size() const { return pipelines.size(); }
|
||||
private:
|
||||
std::unordered_map<PipelineT, Pipeline> pipelines;
|
||||
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
concept SupportedIndexType = std::same_as<T, uint16_t> or std::same_as<T, uint32_t>;
|
||||
|
||||
template<SupportedIndexType T>
|
||||
struct VerticesAndIndices {
|
||||
std::vector<Vertex3D> vertices;
|
||||
std::vector<T> indices;
|
||||
constexpr VkIndexType getIndexType() const {
|
||||
if (std::same_as<T, uint16_t>) {
|
||||
return VK_INDEX_TYPE_UINT16;
|
||||
}
|
||||
else if (std::same_as<T, uint32_t>) {
|
||||
return VK_INDEX_TYPE_UINT32;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct QueueFamilyIndices {
|
||||
std::optional<uint32_t> graphicsFamily;
|
||||
std::optional<uint32_t> presentFamily;
|
||||
std::optional<uint32_t> transferFamily;
|
||||
|
||||
bool hasNecessaryValues() {
|
||||
return graphicsFamily.has_value() && presentFamily.has_value();
|
||||
}
|
||||
bool hasAllValues() {
|
||||
return graphicsFamily.has_value() && presentFamily.has_value() && transferFamily.has_value();
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
std::string s = "[ ";
|
||||
s += "graphicsFamily: ";
|
||||
if (graphicsFamily.has_value()) { s += std::to_string(graphicsFamily.value()); }
|
||||
else { s += "not set"; }
|
||||
s += ", presentFamily: ";
|
||||
if (presentFamily.has_value()) { s += std::to_string(presentFamily.value()); }
|
||||
else { s += "not set"; }
|
||||
s += ", transferFamily: ";
|
||||
if (transferFamily.has_value()) { s += std::to_string(transferFamily.value()); }
|
||||
else { s += "not set"; }
|
||||
return s + " ]";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SwapChainSupport {
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
std::vector<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> presentModes;
|
||||
};
|
||||
} // namespace gz::vk
|
Loading…
Reference in New Issue
Block a user