diff --git a/CMakeLists.txt b/CMakeLists.txt index e060e14ab2c0287e25a5bac91c34f83b5d44fd31..0c1ab40c3633e359c657b6e60f99c3d067135821 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,4 +10,4 @@ add_library(lava-vr ${LAVA_LINK_TYPE} ) target_include_directories(lava-vr PUBLIC ./src) -target_link_libraries(lava-vr PUBLIC lava lava-extras-openvr lava-extras-pipeline) +target_link_libraries(lava-vr PUBLIC lava lava-extras-openvr lava-extras-pipeline lava-extras-geometry) diff --git a/README.md b/README.md index 31f405a3eba9daeabba7fbf0ec06338abefa62e5..01089d8b73d6a1c77c07e5668fc6ab97a92cdbee 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,7 @@ Higher-level utility classes and functions for VR-Applications based on lava and ## RopeLocomotion Use two controllers to move around in the scene in a multi-touch / "Mime pulling an invisible rope" fashion. + +## BatchingRenderer + +Manages an arbitrary amount of meshes with texture and records drawing requests that can be executed at once with a single bind of pipeline and buffers. diff --git a/src/lava-vr/BatchingRenderer.cc b/src/lava-vr/BatchingRenderer.cc new file mode 100644 index 0000000000000000000000000000000000000000..4b17e8af3e9a5cea6ecbbf7df73de90bb30f0ca5 --- /dev/null +++ b/src/lava-vr/BatchingRenderer.cc @@ -0,0 +1,120 @@ +#include "BatchingRenderer.hh" +#include <glm/glm.hpp> +#include <lava-extras/pack/pack.hh> +#include <lava/common/log.hh> +#include <lava/createinfos/Buffers.hh> +#include <lava/createinfos/DescriptorSetLayoutCreateInfo.hh> +#include <lava/createinfos/GraphicsPipelineCreateInfo.hh> +#include <lava/createinfos/Images.hh> +#include <lava/createinfos/Sampler.hh> +#include <lava/features/DebugMarkers.hh> +#include <lava/objects/Buffer.hh> +#include <lava/objects/DescriptorSet.hh> +#include <lava/objects/DescriptorSetLayout.hh> +#include <lava/objects/Device.hh> +#include <lava/objects/GraphicsPipeline.hh> +#include <lava/objects/Image.hh> +#include <lava/objects/ImageData.hh> +#include <lava/objects/RenderPass.hh> +#include <lava/raii/ActiveRenderPass.hh> + +#include <lava-extras/geometry/IO.hh> + +namespace lava { +namespace vr { + +BatchingRenderer::BatchingRenderer( + const lava::Subpass &forwardPass, + const lava::SharedDescriptorSetLayout &cameraLayout) { + auto device = forwardPass.pass->device(); + + mMaterialLayout = lava::DescriptorSetLayoutCreateInfo() // + .addCombinedImageSampler() + .create(device, 100); + auto layout = device->createPipelineLayout<glm::mat4>( + {cameraLayout, mMaterialLayout}); + + auto ci = lava::GraphicsPipelineCreateInfo::defaults(); + ci.setLayout(layout); + + ci.depthStencilState.setDepthTestEnable(true).setDepthWriteEnable(true); + ci.rasterizationState.setFrontFace(vk::FrontFace::eClockwise); + + ci.addStage(lava::pack::shader(device, "albedo.vert")); + ci.addStage(lava::pack::shader(device, "albedo.frag")); + + ci.vertexInputState.addAttribute(&Vertex::pos, 0); + ci.vertexInputState.addAttribute(&Vertex::normal, 1); + ci.vertexInputState.addAttribute(&Vertex::texCoord, 2); + + mPipeline = forwardPass.createPipeline(ci); + + mSampler = device->createSampler({}); +} + +BatchingRenderer::MeshHandle BatchingRenderer::add(const std::string &modelfile, + const std::string &texture) { + MeshHandle result; + + auto debug = mPipeline->device()->get<lava::features::DebugMarkers>(); + + { + std::vector<Vertex> vertices; + auto indices = lava::geometry::Importer().load( + modelfile, + [&](glm::vec3 position, glm::vec3 normal, glm::vec3 /* tangent */, + glm::vec3 texcoord, glm::vec4 /* color */) { + vertices.push_back({position, normal, glm::vec2(texcoord)}); + }); + uint32_t firstIndex = mIndices.size(); + uint32_t offset = mVertices.size(); + mVertices.insert(mVertices.end(), vertices.begin(), vertices.end()); + std::transform(indices.begin(), indices.end(), back_inserter(mIndices), + [&](uint32_t idx) { return idx + offset; }); + result.firstIndex = firstIndex; + result.count = indices.size(); + } + + { + auto image = lava::ImageData::createFromFile(texture)->uploadTo( + mPipeline->device()); + image->changeLayout(vk::ImageLayout::eTransferSrcOptimal, + vk::ImageLayout::eShaderReadOnlyOptimal); + if (debug) + debug->mark(image, "BatchingRenderer - " + texture); + auto view = image->createView(vk::ImageViewType::e2D); + auto set = mMaterialLayout->createDescriptorSet(); + set->write().combinedImageSampler(mSampler, view); + mMaterials.push_back(set); + + result.material = mMaterials.size() - 1; + } + + return result; +} + +void BatchingRenderer::upload() { + auto const &device = mPipeline->device(); + mVertexBuffer = device->createBuffer(lava::arrayBuffer()); + mVertexBuffer->setDataVRAM(mVertices); + + mIndexBuffer = device->createBuffer(lava::indexBuffer()); + mIndexBuffer->setDataVRAM(mIndices); +} + +void BatchingRenderer::draw(lava::InlineSubpass &sub, + lava::SharedDescriptorSet const &cameraSet) { + sub.bindPipeline(mPipeline); + sub.bindVertexBuffers({mVertexBuffer}); + sub.bindIndexBuffer(mIndexBuffer); + sub.bindDescriptorSets({cameraSet}); + + for (auto const &d : mDraws) { + sub.bindDescriptorSets({mMaterials[d.first.material]}, 1); + sub.pushConstantBlock(d.second); + sub.drawIndexed(d.first.count, 1, 0, d.first.firstIndex); + } +} + +} // namespace vr +} // namespace lava diff --git a/src/lava-vr/BatchingRenderer.hh b/src/lava-vr/BatchingRenderer.hh new file mode 100644 index 0000000000000000000000000000000000000000..9f7fc1a2291f1a96eebca584e1f085fc36e31fd7 --- /dev/null +++ b/src/lava-vr/BatchingRenderer.hh @@ -0,0 +1,53 @@ +#pragma once +#include <glm/common.hpp> +#include <glm/mat4x4.hpp> +#include <lava/fwd.hh> +#include <vector> + +namespace lava { +namespace vr { + +class BatchingRenderer { + public: + struct Vertex { + glm::vec3 pos; + glm::vec3 normal; + glm::vec2 texCoord; + }; + + struct MeshHandle { + uint32_t firstIndex; + uint32_t count; + uint32_t material; + }; + + BatchingRenderer(lava::Subpass const &forwardPass, + lava::SharedDescriptorSetLayout const &cameraLayout); + + MeshHandle add(std::string const &modelfile, std::string const &texture); + void upload(); + + void reset() { mDraws.clear(); } + void enqueue(MeshHandle const &handle, glm::mat4 const &modelMatrix) { + mDraws.emplace_back(handle, modelMatrix); + } + void draw(lava::InlineSubpass &sub, + const lava::SharedDescriptorSet &cameraSet); + + protected: + std::vector<Vertex> mVertices; + std::vector<uint32_t> mIndices; + std::vector<lava::SharedDescriptorSet> mMaterials; + + std::vector<std::pair<MeshHandle, glm::mat4>> mDraws; + + lava::SharedBuffer mVertexBuffer; + lava::SharedBuffer mIndexBuffer; + + lava::SharedGraphicsPipeline mPipeline; + lava::SharedDescriptorSetLayout mMaterialLayout; + lava::SharedSampler mSampler; +}; + +} // namespace vr +} // namespace lava