diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index ab0d2aca0eccbd3c6adfea191cefa2c63623b0b9..f3b1eb7c6f81139985c7d7d304836a9b3924a94b 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 26ad7053d5a732968093b0f8ffb2c23a0d70255d..fdf5df7403ed91fd48eef1e593c567c602957937 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 fceaa9cf5498f068b5c767534be0957fed96a033..5db87574c6285d3c7d8f3310041e0b23fa7b0bb1 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 8d4c0d6c104187de2d807cceceff529d83d236d6..d2a9b817ea68c7851fd2123f76b378d8a4d85ac0 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 6df014563a8991e464e02eb10f210c079913d3cb..24687e846ff9eae3275de357331a825f0b4ed2c3 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 d0949cbc3454283b0084c57abf95a06a036c4e9b..e660b442d0b9a0208f95c9d753ef19e926bcac44 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 Binary files a/projects/first_mesh/resources/shaders/frag.spv and b/projects/first_mesh/resources/shaders/frag.spv differ diff --git a/projects/first_mesh/resources/shaders/shader.frag b/projects/first_mesh/resources/shaders/shader.frag index 5757b58893f96abfe4212dde1936a1f03bcd2c3d..ce1efbde47b86321fec089f1b2a7b8380b390a2f 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 e1cbc40e747f9f68e44db6ea9ca3dcd556148a4b..d0500a387f0941094c55605167b2778c5b728aaf 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 13800254d78cb679c81d58a003089aa961986937..dc470d828ac108344ce8501020482e131d0045c5 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 c2b1415f10188f082d8eca576c2143e82f99e7fa..b5a8e98cea635005c2cb82c22921e6c6b6b3b8ed 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 8b6202eb901f26c84585597e327c297902e54b03..4ec492c238af1c5aa8dbade28496e0610f715174 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 ef2fedf6fe85a35df40a284554df40659b23470c..b06c6743e1e19a5e282af248ab6b590eb97529fd 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