diff --git a/src/Application.cpp b/src/Application.cpp index 2979118..4e199de 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include "Util.h" #include "VulkanRenderer.h" @@ -308,7 +310,60 @@ auto Application::run() -> void ImGui::Render(); - m_renderer->render(); + m_renderer->render([&](VulkanRenderer::GL &gl) { + auto view { smath::matrix_look_at(smath::Vec3 { 0.0f, 0.0f, 3.0f }, + smath::Vec3 { 0.0f, 0.0f, 0.0f }, + smath::Vec3 { 0.0f, 1.0f, 0.0f }, false) }; + auto const draw_extent = m_renderer->draw_extent(); + auto const aspect = draw_extent.height == 0 + ? 1.0f + : static_cast(draw_extent.width) + / static_cast(draw_extent.height); + auto projection { smath::matrix_perspective( + smath::deg(70.0f), aspect, 0.1f, 10000.0f) }; + projection[1][1] *= -1; + auto view_projection { projection * view }; + + auto rect_model { smath::scale( + smath::translate(smath::Vec3 { 0.0f, 0.0f, -5.0f }), + smath::Vec3 { 5.0f, 5.0f, 1.0f }) }; + + gl.set_transform(view_projection * rect_model); + + gl.set_texture(); + auto const &meshes { m_renderer->test_meshes() }; + if (meshes.size() > 2 && !meshes[2]->surfaces.empty()) { + auto const &surface = meshes[2]->surfaces[0]; + gl.draw_mesh(meshes[2]->mesh_buffers, view_projection, + surface.count, surface.start_index); + } + + gl.set_texture(&m_renderer->white_texture()); + + gl.begin(VulkanRenderer::GL::GeometryKind::Quads); + + gl.color(smath::Vec3 { 0.0f, 0.0f, 0.0f }); + gl.uv(smath::Vec2 { 1.0f, 1.0f }); + gl.vert(smath::Vec3 { 0.5f, -0.5f, 0.0f }); + + gl.color(smath::Vec3 { 0.5f, 0.5f, 0.5f }); + gl.uv(smath::Vec2 { 1.0f, 0.0f }); + gl.vert(smath::Vec3 { 0.5f, 0.5f, 0.0f }); + + gl.color(smath::Vec3 { 1.0f, 0.0f, 0.0f }); + gl.uv(smath::Vec2 { 0.0f, 1.0f }); + gl.vert(smath::Vec3 { -0.5f, -0.5f, 0.0f }); + + gl.color(smath::Vec3 { 0.0f, 1.0f, 0.0f }); + gl.uv(smath::Vec2 { 0.0f, 0.0f }); + gl.vert(smath::Vec3 { -0.5f, 0.5f, 0.0f }); + + gl.end(); + + gl.draw_rectangle({ -0.5f, 0.5f }, { 0.5f, 0.5f }); + gl.draw_rectangle( + { 0, 0.5f }, { 0.5f, 0.5f }, { Colors::TEAL, 1.0f }); + }); } } diff --git a/src/Colors.h b/src/Colors.h new file mode 100644 index 0000000..14ecc45 --- /dev/null +++ b/src/Colors.h @@ -0,0 +1,177 @@ +#pragma once + +#include + +namespace Lunar::Colors { + +constexpr auto hex_to_vec3(uint32_t hex) -> smath::Vec3 +{ + return smath::Vec3 { static_cast((hex >> 16) & 0xFF) / 255.0f, + static_cast((hex >> 8) & 0xFF) / 255.0f, + static_cast(hex & 0xFF) / 255.0f }; +} + +inline constexpr smath::Vec3 WHITE { hex_to_vec3(0xFFFFFF) }; +inline constexpr smath::Vec3 SILVER { hex_to_vec3(0xC0C0C0) }; +inline constexpr smath::Vec3 GRAY { hex_to_vec3(0x808080) }; +inline constexpr smath::Vec3 BLACK { hex_to_vec3(0x000000) }; +inline constexpr smath::Vec3 RED { hex_to_vec3(0xFF0000) }; +inline constexpr smath::Vec3 MAROON { hex_to_vec3(0x800000) }; +inline constexpr smath::Vec3 YELLOW { hex_to_vec3(0xFFFF00) }; +inline constexpr smath::Vec3 OLIVE { hex_to_vec3(0x808000) }; +inline constexpr smath::Vec3 LIME { hex_to_vec3(0x00FF00) }; +inline constexpr smath::Vec3 GREEN { hex_to_vec3(0x008000) }; +inline constexpr smath::Vec3 AQUA { hex_to_vec3(0x00FFFF) }; +inline constexpr smath::Vec3 TEAL { hex_to_vec3(0x008080) }; +inline constexpr smath::Vec3 BLUE { hex_to_vec3(0x0000FF) }; +inline constexpr smath::Vec3 NAVY { hex_to_vec3(0x000080) }; +inline constexpr smath::Vec3 FUCHSIA { hex_to_vec3(0xFF00FF) }; +inline constexpr smath::Vec3 PURPLE { hex_to_vec3(0x800080) }; + +// Pink colors +inline constexpr smath::Vec3 MEDIUM_VIOLET_RED { hex_to_vec3(0xC71585) }; +inline constexpr smath::Vec3 DEEP_PINK { hex_to_vec3(0xFF1493) }; +inline constexpr smath::Vec3 PALE_VIOLET_RED { hex_to_vec3(0xDB7093) }; +inline constexpr smath::Vec3 HOT_PINK { hex_to_vec3(0xFF69B4) }; +inline constexpr smath::Vec3 LIGHT_PINK { hex_to_vec3(0xFFB6C1) }; +inline constexpr smath::Vec3 PINK { hex_to_vec3(0xFFC0CB) }; + +// Red colors +inline constexpr smath::Vec3 DARK_RED { hex_to_vec3(0x8B0000) }; +inline constexpr smath::Vec3 FIREBRICK { hex_to_vec3(0xB22222) }; +inline constexpr smath::Vec3 CRIMSON { hex_to_vec3(0xDC143C) }; +inline constexpr smath::Vec3 INDIAN_RED { hex_to_vec3(0xCD5C5C) }; +inline constexpr smath::Vec3 LIGHT_CORAL { hex_to_vec3(0xF08080) }; +inline constexpr smath::Vec3 SALMON { hex_to_vec3(0xFA8072) }; +inline constexpr smath::Vec3 DARK_SALMON { hex_to_vec3(0xE9967A) }; +inline constexpr smath::Vec3 LIGHT_SALMON { hex_to_vec3(0xFFA07A) }; + +// Orange colors +inline constexpr smath::Vec3 ORANGE_RED { hex_to_vec3(0xFF4500) }; +inline constexpr smath::Vec3 TOMATO { hex_to_vec3(0xFF6347) }; +inline constexpr smath::Vec3 DARK_ORANGE { hex_to_vec3(0xFF8C00) }; +inline constexpr smath::Vec3 CORAL { hex_to_vec3(0xFF7F50) }; +inline constexpr smath::Vec3 ORANGE { hex_to_vec3(0xFFA500) }; + +// Yellow colors +inline constexpr smath::Vec3 DARK_KHAKI { hex_to_vec3(0xBDB76B) }; +inline constexpr smath::Vec3 GOLD { hex_to_vec3(0xFFD700) }; +inline constexpr smath::Vec3 KHAKI { hex_to_vec3(0xF0E68C) }; +inline constexpr smath::Vec3 PEACH_PUFF { hex_to_vec3(0xFFDAB9) }; +inline constexpr smath::Vec3 PALE_GOLDENROD { hex_to_vec3(0xEEE8AA) }; +inline constexpr smath::Vec3 MOCCASIN { hex_to_vec3(0xFFE4B5) }; +inline constexpr smath::Vec3 PAPAYA_WHIP { hex_to_vec3(0xFFEFD5) }; +inline constexpr smath::Vec3 LIGHT_GOLDENROD_YELLOW { hex_to_vec3(0xFAFAD2) }; +inline constexpr smath::Vec3 LEMON_CHIFFON { hex_to_vec3(0xFFFACD) }; +inline constexpr smath::Vec3 LIGHT_YELLOW { hex_to_vec3(0xFFFFE0) }; + +// Brown colors +inline constexpr smath::Vec3 BROWN { hex_to_vec3(0xA52A2A) }; +inline constexpr smath::Vec3 SADDLE_BROWN { hex_to_vec3(0x8B4513) }; +inline constexpr smath::Vec3 SIENNA { hex_to_vec3(0xA0522D) }; +inline constexpr smath::Vec3 CHOCOLATE { hex_to_vec3(0xD2691E) }; +inline constexpr smath::Vec3 DARK_GOLDENROD { hex_to_vec3(0xB8860B) }; +inline constexpr smath::Vec3 PERU { hex_to_vec3(0xCD853F) }; +inline constexpr smath::Vec3 ROSY_BROWN { hex_to_vec3(0xBC8F8F) }; +inline constexpr smath::Vec3 GOLDENROD { hex_to_vec3(0xDAA520) }; +inline constexpr smath::Vec3 SANDY_BROWN { hex_to_vec3(0xF4A460) }; +inline constexpr smath::Vec3 TAN { hex_to_vec3(0xD2B48C) }; +inline constexpr smath::Vec3 BURLYWOOD { hex_to_vec3(0xDEB887) }; +inline constexpr smath::Vec3 WHEAT { hex_to_vec3(0xF5DEB3) }; +inline constexpr smath::Vec3 NAVAJO_WHITE { hex_to_vec3(0xFFDEAD) }; +inline constexpr smath::Vec3 BISQUE { hex_to_vec3(0xFFE4C4) }; +inline constexpr smath::Vec3 BLANCHED_ALMOND { hex_to_vec3(0xFFEBCD) }; +inline constexpr smath::Vec3 CORNSILK { hex_to_vec3(0xFFF8DC) }; + +// Purple, violet, magenta colors +inline constexpr smath::Vec3 INDIGO { hex_to_vec3(0x4B0082) }; +inline constexpr smath::Vec3 DARK_MAGENTA { hex_to_vec3(0x8B008B) }; +inline constexpr smath::Vec3 DARK_VIOLET { hex_to_vec3(0x9400D3) }; +inline constexpr smath::Vec3 DARK_SLATE_BLUE { hex_to_vec3(0x483D8B) }; +inline constexpr smath::Vec3 BLUE_VIOLET { hex_to_vec3(0x8A2BE2) }; +inline constexpr smath::Vec3 DARK_ORCHID { hex_to_vec3(0x9932CC) }; +inline constexpr smath::Vec3 MAGENTA { hex_to_vec3(0xFF00FF) }; +inline constexpr smath::Vec3 SLATE_BLUE { hex_to_vec3(0x6A5ACD) }; +inline constexpr smath::Vec3 MEDIUM_SLATE_BLUE { hex_to_vec3(0x7B68EE) }; +inline constexpr smath::Vec3 MEDIUM_ORCHID { hex_to_vec3(0xBA55D3) }; +inline constexpr smath::Vec3 MEDIUM_PURPLE { hex_to_vec3(0x9370DB) }; +inline constexpr smath::Vec3 ORCHID { hex_to_vec3(0xDA70D6) }; +inline constexpr smath::Vec3 VIOLET { hex_to_vec3(0xEE82EE) }; +inline constexpr smath::Vec3 PLUM { hex_to_vec3(0xDDA0DD) }; +inline constexpr smath::Vec3 THISTLE { hex_to_vec3(0xD8BFD8) }; +inline constexpr smath::Vec3 LAVENDER { hex_to_vec3(0xE6E6FA) }; + +// Blue colors +inline constexpr smath::Vec3 MIDNIGHT_BLUE { hex_to_vec3(0x191970) }; +inline constexpr smath::Vec3 DARK_BLUE { hex_to_vec3(0x00008B) }; +inline constexpr smath::Vec3 MEDIUM_BLUE { hex_to_vec3(0x0000CD) }; +inline constexpr smath::Vec3 ROYAL_BLUE { hex_to_vec3(0x4169E1) }; +inline constexpr smath::Vec3 STEEL_BLUE { hex_to_vec3(0x4682B4) }; +inline constexpr smath::Vec3 DODGER_BLUE { hex_to_vec3(0x1E90FF) }; +inline constexpr smath::Vec3 DEEP_SKY_BLUE { hex_to_vec3(0x00BFFF) }; +inline constexpr smath::Vec3 CORNFLOWER_BLUE { hex_to_vec3(0x6495ED) }; +inline constexpr smath::Vec3 SKY_BLUE { hex_to_vec3(0x87CEEB) }; +inline constexpr smath::Vec3 LIGHT_SKY_BLUE { hex_to_vec3(0x87CEFA) }; +inline constexpr smath::Vec3 LIGHT_STEEL_BLUE { hex_to_vec3(0xB0C4DE) }; +inline constexpr smath::Vec3 LIGHT_BLUE { hex_to_vec3(0xADD8E6) }; +inline constexpr smath::Vec3 POWDER_BLUE { hex_to_vec3(0xB0E0E6) }; + +// Cyan colors +inline constexpr smath::Vec3 DARK_CYAN { hex_to_vec3(0x008B8B) }; +inline constexpr smath::Vec3 LIGHT_SEA_GREEN { hex_to_vec3(0x20B2AA) }; +inline constexpr smath::Vec3 CADET_BLUE { hex_to_vec3(0x5F9EA0) }; +inline constexpr smath::Vec3 DARK_TURQUOISE { hex_to_vec3(0x00CED1) }; +inline constexpr smath::Vec3 MEDIUM_TURQUOISE { hex_to_vec3(0x48D1CC) }; +inline constexpr smath::Vec3 TURQUOISE { hex_to_vec3(0x40E0D0) }; +inline constexpr smath::Vec3 CYAN { hex_to_vec3(0x00FFFF) }; +inline constexpr smath::Vec3 AQUAMARINE { hex_to_vec3(0x7FFFD4) }; +inline constexpr smath::Vec3 PALE_TURQUOISE { hex_to_vec3(0xAFEEEE) }; +inline constexpr smath::Vec3 LIGHT_CYAN { hex_to_vec3(0xE0FFFF) }; + +// Green colors +inline constexpr smath::Vec3 DARK_GREEN { hex_to_vec3(0x006400) }; +inline constexpr smath::Vec3 DARK_OLIVE_GREEN { hex_to_vec3(0x556B2F) }; +inline constexpr smath::Vec3 FOREST_GREEN { hex_to_vec3(0x228B22) }; +inline constexpr smath::Vec3 SEA_GREEN { hex_to_vec3(0x2E8B57) }; +inline constexpr smath::Vec3 OLIVE_DRAB { hex_to_vec3(0x6B8E23) }; +inline constexpr smath::Vec3 MEDIUM_SEA_GREEN { hex_to_vec3(0x3CB371) }; +inline constexpr smath::Vec3 LIME_GREEN { hex_to_vec3(0x32CD32) }; +inline constexpr smath::Vec3 SPRING_GREEN { hex_to_vec3(0x00FF7F) }; +inline constexpr smath::Vec3 MEDIUM_SPRING_GREEN { hex_to_vec3(0x00FA9A) }; +inline constexpr smath::Vec3 DARK_SEA_GREEN { hex_to_vec3(0x8FBC8F) }; +inline constexpr smath::Vec3 MEDIUM_AQUAMARINE { hex_to_vec3(0x66CDAA) }; +inline constexpr smath::Vec3 YELLOW_GREEN { hex_to_vec3(0x9ACD32) }; +inline constexpr smath::Vec3 LAWN_GREEN { hex_to_vec3(0x7CFC00) }; +inline constexpr smath::Vec3 CHARTREUSE { hex_to_vec3(0x7FFF00) }; +inline constexpr smath::Vec3 LIGHT_GREEN { hex_to_vec3(0x90EE90) }; +inline constexpr smath::Vec3 GREEN_YELLOW { hex_to_vec3(0xADFF2F) }; +inline constexpr smath::Vec3 PALE_GREEN { hex_to_vec3(0x98FB98) }; + +// White colors +inline constexpr smath::Vec3 MISTY_ROSE { hex_to_vec3(0xFFE4E1) }; +inline constexpr smath::Vec3 ANTIQUE_WHITE { hex_to_vec3(0xFAEBD7) }; +inline constexpr smath::Vec3 LINEN { hex_to_vec3(0xFAF0E6) }; +inline constexpr smath::Vec3 BEIGE { hex_to_vec3(0xF5F5DC) }; +inline constexpr smath::Vec3 WHITE_SMOKE { hex_to_vec3(0xF5F5F5) }; +inline constexpr smath::Vec3 LAVENDER_BLUSH { hex_to_vec3(0xFFF0F5) }; +inline constexpr smath::Vec3 OLD_LACE { hex_to_vec3(0xFDF5E6) }; +inline constexpr smath::Vec3 ALICE_BLUE { hex_to_vec3(0xF0F8FF) }; +inline constexpr smath::Vec3 SEASHELL { hex_to_vec3(0xFFF5EE) }; +inline constexpr smath::Vec3 GHOST_WHITE { hex_to_vec3(0xF8F8FF) }; +inline constexpr smath::Vec3 HONEYDEW { hex_to_vec3(0xF0FFF0) }; +inline constexpr smath::Vec3 FLORAL_WHITE { hex_to_vec3(0xFFFAF0) }; +inline constexpr smath::Vec3 AZURE { hex_to_vec3(0xF0FFFF) }; +inline constexpr smath::Vec3 MINT_CREAM { hex_to_vec3(0xF5FFFA) }; +inline constexpr smath::Vec3 SNOW { hex_to_vec3(0xFFFAFA) }; +inline constexpr smath::Vec3 IVORY { hex_to_vec3(0xFFFFF0) }; + +// Gray and black colors +inline constexpr smath::Vec3 DARK_SLATE_GRAY { hex_to_vec3(0x2F4F4F) }; +inline constexpr smath::Vec3 DIM_GRAY { hex_to_vec3(0x696969) }; +inline constexpr smath::Vec3 SLATE_GRAY { hex_to_vec3(0x708090) }; +inline constexpr smath::Vec3 LIGHT_SLATE_GRAY { hex_to_vec3(0x778899) }; +inline constexpr smath::Vec3 DARK_GRAY { hex_to_vec3(0xA9A9A9) }; +inline constexpr smath::Vec3 LIGHT_GRAY { hex_to_vec3(0xD3D3D3) }; +inline constexpr smath::Vec3 GAINSBORO { hex_to_vec3(0xDCDCDC) }; + +} // namespace Lunar::Colors diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp index 64a2b15..d43b395 100644 --- a/src/VulkanRenderer.cpp +++ b/src/VulkanRenderer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -24,8 +25,422 @@ VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE namespace Lunar { +VulkanRenderer::GL::GL(VulkanRenderer &renderer) + : m_renderer(renderer) +{ +} + +auto VulkanRenderer::GL::begin_drawing(vk::CommandBuffer cmd, + AllocatedImage &color_target, AllocatedImage *depth_target) -> void +{ + if (m_drawing) { + end_drawing(); + } + + m_cmd = cmd; + m_color_target = &color_target; + m_depth_target = depth_target; + m_vertices.clear(); + m_indices.clear(); + m_inside_primitive = false; + m_drawing = true; + m_active_pipeline = &m_renderer.m_vk.mesh_pipeline; + m_transform = smath::Mat4::identity(); + m_current_color = { 1.0f, 1.0f, 1.0f, 1.0f }; + m_current_normal = { 0.0f, 0.0f, 1.0f }; + m_current_uv = { 0.0f, 0.0f }; + m_bound_texture = &m_renderer.m_vk.error_image; + + auto const extent = vk::Extent2D { + m_color_target->extent.width, + m_color_target->extent.height, + }; + + auto color_att { vkinit::attachment_info(m_color_target->image_view, + nullptr, vk::ImageLayout::eColorAttachmentOptimal) }; + std::optional depth_att; + if (m_depth_target) { + depth_att = vkinit::depth_attachment_info(m_depth_target->image_view, + vk::ImageLayout::eDepthAttachmentOptimal); + } + + auto render_info { vkinit::render_info( + extent, &color_att, depth_att ? &*depth_att : nullptr) }; + m_cmd.beginRendering(render_info); + + vk::Viewport viewport {}; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = static_cast(extent.width); + viewport.height = static_cast(extent.height); + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + m_cmd.setViewport(0, viewport); + + vk::Rect2D scissor {}; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent = extent; + m_cmd.setScissor(0, scissor); + + bind_pipeline_if_needed(); +} + +auto VulkanRenderer::GL::end_drawing() -> void +{ + if (!m_drawing) + return; + + if (m_inside_primitive) { + end(); + } + + flush(); + m_cmd.endRendering(); + + m_cmd = nullptr; + m_color_target = nullptr; + m_depth_target = nullptr; + m_drawing = false; + m_active_pipeline = nullptr; +} + +auto VulkanRenderer::GL::begin(GeometryKind kind) -> void +{ + assert(m_drawing && "begin_drawing must be called first"); + if (m_inside_primitive) { + end(); + } + + m_current_kind = kind; + m_primitive_start = m_vertices.size(); + m_inside_primitive = true; +} + +auto VulkanRenderer::GL::color(smath::Vec3 const &rgb) -> void +{ + m_current_color = smath::Vec4 { rgb, 1.0f }; +} + +auto VulkanRenderer::GL::color(smath::Vec4 const &rgba) -> void +{ + m_current_color = rgba; +} + +auto VulkanRenderer::GL::uv(smath::Vec2 const &uv) -> void +{ + m_current_uv = uv; +} + +auto VulkanRenderer::GL::normal(smath::Vec3 const &normal) -> void +{ + m_current_normal = normal; +} + +auto VulkanRenderer::GL::set_texture( + std::optional texture) -> void +{ + assert(m_drawing && "begin_drawing must be called first"); + flush(); + m_bound_texture = texture.value_or(&m_renderer.m_vk.error_image); +} + +auto VulkanRenderer::GL::end() -> void +{ + if (!m_inside_primitive) + return; + + auto const count = m_vertices.size() - m_primitive_start; + emit_indices(m_primitive_start, count); + m_inside_primitive = false; +} + +auto VulkanRenderer::GL::flush() -> void +{ + if (!m_drawing || m_vertices.empty() || m_indices.empty()) + return; + + auto const vertex_data_size { m_vertices.size() * sizeof(Vertex) }; + auto const index_data_size { m_indices.size() * sizeof(uint32_t) }; + auto const staging_size { vertex_data_size + index_data_size }; + + auto staging = m_renderer.create_buffer(staging_size, + vk::BufferUsageFlagBits::eTransferSrc, VMA_MEMORY_USAGE_CPU_ONLY); + + void *staging_dst = staging.info.pMappedData; + bool staging_mapped_here { false }; + if (!staging_dst) { + VkResult res = vmaMapMemory( + m_renderer.m_vk.allocator, staging.allocation, &staging_dst); + assert(res == VK_SUCCESS); + staging_mapped_here = true; + } + memcpy(staging_dst, m_vertices.data(), vertex_data_size); + memcpy(reinterpret_cast(staging_dst) + vertex_data_size, + m_indices.data(), index_data_size); + if (staging_mapped_here) { + vmaUnmapMemory(m_renderer.m_vk.allocator, staging.allocation); + } + + auto vertex_buffer { m_renderer.create_buffer(vertex_data_size, + vk::BufferUsageFlagBits::eVertexBuffer + | vk::BufferUsageFlagBits::eTransferDst + | vk::BufferUsageFlagBits::eShaderDeviceAddress, + VMA_MEMORY_USAGE_GPU_ONLY) }; + auto index_buffer { m_renderer.create_buffer(index_data_size, + vk::BufferUsageFlagBits::eIndexBuffer + | vk::BufferUsageFlagBits::eTransferDst, + VMA_MEMORY_USAGE_GPU_ONLY) }; + + m_renderer.immediate_submit([&](vk::CommandBuffer cmd) { + vk::BufferCopy vertex_copy {}; + vertex_copy.srcOffset = 0; + vertex_copy.dstOffset = 0; + vertex_copy.size = vertex_data_size; + cmd.copyBuffer( + staging.buffer, vertex_buffer.buffer, 1, &vertex_copy); + + vk::BufferCopy index_copy {}; + index_copy.srcOffset = vertex_data_size; + index_copy.dstOffset = 0; + index_copy.size = index_data_size; + cmd.copyBuffer( + staging.buffer, index_buffer.buffer, 1, &index_copy); + }, + /*flush_frame_deletion_queue=*/false, + /*clear_frame_descriptors=*/false); + + m_renderer.destroy_buffer(staging); + + auto cmd { m_cmd }; + + bind_pipeline_if_needed(); + + if (m_active_pipeline == &m_renderer.m_vk.mesh_pipeline) { + auto const image_set { + m_renderer.m_vk.get_current_frame().frame_descriptors.allocate( + m_renderer.m_logger, m_renderer.m_vkb.dev.device, + m_renderer.m_vk.single_image_descriptor_layout) + }; + + auto const *image + = m_bound_texture ? m_bound_texture : &m_renderer.m_vk.error_image; + DescriptorWriter() + .write_image(0, image->image_view, + m_renderer.m_vk.default_sampler_nearest.get(), + static_cast( + vk::ImageLayout::eShaderReadOnlyOptimal), + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) + .update_set(m_renderer.m_vkb.dev.device, image_set); + + auto vk_image_set = vk::DescriptorSet { image_set }; + cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, + m_renderer.m_vk.mesh_pipeline.get_layout(), 0, vk_image_set, {}); + + GPUDrawPushConstants push_constants {}; + push_constants.world_matrix = m_transform; + + vk::BufferDeviceAddressInfo device_address_info {}; + device_address_info.buffer = vertex_buffer.buffer; + push_constants.vertex_buffer + = m_renderer.m_device.getBufferAddress(device_address_info); + + cmd.pushConstants(m_renderer.m_vk.mesh_pipeline.get_layout(), + vk::ShaderStageFlagBits::eVertex, 0, sizeof(push_constants), + &push_constants); + } + + cmd.bindIndexBuffer(index_buffer.buffer, 0, vk::IndexType::eUint32); + cmd.drawIndexed(static_cast(m_indices.size()), 1, 0, 0, 0); + + m_renderer.m_vk.get_current_frame().deletion_queue.emplace([=, this]() { + m_renderer.destroy_buffer(index_buffer); + m_renderer.destroy_buffer(vertex_buffer); + }); + + m_vertices.clear(); + m_indices.clear(); +} + +auto VulkanRenderer::GL::use_pipeline(Pipeline &pipeline) -> void +{ + if (&pipeline == m_active_pipeline) { + return; + } + + flush(); + + m_active_pipeline = &pipeline; + bind_pipeline_if_needed(); +} + +auto VulkanRenderer::GL::set_transform(smath::Mat4 const &transform) -> void +{ + flush(); + m_transform = transform; +} + +auto VulkanRenderer::GL::draw_rectangle( + smath::Vec2 pos, smath::Vec2 size, smath::Vec4 rect_color, float rotation) + -> void +{ + auto const half_size = size * 0.5f; + auto const center = pos + half_size; + + auto rotate = [&](smath::Vec2 const &p) { + float const c = std::cos(rotation); + float const s = std::sin(rotation); + return smath::Vec2 { c * p.x() - s * p.y(), s * p.x() + c * p.y() }; + }; + + auto const br = center + rotate(smath::Vec2 { half_size.x(), -half_size.y() }); + auto const tr = center + rotate(smath::Vec2 { half_size.x(), half_size.y() }); + auto const bl = center + rotate(smath::Vec2 { -half_size.x(), -half_size.y() }); + auto const tl = center + rotate(smath::Vec2 { -half_size.x(), half_size.y() }); + + begin(GeometryKind::Quads); + + color(rect_color); + uv(smath::Vec2 { 1.0f, 1.0f }); + vert(smath::Vec3 { br.x(), br.y(), 0.0f }); + + color(rect_color); + uv(smath::Vec2 { 1.0f, 0.0f }); + vert(smath::Vec3 { tr.x(), tr.y(), 0.0f }); + + color(rect_color); + uv(smath::Vec2 { 0.0f, 1.0f }); + vert(smath::Vec3 { bl.x(), bl.y(), 0.0f }); + + color(rect_color); + uv(smath::Vec2 { 0.0f, 0.0f }); + vert(smath::Vec3 { tl.x(), tl.y(), 0.0f }); + + end(); +} + +auto VulkanRenderer::GL::draw_mesh(GPUMeshBuffers const &mesh, + smath::Mat4 const &transform, uint32_t index_count, uint32_t first_index, + int32_t vertex_offset) -> void +{ + assert(m_drawing && "begin_drawing must be called first"); + + flush(); + use_pipeline(m_renderer.m_vk.mesh_pipeline); + + auto const image_set { + m_renderer.m_vk.get_current_frame().frame_descriptors.allocate( + m_renderer.m_logger, m_renderer.m_vkb.dev.device, + m_renderer.m_vk.single_image_descriptor_layout) + }; + auto const *image + = m_bound_texture ? m_bound_texture : &m_renderer.m_vk.error_image; + DescriptorWriter() + .write_image(0, image->image_view, + m_renderer.m_vk.default_sampler_nearest.get(), + static_cast(vk::ImageLayout::eShaderReadOnlyOptimal), + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) + .update_set(m_renderer.m_vkb.dev.device, image_set); + + auto vk_image_set = vk::DescriptorSet { image_set }; + m_cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, + m_renderer.m_vk.mesh_pipeline.get_layout(), 0, vk_image_set, {}); + + GPUDrawPushConstants push_constants {}; + push_constants.world_matrix = transform; + push_constants.vertex_buffer = mesh.vertex_buffer_address; + + m_cmd.pushConstants(m_renderer.m_vk.mesh_pipeline.get_layout(), + vk::ShaderStageFlagBits::eVertex, 0, sizeof(push_constants), + &push_constants); + + m_cmd.bindIndexBuffer(mesh.index_buffer.buffer, 0, vk::IndexType::eUint32); + m_cmd.drawIndexed(index_count, 1, first_index, vertex_offset, 0); +} + +auto VulkanRenderer::GL::push_vertex(smath::Vec3 const &pos) -> void +{ + assert(m_drawing && "begin_drawing must be called first"); + + Vertex v {}; + v.position = pos; + v.u = m_current_uv.x(); + v.v = m_current_uv.y(); + v.normal = m_current_normal; + v.color = m_current_color; + + m_vertices.emplace_back(v); +} + +auto VulkanRenderer::GL::emit_indices(size_t start, size_t count) -> void +{ + switch (m_current_kind) { + case GeometryKind::Triangles: { + for (size_t i = 0; (i + 2) < count; i += 3) { + m_indices.emplace_back(static_cast(start + i + 0)); + m_indices.emplace_back(static_cast(start + i + 1)); + m_indices.emplace_back(static_cast(start + i + 2)); + } + break; + } + case GeometryKind::TriangleStrip: { + if (count < 3) + break; + for (size_t i = 0; (i + 2) < count; i++) { + if (i % 2 == 0) { + m_indices.emplace_back(static_cast(start + i + 0)); + m_indices.emplace_back(static_cast(start + i + 1)); + m_indices.emplace_back(static_cast(start + i + 2)); + } else { + m_indices.emplace_back(static_cast(start + i + 1)); + m_indices.emplace_back(static_cast(start + i + 0)); + m_indices.emplace_back(static_cast(start + i + 2)); + } + } + break; + } + case GeometryKind::TriangleFan: { + if (count < 3) + break; + for (size_t i = 1; (i + 1) < count; i++) { + m_indices.emplace_back(static_cast(start)); + m_indices.emplace_back(static_cast(start + i)); + m_indices.emplace_back(static_cast(start + i + 1)); + } + break; + } + case GeometryKind::Quads: { + if (count < 4) + break; + + size_t const quad_count { count / 4 }; + for (size_t q = 0; q < quad_count; q++) { + size_t const base = start + q * 4; + m_indices.emplace_back(static_cast(base + 0)); + m_indices.emplace_back(static_cast(base + 1)); + m_indices.emplace_back(static_cast(base + 2)); + + m_indices.emplace_back(static_cast(base + 2)); + m_indices.emplace_back(static_cast(base + 1)); + m_indices.emplace_back(static_cast(base + 3)); + } + break; + } + } +} + +auto VulkanRenderer::GL::bind_pipeline_if_needed() -> void +{ + if (!m_drawing || !m_active_pipeline) + return; + + m_cmd.bindPipeline( + vk::PipelineBindPoint::eGraphics, m_active_pipeline->get()); +} + VulkanRenderer::VulkanRenderer(SDL_Window *window, Logger &logger) - : m_window(window) + : gl(*this) + , m_window(window) , m_logger(logger) { if (m_window == nullptr) { @@ -91,7 +506,8 @@ auto VulkanRenderer::resize(uint32_t width, uint32_t height) -> void } auto VulkanRenderer::immediate_submit( - std::function &&function) -> void + std::function &&function, + bool flush_frame_deletion_queue, bool clear_frame_descriptors) -> void { m_device.resetFences(m_vk.imm_fence.get()); m_vk.imm_command_buffer.get().reset(); @@ -112,8 +528,13 @@ auto VulkanRenderer::immediate_submit( VK_CHECK(m_logger, m_device.waitForFences(m_vk.imm_fence.get(), true, 9'999'999'999)); - m_vk.get_current_frame().deletion_queue.flush(); - m_vk.get_current_frame().frame_descriptors.clear_pools(m_vkb.dev.device); + if (flush_frame_deletion_queue) { + m_vk.get_current_frame().deletion_queue.flush(); + } + if (clear_frame_descriptors) { + m_vk.get_current_frame().frame_descriptors.clear_pools( + m_vkb.dev.device); + } } auto VulkanRenderer::vk_init() -> void @@ -647,7 +1068,7 @@ auto VulkanRenderer::default_data_init() -> void }); } -auto VulkanRenderer::render() -> void +auto VulkanRenderer::render(std::function const &record) -> void { defer(m_vk.frame_number++); @@ -698,7 +1119,11 @@ auto VulkanRenderer::render() -> void vkutil::transition_image(cmd, m_vk.depth_image.image, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthAttachmentOptimal); - draw_geometry(cmd); + gl.begin_drawing(cmd, m_vk.draw_image, &m_vk.depth_image); + if (record) { + record(gl); + } + gl.end_drawing(); vkutil::transition_image(cmd, m_vk.draw_image.image, vk::ImageLayout::eColorAttachmentOptimal, @@ -767,130 +1192,6 @@ auto VulkanRenderer::draw_background(vk::CommandBuffer cmd) -> void static_cast(std::ceil(m_vk.draw_extent.height / 16.0)), 1); } -auto VulkanRenderer::draw_geometry(vk::CommandBuffer cmd) -> void -{ - auto gpu_scene_data_buffer { create_buffer(sizeof(GPUSceneData), - vk::BufferUsageFlagBits::eUniformBuffer, VMA_MEMORY_USAGE_CPU_TO_GPU) }; - m_vk.get_current_frame().deletion_queue.emplace( - [=, this]() { destroy_buffer(gpu_scene_data_buffer); }); - - VmaAllocationInfo info {}; - vmaGetAllocationInfo( - m_vk.allocator, gpu_scene_data_buffer.allocation, &info); - - GPUSceneData *scene_uniform_data - = reinterpret_cast(info.pMappedData); - if (!scene_uniform_data) { - VkResult res = vmaMapMemory(m_vk.allocator, - gpu_scene_data_buffer.allocation, (void **)&scene_uniform_data); - assert(res == VK_SUCCESS); - } - defer({ - if (info.pMappedData == nullptr) { - vmaUnmapMemory(m_vk.allocator, gpu_scene_data_buffer.allocation); - } - }); - - *scene_uniform_data = m_vk.scene_data; - - auto const global_desc { - m_vk.get_current_frame().frame_descriptors.allocate( - m_logger, m_vkb.dev.device, m_vk.gpu_scene_data_descriptor_layout) - }; - - DescriptorWriter writer; - writer.write_buffer(0, gpu_scene_data_buffer.buffer, sizeof(GPUSceneData), - 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - writer.update_set(m_vkb.dev.device, global_desc); - - auto color_att { vkinit::attachment_info(m_vk.draw_image.image_view, - nullptr, vk::ImageLayout::eColorAttachmentOptimal) }; - auto depth_att { vkinit::depth_attachment_info(m_vk.depth_image.image_view, - vk::ImageLayout::eDepthAttachmentOptimal) }; - auto const render_info { vkinit::render_info( - m_vk.draw_extent, &color_att, &depth_att) }; - - cmd.beginRendering(render_info); - - cmd.bindPipeline( - vk::PipelineBindPoint::eGraphics, m_vk.triangle_pipeline.get()); - - vk::Viewport viewport {}; - viewport.x = 0; - viewport.y = 0; - viewport.width = static_cast(m_vk.draw_extent.width); - viewport.height = static_cast(m_vk.draw_extent.height); - viewport.minDepth = 0.0f; - viewport.maxDepth = 1.0f; - cmd.setViewport(0, viewport); - - vk::Rect2D scissor {}; - scissor.offset.x = 0; - scissor.offset.y = 0; - scissor.extent = m_vk.draw_extent; - cmd.setScissor(0, scissor); - - cmd.bindPipeline( - vk::PipelineBindPoint::eGraphics, m_vk.mesh_pipeline.get()); - - auto const image_set { m_vk.get_current_frame().frame_descriptors.allocate( - m_logger, m_vkb.dev.device, m_vk.single_image_descriptor_layout) }; - DescriptorWriter() - .write_image(0, m_vk.error_image.image_view, - m_vk.default_sampler_nearest.get(), - static_cast(vk::ImageLayout::eShaderReadOnlyOptimal), - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) - .update_set(m_vkb.dev.device, image_set); - - auto vk_image_set = vk::DescriptorSet { image_set }; - cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, - m_vk.mesh_pipeline.get_layout(), 0, vk_image_set, {}); - - auto view { smath::matrix_look_at(smath::Vec3 { 0.0f, 0.0f, 3.0f }, - smath::Vec3 { 0.0f, 0.0f, 0.0f }, smath::Vec3 { 0.0f, 1.0f, 0.0f }, - false) }; - auto projection { - smath::matrix_perspective(smath::deg(70.0f), - static_cast(m_vk.draw_extent.width) - / static_cast(m_vk.draw_extent.height), - 0.1f, 10000.0f), - }; - projection[1][1] *= -1; - auto view_projection { projection * view }; - - GPUDrawPushConstants push_constants; - auto rect_model { smath::scale( - smath::translate(smath::Vec3 { 0.0f, 0.0f, -5.0f }), - smath::Vec3 { 5.0f, 5.0f, 1.0f }) }; - push_constants.world_matrix = view_projection * rect_model; - push_constants.vertex_buffer = m_vk.rectangle.vertex_buffer_address; - - cmd.pushConstants(m_vk.mesh_pipeline.get_layout(), - vk::ShaderStageFlagBits::eVertex, 0, sizeof(push_constants), - &push_constants); - cmd.bindIndexBuffer( - m_vk.rectangle.index_buffer.buffer, 0, vk::IndexType::eUint32); - - cmd.drawIndexed(6, 1, 0, 0, 0); - - push_constants.vertex_buffer - = m_vk.test_meshes[2]->mesh_buffers.vertex_buffer_address; - - auto model { smath::Mat4::identity() }; - push_constants.world_matrix = view_projection * model; - - cmd.pushConstants(m_vk.mesh_pipeline.get_layout(), - vk::ShaderStageFlagBits::eVertex, 0, sizeof(push_constants), - &push_constants); - cmd.bindIndexBuffer(m_vk.test_meshes[2]->mesh_buffers.index_buffer.buffer, - 0, vk::IndexType::eUint32); - - cmd.drawIndexed(m_vk.test_meshes[2]->surfaces[0].count, 1, - m_vk.test_meshes[2]->surfaces[0].start_index, 0, 0); - - cmd.endRendering(); -} - auto VulkanRenderer::draw_imgui( vk::CommandBuffer cmd, vk::ImageView target_image_view) -> void { @@ -1168,7 +1469,8 @@ auto VulkanRenderer::create_buffer(size_t alloc_size, VmaAllocationCreateInfo alloc_ci {}; alloc_ci.usage = memory_usage; alloc_ci.flags = 0; - if (memory_usage == VMA_MEMORY_USAGE_CPU_ONLY) { + if (memory_usage == VMA_MEMORY_USAGE_CPU_ONLY + || memory_usage == VMA_MEMORY_USAGE_CPU_TO_GPU) { alloc_ci.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; } diff --git a/src/VulkanRenderer.h b/src/VulkanRenderer.h index 835159b..58c0a9b 100644 --- a/src/VulkanRenderer.h +++ b/src/VulkanRenderer.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include #include @@ -9,6 +11,7 @@ #include #include +#include "Colors.h" #include "DeletionQueue.h" #include "DescriptorAllocator.h" #include "Loader.h" @@ -26,19 +29,121 @@ struct GPUDrawPushConstants { constexpr unsigned FRAME_OVERLAP = 2; struct VulkanRenderer { + struct GL { + enum class GeometryKind { + Triangles, + TriangleStrip, + TriangleFan, + Quads + }; + + explicit GL(VulkanRenderer &renderer); + + auto begin_drawing(vk::CommandBuffer cmd, AllocatedImage &color_target, + AllocatedImage *depth_target = nullptr) -> void; + auto end_drawing() -> void; + + auto begin(GeometryKind kind) -> void; + template + auto vert(smath::Vec const &position) -> void + { + static_assert(N == 2 || N == 3 || N == 4, + "Position must be a 2D, 3D, or 4D vec"); + + smath::Vec3 pos { 0.0f, 0.0f, 0.0f }; + pos[0] = position[0]; + pos[1] = position[1]; + if constexpr (N >= 3) { + pos[2] = position[2]; + } + + push_vertex(pos); + } + auto color(smath::Vec3 const &rgb) -> void; + auto color(smath::Vec4 const &rgba) -> void; + auto uv(smath::Vec2 const &uv) -> void; + auto normal(smath::Vec3 const &normal) -> void; + auto set_texture(std::optional texture + = std::nullopt) -> void; + auto draw_rectangle(smath::Vec2 pos, smath::Vec2 size, + smath::Vec4 color = smath::Vec4 { Colors::WHITE, 1.0f }, + float rotation = 0.0f) -> void; + auto end() -> void; + auto flush() -> void; + + auto use_pipeline(Pipeline &pipeline) -> void; + auto set_transform(smath::Mat4 const &transform) -> void; + auto draw_mesh(GPUMeshBuffers const &mesh, smath::Mat4 const &transform, + uint32_t index_count, uint32_t first_index = 0, + int32_t vertex_offset = 0) -> void; + + private: + auto push_vertex(smath::Vec3 const &pos) -> void; + auto emit_indices(size_t start, size_t count) -> void; + auto bind_pipeline_if_needed() -> void; + + VulkanRenderer &m_renderer; + vk::CommandBuffer m_cmd {}; + AllocatedImage *m_color_target { nullptr }; + AllocatedImage *m_depth_target { nullptr }; + GeometryKind m_current_kind { GeometryKind::Triangles }; + bool m_inside_primitive { false }; + bool m_drawing { false }; + Pipeline *m_active_pipeline { nullptr }; + smath::Mat4 m_transform { smath::Mat4::identity() }; + smath::Vec4 m_current_color { 1.0f, 1.0f, 1.0f, 1.0f }; + smath::Vec3 m_current_normal { 0.0f, 0.0f, 1.0f }; + smath::Vec2 m_current_uv { 0.0f, 0.0f }; + AllocatedImage const *m_bound_texture { nullptr }; + size_t m_primitive_start { 0 }; + std::vector m_vertices; + std::vector m_indices; + }; + VulkanRenderer(SDL_Window *window, Logger &logger); ~VulkanRenderer(); - auto render() -> void; + auto render(std::function const &record = {}) -> void; auto resize(uint32_t width, uint32_t height) -> void; - auto immediate_submit(std::function &&function) - -> void; + auto immediate_submit(std::function &&function, + bool flush_frame_deletion_queue = true, + bool clear_frame_descriptors = true) -> void; auto upload_mesh(std::span indices, std::span vertices) -> GPUMeshBuffers; + auto rectangle_mesh() const -> GPUMeshBuffers const & + { + return m_vk.rectangle; + } + auto test_meshes() const -> std::vector> const & + { + return m_vk.test_meshes; + } + auto white_texture() const -> AllocatedImage const & + { + return m_vk.white_image; + } + auto gray_texture() const -> AllocatedImage const & + { + return m_vk.gray_image; + } + auto black_texture() const -> AllocatedImage const & + { + return m_vk.black_image; + } + auto error_texture() const -> AllocatedImage const & + { + return m_vk.error_image; + } + auto draw_extent() const -> vk::Extent2D { return m_vk.draw_extent; } + auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; } + auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; } + auto gl_api() -> GL & { return gl; } auto logger() const -> Logger & { return m_logger; } + GL gl; + private: auto vk_init() -> void; auto swapchain_init() -> void; @@ -53,7 +158,6 @@ private: auto default_data_init() -> void; auto draw_background(vk::CommandBuffer cmd) -> void; - auto draw_geometry(vk::CommandBuffer cmd) -> void; auto draw_imgui(vk::CommandBuffer cmd, vk::ImageView target_image_view) -> void;