GL 1.x style API

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2025-12-16 00:38:00 +02:00
parent 608dc583be
commit 59999f211d
4 changed files with 774 additions and 136 deletions

View File

@@ -23,6 +23,8 @@
#include <libudev.h>
#include <linux/input-event-codes.h>
#include <smath.hpp>
#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<float>(draw_extent.width)
/ static_cast<float>(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 });
});
}
}

177
src/Colors.h Normal file
View File

@@ -0,0 +1,177 @@
#pragma once
#include <smath.hpp>
namespace Lunar::Colors {
constexpr auto hex_to_vec3(uint32_t hex) -> smath::Vec3
{
return smath::Vec3 { static_cast<float>((hex >> 16) & 0xFF) / 255.0f,
static_cast<float>((hex >> 8) & 0xFF) / 255.0f,
static_cast<float>(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

View File

@@ -6,6 +6,7 @@
#include <cstring>
#include <format>
#include <iostream>
#include <optional>
#include <print>
#include <stdexcept>
@@ -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<vk::RenderingAttachmentInfo> 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<float>(extent.width);
viewport.height = static_cast<float>(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<AllocatedImage const *> 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<uint8_t *>(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<VkImageLayout>(
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<uint32_t>(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<VkImageLayout>(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<uint32_t>(start + i + 0));
m_indices.emplace_back(static_cast<uint32_t>(start + i + 1));
m_indices.emplace_back(static_cast<uint32_t>(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<uint32_t>(start + i + 0));
m_indices.emplace_back(static_cast<uint32_t>(start + i + 1));
m_indices.emplace_back(static_cast<uint32_t>(start + i + 2));
} else {
m_indices.emplace_back(static_cast<uint32_t>(start + i + 1));
m_indices.emplace_back(static_cast<uint32_t>(start + i + 0));
m_indices.emplace_back(static_cast<uint32_t>(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<uint32_t>(start));
m_indices.emplace_back(static_cast<uint32_t>(start + i));
m_indices.emplace_back(static_cast<uint32_t>(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<uint32_t>(base + 0));
m_indices.emplace_back(static_cast<uint32_t>(base + 1));
m_indices.emplace_back(static_cast<uint32_t>(base + 2));
m_indices.emplace_back(static_cast<uint32_t>(base + 2));
m_indices.emplace_back(static_cast<uint32_t>(base + 1));
m_indices.emplace_back(static_cast<uint32_t>(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<void(vk::CommandBuffer cmd)> &&function) -> void
std::function<void(vk::CommandBuffer cmd)> &&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));
if (flush_frame_deletion_queue) {
m_vk.get_current_frame().deletion_queue.flush();
m_vk.get_current_frame().frame_descriptors.clear_pools(m_vkb.dev.device);
}
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<void(GL &)> 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<uint32_t>(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<GPUSceneData *>(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<float>(m_vk.draw_extent.width);
viewport.height = static_cast<float>(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<VkImageLayout>(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<float>(m_vk.draw_extent.width)
/ static_cast<float>(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;
}

View File

@@ -1,6 +1,8 @@
#pragma once
#include <array>
#include <functional>
#include <optional>
#include <vector>
#include <SDL3/SDL_video.h>
@@ -9,6 +11,7 @@
#include <vk_mem_alloc.h>
#include <vulkan/vulkan.hpp>
#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<size_t N>
auto vert(smath::Vec<N, float> 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<AllocatedImage const *> 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<Vertex> m_vertices;
std::vector<uint32_t> m_indices;
};
VulkanRenderer(SDL_Window *window, Logger &logger);
~VulkanRenderer();
auto render() -> void;
auto render(std::function<void(GL &)> const &record = {}) -> void;
auto resize(uint32_t width, uint32_t height) -> void;
auto immediate_submit(std::function<void(vk::CommandBuffer cmd)> &&function)
-> void;
auto immediate_submit(std::function<void(vk::CommandBuffer cmd)> &&function,
bool flush_frame_deletion_queue = true,
bool clear_frame_descriptors = true) -> void;
auto upload_mesh(std::span<uint32_t> indices, std::span<Vertex> vertices)
-> GPUMeshBuffers;
auto rectangle_mesh() const -> GPUMeshBuffers const &
{
return m_vk.rectangle;
}
auto test_meshes() const -> std::vector<std::shared_ptr<Mesh>> 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;