Files
tanks-reborn/source/render/renderer.cpp

217 lines
7.6 KiB
C++

#include "renderer.hpp"
#include "ui/display.hpp"
#include "vuk/ImageAttachment.hpp"
#include "vuk/RenderGraph.hpp"
#include "vuk/Types.hpp"
#include "vuk/Value.hpp"
#include "vuk/runtime/CommandBuffer.hpp"
#include "vuk/runtime/vk/Allocator.hpp"
#include "vuk/runtime/vk/AllocatorHelpers.hpp"
#include "vuk/runtime/vk/DeviceFrameResource.hpp"
#include "vuk/runtime/vk/Image.hpp"
#include "vuk/runtime/vk/VkRuntime.hpp"
#include "vuk/vsl/Core.hpp"
#include <cstdint>
#include <vuk/runtime/vk/Pipeline.hpp>
#include <vuk/runtime/vk/PipelineTypes.hpp>
#include <vuk/vsl/BindlessArray.hpp>
#include <glm/glm.hpp>
#include <span>
#include <string_view>
[[nodiscard]] auto render_mesh() {
return vuk::make_pass(
"render mesh", [](vuk::CommandBuffer &command_buffer,
VUK_BA(vuk::eMemoryRead) camera_buffer,
VUK_BA(vuk::eIndirectRead) indirect_buffer,
VUK_BA(vuk::eVertexRead) vertex_buffer,
VUK_BA(vuk::eIndexRead) index_buffer,
VUK_IA(vuk::eColorWrite) color_target) {
auto attributes = std::vector<vuk::VertexInputAttributeDescription>();
attributes.push_back({
.location = 0,
.binding = 0,
.format = vuk::Format::eR32G32B32Sfloat,
.offset = 0,
});
attributes.push_back({
.location = 1,
.binding = 0,
.format = vuk::Format::eR32G32B32Sfloat,
.offset = sizeof(glm::vec3),
});
attributes.push_back({
.location = 2,
.binding = 0,
.format = vuk::Format::eR32G32Sfloat,
.offset = sizeof(glm::vec3) + sizeof(glm::vec3),
});
command_buffer.bind_graphics_pipeline("mesh")
.set_dynamic_state(vuk::DynamicStateFlagBits::eScissor |
vuk::DynamicStateFlagBits::eViewport)
.set_viewport(0, vuk::Rect2D::framebuffer())
.set_scissor(0, vuk::Rect2D::framebuffer())
.set_rasterization({
//.polygonMode = vuk::PolygonMode::eLine,
})
/*
.set_depth_stencil({
.depthTestEnable = false,
.depthWriteEnable = false,
.depthCompareOp = vuk::CompareOp::eGreater,
})*/
.set_color_blend(color_target, {})
.bind_vertex_buffer(0, vertex_buffer, attributes, sizeof(float) * 8)
.bind_index_buffer(index_buffer, vuk::IndexType::eUint32)
.bind_buffer(0, 0, camera_buffer)
.draw_indexed_indirect(1, indirect_buffer);
return std::make_tuple(std::move(camera_buffer),
std::move(color_target));
});
}
namespace trb::render {
std::uint32_t renderer::next_mesh_id() {
// Is there someway to do this with a do while?
auto generated_id = id_dist(rng);
while (registered_meshes.contains(generated_id)) [[unlikely]] {
generated_id = id_dist(rng);
}
return generated_id;
}
vuk::PipelineBaseCreateInfo
renderer::create_compute_pipeline(std::string_view comp_module,
std::string_view comp_entry) {
const auto comp_bytes = compiler.compile(comp_module, comp_entry);
auto pipeline_info = vuk::PipelineBaseCreateInfo();
pipeline_info.add_spirv(comp_bytes, comp_module.data(), comp_entry.data());
return pipeline_info;
}
vuk::PipelineBaseCreateInfo renderer::create_graphics_pipeline(
std::string_view vs_module, std::string_view vs_entry,
std::string_view fs_module, std::string_view fs_entry) {
const auto vs_bytes = compiler.compile(vs_module, vs_entry);
const auto fs_bytes = compiler.compile(fs_module, fs_entry);
auto pipeline_info = vuk::PipelineBaseCreateInfo();
pipeline_info.add_spirv(vs_bytes, vs_module.data(), vs_entry.data());
pipeline_info.add_spirv(fs_bytes, fs_module.data(), fs_entry.data());
return pipeline_info;
}
vuk::PipelineBaseCreateInfo
renderer::create_graphics_pipeline(std::string_view module_name) {
return create_graphics_pipeline(module_name, "vert_main", module_name,
"frag_main");
}
void renderer::create_pipelines() {
context.runtime.create_named_pipeline(
"mesh", create_graphics_pipeline("render_mesh.slang"));
}
vuk::Value<vuk::ImageAttachment> renderer::get_swap_target() {
auto imported_swapchain = vuk::acquire_swapchain(context.swapchain);
auto swapchain_image =
vuk::acquire_next_image("swp_img", std::move(imported_swapchain));
return vuk::clear_image(std::move(swapchain_image),
vuk::ClearColor{0.0f, 0.0f, 1.0f, 1.0f});
}
renderer::vuk_frame_resources renderer::get_next_frame_resources() {
auto &frame_resource = context.superframe_resource.get_next_frame();
context.runtime.next_frame();
return {
.allocator = vuk::Allocator(frame_resource),
.swap_target = get_swap_target(),
};
}
renderer::renderer(ui::display *display)
: context(display), rng(std::random_device()()) {
create_pipelines();
}
void renderer::render(std::span<const std::uint32_t> meshes, glm::mat4 view,
glm::mat4 projection) {
auto [frame_allocator, frame_target] = get_next_frame_resources();
struct {
glm::mat4 view;
glm::mat4 projection;
} camera_data{
.view = view,
.projection = projection,
};
auto [camera_buffer, camera_future] = vuk::create_buffer(
frame_allocator, vuk::MemoryUsage::eCPUtoGPU,
vuk::DomainFlagBits::eTransferOperation, std::span(&camera_data, 1));
for (const auto id : meshes) {
auto &mesh = registered_meshes.at(id);
std::tie(camera_future, frame_target) = render_mesh()(
camera_future,
vuk::make_constant("mesh indirect command", *mesh.indirect_command),
vuk::make_constant("mesh vertices", *mesh.vertices),
vuk::make_constant("mesh indices", *mesh.indices), frame_target);
}
auto entire_thing = vuk::enqueue_presentation(frame_target);
entire_thing.submit(frame_allocator, context.compiler);
}
std::uint32_t renderer::register_mesh(const game::mesh_node &mesh) {
const auto id = next_mesh_id();
auto [vertex_buffer, vertex_future] = vuk::create_buffer(
context.superframe_allocator, vuk::MemoryUsage::eCPUtoGPU,
vuk::DomainFlagBits::eTransferOperation, std::span(mesh.vertices));
auto [index_buffer, index_future] = vuk::create_buffer(
context.superframe_allocator, vuk::MemoryUsage::eCPUtoGPU,
vuk::DomainFlagBits::eTransferOperation, std::span(mesh.indices));
const auto indirect_draw = std::array<std::uint32_t, 5>({
static_cast<std::uint32_t>(mesh.indices.size()), // index count
1, // instance count
0, // first index
0, // vertex offset (int32 technically)
0, // first instance
});
auto [indirect_buffer, indirect_future] = vuk::create_buffer(
context.superframe_allocator, vuk::MemoryUsage::eCPUtoGPU,
vuk::DomainFlagBits::eTransferOperation,
std::span<const std::uint32_t>(indirect_draw));
auto futures = std::vector<vuk::UntypedValue>();
futures.push_back(std::move(vertex_future));
futures.push_back(std::move(index_future));
futures.push_back(std::move(indirect_future));
// TODO: Make async?
vuk::wait_for_values_explicit(context.superframe_allocator, context.compiler,
std::span(futures));
registered_meshes.insert(
{id, loaded_mesh{
.vertices = std::move(vertex_buffer),
.indices = std::move(index_buffer),
.indirect_command = std::move(indirect_buffer),
}});
return id;
}
} // namespace trb::render