mirror of
https://github.com/slendidev/lunar.git
synced 2026-01-30 16:28:58 +02:00
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ result
|
||||
.cache
|
||||
.direnv
|
||||
.clangd
|
||||
screenshot*
|
||||
|
||||
@@ -161,6 +161,7 @@ exe = executable('vr-compositor',
|
||||
'src/Loader.cpp',
|
||||
'src/DescriptorWriter.cpp',
|
||||
'src/CPUTexture.cpp',
|
||||
'src/Skybox.cpp',
|
||||
'src/VulkanRenderer.cpp',
|
||||
'src/Application.cpp',
|
||||
],
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
295
src/Skybox.cpp
Normal 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
32
src/Skybox.h
Normal 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
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user