Add skybox

Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
2026-01-11 15:01:48 +02:00
parent 979dab81b1
commit e55601b5a6
8 changed files with 508 additions and 86 deletions

View File

@@ -51,7 +51,6 @@
#include <smath.hpp>
#include "GraphicsPipelineBuilder.h"
#include "Util.h"
#include "VulkanRenderer.h"
@@ -419,7 +418,10 @@ Application::Application()
}
m_renderer = std::make_unique<VulkanRenderer>(m_window, m_logger);
init_skybox_pipeline();
m_renderer->set_antialiasing_immediate(
VulkanRenderer::AntiAliasingKind::MSAA_4X);
m_skybox.init(*m_renderer, asset_directory() / "cubemap.png");
init_test_meshes();
init_input();
@@ -428,8 +430,6 @@ Application::Application()
m_logger.info("App init done!");
m_renderer->set_antialiasing(VulkanRenderer::AntiAliasingKind::MSAA_4X);
m_cursor = PolarCoordinate::from_vec3(m_camera.target - m_camera.position);
}
@@ -437,6 +437,7 @@ Application::~Application()
{
if (m_renderer) {
m_renderer->device().waitIdle();
m_skybox.destroy(*m_renderer);
for (auto const &mesh : m_test_meshes) {
m_renderer->destroy_buffer(mesh->mesh_buffers.index_buffer);
m_renderer->destroy_buffer(mesh->mesh_buffers.vertex_buffer);
@@ -444,7 +445,6 @@ Application::~Application()
}
m_test_meshes.clear();
m_skybox_pipeline.reset();
m_renderer.reset();
shutdown_input();
@@ -455,83 +455,6 @@ Application::~Application()
m_logger.info("App destroy done!");
}
auto Application::init_skybox_pipeline() -> void
{
struct SkyboxPushConstants {
smath::Mat4 mvp;
};
Pipeline::Builder builder { m_renderer->device(), m_logger };
uint8_t skybox_vert_shader_data[] {
#embed "skybox_vert.spv"
};
auto skybox_vert_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_vert_shader_data,
sizeof(skybox_vert_shader_data)),
m_renderer->device());
if (!skybox_vert_shader) {
m_logger.err("Failed to load skybox vert shader");
}
uint8_t skybox_frag_shader_data[] {
#embed "skybox_frag.spv"
};
auto skybox_frag_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_frag_shader_data,
sizeof(skybox_frag_shader_data)),
m_renderer->device());
if (!skybox_frag_shader) {
m_logger.err("Failed to load skybox frag shader");
}
vk::PushConstantRange push_constant_range {};
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eVertex;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(SkyboxPushConstants);
std::array push_constant_ranges { push_constant_range };
builder.set_push_constant_ranges(push_constant_ranges);
std::array descriptor_set_layouts {
m_renderer->single_image_descriptor_layout(),
};
builder.set_descriptor_set_layouts(descriptor_set_layouts);
VkVertexInputBindingDescription binding {};
binding.binding = 0;
binding.stride = sizeof(smath::Vec3);
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attribute {};
attribute.location = 0;
attribute.binding = 0;
attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
attribute.offset = 0;
std::array bindings { binding };
std::array attributes { attribute };
m_skybox_pipeline = builder.build_graphics(
[&](GraphicsPipelineBuilder &pipeline_builder)
-> GraphicsPipelineBuilder & {
pipeline_builder.set_vertex_input(bindings, attributes);
return pipeline_builder
.set_shaders(skybox_vert_shader.get(), skybox_frag_shader.get())
.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
.set_polygon_mode(VK_POLYGON_MODE_FILL)
.set_cull_mode(
VK_CULL_MODE_FRONT_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE)
.set_multisampling(static_cast<VkSampleCountFlagBits>(
m_renderer->msaa_samples()))
.disable_blending()
.enable_depth_testing(false, VK_COMPARE_OP_LESS_OR_EQUAL)
.set_color_attachment_format(
static_cast<VkFormat>(m_renderer->draw_image_format()))
.set_depth_format(
static_cast<VkFormat>(m_renderer->depth_image_format()));
});
}
auto Application::binary_directory() const -> std::filesystem::path
{
auto const *base_path = SDL_GetBasePath();
@@ -872,6 +795,12 @@ auto Application::run() -> void
projection[1][1] *= -1;
auto view_projection { projection * view };
auto skybox_view = view;
skybox_view[3][0] = 0.0f;
skybox_view[3][1] = 0.0f;
skybox_view[3][2] = 0.0f;
m_skybox.draw(gl, projection * skybox_view);
gl.set_transform(view_projection);
gl.set_texture();

View File

@@ -11,7 +11,7 @@
#include "Loader.h"
#include "Logger.h"
#include "Pipeline.h"
#include "Skybox.h"
#include "Types.h"
struct libinput;
@@ -37,7 +37,6 @@ struct Application {
private:
auto init_input() -> void;
auto init_skybox_pipeline() -> void;
auto init_test_meshes() -> void;
auto asset_directory() -> std::filesystem::path;
auto shutdown_input() -> void;
@@ -48,7 +47,7 @@ private:
SDL_Window *m_window { nullptr };
Logger m_logger { "Lunar" };
std::unique_ptr<VulkanRenderer> m_renderer;
Pipeline m_skybox_pipeline;
Skybox m_skybox;
std::vector<std::shared_ptr<Mesh>> m_test_meshes;
udev *m_udev { nullptr };

295
src/Skybox.cpp Normal file
View File

@@ -0,0 +1,295 @@
#include "Skybox.h"
#include <array>
#include <cstddef>
#include <span>
#include <vector>
#include "CPUTexture.h"
#include "DescriptorWriter.h"
#include "GraphicsPipelineBuilder.h"
#include "Util.h"
namespace Lunar {
namespace {
struct SkyboxPushConstants {
smath::Mat4 mvp;
};
struct FaceOffset {
uint32_t x;
uint32_t y;
};
constexpr std::array<FaceOffset, 6> kCrossOffsets {
FaceOffset { 2, 1 }, // +X
FaceOffset { 0, 1 }, // -X
FaceOffset { 1, 0 }, // +Y
FaceOffset { 1, 2 }, // -Y
FaceOffset { 1, 1 }, // +Z
FaceOffset { 3, 1 }, // -Z
};
} // namespace
auto Skybox::init(VulkanRenderer &renderer, std::filesystem::path const &path)
-> void
{
if (ok) {
destroy(renderer);
ok = false;
}
CPUTexture texture { path };
if (texture.width == 0 || texture.height == 0) {
renderer.logger().err("Skybox texture is empty: {}", path.string());
ok = false;
return;
}
if (texture.width % 4 != 0 || texture.height % 3 != 0
|| texture.width / 4 != texture.height / 3) {
renderer.logger().err(
"Skybox texture must be 4x3 faces: {}", path.string());
ok = false;
return;
}
uint32_t const face_size = texture.width / 4;
size_t const face_bytes = static_cast<size_t>(face_size) * face_size * 4;
std::vector<uint8_t> cubemap_pixels(face_bytes * kCrossOffsets.size());
for (size_t face = 0; face < kCrossOffsets.size(); ++face) {
auto const offset = kCrossOffsets[face];
for (uint32_t y = 0; y < face_size; ++y) {
for (uint32_t x = 0; x < face_size; ++x) {
uint32_t const src_x = offset.x * face_size + x;
uint32_t const src_y = offset.y * face_size + y;
size_t const src_index
= (static_cast<size_t>(src_y) * texture.width + src_x) * 4;
size_t const dst_index = face * face_bytes
+ (static_cast<size_t>(y) * face_size + x) * 4;
std::copy_n(texture.pixels.data() + src_index, 4,
cubemap_pixels.data() + dst_index);
}
}
}
m_cubemap = renderer.create_cubemap(cubemap_pixels, face_size,
texture.format, vk::ImageUsageFlagBits::eSampled);
if (!m_cubemap.image) {
renderer.logger().err("Failed to create cubemap image");
return;
}
vk::SamplerCreateInfo sampler_ci {};
sampler_ci.magFilter = vk::Filter::eLinear;
sampler_ci.minFilter = vk::Filter::eLinear;
sampler_ci.mipmapMode = vk::SamplerMipmapMode::eLinear;
sampler_ci.addressModeU = vk::SamplerAddressMode::eClampToEdge;
sampler_ci.addressModeV = vk::SamplerAddressMode::eClampToEdge;
sampler_ci.addressModeW = vk::SamplerAddressMode::eClampToEdge;
m_sampler = renderer.device().createSamplerUnique(sampler_ci);
vk::DescriptorPoolSize pool_size {};
pool_size.type = vk::DescriptorType::eCombinedImageSampler;
pool_size.descriptorCount = 1;
vk::DescriptorPoolCreateInfo pool_ci {};
pool_ci.maxSets = 1;
pool_ci.poolSizeCount = 1;
pool_ci.pPoolSizes = &pool_size;
m_descriptor_pool = renderer.device().createDescriptorPoolUnique(pool_ci);
vk::DescriptorSetAllocateInfo alloc_info {};
alloc_info.descriptorPool = m_descriptor_pool.get();
alloc_info.descriptorSetCount = 1;
vk::DescriptorSetLayout layout = renderer.single_image_descriptor_layout();
alloc_info.pSetLayouts = &layout;
m_descriptor_set
= renderer.device().allocateDescriptorSets(alloc_info).front();
DescriptorWriter()
.write_image(0, m_cubemap.image_view, m_sampler.get(),
static_cast<VkImageLayout>(vk::ImageLayout::eShaderReadOnlyOptimal),
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
.update_set(renderer.device(), m_descriptor_set);
std::vector<Vertex> vertices;
vertices.reserve(8);
auto push_vertex = [&](smath::Vec3 const &pos) {
Vertex v {};
v.position = pos;
v.u = 0.0f;
v.v = 0.0f;
v.normal = smath::Vec3 { 0.0f, 0.0f, 1.0f };
v.color = smath::Vec4 { 1.0f, 1.0f, 1.0f, 1.0f };
vertices.emplace_back(v);
};
push_vertex(smath::Vec3 { -1.0f, -1.0f, -1.0f });
push_vertex(smath::Vec3 { 1.0f, -1.0f, -1.0f });
push_vertex(smath::Vec3 { 1.0f, 1.0f, -1.0f });
push_vertex(smath::Vec3 { -1.0f, 1.0f, -1.0f });
push_vertex(smath::Vec3 { -1.0f, -1.0f, 1.0f });
push_vertex(smath::Vec3 { 1.0f, -1.0f, 1.0f });
push_vertex(smath::Vec3 { 1.0f, 1.0f, 1.0f });
push_vertex(smath::Vec3 { -1.0f, 1.0f, 1.0f });
std::vector<uint32_t> indices {
4,
5,
6,
4,
6,
7, // +Z
1,
0,
3,
1,
3,
2, // -Z
5,
1,
2,
5,
2,
6, // +X
0,
4,
7,
0,
7,
3, // -X
7,
6,
2,
7,
2,
3, // +Y
0,
1,
5,
0,
5,
4, // -Y
};
m_index_count = static_cast<uint32_t>(indices.size());
m_cube_mesh = renderer.upload_mesh(indices, vertices);
Pipeline::Builder pipeline_builder { renderer.device(), renderer.logger() };
uint8_t skybox_vert_shader_data[] {
#embed "skybox_vert.spv"
};
auto skybox_vert_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_vert_shader_data,
sizeof(skybox_vert_shader_data)),
renderer.device());
if (!skybox_vert_shader) {
renderer.logger().err("Failed to load skybox vert shader");
ok = false;
return;
}
uint8_t skybox_frag_shader_data[] {
#embed "skybox_frag.spv"
};
auto skybox_frag_shader
= vkutil::load_shader_module(std::span<uint8_t>(skybox_frag_shader_data,
sizeof(skybox_frag_shader_data)),
renderer.device());
if (!skybox_frag_shader) {
renderer.logger().err("Failed to load skybox frag shader");
ok = false;
return;
}
vk::PushConstantRange push_constant_range {};
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eVertex;
push_constant_range.offset = 0;
push_constant_range.size = sizeof(SkyboxPushConstants);
std::array push_constant_ranges { push_constant_range };
pipeline_builder.set_push_constant_ranges(push_constant_ranges);
std::array descriptor_set_layouts {
renderer.single_image_descriptor_layout()
};
pipeline_builder.set_descriptor_set_layouts(descriptor_set_layouts);
VkVertexInputBindingDescription binding {};
binding.binding = 0;
binding.stride = sizeof(Vertex);
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
VkVertexInputAttributeDescription attribute {};
attribute.location = 0;
attribute.binding = 0;
attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
attribute.offset = offsetof(Vertex, position);
std::array bindings { binding };
std::array attributes { attribute };
m_pipeline = pipeline_builder.build_graphics(
[&](GraphicsPipelineBuilder &builder) -> GraphicsPipelineBuilder & {
builder.set_vertex_input(bindings, attributes);
return builder
.set_shaders(skybox_vert_shader.get(), skybox_frag_shader.get())
.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
.set_polygon_mode(VK_POLYGON_MODE_FILL)
.set_cull_mode(
VK_CULL_MODE_FRONT_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE)
.set_multisampling(
static_cast<VkSampleCountFlagBits>(renderer.msaa_samples()))
.disable_blending()
.enable_depth_testing(false, VK_COMPARE_OP_LESS_OR_EQUAL)
.set_color_attachment_format(
static_cast<VkFormat>(renderer.draw_image_format()))
.set_depth_format(
static_cast<VkFormat>(renderer.depth_image_format()));
});
ok = true;
}
auto Skybox::destroy(VulkanRenderer &renderer) -> void
{
if (m_cube_mesh.index_buffer.buffer) {
renderer.destroy_buffer(m_cube_mesh.index_buffer);
}
if (m_cube_mesh.vertex_buffer.buffer) {
renderer.destroy_buffer(m_cube_mesh.vertex_buffer);
}
if (m_cubemap.image) {
renderer.destroy_image(m_cubemap);
}
m_sampler.reset();
m_descriptor_pool.reset();
m_pipeline.reset();
m_descriptor_set = vk::DescriptorSet {};
m_cube_mesh = {};
m_cubemap = {};
m_index_count = 0;
ok = false;
}
auto Skybox::draw(VulkanRenderer::GL &gl, smath::Mat4 const &mvp) -> void
{
if (!ok) {
return;
}
SkyboxPushConstants push_constants { mvp };
auto bytes = std::as_bytes(std::span { &push_constants, 1 });
gl.draw_indexed(m_pipeline, m_descriptor_set, m_cube_mesh.vertex_buffer,
m_cube_mesh.index_buffer, m_index_count, bytes);
}
} // namespace Lunar

32
src/Skybox.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <filesystem>
#include <vulkan/vulkan.hpp>
#include "Pipeline.h"
#include "Types.h"
#include "VulkanRenderer.h"
namespace Lunar {
struct Skybox {
bool ok { false };
auto init(VulkanRenderer &renderer, std::filesystem::path const &path)
-> void;
auto destroy(VulkanRenderer &renderer) -> void;
auto draw(VulkanRenderer::GL &gl, smath::Mat4 const &mvp) -> void;
private:
Pipeline m_pipeline {};
GPUMeshBuffers m_cube_mesh {};
AllocatedImage m_cubemap {};
vk::UniqueSampler m_sampler {};
vk::UniqueDescriptorPool m_descriptor_pool {};
vk::DescriptorSet m_descriptor_set {};
uint32_t m_index_count { 0 };
};
} // namespace Lunar

View File

@@ -517,6 +517,36 @@ auto VulkanRenderer::GL::draw_mesh(GPUMeshBuffers const &mesh,
m_cmd.drawIndexed(index_count, 1, first_index, vertex_offset, 0);
}
auto VulkanRenderer::GL::draw_indexed(Pipeline &pipeline,
vk::DescriptorSet descriptor_set, AllocatedBuffer const &vertex_buffer,
AllocatedBuffer const &index_buffer, uint32_t index_count,
std::span<std::byte const> push_constants) -> void
{
assert(m_drawing && "begin_drawing must be called first");
if (m_inside_primitive) {
end();
}
flush();
use_pipeline(pipeline);
auto cmd { m_cmd };
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
pipeline.get_layout(), 0, descriptor_set, {});
if (!push_constants.empty()) {
cmd.pushConstants(pipeline.get_layout(),
vk::ShaderStageFlagBits::eVertex, 0,
static_cast<uint32_t>(push_constants.size()),
push_constants.data());
}
vk::DeviceSize offset = 0;
cmd.bindVertexBuffers(0, vertex_buffer.buffer, offset);
cmd.bindIndexBuffer(index_buffer.buffer, 0, vk::IndexType::eUint32);
cmd.drawIndexed(index_count, 1, 0, 0, 0);
}
auto VulkanRenderer::GL::push_vertex(smath::Vec3 const &pos) -> void
{
assert(m_drawing && "begin_drawing must be called first");
@@ -678,6 +708,11 @@ auto VulkanRenderer::set_antialiasing(AntiAliasingKind kind) -> void
});
}
auto VulkanRenderer::set_antialiasing_immediate(AntiAliasingKind kind) -> void
{
apply_antialiasing(kind);
}
auto VulkanRenderer::apply_antialiasing(AntiAliasingKind kind) -> void
{
auto requested_samples = [&](AntiAliasingKind aa) {
@@ -2162,6 +2197,128 @@ auto VulkanRenderer::create_image(CPUTexture const &texture,
texture.pixels.data(), size, texture.format, flags, mipmapped);
}
auto VulkanRenderer::create_cubemap(std::span<uint8_t const> pixels,
uint32_t face_size, vk::Format format, vk::ImageUsageFlags flags)
-> AllocatedImage
{
size_t const face_bytes = static_cast<size_t>(face_size) * face_size * 4;
if (pixels.size() < face_bytes * 6) {
m_logger.err("Cubemap data size is invalid");
return {};
}
auto const upload_buffer {
create_buffer(pixels.size(), vk::BufferUsageFlagBits::eTransferSrc,
VMA_MEMORY_USAGE_CPU_TO_GPU),
};
VmaAllocationInfo info {};
vmaGetAllocationInfo(m_vk.allocator, upload_buffer.allocation, &info);
void *mapped_data { reinterpret_cast<GPUSceneData *>(info.pMappedData) };
bool mapped_here { false };
if (!mapped_data) {
VkResult res = vmaMapMemory(
m_vk.allocator, upload_buffer.allocation, (void **)&mapped_data);
assert(res == VK_SUCCESS);
mapped_here = true;
}
memcpy(mapped_data, pixels.data(), pixels.size());
AllocatedImage new_image {};
new_image.format = format;
new_image.extent = vk::Extent3D { face_size, face_size, 1 };
auto img_ci { vkinit::image_create_info(format,
flags | vk::ImageUsageFlagBits::eTransferDst, new_image.extent,
vk::SampleCountFlagBits::e1) };
img_ci.arrayLayers = 6;
img_ci.flags = vk::ImageCreateFlagBits::eCubeCompatible;
VmaAllocationCreateInfo alloc_ci {};
alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY;
alloc_ci.requiredFlags
= VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_CHECK(m_logger,
vmaCreateImage(m_vk.allocator,
reinterpret_cast<VkImageCreateInfo const *>(&img_ci), &alloc_ci,
reinterpret_cast<VkImage *>(&new_image.image),
&new_image.allocation, nullptr));
vk::ImageViewCreateInfo view_ci {};
view_ci.viewType = vk::ImageViewType::eCube;
view_ci.image = new_image.image;
view_ci.format = format;
view_ci.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
view_ci.subresourceRange.baseMipLevel = 0;
view_ci.subresourceRange.levelCount = 1;
view_ci.subresourceRange.baseArrayLayer = 0;
view_ci.subresourceRange.layerCount = 6;
new_image.image_view = m_device.createImageView(view_ci);
immediate_submit([&](vk::CommandBuffer cmd) {
vk::ImageMemoryBarrier to_transfer {};
to_transfer.srcAccessMask = vk::AccessFlagBits::eNone;
to_transfer.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
to_transfer.oldLayout = vk::ImageLayout::eUndefined;
to_transfer.newLayout = vk::ImageLayout::eTransferDstOptimal;
to_transfer.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
to_transfer.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
to_transfer.image = new_image.image;
to_transfer.subresourceRange.aspectMask
= vk::ImageAspectFlagBits::eColor;
to_transfer.subresourceRange.baseMipLevel = 0;
to_transfer.subresourceRange.levelCount = 1;
to_transfer.subresourceRange.baseArrayLayer = 0;
to_transfer.subresourceRange.layerCount = 6;
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, to_transfer);
std::array<vk::BufferImageCopy, 6> regions {};
for (uint32_t layer = 0; layer < 6; ++layer) {
vk::BufferImageCopy region {};
region.bufferOffset = face_bytes * layer;
region.imageSubresource.aspectMask
= vk::ImageAspectFlagBits::eColor;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = layer;
region.imageSubresource.layerCount = 1;
region.imageExtent = new_image.extent;
regions[layer] = region;
}
cmd.copyBufferToImage(upload_buffer.buffer, new_image.image,
vk::ImageLayout::eTransferDstOptimal, regions);
vk::ImageMemoryBarrier to_read {};
to_read.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
to_read.dstAccessMask = vk::AccessFlagBits::eShaderRead;
to_read.oldLayout = vk::ImageLayout::eTransferDstOptimal;
to_read.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
to_read.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
to_read.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
to_read.image = new_image.image;
to_read.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
to_read.subresourceRange.baseMipLevel = 0;
to_read.subresourceRange.levelCount = 1;
to_read.subresourceRange.baseArrayLayer = 0;
to_read.subresourceRange.layerCount = 6;
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {}, to_read);
});
if (mapped_here) {
vmaUnmapMemory(m_vk.allocator, upload_buffer.allocation);
}
destroy_buffer(upload_buffer);
return new_image;
}
auto VulkanRenderer::destroy_image(AllocatedImage const &img) -> void
{
if (img.image_view) {

View File

@@ -1,6 +1,7 @@
#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <mutex>
@@ -92,6 +93,10 @@ struct VulkanRenderer {
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;
auto draw_indexed(Pipeline &pipeline, vk::DescriptorSet descriptor_set,
AllocatedBuffer const &vertex_buffer,
AllocatedBuffer const &index_buffer, uint32_t index_count,
std::span<std::byte const> push_constants) -> void;
private:
auto push_vertex(smath::Vec3 const &pos) -> void;
@@ -131,6 +136,7 @@ struct VulkanRenderer {
auto render(std::function<void(GL &)> const &record = {}) -> void;
auto resize(uint32_t width, uint32_t height) -> void;
auto set_antialiasing(AntiAliasingKind kind) -> void;
auto set_antialiasing_immediate(AntiAliasingKind kind) -> void;
auto antialiasing() const -> AntiAliasingKind
{
return m_vk.antialiasing_kind;
@@ -144,6 +150,9 @@ struct VulkanRenderer {
auto destroy_buffer(AllocatedBuffer const &buffer) -> void;
auto create_image(CPUTexture const &texture, vk::ImageUsageFlags flags,
bool mipmapped = false) -> AllocatedImage;
auto create_cubemap(std::span<uint8_t const> pixels, uint32_t face_size,
vk::Format format, vk::ImageUsageFlags flags) -> AllocatedImage;
auto destroy_image(AllocatedImage const &img) -> void;
auto rectangle_mesh() const -> GPUMeshBuffers const &
{
return m_vk.rectangle;
@@ -262,7 +271,6 @@ private:
bool mipmapped = false) -> AllocatedImage;
auto create_image(void const *data, vk::Extent3D size, vk::Format format,
vk::ImageUsageFlags flags, bool mipmapped = false) -> AllocatedImage;
auto destroy_image(AllocatedImage const &img) -> void;
auto create_buffer(size_t alloc_size, vk::BufferUsageFlags usage,
VmaMemoryUsage memory_usage) -> AllocatedBuffer;