engine/src/renderer.cpp

312 lines
13 KiB
C++
Raw Normal View History

#include "renderer.h"
2024-03-19 11:32:51 +00:00
#include <array>
#include "application_component.h"
2024-03-19 11:32:51 +00:00
#include "util/files.h"
2023-08-29 21:10:05 +00:00
#include <glm/mat4x4.hpp>
#include <glm/trigonometric.hpp>
#include <glm/ext/matrix_clip_space.hpp>
2023-10-01 10:38:27 +00:00
#include "imgui/imgui.h"
namespace engine {
2023-08-29 17:06:04 +00:00
Renderer::Renderer(Application& app, gfx::GraphicsSettings settings) : ApplicationComponent(app)
2023-11-05 01:04:05 +00:00
{
device_ = std::make_unique<GFXDevice>(GetAppName(), GetAppVersion(), GetWindowHandle(), settings);
2023-11-05 01:04:05 +00:00
// sort out descriptor set layouts:
std::vector<gfx::DescriptorSetLayoutBinding> globalSetBindings;
{
auto& binding0 = globalSetBindings.emplace_back();
binding0.descriptor_type = gfx::DescriptorType::kUniformBuffer;
binding0.stage_flags = gfx::ShaderStageFlags::kVertex;
2024-03-19 11:32:51 +00:00
auto& binding1 = globalSetBindings.emplace_back();
binding1.descriptor_type = gfx::DescriptorType::kCombinedImageSampler;
binding1.stage_flags = gfx::ShaderStageFlags::kFragment;
2023-11-05 01:04:05 +00:00
}
global_uniform.layout = device_->CreateDescriptorSetLayout(globalSetBindings);
global_uniform.set = device_->AllocateDescriptorSet(global_uniform.layout);
global_uniform.uniform_buffer_data.data = glm::mat4{1.0f};
global_uniform.uniform_buffer = device_->CreateUniformBuffer(sizeof(global_uniform.uniform_buffer_data), &global_uniform.uniform_buffer_data);
device_->UpdateDescriptorUniformBuffer(global_uniform.set, 0, global_uniform.uniform_buffer, 0, sizeof(global_uniform.uniform_buffer_data));
2024-03-19 11:32:51 +00:00
// binding1 is updated towards the end of the constructor once the skybox texture is loaded
2023-11-05 01:04:05 +00:00
std::vector<gfx::DescriptorSetLayoutBinding> frameSetBindings;
{
auto& binding0 = frameSetBindings.emplace_back();
binding0.descriptor_type = gfx::DescriptorType::kUniformBuffer;
binding0.stage_flags = gfx::ShaderStageFlags::kVertex;
}
frame_uniform.layout = device_->CreateDescriptorSetLayout(frameSetBindings);
frame_uniform.set = device_->AllocateDescriptorSet(frame_uniform.layout);
frame_uniform.uniform_buffer_data.data = glm::mat4{1.0f};
frame_uniform.uniform_buffer = device_->CreateUniformBuffer(sizeof(frame_uniform.uniform_buffer_data), &frame_uniform.uniform_buffer_data);
device_->UpdateDescriptorUniformBuffer(frame_uniform.set, 0, frame_uniform.uniform_buffer, 0, sizeof(frame_uniform.uniform_buffer_data));
std::vector<gfx::DescriptorSetLayoutBinding> materialSetBindings;
gfx::DescriptorSetLayoutBinding materialSetBinding{};
materialSetBinding.descriptor_type = gfx::DescriptorType::kCombinedImageSampler;
materialSetBinding.stage_flags = gfx::ShaderStageFlags::kFragment;
materialSetBindings.push_back(materialSetBinding); // albedo
materialSetBindings.push_back(materialSetBinding); // normal
materialSetBindings.push_back(materialSetBinding); // occlusion
materialSetBindings.push_back(materialSetBinding); // metallic-roughness
material_set_layout = device_->CreateDescriptorSetLayout(materialSetBindings);
device_->SetupImguiBackend();
gfx::VertexFormat debug_vertex_format{};
debug_vertex_format.stride = 0;
// debug_vertex_format.vertex_attrib_descriptions = empty
2024-03-19 11:32:51 +00:00
{
gfx::PipelineInfo debug_pipeline_info{};
debug_pipeline_info.vert_shader_path = GetResourcePath("engine/shaders/debug.vert");
debug_pipeline_info.frag_shader_path = GetResourcePath("engine/shaders/debug.frag");
debug_pipeline_info.vertex_format = debug_vertex_format;
debug_pipeline_info.alpha_blending = false;
debug_pipeline_info.backface_culling = false; // probably ignored for line rendering
debug_pipeline_info.write_z = false; // lines don't need the depth buffer
// debug_pipeline_info.descriptor_set_layouts = empty;
debug_pipeline_info.line_primitives = true;
debug_rendering_things_.pipeline = device_->CreatePipeline(debug_pipeline_info);
}
// create the skybox cubemap
{
constexpr uint32_t cubemap_w = 2048;
constexpr uint32_t cubemap_h = 2048;
int w{}, h{};
std::array<std::unique_ptr<std::vector<uint8_t>>, 6> face_unq_ptrs{};
std::array<const void*, 6> face_unsafe_ptrs{};
for (int face = 0; face < 6; ++face) {
std::string path = std::string("engine/textures/skybox") + std::to_string(face) + std::string(".jpg");
face_unq_ptrs[face] = util::ReadImageFile(GetResourcePath(path), w, h);
if (cubemap_w != w || cubemap_h != h) throw std::runtime_error("Skybox textures must be 512x512!");
face_unsafe_ptrs[face] = face_unq_ptrs[face]->data();
}
skybox_cubemap = device_->CreateImageCubemap(cubemap_w, cubemap_h, gfx::ImageFormat::kSRGB, face_unsafe_ptrs);
gfx::SamplerInfo sampler_info{};
sampler_info.magnify = gfx::Filter::kLinear;
sampler_info.minify = gfx::Filter::kLinear;
sampler_info.mipmap = gfx::Filter::kLinear;
sampler_info.wrap_u = gfx::WrapMode::kClampToEdge;
sampler_info.wrap_v = gfx::WrapMode::kClampToEdge;
sampler_info.wrap_w = gfx::WrapMode::kClampToEdge;
sampler_info.anisotropic_filtering = true;
skybox_sampler = device_->CreateSampler(sampler_info);
}
// create skybox shader
{
gfx::VertexFormat vertex_format{};
vertex_format.attribute_descriptions.emplace_back(0, gfx::VertexAttribFormat::kFloat3, 0);
vertex_format.stride = 3 * sizeof(float);
gfx::PipelineInfo pipeline_info{};
pipeline_info.vert_shader_path = GetResourcePath("engine/shaders/skybox.vert");
pipeline_info.frag_shader_path = GetResourcePath("engine/shaders/skybox.frag");
pipeline_info.vertex_format = vertex_format;
pipeline_info.alpha_blending = false;
pipeline_info.backface_culling = true;
pipeline_info.write_z = false;
pipeline_info.line_primitives = false;
pipeline_info.descriptor_set_layouts.push_back(GetGlobalSetLayout());
pipeline_info.descriptor_set_layouts.push_back(GetFrameSetLayout());
skybox_pipeline = device_->CreatePipeline(pipeline_info);
device_->UpdateDescriptorCombinedImageSampler(global_uniform.set, 1, skybox_cubemap, skybox_sampler);
}
// create skybox vertex buffer
{
std::vector<glm::vec3> v{};
v.push_back({0.0f, 0.0f, 2.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({2.0f, 0.0f, 0.0f});
v.push_back({2.0f, 0.0f, 0.0f});
v.push_back({2.0f, 0.0f, 2.0f});
v.push_back({0.0f, 0.0f, 2.0f});
// back
v.push_back({2.0f, 2.0f, 2.0f });
v.push_back({ 2.0f, 2.0f, 0.0f});
v.push_back({0.0f, 2.0f, 0.0f});
v.push_back({0.0f, 2.0f, 0.0f});
v.push_back({0.0f, 2.0f, 2.0f });
v.push_back({ 2.0f, 2.0f, 2.0f });
// left
v.push_back({0.0f, 2.0f, 2.0f });
v.push_back({0.0f, 2.0f, 0.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({0.0f, 0.0f, 2.0f });
v.push_back({0.0f, 2.0f, 2.0f });
// right
v.push_back({ 2.0f, 0.0f, 2.0f });
v.push_back({ 2.0f, 0.0f, 0.0f});
v.push_back({ 2.0f, 2.0f, 0.0f});
v.push_back({ 2.0f, 2.0f, 0.0f});
v.push_back({ 2.0f, 2.0f, 2.0f });
v.push_back({ 2.0f, 0.0f, 2.0f });
// top
v.push_back({0.0f, 2.0f, 2.0f });
v.push_back({0.0f, 0.0f, 2.0f });
v.push_back({ 2.0f, 0.0f, 2.0f });
v.push_back({ 2.0f, 0.0f, 2.0f });
v.push_back({ 2.0f, 2.0f, 2.0f });
v.push_back({0.0f, 2.0f, 2.0f });
// bottom
v.push_back({ 2.0f, 2.0f, 0.0f});
v.push_back({ 2.0f, 0.0f, 0.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({0.0f, 0.0f, 0.0f});
v.push_back({0.0f, 2.0f, 0.0f});
v.push_back({ 2.0f, 2.0f, 0.0f});
for (glm::vec3& pos : v) {
pos.x -= 1.0f;
pos.y -= 1.0f;
pos.z -= 1.0f;
}
for (size_t i = 0; i < v.size(); i += 3) {
std::swap(v[i], v[i + 2]);
}
skybox_buffer = device_->CreateBuffer(gfx::BufferType::kVertex, v.size() * sizeof(glm::vec3), v.data());
}
2023-08-29 17:06:04 +00:00
};
2023-11-05 01:04:05 +00:00
Renderer::~Renderer()
{
2024-03-19 11:32:51 +00:00
device_->DestroyBuffer(skybox_buffer);
device_->DestroyPipeline(skybox_pipeline);
device_->DestroySampler(skybox_sampler);
device_->DestroyImage(skybox_cubemap);
device_->DestroyPipeline(debug_rendering_things_.pipeline);
2023-11-05 01:04:05 +00:00
for (const auto& [info, sampler] : samplers) {
device_->DestroySampler(sampler);
}
device_->DestroyDescriptorSetLayout(material_set_layout);
2023-08-29 17:06:04 +00:00
2023-11-05 01:04:05 +00:00
device_->DestroyUniformBuffer(frame_uniform.uniform_buffer);
device_->DestroyDescriptorSetLayout(frame_uniform.layout);
2023-08-29 17:06:04 +00:00
2023-11-05 01:04:05 +00:00
device_->DestroyUniformBuffer(global_uniform.uniform_buffer);
device_->DestroyDescriptorSetLayout(global_uniform.layout);
2023-08-29 17:06:04 +00:00
}
2023-11-05 01:04:05 +00:00
void Renderer::PreRender(bool window_is_resized, glm::mat4 camera_transform)
{
if (window_is_resized) {
uint32_t w, h;
device_->GetViewportSize(&w, &h);
viewport_aspect_ratio_ = (float)w / (float)h;
const glm::mat4 proj_matrix =
glm::perspectiveRH_ZO(camera_settings_.vertical_fov_radians, viewport_aspect_ratio_, camera_settings_.clip_near, camera_settings_.clip_far);
/* update SET 0 (rarely changing uniforms)*/
global_uniform.uniform_buffer_data.data = proj_matrix;
device_->WriteUniformBuffer(global_uniform.uniform_buffer, 0, sizeof(global_uniform.uniform_buffer_data), &global_uniform.uniform_buffer_data);
}
// set camera view matrix uniform
/* update SET 1 (per frame uniforms) */
const glm::mat4 view_matrix = glm::inverse(camera_transform);
frame_uniform.uniform_buffer_data.data = view_matrix;
device_->WriteUniformBuffer(frame_uniform.uniform_buffer, 0, sizeof(frame_uniform.uniform_buffer_data), &frame_uniform.uniform_buffer_data);
2023-08-29 21:10:05 +00:00
}
2024-03-14 04:25:02 +00:00
void Renderer::Render(const RenderList* static_list, const RenderList* dynamic_list, const std::vector<Line>& debug_lines)
2023-11-05 01:04:05 +00:00
{
last_bound_pipeline_ = nullptr;
2023-08-31 13:18:42 +00:00
2023-11-05 01:04:05 +00:00
gfx::DrawBuffer* draw_buffer = device_->BeginRender();
2023-08-29 21:10:05 +00:00
2023-11-05 01:04:05 +00:00
if (static_list) {
if (!static_list->empty()) {
DrawRenderList(draw_buffer, *static_list);
}
}
if (dynamic_list) {
if (!dynamic_list->empty()) {
DrawRenderList(draw_buffer, *dynamic_list);
}
}
2023-08-31 13:18:42 +00:00
struct DebugPush {
glm::vec4 pos1;
glm::vec4 pos2;
glm::vec3 color;
};
2024-03-19 11:32:51 +00:00
// draw skybox
{
device_->CmdBindPipeline(draw_buffer, skybox_pipeline);
device_->CmdBindDescriptorSet(draw_buffer, skybox_pipeline, global_uniform.set, 0);
device_->CmdBindDescriptorSet(draw_buffer, skybox_pipeline, frame_uniform.set, 1);
device_->CmdBindVertexBuffer(draw_buffer, 0, skybox_buffer);
device_->CmdDraw(draw_buffer, 36, 1, 0, 0);
}
// draw debug shit here
device_->CmdBindPipeline(draw_buffer, debug_rendering_things_.pipeline);
DebugPush push{};
2024-03-14 04:25:02 +00:00
for (const Line& l : debug_lines) {
push.pos1 = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos1, 1.0f);
push.pos2 = global_uniform.uniform_buffer_data.data * frame_uniform.uniform_buffer_data.data * glm::vec4(l.pos2, 1.0f);
push.color = l.color;
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push);
2024-03-14 04:25:02 +00:00
device_->CmdDraw(draw_buffer, 2, 1, 0, 0);
}
// also make a lil crosshair
2024-03-19 11:32:51 +00:00
push.color = glm::vec3{1.0f, 1.0f, 1.0f};
push.pos1 = glm::vec4(-0.05f, 0.0f, 0.0f, 1.0f);
push.pos2 = glm::vec4(0.05f, 0.0f, 0.0f, 1.0f);
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push);
2024-03-14 04:25:02 +00:00
device_->CmdDraw(draw_buffer, 2, 1, 0, 0);
push.pos1 = glm::vec4(0.0f, -0.05f, 0.0f, 1.0f);
push.pos2 = glm::vec4(0.0f, 0.05f, 0.0f, 1.0f);
device_->CmdPushConstants(draw_buffer, debug_rendering_things_.pipeline, 0, sizeof(DebugPush), &push);
device_->CmdDraw(draw_buffer, 2, 1, 0, 0);
2023-11-05 01:04:05 +00:00
device_->CmdRenderImguiDrawData(draw_buffer, ImGui::GetDrawData());
2023-10-01 10:38:27 +00:00
2023-11-05 01:04:05 +00:00
device_->FinishRender(draw_buffer);
2023-08-31 13:18:42 +00:00
}
2023-11-05 01:04:05 +00:00
void Renderer::DrawRenderList(gfx::DrawBuffer* draw_buffer, const RenderList& render_list)
{
// if a pipeline hasn't been bound yet at all
if (last_bound_pipeline_ == nullptr) {
const gfx::Pipeline* first_pipeline = render_list.begin()->pipeline;
// these bindings persist between all pipelines
device_->CmdBindDescriptorSet(draw_buffer, first_pipeline, global_uniform.set, 0);
device_->CmdBindDescriptorSet(draw_buffer, first_pipeline, frame_uniform.set, 1);
device_->CmdBindPipeline(draw_buffer, first_pipeline);
last_bound_pipeline_ = first_pipeline;
}
for (const auto& entry : render_list) {
if (entry.pipeline != last_bound_pipeline_) {
device_->CmdBindPipeline(draw_buffer, entry.pipeline);
last_bound_pipeline_ = entry.pipeline;
}
device_->CmdBindDescriptorSet(draw_buffer, entry.pipeline, entry.material_set, 2);
device_->CmdPushConstants(draw_buffer, entry.pipeline, 0, sizeof(entry.model_matrix), &entry.model_matrix);
device_->CmdBindVertexBuffer(draw_buffer, 0, entry.vertex_buffer);
device_->CmdBindIndexBuffer(draw_buffer, entry.index_buffer);
device_->CmdDrawIndexed(draw_buffer, entry.index_count, 1, 0, 0, 0);
2023-08-31 13:18:42 +00:00
}
2023-08-29 21:10:05 +00:00
}
2023-11-05 01:04:05 +00:00
} // namespace engine