From 298c2f84b95b5b631c1c494f7fbf010002d65bd8 Mon Sep 17 00:00:00 2001 From: Alexander Gauggel <agauggel@uni-koblenz.de> Date: Sun, 30 May 2021 13:39:15 +0200 Subject: [PATCH] [#52] Mesh now loading UVs and Normals... but at what cost? --- include/vkcv/Core.hpp | 7 ++- include/vkcv/PipelineConfig.hpp | 26 +++++------ include/vkcv/VertexLayout.hpp | 19 ++++++-- modules/asset_loader/CMakeLists.txt | 2 +- .../include/vkcv/asset/asset_loader.hpp | 13 +----- .../src/vkcv/asset/asset_loader.cpp | 17 ++++---- .../first_mesh/resources/shaders/frag.spv | Bin 572 -> 652 bytes .../first_mesh/resources/shaders/shader.frag | 3 +- projects/first_mesh/src/main.cpp | 10 ++++- src/vkcv/Core.cpp | 16 ++++--- src/vkcv/PipelineConfig.cpp | 10 ++++- src/vkcv/PipelineManager.cpp | 41 ++++++++++++++---- src/vkcv/VertexLayout.cpp | 16 +------ 13 files changed, 107 insertions(+), 73 deletions(-) diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index ab0d2aca..f3b1eb7c 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -21,6 +21,11 @@ namespace vkcv { + struct VertexBufferBinding { + vk::DeviceSize offset; + BufferHandle buffer; + }; + // forward declarations class PassManager; class PipelineManager; @@ -188,7 +193,7 @@ namespace vkcv */ void renderMesh(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, const int width, const int height, const size_t pushConstantSize, const void* pushConstantData, - const BufferHandle vertexBuffer, const BufferHandle indexBuffer, const size_t indexCount); + const std::vector<VertexBufferBinding> & vertexBufferBindings, const BufferHandle indexBuffer, const size_t indexCount); /** * @brief end recording and present image diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp index 26ad7053..fdf5df74 100644 --- a/include/vkcv/PipelineConfig.hpp +++ b/include/vkcv/PipelineConfig.hpp @@ -9,17 +9,11 @@ #include <cstdint> #include "vkcv/Handles.hpp" #include "ShaderProgram.hpp" +#include <vkcv/VertexLayout.hpp> namespace vkcv { - class PipelineConfig { - - public: - /** - * Default constructer is deleted! - */ - PipelineConfig() = delete; - + struct PipelineConfig { /** * Constructor for the pipeline. Creates a pipeline using @p vertexCode, @p fragmentCode as well as the * dimensions of the application window @p width and @p height. A handle for the Render Pass is also needed, @p passHandle. @@ -29,12 +23,18 @@ namespace vkcv { * @param width width of the application window * @param passHandle handle for Render Pass */ - PipelineConfig(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle); + PipelineConfig( + const ShaderProgram& shaderProgram, + uint32_t width, + uint32_t height, + PassHandle &passHandle, + const std::vector<VertexAttribute> &vertexAttributes); - ShaderProgram m_ShaderProgram; - uint32_t m_Height; - uint32_t m_Width; - PassHandle m_PassHandle; + ShaderProgram m_ShaderProgram; + uint32_t m_Height; + uint32_t m_Width; + PassHandle m_PassHandle; + std::vector<VertexAttribute> m_vertexAttributes; }; } \ No newline at end of file diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp index fceaa9cf..5db87574 100644 --- a/include/vkcv/VertexLayout.hpp +++ b/include/vkcv/VertexLayout.hpp @@ -3,9 +3,21 @@ #include <unordered_map> #include <vector> #include <iostream> -#include <vulkan/vulkan.hpp> namespace vkcv{ + + /* With these enums, 0 is reserved to signal uninitialized or invalid data. */ + enum class PrimitiveType { UNDEFINED, POSITION, NORMAL, TEXCOORD_0 }; + /* This struct describes one vertex attribute of a vertex buffer. */ + typedef struct { + PrimitiveType type; // POSITION, NORMAL, ... + uint32_t offset; // offset in bytes + uint32_t length; // length of ... in bytes + uint32_t stride; // stride in bytes + uint16_t componentType; // eg. 5126 for float + uint8_t componentCount; // eg. 3 for vec3 + } VertexAttribute; + enum class VertexFormat{ FLOAT, FLOAT2, @@ -17,6 +29,8 @@ namespace vkcv{ INT4 }; + uint32_t getFormatSize(VertexFormat format); + struct VertexInputAttachment{ VertexInputAttachment() = delete; VertexInputAttachment(uint32_t location, uint32_t binding, VertexFormat format, uint32_t offset) noexcept; @@ -33,7 +47,4 @@ namespace vkcv{ std::unordered_map<uint32_t, VertexInputAttachment> attachmentMap; uint32_t stride; }; - - // currently assuming default 32 bit formats, no lower precision or normalized variants supported - vk::Format vertexFormatToVulkanFormat(const VertexFormat format); } \ No newline at end of file diff --git a/modules/asset_loader/CMakeLists.txt b/modules/asset_loader/CMakeLists.txt index 8d4c0d6c..d2a9b817 100644 --- a/modules/asset_loader/CMakeLists.txt +++ b/modules/asset_loader/CMakeLists.txt @@ -31,7 +31,7 @@ include(config/FX_GLTF.cmake) include(config/STB.cmake) # link the required libraries to the module -target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries}) +target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv) # including headers of dependencies and the VkCV framework target_include_directories(vkcv_asset_loader SYSTEM BEFORE PRIVATE ${vkcv_asset_loader_includes}) diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp index 6df01456..24687e84 100644 --- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp +++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp @@ -8,6 +8,7 @@ #include <string> #include <vector> #include <cstdint> +#include <vkcv/VertexLayout.hpp> /* These macros define limits of the following structs. Implementations can * test against these limits when performing sanity checks. The main constraint @@ -49,8 +50,6 @@ enum PrimitiveMode { POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP, TRIANGLEFAN }; -/* With these enums, 0 is reserved to signal uninitialized or invalid data. */ -enum PrimitiveType { POSITION=1, NORMAL, TEXCOORD_0 }; /* The indices in the index buffer can be of different bit width. */ enum IndexType { UINT32=0, UINT16=1, UINT8=2 }; @@ -58,16 +57,6 @@ typedef struct { // TODO not yet needed for the first (unlit) triangle } Material; -/* This struct describes one vertex attribute of a vertex buffer. */ -typedef struct { - PrimitiveType type; // POSITION, NORMAL, ... - uint32_t offset; // offset in bytes - uint32_t length; // length of ... in bytes - uint32_t stride; // stride in bytes - uint16_t componentType; // eg. 5126 for float - uint8_t componentCount; // eg. 3 for vec3 -} VertexAttribute; - /* This struct represents one (possibly the only) part of a mesh. There is * always one vertexBuffer and zero or one indexBuffer (indexed rendering is * common but not always used). If there is no index buffer, this is indicated diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp index d0949cbc..e660b442 100644 --- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp +++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp @@ -85,12 +85,12 @@ int loadMesh(const std::string &path, Mesh &mesh) { VertexAttribute attribute; if (attrib.first == "POSITION") { - attribute.type = POSITION; + attribute.type = PrimitiveType::POSITION; posAccessor = accessor; } else if (attrib.first == "NORMAL") { - attribute.type = NORMAL; + attribute.type = PrimitiveType::NORMAL; } else if (attrib.first == "TEXCOORD_0") { - attribute.type = TEXCOORD_0; + attribute.type = PrimitiveType::TEXCOORD_0; } else { return 0; } @@ -126,15 +126,16 @@ int loadMesh(const std::string &path, Mesh &mesh) { } } - const fx::gltf::BufferView& vertexBufferView = object.bufferViews[posAccessor.bufferView]; - const fx::gltf::Buffer& vertexBuffer = object.buffers[vertexBufferView.buffer]; + const fx::gltf::BufferView& vertexBufferView = object.bufferViews[posAccessor.bufferView]; + const fx::gltf::Buffer& vertexBuffer = object.buffers[vertexBufferView.buffer]; + // FIXME: This only works when all vertex attributes are in one buffer std::vector<uint8_t> vertexBufferData; - vertexBufferData.resize(vertexBufferView.byteLength); + vertexBufferData.resize(vertexBuffer.byteLength); { - const size_t off = vertexBufferView.byteOffset; + const size_t off = 0; const void *const ptr = ((char*)vertexBuffer.data.data()) + off; - if (!memcpy(vertexBufferData.data(), ptr, vertexBufferView.byteLength)) { + if (!memcpy(vertexBufferData.data(), ptr, vertexBuffer.byteLength)) { std::cerr << "ERROR copying vertex buffer data.\n"; return 0; } diff --git a/projects/first_mesh/resources/shaders/frag.spv b/projects/first_mesh/resources/shaders/frag.spv index e552acc2cc06e3adb7bcdbdea4256618a453fdca..50030afba4d1c527a1f9bc59e16cf50a50f72f94 100644 GIT binary patch delta 265 zcmdnP(!<Kj%%sfDz`)4B#lXuTGLhGtm4|_WL3m>9dQlLgAhEdEFTW@^F^7SHm4Ssp zc(Nj+ydX$MjsXJP!J>hT(UIB=EDUT63=CWl33~+w76xtxkl73j><kPH)=)l3HzNZB zgE#{VgCGL~SPv5e0|Q9C5L7)#9Hbt^7oPl;Q9Fj0fq?;}vyXulY#JW}0|Q75WLp3O q8(54VYCA{_gd3r11sE6@K<YvAb_~o6oD2*MAhEv;j0_eGj0^zVxf5^z delta 185 zcmeBS-NVAm%%sfDz`)4B#lXuTFp<}rm79Tqfq!D`dQlLgAhEdEFTW@^F^7SHm4StU zf3hN@ydX$MjsXJP!J>hT(VhwnEDT%>AhQ@4*cliYtf736YDNYI25|-!23`gR1~vu; y1||ju29SC_sCtk%NIi(pKbe(DTZV^$fdQn`j)9qhlYxN&#Q)2{$Y8<1$N&I5O%H$o diff --git a/projects/first_mesh/resources/shaders/shader.frag b/projects/first_mesh/resources/shaders/shader.frag index 5757b588..ce1efbde 100644 --- a/projects/first_mesh/resources/shaders/shader.frag +++ b/projects/first_mesh/resources/shaders/shader.frag @@ -7,5 +7,6 @@ layout(location = 1) in vec2 passUV; layout(location = 0) out vec3 outColor; void main() { - outColor = passNormal; + outColor = 0.5 * passNormal + 0.5; + //outColor = vec3(abs(passUV), 0); } \ No newline at end of file diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index e1cbc40e..d0500a38 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -76,7 +76,7 @@ int main(int argc, const char** argv) { triangleShaderProgram.reflectShader(vkcv::ShaderStage::VERTEX); triangleShaderProgram.reflectShader(vkcv::ShaderStage::FRAGMENT); - const vkcv::PipelineConfig trianglePipelineDefinition(triangleShaderProgram, windowWidth, windowHeight, trianglePass); + const vkcv::PipelineConfig trianglePipelineDefinition(triangleShaderProgram, windowWidth, windowHeight, trianglePass, mesh.vertexGroups[0].vertexBuffer.attributes); vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); if (!trianglePipeline) { @@ -84,6 +84,12 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } + std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + { mesh.vertexGroups[0].vertexBuffer.attributes[0].offset, vertexBuffer.getHandle() }, + { mesh.vertexGroups[0].vertexBuffer.attributes[1].offset, vertexBuffer.getHandle() }, + { mesh.vertexGroups[0].vertexBuffer.attributes[2].offset, vertexBuffer.getHandle() } + }; + auto start = std::chrono::system_clock::now(); while (window.isWindowOpen()) { core.beginFrame(); @@ -94,7 +100,7 @@ int main(int argc, const char** argv) { cameraManager.getCamera().updateView(std::chrono::duration<double>(deltatime).count()); const glm::mat4 mvp = cameraManager.getCamera().getProjection() * cameraManager.getCamera().getView(); - core.renderMesh(trianglePass, trianglePipeline, windowWidth, windowHeight, sizeof(mvp), &mvp, vertexBuffer.getHandle(), indexBuffer.getHandle(), mesh.vertexGroups[0].numIndices); + core.renderMesh(trianglePass, trianglePipeline, windowWidth, windowHeight, sizeof(mvp), &mvp, vertexBufferBindings, indexBuffer.getHandle(), mesh.vertexGroups[0].numIndices); core.endFrame(); } return 0; diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 13800254..dc470d82 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -164,7 +164,7 @@ namespace vkcv void Core::renderMesh(const PassHandle renderpassHandle, const PipelineHandle pipelineHandle, const int width, const int height, const size_t pushConstantSize, const void *pushConstantData, - const BufferHandle vertexBuffer, const BufferHandle indexBuffer, const size_t indexCount) { + const std::vector<VertexBufferBinding>& vertexBufferBindings, const BufferHandle indexBuffer, const size_t indexCount) { if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { return; @@ -175,17 +175,18 @@ namespace vkcv const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle); const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height)); - const vk::Buffer vulkanVertexBuffer = m_BufferManager->getBuffer(vertexBuffer); const vk::Buffer vulkanIndexBuffer = m_BufferManager->getBuffer(indexBuffer); const vk::Framebuffer framebuffer = createFramebuffer(m_Context.getDevice(), renderpass, width, height, imageView); m_TemporaryFramebuffers.push_back(framebuffer); + auto &bufferManager = m_BufferManager; + SubmitInfo submitInfo; submitInfo.queueType = QueueType::Graphics; submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; submitCommands(submitInfo, [renderpass, renderArea, imageView, framebuffer, pipeline, pipelineLayout, - pushConstantSize, pushConstantData, vulkanVertexBuffer, indexCount, vulkanIndexBuffer](const vk::CommandBuffer& cmdBuffer) { + pushConstantSize, pushConstantData, &vertexBufferBindings, indexCount, vulkanIndexBuffer, &bufferManager](const vk::CommandBuffer& cmdBuffer) { const std::array<float, 4> clearColor = { 0.f, 0.f, 0.f, 1.f }; const vk::ClearValue clearValues(clearColor); @@ -195,10 +196,15 @@ namespace vkcv cmdBuffer.beginRenderPass(beginInfo, subpassContents, {}); cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); + + for (uint32_t i = 0; i < vertexBufferBindings.size(); i++) { + const auto &vertexBinding = vertexBufferBindings[i]; + const auto vertexBuffer = bufferManager->getBuffer(vertexBinding.buffer); + cmdBuffer.bindVertexBuffers(i, (vertexBuffer), (vertexBinding.offset)); + } - cmdBuffer.bindVertexBuffers(0, (vulkanVertexBuffer), { 0 }); cmdBuffer.bindIndexBuffer(vulkanIndexBuffer, 0, vk::IndexType::eUint16); //FIXME: choose proper size - cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData); + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData); cmdBuffer.drawIndexed(indexCount, 1, 0, 0, {}); cmdBuffer.endRenderPass(); }, nullptr); diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp index c2b1415f..b5a8e98c 100644 --- a/src/vkcv/PipelineConfig.cpp +++ b/src/vkcv/PipelineConfig.cpp @@ -8,10 +8,16 @@ namespace vkcv { - PipelineConfig::PipelineConfig(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle): + PipelineConfig::PipelineConfig( + const ShaderProgram& shaderProgram, + uint32_t width, + uint32_t height, + PassHandle &passHandle, + const std::vector<VertexAttribute> &vertexAttributes): m_ShaderProgram(shaderProgram), m_Height(height), m_Width(width), - m_PassHandle(passHandle) + m_PassHandle(passHandle), + m_vertexAttributes(vertexAttributes) {} } diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp index 8b6202eb..4ec492c2 100644 --- a/src/vkcv/PipelineManager.cpp +++ b/src/vkcv/PipelineManager.cpp @@ -23,6 +23,21 @@ namespace vkcv m_NextPipelineId = 0; } + // currently assuming default 32 bit formats, no lower precision or normalized variants supported + vk::Format vertexFormatToVulkanFormat(const VertexFormat format) { + switch (format) { + case VertexFormat::FLOAT: return vk::Format::eR32Sfloat; + case VertexFormat::FLOAT2: return vk::Format::eR32G32Sfloat; + case VertexFormat::FLOAT3: return vk::Format::eR32G32B32Sfloat; + case VertexFormat::FLOAT4: return vk::Format::eR32G32B32A32Sfloat; + case VertexFormat::INT: return vk::Format::eR32Sint; + case VertexFormat::INT2: return vk::Format::eR32G32Sint; + case VertexFormat::INT3: return vk::Format::eR32G32B32Sint; + case VertexFormat::INT4: return vk::Format::eR32G32B32A32Sint; + default: std::cerr << "Warning: Unknown vertex format" << std::endl; return vk::Format::eUndefined; + } + } + PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, const vk::RenderPass &pass) { const bool existsVertexShader = config.m_ShaderProgram.existsShader(ShaderStage::VERTEX); @@ -69,19 +84,27 @@ namespace vkcv // vertex input state // Fill up VertexInputBindingDescription and VertexInputAttributeDescription Containers - std::vector<vk::VertexInputBindingDescription> vertexBindingDescriptions; - std::vector<vk::VertexInputAttributeDescription> vertexAttributeDescriptions; + std::vector<vk::VertexInputAttributeDescription> vertexAttributeDescriptions; + std::vector<vk::VertexInputBindingDescription> vertexBindingDescriptions; VertexLayout layout = config.m_ShaderProgram.getVertexLayout(); std::unordered_map<uint32_t, VertexInputAttachment> attachments = layout.attachmentMap; - for (auto& attachment: attachments) { - uint32_t location = attachment.second.location; - uint32_t binding = attachment.second.binding; - uint32_t offset = attachment.second.offset; - vk::Format vertexFormat = vertexFormatToVulkanFormat(attachment.second.format); - vertexBindingDescriptions.push_back({binding, layout.stride, vk::VertexInputRate::eVertex}); // TODO: What's about the input rate? - vertexAttributeDescriptions.push_back({location, binding, vk::Format::eR32G32B32Sfloat, offset}); + for (int i = 0; i < attachments.size(); i++) { + VertexInputAttachment &attachment = attachments.at(i); + + uint32_t location = attachment.location; + uint32_t binding = i; + vk::Format vertexFormat = vertexFormatToVulkanFormat(attachment.format); + + //FIXME: hoping that order is the same and compatible: add explicit mapping and validation + const VertexAttribute attribute = config.m_vertexAttributes[i]; + + vertexAttributeDescriptions.push_back({location, binding, vertexFormatToVulkanFormat(attachment.format), 0}); + vertexBindingDescriptions.push_back(vk::VertexInputBindingDescription( + binding, + attribute.stride + getFormatSize(attachment.format), + vk::VertexInputRate::eVertex)); } // Handover Containers to PipelineVertexInputStateCreateIngo Struct diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp index ef2fedf6..b06c6743 100644 --- a/src/vkcv/VertexLayout.cpp +++ b/src/vkcv/VertexLayout.cpp @@ -5,7 +5,7 @@ #include "vkcv/VertexLayout.hpp" namespace vkcv { - uint32_t static getFormatSize(VertexFormat format) { + uint32_t getFormatSize(VertexFormat format) { switch (format) { case VertexFormat::FLOAT: return 4; @@ -50,18 +50,4 @@ namespace vkcv { } } - vk::Format vertexFormatToVulkanFormat(const VertexFormat format) { - switch (format) { - case VertexFormat::FLOAT : return vk::Format::eR32Sfloat; - case VertexFormat::FLOAT2 : return vk::Format::eR32G32Sfloat; - case VertexFormat::FLOAT3 : return vk::Format::eR32G32B32Sfloat; - case VertexFormat::FLOAT4 : return vk::Format::eR32G32B32A32Sfloat; - case VertexFormat::INT : return vk::Format::eR32Sint; - case VertexFormat::INT2 : return vk::Format::eR32G32Sint; - case VertexFormat::INT3 : return vk::Format::eR32G32B32Sint; - case VertexFormat::INT4 : return vk::Format::eR32G32B32A32Sint; - default: std::cerr << "Warning: Unknown vertex format" << std::endl; return vk::Format::eUndefined; - } - } - } \ No newline at end of file -- GitLab