-
Tobias Frisch authored
Signed-off-by:
Tobias Frisch <tfrisch@uni-koblenz.de>
Tobias Frisch authoredSigned-off-by:
Tobias Frisch <tfrisch@uni-koblenz.de>
main.cpp 23.59 KiB
#include <iostream>
#include <vkcv/Buffer.hpp>
#include <vkcv/Core.hpp>
#include <vkcv/Pass.hpp>
#include <vkcv/Sampler.hpp>
#include <vkcv/Image.hpp>
#include <vkcv/camera/CameraManager.hpp>
#include <vkcv/gui/GUI.hpp>
#include <vkcv/asset/asset_loader.hpp>
#include <vkcv/shader/GLSLCompiler.hpp>
struct Plane {
glm::vec3 pointOnPlane;
float padding0;
glm::vec3 normal;
float padding1;
};
struct CameraPlanes {
Plane planes[6];
};
CameraPlanes computeCameraPlanes(const vkcv::camera::Camera& camera) {
const float fov = camera.getFov();
const glm::vec3 pos = camera.getPosition();
const float ratio = camera.getRatio();
const glm::vec3 forward = glm::normalize(camera.getFront());
float near;
float far;
camera.getNearFar(near, far);
glm::vec3 up = glm::vec3(0, -1, 0);
glm::vec3 right = glm::normalize(glm::cross(forward, up));
up = glm::cross(forward, right);
const glm::vec3 nearCenter = pos + forward * near;
const glm::vec3 farCenter = pos + forward * far;
const float tanFovHalf = glm::tan(fov / 2);
const glm::vec3 nearUpCenter = nearCenter + up * tanFovHalf * near;
const glm::vec3 nearDownCenter = nearCenter - up * tanFovHalf * near;
const glm::vec3 nearRightCenter = nearCenter + right * tanFovHalf * near * ratio;
const glm::vec3 nearLeftCenter = nearCenter - right * tanFovHalf * near * ratio;
const glm::vec3 farUpCenter = farCenter + up * tanFovHalf * far;
const glm::vec3 farDownCenter = farCenter - up * tanFovHalf * far;
const glm::vec3 farRightCenter = farCenter + right * tanFovHalf * far * ratio;
const glm::vec3 farLeftCenter = farCenter - right * tanFovHalf * far * ratio;
CameraPlanes cameraPlanes;
// near
cameraPlanes.planes[0].pointOnPlane = nearCenter;
cameraPlanes.planes[0].normal = -forward;
// far
cameraPlanes.planes[1].pointOnPlane = farCenter;
cameraPlanes.planes[1].normal = forward;
// top
cameraPlanes.planes[2].pointOnPlane = nearUpCenter;
cameraPlanes.planes[2].normal = glm::normalize(glm::cross(farUpCenter - nearUpCenter, right));
// bot
cameraPlanes.planes[3].pointOnPlane = nearDownCenter;
cameraPlanes.planes[3].normal = glm::normalize(glm::cross(right, farDownCenter - nearDownCenter));
// right
cameraPlanes.planes[4].pointOnPlane = nearRightCenter;
cameraPlanes.planes[4].normal = glm::normalize(glm::cross(up, farRightCenter - nearRightCenter));
// left
cameraPlanes.planes[5].pointOnPlane = nearLeftCenter;
cameraPlanes.planes[5].normal = glm::normalize(glm::cross(farLeftCenter - nearLeftCenter, up));
return cameraPlanes;
}
struct Vertex
{
glm::vec3 position;
glm::vec3 normal;
glm::vec2 uv;
};
struct CompiledMaterial
{
std::vector<vkcv::Image> baseColor;
std::vector<vkcv::Image> metalRough;
std::vector<vkcv::Image> normal;
std::vector<vkcv::Image> occlusion;
};
void interleaveScene(vkcv::asset::Scene scene,
std::vector<std::vector<Vertex>> &interleavedVertexBuffers,
std::vector<glm::vec4> &boundingBoxBuffers) {
for(const auto &mesh : scene.meshes) {
for(auto vertexGroupIndex : mesh.vertexGroups) {
const auto &vertexGroup = scene.vertexGroups[vertexGroupIndex];
const vkcv::asset::VertexAttribute* positionAttribute = nullptr;
const vkcv::asset::VertexAttribute* normalAttribute = nullptr;
const vkcv::asset::VertexAttribute* uvAttribute = nullptr;
for (const auto& attribute : vertexGroup.vertexBuffer.attributes) {
switch (attribute.type) {
case vkcv::asset::PrimitiveType::POSITION:
positionAttribute = &attribute;
break;
case vkcv::asset::PrimitiveType::NORMAL:
normalAttribute = &attribute;
break;
case vkcv::asset::PrimitiveType::TEXCOORD_0:
uvAttribute = &attribute;
break;
default:
break;
}
}
assert(positionAttribute && normalAttribute && uvAttribute);
const uint64_t &verticesCount = vertexGroup.numVertices;
const std::vector<uint8_t> &vertexData = vertexGroup.vertexBuffer.data;
std::vector<Vertex> vertices;
vertices.reserve(verticesCount);
const size_t positionStride = positionAttribute->stride == 0 ? sizeof(glm::vec3) : positionAttribute->stride;
const size_t normalStride = normalAttribute->stride == 0 ? sizeof(glm::vec3) : normalAttribute->stride;
const size_t uvStride = uvAttribute->stride == 0 ? sizeof(glm::vec2) : uvAttribute->stride;
glm::vec3 max_pos(-std::numeric_limits<float>::max());
glm::vec3 min_pos(std::numeric_limits<float>::max());
for(size_t i = 0; i < verticesCount; i++)
{
const size_t positionOffset = positionAttribute->offset + positionStride * i;
const size_t normalOffset = normalAttribute->offset + normalStride * i;
const size_t uvOffset = uvAttribute->offset + uvStride * i;
Vertex v;
v.position = *reinterpret_cast<const glm::vec3*>(&(vertexData[positionOffset]));
v.normal = *reinterpret_cast<const glm::vec3*>(&(vertexData[normalOffset]));
v.uv = *reinterpret_cast<const glm::vec3*>(&(vertexData[uvOffset]));
glm::vec3 posWorld = glm::make_mat4(mesh.modelMatrix.data()) * glm::vec4(v.position, 1);
max_pos.x = glm::max(max_pos.x, posWorld.x);
max_pos.y = glm::max(max_pos.y, posWorld.y);
max_pos.z = glm::max(max_pos.z, posWorld.z);
min_pos.x = glm::min(min_pos.x, posWorld.x);
min_pos.y = glm::min(min_pos.y, posWorld.y);
min_pos.z = glm::min(min_pos.z, posWorld.z);
vertices.push_back(v);
}
const glm::vec3 boundingPosition = (max_pos + min_pos) / 2.0f;
const float radius = glm::distance(max_pos, min_pos) / 2.0f;
boundingBoxBuffers.emplace_back(boundingPosition.x,
boundingPosition.y,
boundingPosition.z,
radius);
interleavedVertexBuffers.push_back(vertices);
}
}
}
// Assumes the meshes use index buffers
void compileMeshForIndirectDraw(vkcv::Core &core,
const vkcv::asset::Scene &scene,
std::vector<uint8_t> &compiledVertexBuffer,
std::vector<uint8_t> &compiledIndexBuffer,
CompiledMaterial &compiledMat,
std::vector<vk::DrawIndexedIndirectCommand> &indexedIndirectCommands)
{
vkcv::Image pseudoImg = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, 2, 2);
std::vector<uint8_t> pseudoData = {0, 0, 0, 0};
pseudoImg.fill(pseudoData.data());
auto mipStream = core.createCommandStream(vkcv::QueueType::Graphics);
pseudoImg.recordMipChainGeneration(mipStream, core.getDownsampler());
uint32_t vertexOffset = 0;
for (const auto &mesh : scene.meshes)
{
for(auto &vertexGroupIndex : mesh.vertexGroups)
{
auto &vertexGroup = scene.vertexGroups[vertexGroupIndex];
auto &material = scene.materials[vertexGroup.materialIndex];
if(material.baseColor == -1)
{
std::cout << "baseColor is -1! Pushing pseudo-texture!" << std::endl;
compiledMat.baseColor.push_back(pseudoImg);
}
else
{
auto &baseColor = scene.textures[material.baseColor];
vkcv::Image baseColorImg = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, baseColor.w, baseColor.h);
baseColorImg.fill(baseColor.data.data());
baseColorImg.recordMipChainGeneration(mipStream, core.getDownsampler());
compiledMat.baseColor.push_back(baseColorImg);
}
indexedIndirectCommands.emplace_back(static_cast<uint32_t>(vertexGroup.numIndices),
1,
static_cast<uint32_t>(compiledIndexBuffer.size() / 4),
vertexOffset,
static_cast<uint32_t>(indexedIndirectCommands.size()));
vertexOffset += vertexGroup.numVertices;
compiledVertexBuffer.insert(compiledVertexBuffer.end(),
vertexGroup.vertexBuffer.data.begin(),
vertexGroup.vertexBuffer.data.end());
if(vertexGroup.indexBuffer.type == vkcv::asset::IndexType::UINT8)
{
for(auto index : vertexGroup.indexBuffer.data)
{
uint32_t index32 = static_cast<uint32_t>(index);
uint8_t firstByte = index32;
uint8_t secondByte = index32 >> 8;
uint8_t thirdByte = index32 >> 16;
uint8_t fourthByte = index32 >> 24;
compiledIndexBuffer.push_back(firstByte);
compiledIndexBuffer.push_back(secondByte);
compiledIndexBuffer.push_back(thirdByte);
compiledIndexBuffer.push_back(fourthByte);
}
}
else if(vertexGroup.indexBuffer.type == vkcv::asset::IndexType::UINT16)
{
for (size_t i = 0; i < vertexGroup.indexBuffer.data.size(); i += 2)
{
uint16_t index16 = *reinterpret_cast<const uint16_t*>(&vertexGroup.indexBuffer.data[i]);
uint32_t index32 = static_cast<uint32_t>(index16);
uint8_t firstByte = index32;
uint8_t secondByte = index32 >> 8;
uint8_t thirdByte = index32 >> 16;
uint8_t fourthByte = index32 >> 24;
compiledIndexBuffer.push_back(firstByte);
compiledIndexBuffer.push_back(secondByte);
compiledIndexBuffer.push_back(thirdByte);
compiledIndexBuffer.push_back(fourthByte);
}
}
else
{
compiledIndexBuffer.insert(compiledIndexBuffer.end(),
vertexGroup.indexBuffer.data.begin(),
vertexGroup.indexBuffer.data.end());
}
}
}
core.submitCommandStream(mipStream, false);
}
int main(int argc, const char** argv) {
const std::string applicationName = "Indirect draw";
vkcv_log(vkcv::LogLevel::TIME, "Startup");
vkcv::Features features;
features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
features.requireFeature([](vk::PhysicalDeviceFeatures &features){
features.setMultiDrawIndirect(true);
});
features.requireExtension(VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME);
features.requireExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
features.requireExtensionFeature<vk::PhysicalDeviceDescriptorIndexingFeatures>(
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, [](vk::PhysicalDeviceDescriptorIndexingFeatures &features) {
// features.setShaderInputAttachmentArrayDynamicIndexing(true);
features.setShaderUniformTexelBufferArrayDynamicIndexing(true);
features.setShaderStorageTexelBufferArrayDynamicIndexing(true);
features.setShaderUniformBufferArrayNonUniformIndexing(true);
features.setShaderSampledImageArrayNonUniformIndexing(true);
features.setShaderStorageBufferArrayNonUniformIndexing(true);
features.setShaderStorageImageArrayNonUniformIndexing(true);
// features.setShaderInputAttachmentArrayNonUniformIndexing(true);
features.setShaderUniformTexelBufferArrayNonUniformIndexing(true);
features.setShaderStorageTexelBufferArrayNonUniformIndexing(true);
features.setDescriptorBindingUniformBufferUpdateAfterBind(true);
features.setDescriptorBindingSampledImageUpdateAfterBind(true);
features.setDescriptorBindingStorageImageUpdateAfterBind(true);
features.setDescriptorBindingStorageBufferUpdateAfterBind(true);
features.setDescriptorBindingUniformTexelBufferUpdateAfterBind(true);
features.setDescriptorBindingStorageTexelBufferUpdateAfterBind(true);
features.setDescriptorBindingUpdateUnusedWhilePending(true);
features.setDescriptorBindingPartiallyBound(true);
features.setDescriptorBindingVariableDescriptorCount(true);
features.setRuntimeDescriptorArray(true);
}
);
features.tryExtensionFeature<vk::PhysicalDeviceHostImageCopyFeaturesEXT>(
VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME, [](vk::PhysicalDeviceHostImageCopyFeaturesEXT& features) {
features.setHostImageCopy(true);
}
);
vkcv_log(vkcv::LogLevel::TIME, "Features configured");
vkcv::Core core = vkcv::Core::create(
applicationName,
VK_MAKE_VERSION(0, 0, 1),
{ vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer },
features
);
vkcv_log(vkcv::LogLevel::TIME, "Core created");
vkcv::WindowHandle windowHandle = core.createWindow(applicationName,800,600,true);
vkcv::Window& window = core.getWindow(windowHandle);
vkcv_log(vkcv::LogLevel::TIME, "Window created");
vkcv::gui::GUI gui (core, windowHandle);
vkcv_log(vkcv::LogLevel::TIME, "GUI initialized");
vkcv::asset::Scene asset_scene;
const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf";
int result = vkcv::asset::loadScene(path, asset_scene);
vkcv_log(vkcv::LogLevel::TIME, "Scene loaded");
if (result == 1) {
std::cout << "Loading Sponza successful!" << std::endl;
} else {
std::cerr << "Loading Sponza failed: " << result << std::endl;
return 1;
}
assert(!asset_scene.vertexGroups.empty());
if (asset_scene.textures.empty())
{
std::cerr << "Error. No textures found. Exiting." << std::endl;
return EXIT_FAILURE;
}
vkcv::PassHandle passHandle = vkcv::passSwapchain(
core,
window.getSwapchain(),
{ vk::Format::eUndefined, vk::Format::eD32Sfloat }
);
if (!passHandle) {
std::cerr << "Error. Could not create renderpass. Exiting." << std::endl;
return EXIT_FAILURE;
}
vkcv_log(vkcv::LogLevel::TIME, "Scene verified");
vkcv::ShaderProgram sponzaProgram;
vkcv::shader::GLSLCompiler compiler;
compiler.compileProgram(sponzaProgram, {
{ vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert") },
{ vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag") },
}, nullptr);
vkcv::ShaderProgram cullingProgram;
compiler.compileProgram(cullingProgram, {
{ vkcv::ShaderStage::COMPUTE, std::filesystem::path("resources/shaders/culling.comp") },
}, nullptr);
vkcv_log(vkcv::LogLevel::TIME, "Shaders compiled");
// vertex layout for the pipeline. (assumed to be) used by all sponza meshes.
const std::vector<vkcv::VertexAttachment> vertexAttachments = sponzaProgram.getVertexAttachments();
const vkcv::VertexLayout sponzaVertexLayout {
{ vkcv::createVertexBinding(0, { vertexAttachments }) }
};
vkcv_log(vkcv::LogLevel::TIME, "Vertex layout configured");
std::vector<uint8_t> compiledVertexBuffer; // IGNORED, since the vertex buffer is not interleaved!
std::vector<uint8_t> compiledIndexBuffer;
CompiledMaterial compiledMaterial;
std::vector<vk::DrawIndexedIndirectCommand> indexedIndirectCommands;
compileMeshForIndirectDraw(core,
asset_scene,
compiledVertexBuffer,
compiledIndexBuffer,
compiledMaterial,
indexedIndirectCommands);
vkcv_log(vkcv::LogLevel::TIME, "Mesh compiled");
std::vector<std::vector<Vertex>> interleavedVertices;
std::vector<glm::vec4> compiledBoundingBoxBuffer;
interleaveScene(asset_scene,
interleavedVertices,
compiledBoundingBoxBuffer);
vkcv_log(vkcv::LogLevel::TIME, "Scene interleaved");
std::vector<Vertex> compiledInterleavedVertexBuffer;
for(auto& vertexGroup : interleavedVertices )
{
compiledInterleavedVertexBuffer.insert(compiledInterleavedVertexBuffer.end(),vertexGroup.begin(),vertexGroup.end());
}
auto vkCompiledVertexBuffer = vkcv::buffer<Vertex>(
core,
vkcv::BufferType::VERTEX,
compiledInterleavedVertexBuffer.size(),
vkcv::BufferMemoryType::DEVICE_LOCAL);
vkCompiledVertexBuffer.fill(compiledInterleavedVertexBuffer.data());
auto vkCompiledIndexBuffer = vkcv::buffer<uint8_t>(
core,
vkcv::BufferType::INDEX,
compiledIndexBuffer.size(),
vkcv::BufferMemoryType::DEVICE_LOCAL);
vkCompiledIndexBuffer.fill(compiledIndexBuffer.data());
vkcv::Buffer<vk::DrawIndexedIndirectCommand> indirectBuffer = vkcv::buffer<vk::DrawIndexedIndirectCommand>(
core,
vkcv::BufferType::INDIRECT,
indexedIndirectCommands.size(),
vkcv::BufferMemoryType::DEVICE_LOCAL);
indirectBuffer.fill(indexedIndirectCommands);
auto boundingBoxBuffer = vkcv::buffer<glm::vec4>(
core,
vkcv::BufferType::STORAGE,
compiledBoundingBoxBuffer.size());
boundingBoxBuffer.fill(compiledBoundingBoxBuffer);
std::vector<glm::mat4> modelMatrix;
for( auto& mesh : asset_scene.meshes)
{
modelMatrix.push_back(glm::make_mat4(mesh.modelMatrix.data()));
}
vkcv::Buffer<glm::mat4> modelBuffer = vkcv::buffer<glm::mat4>(
core,
vkcv::BufferType::STORAGE,
modelMatrix.size(),
vkcv::BufferMemoryType::DEVICE_LOCAL
);
modelBuffer.fill(modelMatrix);
vkcv_log(vkcv::LogLevel::TIME, "Buffers filled");
const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = {
vkcv::vertexBufferBinding(vkCompiledVertexBuffer)
};
vkcv::VertexData vertexData (vertexBufferBindings);
vertexData.setIndexBuffer(vkCompiledIndexBuffer.getHandle(), vkcv::IndexBitCount::Bit32);
//assert(compiledMaterial.baseColor.size() == compiledMaterial.metalRough.size());
vkcv::DescriptorBindings descriptorBindings = sponzaProgram.getReflectedDescriptors().at(0);
descriptorBindings[2].descriptorCount = compiledMaterial.baseColor.size();
vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings);
vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout);
vkcv::SamplerHandle standardSampler = vkcv::samplerLinear(core);
vkcv::DescriptorWrites setWrites;
std::vector<vkcv::SampledImageDescriptorWrite> textureArrayWrites;
for(uint32_t i = 0; i < compiledMaterial.baseColor.size(); i++)
{
setWrites.writeSampledImage(2, compiledMaterial.baseColor[i].getHandle(), 0, false, i);
}
setWrites.writeSampler(0, standardSampler);
setWrites.writeStorageBuffer(1, modelBuffer.getHandle());
core.writeDescriptorSet(descriptorSet, setWrites);
vkcv_log(vkcv::LogLevel::TIME, "DescriptorSets written");
vkcv::GraphicsPipelineHandle sponzaPipelineHandle = core.createGraphicsPipeline(
vkcv::GraphicsPipelineConfig(
sponzaProgram,
passHandle,
{ sponzaVertexLayout },
{ descriptorSetLayout }
)
);
if (!sponzaPipelineHandle) {
std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl;
return EXIT_FAILURE;
}
vkcv_log(vkcv::LogLevel::TIME, "Graphics pipeline created");
vkcv::DescriptorBindings cullingBindings = cullingProgram.getReflectedDescriptors().at(0);
vkcv::DescriptorSetLayoutHandle cullingSetLayout = core.createDescriptorSetLayout(cullingBindings);
vkcv::DescriptorSetHandle cullingDescSet = core.createDescriptorSet(cullingSetLayout);
vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = vkcv::buffer<CameraPlanes>(
core,
vkcv::BufferType::UNIFORM,
1);
vkcv::DescriptorWrites cullingWrites;
cullingWrites.writeStorageBuffer(1, indirectBuffer.getHandle());
cullingWrites.writeStorageBuffer(2, boundingBoxBuffer.getHandle());
cullingWrites.writeUniformBuffer(0, cameraPlaneBuffer.getHandle());
core.writeDescriptorSet(cullingDescSet, cullingWrites);
vkcv_log(vkcv::LogLevel::TIME, "Culling descriptor set written");
const vkcv::ComputePipelineConfig computeCullingConfig {
cullingProgram,
{cullingSetLayout}
};
vkcv::ComputePipelineHandle cullingPipelineHandle = core.createComputePipeline(computeCullingConfig);
if (!cullingPipelineHandle) {
std::cerr << "Error. Could not create culling pipeline. Exiting." << std::endl;
return EXIT_FAILURE;
}
vkcv_log(vkcv::LogLevel::TIME, "Compute pipeline created");
vkcv::camera::CameraManager cameraManager (window);
auto camHandle = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT);
cameraManager.getCamera(camHandle).setPosition(glm::vec3(0, 0, -3));
cameraManager.getCamera(camHandle).setNearFar(0.1f, 20.f);
vkcv::ImageHandle depthBuffer;
const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle();
float ceiledDispatchCount = static_cast<float>(indexedIndirectCommands.size()) / 64.0f;
ceiledDispatchCount = std::ceil(ceiledDispatchCount);
const vkcv::DispatchSize dispatchCount = static_cast<uint32_t>(ceiledDispatchCount);
vkcv::DescriptorSetUsage cullingUsage = vkcv::useDescriptorSet(0, cullingDescSet);
vkcv::PushConstants emptyPushConstant(0);
bool updateFrustumPlanes = true;
core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt,
uint32_t swapchainWidth, uint32_t swapchainHeight) {
if ((!depthBuffer) ||
(swapchainWidth != core.getImageWidth(depthBuffer)) ||
(swapchainHeight != core.getImageHeight(depthBuffer))) {
depthBuffer = core.createImage(
vk::Format::eD32Sfloat,
vkcv::ImageConfig(
swapchainWidth,
swapchainHeight
)
);
}
cameraManager.update(dt);
vkcv::camera::Camera cam = cameraManager.getActiveCamera();
vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>();
pushConstants.appendDrawcall(cam.getProjection() * cam.getView());
if (updateFrustumPlanes) {
const CameraPlanes cameraPlanes = computeCameraPlanes(cam);
cameraPlaneBuffer.fill({ cameraPlanes });
}
const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer };
auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics);
core.recordComputeDispatchToCmdStream(
cmdStream,
cullingPipelineHandle,
dispatchCount,
{cullingUsage},
emptyPushConstant
);
core.recordBufferMemoryBarrier(cmdStream, indirectBuffer.getHandle());
vkcv::IndirectDrawcall drawcall (
indirectBuffer.getHandle(),
vertexData,
indexedIndirectCommands.size()
);
drawcall.useDescriptorSet(0, descriptorSet);
core.recordIndirectDrawcallsToCmdStream(
cmdStream,
sponzaPipelineHandle,
pushConstants,
{ drawcall },
renderTargets,
windowHandle
);
core.prepareSwapchainImageForPresent(cmdStream);
core.submitCommandStream(cmdStream);
gui.beginGUI();
ImGui::Begin("Settings");
ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes);
ImGui::Text("Deltatime %fms, %f", dt * 1000, 1/dt);
ImGui::End();
gui.endGUI();
});
return 0;
}