Compare commits
8 Commits
b18f3719ed
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7213c85fdf | |||
| 9d4b85c940 | |||
| 3ce7962d4c | |||
| 5b363a8c57 | |||
| b8a15406da | |||
| 46b2698abb | |||
| 8358558ddb | |||
| f15ade11b7 |
@@ -36,24 +36,32 @@ add_executable(vk_expe
|
||||
src/core/utils.cpp
|
||||
src/core/Logger.cpp
|
||||
|
||||
src/vk/Wrapper.cpp
|
||||
src/vk/Context.cpp
|
||||
src/vk/CommandPool.cpp
|
||||
src/vk/CommandBuffer.cpp
|
||||
src/vk/Fence.cpp
|
||||
src/vk/Semaphore.cpp
|
||||
src/vk/RenderPass.cpp
|
||||
src/vk/Framebuffer.cpp
|
||||
src/vk/ShaderModule.cpp
|
||||
src/vk/Pipeline.cpp
|
||||
src/vk/Memory.cpp
|
||||
src/vk/Buffer.cpp
|
||||
src/vk/Image.cpp
|
||||
src/vk/ImageView.cpp
|
||||
src/vk/DescriptorSetLayout.cpp
|
||||
src/vk/PipelineLayout.cpp
|
||||
src/vk/DescriptorPool.cpp
|
||||
src/vk/DescriptorSet.cpp
|
||||
src/vk/Swapchain.cpp
|
||||
src/vk/RenderPass.cpp
|
||||
|
||||
src/main.cpp
|
||||
src/Camera.cpp
|
||||
src/Simplex.cpp
|
||||
src/Planet.cpp
|
||||
src/VkExpe.cpp
|
||||
src/VulkanTutorial.cpp
|
||||
src/Renderer.cpp
|
||||
)
|
||||
|
||||
add_shaders(vk_expe
|
||||
|
||||
@@ -43,7 +43,7 @@ void main() {
|
||||
|
||||
vec2 v0 = p0 - p2;
|
||||
vec2 v1 = p1 - p2;
|
||||
out_edge_dist[i] = determinant(mat2(v0, v1)) / length(v1);
|
||||
out_edge_dist[i] = abs(determinant(mat2(v0, v1))) / length(v1);
|
||||
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ layout(location = 1) out vec3 out_normal;
|
||||
layout(location = 2) out vec3 out_color;
|
||||
|
||||
void main() {
|
||||
float lod = clamp(
|
||||
2.0 * dot(transpose(uniforms.scene_from_model)[0].xyz, in_position) + 0.5,
|
||||
0.0, 1.0
|
||||
);
|
||||
vec3 position = mix(in_position2, in_position, lod);
|
||||
// float lod = clamp(
|
||||
// 2.0 * dot(transpose(uniforms.scene_from_model)[0].xyz, in_position) + 0.5,
|
||||
// 0.0, 1.0
|
||||
// );
|
||||
// vec3 position = mix(in_position2, in_position, lod);
|
||||
vec3 position = in_position;
|
||||
out_position =
|
||||
uniforms.projection_from_scene *
|
||||
uniforms.scene_from_model *
|
||||
|
||||
70
src/Camera.cpp
Normal file
70
src/Camera.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
|
||||
#include <Camera.h>
|
||||
|
||||
|
||||
Camera::Camera() noexcept
|
||||
: m_left(-1)
|
||||
, m_right(1)
|
||||
, m_top(-1)
|
||||
, m_bottom(1)
|
||||
, m_near(0.1)
|
||||
, m_far(100)
|
||||
, m_position(Vector3::Zero())
|
||||
, m_direction(Vector3::UnitZ())
|
||||
, m_down(Vector3::UnitY())
|
||||
{}
|
||||
|
||||
Camera::~Camera() = default;
|
||||
|
||||
|
||||
void Camera::set_projection(Real h_fov, Real width_height_ratio) noexcept {
|
||||
const Real hx = std::tan(h_fov / Real(2));
|
||||
const Real hy = hx / width_height_ratio;
|
||||
set_projection(
|
||||
-hx, hx,
|
||||
-hy, hy
|
||||
);
|
||||
}
|
||||
|
||||
void Camera::update_aspect_ratio(Real width_height_ratio) noexcept {
|
||||
const Real prev_ratio = (m_right - m_left) / (m_bottom - m_top);
|
||||
const Real next_ratio = width_height_ratio;
|
||||
|
||||
const Real sx = (Real(1) + Real(1) / prev_ratio) / (Real(1) + Real(1) / next_ratio);
|
||||
const Real sy = (Real(1) + prev_ratio) / (Real(1) + next_ratio);
|
||||
|
||||
m_left *= sx;
|
||||
m_right *= sx;
|
||||
m_top *= sy;
|
||||
m_bottom *= sy;
|
||||
}
|
||||
|
||||
|
||||
Matrix3 Camera::basis() const noexcept {
|
||||
Matrix3 basis;
|
||||
basis << m_down.cross(m_direction), m_down, m_direction;
|
||||
return basis;
|
||||
}
|
||||
|
||||
Matrix4 Camera::view_matrix() const noexcept {
|
||||
Matrix3 linear_view;
|
||||
linear_view <<
|
||||
m_down.cross(m_direction).transpose(),
|
||||
m_down.transpose(),
|
||||
m_direction.transpose();
|
||||
Matrix4 view;
|
||||
view << linear_view, linear_view * -m_position,
|
||||
Vector4::UnitW().transpose();
|
||||
return view;
|
||||
}
|
||||
|
||||
Matrix4 Camera::projection_matrix() const noexcept {
|
||||
return ::projection_matrix(
|
||||
m_near * m_left, m_near * m_right,
|
||||
m_near * m_top, m_near * m_bottom,
|
||||
m_near, m_far
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
92
src/Camera.h
Normal file
92
src/Camera.h
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <core/math.h>
|
||||
|
||||
|
||||
class Camera {
|
||||
public:
|
||||
Camera() noexcept;
|
||||
~Camera();
|
||||
|
||||
inline Real left() const noexcept {
|
||||
return m_left;
|
||||
}
|
||||
|
||||
inline Real right() const noexcept {
|
||||
return m_right;
|
||||
}
|
||||
|
||||
inline Real top() const noexcept {
|
||||
return m_top;
|
||||
}
|
||||
|
||||
inline Real bottom() const noexcept {
|
||||
return m_bottom;
|
||||
}
|
||||
|
||||
void set_projection(Real left, Real right, Real top, Real bottom) noexcept {
|
||||
m_left = left;
|
||||
m_right = right;
|
||||
m_top = top;
|
||||
m_bottom = bottom;
|
||||
}
|
||||
void set_projection(Real h_fov, Real width_height_ratio) noexcept;
|
||||
void update_aspect_ratio(Real width_height_ratio) noexcept;
|
||||
|
||||
|
||||
inline Real near() const noexcept {
|
||||
return m_near;
|
||||
}
|
||||
|
||||
inline Real far() const noexcept {
|
||||
return m_far;
|
||||
}
|
||||
|
||||
void set_clip_distances(Real near, Real far) noexcept {
|
||||
m_near = near;
|
||||
m_far = far;
|
||||
}
|
||||
|
||||
|
||||
inline Vector3 position() const noexcept {
|
||||
return m_position;
|
||||
}
|
||||
|
||||
inline void set_position(const Vector3& position) noexcept {
|
||||
m_position = position;
|
||||
}
|
||||
|
||||
inline Vector3 direction() const noexcept {
|
||||
return m_direction;
|
||||
}
|
||||
|
||||
inline void set_direction(const Vector3& direction) noexcept {
|
||||
m_direction = direction;
|
||||
}
|
||||
|
||||
inline Vector3 down() const noexcept {
|
||||
return m_down;
|
||||
}
|
||||
|
||||
inline void set_down(const Vector3& down) noexcept {
|
||||
m_down = down;
|
||||
}
|
||||
|
||||
Matrix3 basis() const noexcept;
|
||||
Matrix4 view_matrix() const noexcept;
|
||||
Matrix4 projection_matrix() const noexcept;
|
||||
|
||||
private:
|
||||
Real m_left;
|
||||
Real m_right;
|
||||
Real m_top;
|
||||
Real m_bottom;
|
||||
|
||||
Real m_near;
|
||||
Real m_far;
|
||||
|
||||
Vector3 m_position;
|
||||
Vector3 m_direction;
|
||||
Vector3 m_down;
|
||||
};
|
||||
825
src/Renderer.cpp
Normal file
825
src/Renderer.cpp
Normal file
@@ -0,0 +1,825 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#include <Renderer.h>
|
||||
|
||||
#include <Planet.h>
|
||||
|
||||
#include <vk/ShaderModule.h>
|
||||
|
||||
#include <core/utils.h>
|
||||
#include <core/Logger.h>
|
||||
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
VkVertexInputBindingDescription vertex_binding_description() {
|
||||
return {
|
||||
.binding = 0,
|
||||
.stride = sizeof(Vertex),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
||||
};
|
||||
}
|
||||
|
||||
std::array<VkVertexInputAttributeDescription, 4> vertex_attributes_description() {
|
||||
return {
|
||||
VkVertexInputAttributeDescription {
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(Vertex, position),
|
||||
},
|
||||
VkVertexInputAttributeDescription {
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(Vertex, position2),
|
||||
},
|
||||
VkVertexInputAttributeDescription {
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(Vertex, normal),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(Vertex, color),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
std::vector<Vertex> vertices = {
|
||||
{{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{ 1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
{{ 1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f, 0.0f}},
|
||||
|
||||
{{0.0f, -1.0f, -1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 0.0f}},
|
||||
{{0.0f, -1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 0.0f}},
|
||||
{{0.0f, 1.0f, -1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 0.0f}},
|
||||
{{0.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 0.0f}},
|
||||
|
||||
{{-1.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}},
|
||||
{{ 1.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}},
|
||||
{{-1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}},
|
||||
{{ 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}},
|
||||
};
|
||||
|
||||
std::vector<uint32_t> indices = {
|
||||
0, 1, 2,
|
||||
2, 1, 3,
|
||||
|
||||
4, 5, 6,
|
||||
6, 5, 7,
|
||||
|
||||
8, 9, 10,
|
||||
10, 9, 11,
|
||||
};
|
||||
|
||||
struct Uniforms {
|
||||
alignas(16) Eigen::Matrix4f scene_from_model;
|
||||
alignas(16) Eigen::Matrix4f projection_from_scene;
|
||||
alignas(8) Eigen::Vector2f half_screen_size;
|
||||
alignas(4) float lod;
|
||||
};
|
||||
|
||||
|
||||
Renderer::Renderer() {
|
||||
m_swapchain.register_creation_callback(
|
||||
std::bind(&Renderer::create_swapchain_objects, this));
|
||||
|
||||
// auto const subdiv_count = 4;
|
||||
|
||||
// vertices.resize(6 * Cell::vertex_count(subdiv_count));
|
||||
// indices.resize(6 * 3 * Cell::triangle_count(subdiv_count));
|
||||
|
||||
// Vector3AV positions(
|
||||
// vertices.size(), vertices.data(),
|
||||
// sizeof(Vertex), offsetof(Vertex, position)
|
||||
// );
|
||||
|
||||
// Vector3AV positions2(
|
||||
// vertices.size(), vertices.data(),
|
||||
// sizeof(Vertex), offsetof(Vertex, position2)
|
||||
// );
|
||||
|
||||
// Vector3AV normals(
|
||||
// vertices.size(), vertices.data(),
|
||||
// sizeof(Vertex), offsetof(Vertex, normal)
|
||||
// );
|
||||
|
||||
// Vector3AV colors(
|
||||
// vertices.size(), vertices.data(),
|
||||
// sizeof(Vertex), offsetof(Vertex, color)
|
||||
// );
|
||||
|
||||
// TriangleAV triangles(
|
||||
// indices.size() / 3,
|
||||
// indices.data()
|
||||
// );
|
||||
|
||||
// Planet planet;
|
||||
// planet.build_mesh(positions, positions2, normals, triangles, subdiv_count);
|
||||
|
||||
// for (size_t vertex_index = 0; vertex_index < vertices.size(); vertex_index += 1) {
|
||||
// colors[vertex_index] =
|
||||
// positions[vertex_index] / 2.0f + Vector3::Constant(0.5f);
|
||||
// }
|
||||
|
||||
// for(size_t vi = 0; vi < vertices.size(); vi += 1)
|
||||
// logger.debug() << "v" << vi << ": "
|
||||
// << vertices[vi].position.transpose();
|
||||
// for(size_t ii = 0; 3 * ii < indices.size(); ii += 1)
|
||||
// logger.debug() << "i" << ii << ": "
|
||||
// << indices[3 * ii + 0] << ", "
|
||||
// << indices[3 * ii + 1] << ", "
|
||||
// << indices[3 * ii + 2];
|
||||
// abort();
|
||||
}
|
||||
|
||||
Renderer::~Renderer() {
|
||||
if(m_context)
|
||||
vkDeviceWaitIdle(m_context.device());
|
||||
}
|
||||
|
||||
|
||||
void Renderer::initialize(SDL_Window* window) {
|
||||
auto const context_settings = vk::ContextSettings()
|
||||
#if defined(VKEXPE_ENABLE_VALIDATION) || !defined(NDEBUG)
|
||||
.with_debug(true)
|
||||
#endif
|
||||
.with_queue(GRAPHIC_QUEUE, VK_QUEUE_GRAPHICS_BIT)
|
||||
.with_window(window);
|
||||
m_context.initialize(context_settings);
|
||||
|
||||
create_command_pool();
|
||||
|
||||
create_descriptor_set_layout();
|
||||
create_pipeline_layout();
|
||||
create_render_pass();
|
||||
|
||||
// m_uniform_buffer_memory is allocated when uniform buffer is first created.
|
||||
|
||||
create_vertex_buffer();
|
||||
create_index_buffer();
|
||||
|
||||
auto const swapchain_settings = vk::SwapchainSettings(&m_context)
|
||||
.with_queue(m_context.queue_family(GRAPHIC_QUEUE));
|
||||
m_swapchain.initialize(swapchain_settings);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::set_camera(const Camera& camera) {
|
||||
m_camera = camera;
|
||||
}
|
||||
|
||||
|
||||
void Renderer::draw_frame() {
|
||||
m_swapchain.begin_frame();
|
||||
const auto image_index = m_swapchain.current_image_index();
|
||||
auto& image_states = m_image_states[image_index];
|
||||
|
||||
m_camera.update_aspect_ratio(Real(m_swapchain.extent().width) / Real(m_swapchain.extent().height));
|
||||
const Matrix4 view = m_camera.view_matrix();
|
||||
const Matrix4 proj = m_camera.projection_matrix();
|
||||
|
||||
Transform model = Transform::Identity();
|
||||
|
||||
const Uniforms uniforms = {
|
||||
.scene_from_model = model.matrix(),
|
||||
.projection_from_scene = proj * view,
|
||||
.half_screen_size = {
|
||||
0.5 * m_swapchain.extent().width,
|
||||
0.5 * m_swapchain.extent().height,
|
||||
},
|
||||
// .lod = std::cos(alpha) * 0.5f + 0.5f,
|
||||
};
|
||||
|
||||
void* uniform_buffer = m_uniform_buffer_memory.map(
|
||||
m_context,
|
||||
image_states.m_uniform_buffer_offset,
|
||||
sizeof(Uniforms)
|
||||
);
|
||||
std::memcpy(uniform_buffer, &uniforms, sizeof(Uniforms));
|
||||
m_uniform_buffer_memory.unmap(m_context);
|
||||
|
||||
std::vector<VkDrawIndexedIndirectCommand> draw_commands {
|
||||
{
|
||||
.indexCount = uint32_t(indices.size()),
|
||||
.instanceCount = 1,
|
||||
},
|
||||
};
|
||||
Byte* draw_buffer = image_states.m_draw_buffer.memory().map(
|
||||
m_context,
|
||||
0,
|
||||
DrawBufferOffset + 1 * sizeof(VkDrawIndexedIndirectCommand)
|
||||
);
|
||||
*reinterpret_cast<uint32_t*>(draw_buffer) = uint32_t(draw_commands.size());
|
||||
std::memcpy(
|
||||
draw_buffer + DrawBufferOffset,
|
||||
draw_commands.data(),
|
||||
draw_commands.size() * sizeof(VkDrawIndexedIndirectCommand)
|
||||
);
|
||||
image_states.m_draw_buffer.memory().unmap(m_context);
|
||||
|
||||
VkSemaphore wait_semaphores[] = {
|
||||
m_swapchain.ready_to_render(),
|
||||
};
|
||||
VkSemaphore done_semaphores[] = {
|
||||
image_states.m_render_done,
|
||||
};
|
||||
VkPipelineStageFlags stages[] = {
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
};
|
||||
VkCommandBuffer command_buffers[] = {
|
||||
image_states.m_command_buffer,
|
||||
};
|
||||
VkSubmitInfo submit_info {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = wait_semaphores,
|
||||
.pWaitDstStageMask = stages,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = command_buffers,
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = done_semaphores,
|
||||
};
|
||||
|
||||
if(vkQueueSubmit(
|
||||
m_context.queue(GRAPHIC_QUEUE),
|
||||
1, &submit_info,
|
||||
m_swapchain.render_done()
|
||||
))
|
||||
throw std::runtime_error("failed to submit draw command buffer");
|
||||
|
||||
m_swapchain.swap_buffers(done_semaphores);
|
||||
}
|
||||
|
||||
void Renderer::invalidate_swapchain() {
|
||||
m_swapchain.invalidate();
|
||||
}
|
||||
|
||||
|
||||
void Renderer::create_swapchain_objects() {
|
||||
m_image_states.clear();
|
||||
m_swapchain_states.~SwapchainStates();
|
||||
|
||||
new(&m_swapchain_states) SwapchainStates();
|
||||
initialize_swapchain_states();
|
||||
|
||||
m_image_states.resize(m_swapchain.image_count());
|
||||
for(size_t image_index = 0; image_index < m_image_states.size(); image_index += 1)
|
||||
initialize_image_states(image_index);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::create_command_pool() {
|
||||
m_command_pool = vk::CommandPool(
|
||||
m_context,
|
||||
m_context.queue_family(GRAPHIC_QUEUE)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::create_descriptor_set_layout() {
|
||||
VkDescriptorSetLayoutBinding binding[] {
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT,
|
||||
}
|
||||
};
|
||||
m_descriptor_set_layout = vk::DescriptorSetLayout(m_context, binding);
|
||||
}
|
||||
|
||||
void Renderer::create_pipeline_layout() {
|
||||
VkDescriptorSetLayout set_layouts[] {
|
||||
m_descriptor_set_layout,
|
||||
};
|
||||
m_pipeline_layout = vk::PipelineLayout(m_context, set_layouts);
|
||||
}
|
||||
|
||||
void Renderer::create_render_pass() {
|
||||
VkAttachmentDescription attachments[] {
|
||||
{
|
||||
.format = m_context.surface_format().format,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
},
|
||||
{
|
||||
.format = VK_FORMAT_X8_D24_UNORM_PACK32,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||
},
|
||||
};
|
||||
|
||||
VkAttachmentReference color_attachment_ref {
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
};
|
||||
VkAttachmentReference depth_attachment_ref {
|
||||
.attachment = 1,
|
||||
.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
||||
};
|
||||
|
||||
VkSubpassDescription subpass {
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &color_attachment_ref,
|
||||
.pDepthStencilAttachment = &depth_attachment_ref,
|
||||
};
|
||||
|
||||
VkSubpassDependency subpass_dep = {
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask =
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstStageMask =
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask =
|
||||
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
};
|
||||
|
||||
VkRenderPassCreateInfo render_pass_info {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = uint32_t(std::extent_v<typeof(attachments)>),
|
||||
.pAttachments = attachments,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &subpass_dep,
|
||||
};
|
||||
|
||||
m_render_pass = vk::RenderPass(m_context, render_pass_info);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::create_vertex_buffer() {
|
||||
VkDeviceSize size = sizeof(vertices[0]) * vertices.size();
|
||||
m_vertex_buffer = vk::Buffer(
|
||||
m_context,
|
||||
size,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||
);
|
||||
m_vertex_buffer.upload(size, vertices.data(), m_context.queue_family(GRAPHIC_QUEUE));
|
||||
}
|
||||
|
||||
void Renderer::create_index_buffer() {
|
||||
VkDeviceSize size = sizeof(indices[0]) * indices.size();
|
||||
m_index_buffer = vk::Buffer(
|
||||
m_context,
|
||||
size,
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||
);
|
||||
m_index_buffer.upload(size, indices.data(), m_context.queue_family(GRAPHIC_QUEUE));
|
||||
}
|
||||
|
||||
|
||||
void Renderer::initialize_swapchain_states() {
|
||||
create_descriptor_pool();
|
||||
create_graphic_pipeline();
|
||||
}
|
||||
|
||||
|
||||
void Renderer::create_descriptor_pool() {
|
||||
VkDescriptorPoolSize pool_sizes[] {
|
||||
{
|
||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = uint32_t(m_swapchain.image_count()),
|
||||
}
|
||||
};
|
||||
|
||||
m_swapchain_states.m_descriptor_pool = vk::DescriptorPool(
|
||||
m_context,
|
||||
uint32_t(m_swapchain.image_count()),
|
||||
pool_sizes
|
||||
);
|
||||
}
|
||||
|
||||
void Renderer::create_graphic_pipeline() {
|
||||
auto vertex_shader_module = vk::ShaderModule(m_context, "shaders/shader.vert.spv");
|
||||
auto geometry_shader_module = vk::ShaderModule(m_context, "shaders/shader.geom.spv");
|
||||
auto fragment_shader_module = vk::ShaderModule(m_context, "shaders/shader.frag.spv");
|
||||
|
||||
VkPipelineShaderStageCreateInfo shader_stage_infos[] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = vertex_shader_module,
|
||||
.pName = "main",
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_GEOMETRY_BIT,
|
||||
.module = geometry_shader_module,
|
||||
.pName = "main",
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.module = fragment_shader_module,
|
||||
.pName = "main",
|
||||
},
|
||||
};
|
||||
|
||||
const VkVertexInputBindingDescription vertex_bindings[] = {
|
||||
vertex_binding_description(),
|
||||
};
|
||||
const auto vertex_attributes = vertex_attributes_description();
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = 1,
|
||||
.pVertexBindingDescriptions = vertex_bindings,
|
||||
.vertexAttributeDescriptionCount = uint32_t(vertex_attributes.size()),
|
||||
.pVertexAttributeDescriptions = vertex_attributes.data(),
|
||||
};
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo assembly_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
.primitiveRestartEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
VkViewport viewport {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = float(m_swapchain.extent().width),
|
||||
.height = float(m_swapchain.extent().height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
|
||||
VkRect2D scissor = {
|
||||
.offset = { 0, 0 },
|
||||
.extent = m_swapchain.extent(),
|
||||
};
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.viewportCount = 1,
|
||||
.pViewports = &viewport,
|
||||
.scissorCount = 1,
|
||||
.pScissors = &scissor,
|
||||
};
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.depthClampEnable = VK_FALSE,
|
||||
.rasterizerDiscardEnable = VK_FALSE,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
.cullMode = VK_CULL_MODE_NONE,
|
||||
// .cullMode = VK_CULL_MODE_BACK_BIT,
|
||||
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
||||
.depthBiasEnable = VK_FALSE,
|
||||
.depthBiasConstantFactor = 0.0f,
|
||||
.depthBiasClamp = 0.0f,
|
||||
.depthBiasSlopeFactor = 0.0f,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisample_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.sampleShadingEnable = VK_FALSE,
|
||||
.minSampleShading = 1.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
||||
.depthTestEnable = VK_TRUE,
|
||||
.depthWriteEnable = VK_TRUE,
|
||||
.depthCompareOp = VK_COMPARE_OP_LESS,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendAttachmentState color_blend_attachment {
|
||||
.blendEnable = VK_FALSE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||
.colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT |
|
||||
VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT |
|
||||
VK_COLOR_COMPONENT_A_BIT,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_COPY,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &color_blend_attachment,
|
||||
.blendConstants = { 0.0f, 0.0f, 0.0f, 0.0f },
|
||||
};
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_info {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.stageCount = 3,
|
||||
.pStages = shader_stage_infos,
|
||||
.pVertexInputState = &vertex_info,
|
||||
.pInputAssemblyState = &assembly_info,
|
||||
.pViewportState = &viewport_info,
|
||||
.pRasterizationState = &rasterization_info,
|
||||
.pMultisampleState = &multisample_info,
|
||||
.pDepthStencilState = &depth_stencil_info,
|
||||
.pColorBlendState = &color_blend_info,
|
||||
.pDynamicState = nullptr,
|
||||
.layout = m_pipeline_layout,
|
||||
.renderPass = m_render_pass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = -1,
|
||||
};
|
||||
|
||||
m_swapchain_states.m_pipeline = vk::Pipeline(m_context, pipeline_info);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::initialize_image_states(size_t image_index) {
|
||||
auto& image_states = m_image_states[image_index];
|
||||
image_states.m_image_index = image_index;
|
||||
|
||||
create_depth_buffer(image_states);
|
||||
create_framebuffer(image_states);
|
||||
|
||||
create_uniform_buffer(image_states);
|
||||
create_descriptor_set(image_states);
|
||||
|
||||
create_draw_buffer(image_states);
|
||||
|
||||
create_command_buffer(image_states);
|
||||
|
||||
image_states.m_render_done = vk::Semaphore(m_context);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::create_depth_buffer(ImageStates& image_states)
|
||||
{
|
||||
image_states.m_depth_buffer = vk::Image(
|
||||
m_context,
|
||||
VkImageCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = VK_FORMAT_X8_D24_UNORM_PACK32,
|
||||
.extent = {
|
||||
m_swapchain.extent().width,
|
||||
m_swapchain.extent().height,
|
||||
1
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
},
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||
);
|
||||
|
||||
image_states.m_depth_buffer_view = vk::ImageView(
|
||||
m_context,
|
||||
VkImageViewCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image_states.m_depth_buffer,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = VK_FORMAT_X8_D24_UNORM_PACK32,
|
||||
.components = {
|
||||
.r = VK_COMPONENT_SWIZZLE_R,
|
||||
.g = VK_COMPONENT_SWIZZLE_G,
|
||||
.b = VK_COMPONENT_SWIZZLE_B,
|
||||
.a = VK_COMPONENT_SWIZZLE_A,
|
||||
},
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Renderer::create_framebuffer(ImageStates& image_states) {
|
||||
VkImageView views[] {
|
||||
m_swapchain.image_view(image_states.m_image_index),
|
||||
image_states.m_depth_buffer_view,
|
||||
};
|
||||
image_states.m_framebuffer = vk::Framebuffer(
|
||||
m_context,
|
||||
m_render_pass,
|
||||
views,
|
||||
m_swapchain.extent()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::create_uniform_buffer(ImageStates& image_states) {
|
||||
const auto image_index = image_states.m_image_index;
|
||||
|
||||
image_states.m_uniform_buffer = vk::Buffer(
|
||||
m_context,
|
||||
sizeof(Uniforms),
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||
);
|
||||
auto& buffer = image_states.m_uniform_buffer;
|
||||
|
||||
if(!m_uniform_buffer_memory) {
|
||||
VkMemoryRequirements memory_requirements =
|
||||
buffer.memory_requirements();
|
||||
|
||||
m_uniform_buffer_memory = m_context.allocator().allocate(
|
||||
memory_requirements.size * m_swapchain.image_count(),
|
||||
memory_requirements.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||
);
|
||||
|
||||
for (size_t index = 0; index < m_swapchain.image_count(); index += 1)
|
||||
m_image_states[index].m_uniform_buffer_offset =
|
||||
index * memory_requirements.size;
|
||||
}
|
||||
|
||||
buffer.bind_memory(
|
||||
m_uniform_buffer_memory,
|
||||
image_states.m_uniform_buffer_offset
|
||||
);
|
||||
}
|
||||
|
||||
void Renderer::create_descriptor_set(ImageStates& image_states) {
|
||||
image_states.m_descriptor_set = vk::DescriptorSet(
|
||||
m_context,
|
||||
m_swapchain_states.m_descriptor_pool,
|
||||
m_descriptor_set_layout
|
||||
);
|
||||
|
||||
VkDescriptorBufferInfo buffer_info {
|
||||
.buffer = image_states.m_uniform_buffer,
|
||||
.offset = 0,
|
||||
.range = sizeof(Uniforms),
|
||||
};
|
||||
VkWriteDescriptorSet write {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = image_states.m_descriptor_set,
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.pBufferInfo = &buffer_info,
|
||||
};
|
||||
vkUpdateDescriptorSets(
|
||||
m_context.device(),
|
||||
1, &write,
|
||||
0, nullptr
|
||||
);
|
||||
}
|
||||
|
||||
void Renderer::create_draw_buffer(ImageStates& image_states) {
|
||||
image_states.m_draw_buffer = vk::Buffer(
|
||||
m_context,
|
||||
DrawBufferOffset + MaxDrawTileCount * sizeof(VkDrawIndexedIndirectCommand),
|
||||
VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||
);
|
||||
}
|
||||
|
||||
void Renderer::create_command_buffer(ImageStates& image_states) {
|
||||
image_states.m_command_buffer = vk::CommandBuffer(m_context, m_command_pool);
|
||||
|
||||
VkCommandBufferBeginInfo begin_info {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = 0,
|
||||
.pInheritanceInfo = nullptr,
|
||||
};
|
||||
|
||||
logger.debug() << "record command buffer " << image_states.m_image_index
|
||||
<< " (" << image_states.m_command_buffer << ")";
|
||||
|
||||
if(vkBeginCommandBuffer(
|
||||
image_states.m_command_buffer,
|
||||
&begin_info
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to begin command buffer");
|
||||
|
||||
VkClearValue clear_values[] = {
|
||||
{
|
||||
.color = {
|
||||
.float32 = { 0.0f, 0.0f, 0.0f, 1.0f }
|
||||
}
|
||||
},
|
||||
{
|
||||
.depthStencil = {
|
||||
.depth = 1.0f,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
VkRenderPassBeginInfo pass_info {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = m_render_pass,
|
||||
.framebuffer = image_states.m_framebuffer,
|
||||
.renderArea = {
|
||||
.offset = { 0, 0 },
|
||||
.extent = m_swapchain.extent(),
|
||||
},
|
||||
.clearValueCount = uint32_t(std::extent_v<typeof(clear_values)>),
|
||||
.pClearValues = clear_values,
|
||||
};
|
||||
|
||||
vkCmdBeginRenderPass(
|
||||
image_states.m_command_buffer,
|
||||
&pass_info,
|
||||
VK_SUBPASS_CONTENTS_INLINE
|
||||
);
|
||||
|
||||
vkCmdBindPipeline(
|
||||
image_states.m_command_buffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_swapchain_states.m_pipeline
|
||||
);
|
||||
|
||||
VkBuffer vertex_buffers[] = {
|
||||
m_vertex_buffer,
|
||||
};
|
||||
VkDeviceSize offsets[] = {
|
||||
0,
|
||||
};
|
||||
vkCmdBindVertexBuffers(
|
||||
image_states.m_command_buffer,
|
||||
0,
|
||||
1,
|
||||
vertex_buffers,
|
||||
offsets
|
||||
);
|
||||
|
||||
vkCmdBindIndexBuffer(
|
||||
image_states.m_command_buffer,
|
||||
m_index_buffer,
|
||||
0,
|
||||
VK_INDEX_TYPE_UINT32
|
||||
);
|
||||
|
||||
VkDescriptorSet descriptor_sets[] {
|
||||
image_states.m_descriptor_set,
|
||||
};
|
||||
vkCmdBindDescriptorSets(
|
||||
image_states.m_command_buffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_pipeline_layout,
|
||||
0, 1, descriptor_sets,
|
||||
0, nullptr
|
||||
);
|
||||
|
||||
m_context.cmdDrawIndexedIndirectCount(
|
||||
image_states.m_command_buffer,
|
||||
image_states.m_draw_buffer,
|
||||
DrawBufferOffset,
|
||||
image_states.m_draw_buffer,
|
||||
0,
|
||||
MaxDrawTileCount,
|
||||
sizeof(VkDrawIndexedIndirectCommand)
|
||||
);
|
||||
|
||||
vkCmdEndRenderPass(image_states.m_command_buffer);
|
||||
|
||||
if(vkEndCommandBuffer(image_states.m_command_buffer) != VK_SUCCESS)
|
||||
throw("failed to record command buffer");
|
||||
}
|
||||
140
src/Renderer.h
Normal file
140
src/Renderer.h
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <Camera.h>
|
||||
|
||||
#include <vk/Swapchain.h>
|
||||
#include <vk/DescriptorSet.h>
|
||||
#include <vk/DescriptorPool.h>
|
||||
#include <vk/PipelineLayout.h>
|
||||
#include <vk/DescriptorSetLayout.h>
|
||||
#include <vk/Image.h>
|
||||
#include <vk/Buffer.h>
|
||||
#include <vk/Pipeline.h>
|
||||
#include <vk/Framebuffer.h>
|
||||
#include <vk/RenderPass.h>
|
||||
#include <vk/Semaphore.h>
|
||||
#include <vk/CommandBuffer.h>
|
||||
#include <vk/CommandPool.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <core/math.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
VkVertexInputBindingDescription vertex_binding_description();
|
||||
std::array<VkVertexInputAttributeDescription, 4> vertex_attributes_description();
|
||||
|
||||
|
||||
class Renderer {
|
||||
public:
|
||||
enum QueueIndex {
|
||||
GRAPHIC_QUEUE,
|
||||
};
|
||||
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
using Duration = Clock::duration;
|
||||
using SecondsD = std::chrono::duration<double>;
|
||||
|
||||
public:
|
||||
Renderer();
|
||||
Renderer(const Renderer&) = delete;
|
||||
~Renderer();
|
||||
|
||||
Renderer& operator=(const Renderer&) = delete;
|
||||
|
||||
void initialize(SDL_Window* window);
|
||||
|
||||
void set_camera(const Camera& camera);
|
||||
|
||||
void draw_frame();
|
||||
void invalidate_swapchain();
|
||||
|
||||
private:
|
||||
struct SwapchainStates;
|
||||
struct ImageStates;
|
||||
|
||||
private:
|
||||
void create_swapchain_objects();
|
||||
|
||||
void create_command_pool();
|
||||
|
||||
void create_descriptor_set_layout();
|
||||
void create_pipeline_layout();
|
||||
void create_render_pass();
|
||||
|
||||
void create_vertex_buffer();
|
||||
void create_index_buffer();
|
||||
|
||||
void initialize_swapchain_states();
|
||||
|
||||
void create_descriptor_pool();
|
||||
void create_graphic_pipeline();
|
||||
|
||||
void initialize_image_states(size_t image_index);
|
||||
|
||||
void create_depth_buffer(ImageStates& image_states);
|
||||
void create_framebuffer(ImageStates& image_states);
|
||||
|
||||
void create_uniform_buffer(ImageStates& image_states);
|
||||
void create_descriptor_set(ImageStates& image_states);
|
||||
|
||||
void create_draw_buffer(ImageStates& image_states);
|
||||
|
||||
void create_command_buffer(ImageStates& image_states);
|
||||
|
||||
private:
|
||||
static constexpr uint32_t DrawBufferOffset = 4;
|
||||
static constexpr uint32_t MaxDrawTileCount = 256;
|
||||
|
||||
private:
|
||||
vk::Context m_context;
|
||||
|
||||
vk::CommandPool m_command_pool;
|
||||
|
||||
vk::DescriptorSetLayout m_descriptor_set_layout;
|
||||
vk::PipelineLayout m_pipeline_layout;
|
||||
vk::RenderPass m_render_pass;
|
||||
|
||||
vk::Buffer m_vertex_buffer;
|
||||
vk::Buffer m_index_buffer;
|
||||
|
||||
vk::MemoryBlock m_uniform_buffer_memory;
|
||||
|
||||
vk::Swapchain m_swapchain;
|
||||
|
||||
struct SwapchainStates {
|
||||
vk::DescriptorPool m_descriptor_pool;
|
||||
vk::Pipeline m_pipeline;
|
||||
};
|
||||
SwapchainStates m_swapchain_states;
|
||||
|
||||
struct ImageStates {
|
||||
size_t m_image_index;
|
||||
|
||||
vk::Image m_depth_buffer;
|
||||
vk::ImageView m_depth_buffer_view;
|
||||
vk::Framebuffer m_framebuffer;
|
||||
|
||||
vk::Buffer m_uniform_buffer;
|
||||
VkDeviceSize m_uniform_buffer_offset;
|
||||
vk::DescriptorSet m_descriptor_set;
|
||||
|
||||
vk::Buffer m_draw_buffer;
|
||||
|
||||
vk::CommandBuffer m_command_buffer;
|
||||
vk::Semaphore m_render_done;
|
||||
};
|
||||
std::vector<ImageStates> m_image_states;
|
||||
|
||||
Camera m_camera;
|
||||
};
|
||||
@@ -15,6 +15,12 @@ void SdlWindowDeleter::operator()(SDL_Window* window) const {
|
||||
|
||||
|
||||
VkExpe::VkExpe(int argc, char** argv) {
|
||||
m_camera.set_position(Vector3(0.0f, 0.0f, -3.0f));
|
||||
m_camera.set_direction(Vector3::UnitZ());
|
||||
m_camera.set_down(Vector3::UnitY());
|
||||
|
||||
m_camera.set_projection(M_PI / 3.0, 1.0);
|
||||
m_camera.set_clip_distances(0.1, 10);
|
||||
}
|
||||
|
||||
VkExpe::~VkExpe() {
|
||||
@@ -43,7 +49,7 @@ void VkExpe::initialize() {
|
||||
}
|
||||
|
||||
void VkExpe::shutdown() {
|
||||
m_vulkan.shutdown();
|
||||
recreate_object(m_vulkan);
|
||||
|
||||
m_window.reset();
|
||||
|
||||
@@ -64,9 +70,22 @@ void VkExpe::run() {
|
||||
|
||||
auto last_time = std::chrono::high_resolution_clock::now();
|
||||
|
||||
SDL_Event event;
|
||||
// SDL_Event event;
|
||||
std::vector<SDL_Event> events;
|
||||
events.reserve(128);
|
||||
while(m_running) {
|
||||
while(SDL_PollEvent(&event)) {
|
||||
SDL_PumpEvents();
|
||||
|
||||
int event_count = SDL_PeepEvents(nullptr, 0, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
|
||||
if(event_count < 0) {
|
||||
logger.error() << SDL_GetError();
|
||||
break;
|
||||
}
|
||||
|
||||
events.resize(event_count);
|
||||
SDL_PeepEvents(events.data(), event_count, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT);
|
||||
|
||||
for(const auto& event: events) {
|
||||
switch(event.type) {
|
||||
case SDL_QUIT:
|
||||
m_running = false;
|
||||
@@ -109,7 +128,7 @@ void VkExpe::run() {
|
||||
|
||||
update(std::chrono::duration<double>(elapsed).count());
|
||||
|
||||
m_vulkan.set_camera(m_camera_position, m_camera_z, m_camera_y);
|
||||
m_vulkan.set_camera(m_camera);
|
||||
m_vulkan.draw_frame();
|
||||
|
||||
m_mouse_offset = Vector2::Zero();
|
||||
@@ -128,14 +147,16 @@ void VkExpe::update(double elapsed) {
|
||||
const Real x_sensi = 0.001;
|
||||
const Real y_sensi = -0.001;
|
||||
|
||||
const Vector3 camera_x = m_camera_y.cross(m_camera_z);
|
||||
const Vector3 camera_z = m_camera.direction();
|
||||
const Vector3 camera_y = m_camera.down();
|
||||
const Vector3 camera_x = camera_y.cross(camera_z);
|
||||
Vector3 axis =
|
||||
x_sensi * m_mouse_offset[0] * m_camera_y +
|
||||
x_sensi * m_mouse_offset[0] * camera_y +
|
||||
y_sensi * m_mouse_offset[1] * camera_x;
|
||||
Real rot_norm = axis.norm();
|
||||
AngleAxis rot(rot_norm, axis / rot_norm);
|
||||
m_camera_y = rot * m_camera_y;
|
||||
m_camera_z = rot * m_camera_z;
|
||||
m_camera.set_down(rot * camera_y);
|
||||
m_camera.set_direction(rot * camera_z);
|
||||
}
|
||||
|
||||
Vector3 walk_direction = Vector3::Zero();
|
||||
@@ -156,8 +177,9 @@ void VkExpe::update(double elapsed) {
|
||||
walk_direction.normalize();
|
||||
const Real base_velocity = 1;
|
||||
|
||||
Matrix3 camera_basis;
|
||||
camera_basis << m_camera_y.cross(m_camera_z), m_camera_y, m_camera_z;
|
||||
m_camera_position += elapsed * base_velocity * (camera_basis * walk_direction);
|
||||
const Matrix3 basis = m_camera.basis();
|
||||
m_camera.set_position(
|
||||
m_camera.position() + elapsed * base_velocity * (basis * walk_direction)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <VulkanTutorial.h>
|
||||
#include <Renderer.h>
|
||||
#include <Camera.h>
|
||||
|
||||
#include <core/math.h>
|
||||
|
||||
@@ -32,11 +33,9 @@ public:
|
||||
void update(double elapsed);
|
||||
|
||||
private:
|
||||
VulkanTutorial m_vulkan;
|
||||
Renderer m_vulkan;
|
||||
|
||||
Vector3 m_camera_position = Vector3(0.0f, 0.0f, -3.0f);
|
||||
Vector3 m_camera_z = Vector3(0.0f, 0.0f, 1.0f);
|
||||
Vector3 m_camera_y = Vector3(0.0f, 1.0f, 0.0f);
|
||||
Camera m_camera;
|
||||
|
||||
WindowUP m_window;
|
||||
bool m_running = false;
|
||||
|
||||
@@ -1,787 +0,0 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#include <VulkanTutorial.h>
|
||||
|
||||
#include <Planet.h>
|
||||
|
||||
#include <vk/ShaderModule.h>
|
||||
|
||||
#include <core/utils.h>
|
||||
#include <core/Logger.h>
|
||||
|
||||
#include <SDL2/SDL_vulkan.h>
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
VkVertexInputBindingDescription vertex_binding_description() {
|
||||
return {
|
||||
.binding = 0,
|
||||
.stride = sizeof(Vertex),
|
||||
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
|
||||
};
|
||||
}
|
||||
|
||||
std::array<VkVertexInputAttributeDescription, 4> vertex_attributes_description() {
|
||||
return {
|
||||
VkVertexInputAttributeDescription {
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(Vertex, position),
|
||||
},
|
||||
VkVertexInputAttributeDescription {
|
||||
.location = 1,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(Vertex, position2),
|
||||
},
|
||||
VkVertexInputAttributeDescription {
|
||||
.location = 2,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(Vertex, normal),
|
||||
},
|
||||
{
|
||||
.location = 3,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
.offset = offsetof(Vertex, color),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
std::vector<Vertex> vertices = {
|
||||
// {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}},
|
||||
// {{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}},
|
||||
// {{-0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}},
|
||||
// {{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}},
|
||||
|
||||
// {{-0.5f, -0.5f, 0.5f}, {0.0f, 1.0f, 1.0f}},
|
||||
// {{0.5f, -0.5f, 0.5f}, {1.0f, 0.0f, 1.0f}},
|
||||
// {{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 0.0f}},
|
||||
// {{0.5f, 0.5f, 0.5f}, {0.2f, 0.2f, 0.2f}},
|
||||
};
|
||||
|
||||
std::vector<uint32_t> indices = {
|
||||
// 0, 1, 2,
|
||||
// 2, 1, 3,
|
||||
|
||||
// 1, 5, 3,
|
||||
// 3, 5, 7,
|
||||
|
||||
// 5, 4, 7,
|
||||
// 7, 4, 6,
|
||||
|
||||
// 4, 0, 6,
|
||||
// 6, 0, 2,
|
||||
|
||||
// 0, 4, 1,
|
||||
// 1, 4, 5,
|
||||
|
||||
// 2, 3, 6,
|
||||
// 6, 3, 7,
|
||||
};
|
||||
|
||||
struct Uniforms {
|
||||
alignas(16) Eigen::Matrix4f scene_from_model;
|
||||
alignas(16) Eigen::Matrix4f projection_from_scene;
|
||||
alignas(8) Eigen::Vector2f half_screen_size;
|
||||
alignas(4) float lod;
|
||||
};
|
||||
|
||||
|
||||
VulkanTutorial::VulkanTutorial() {
|
||||
m_swapchain.register_creation_callback(
|
||||
std::bind(&VulkanTutorial::create_swapchain_objects, this, std::placeholders::_1));
|
||||
m_swapchain.register_destruction_callback(
|
||||
std::bind(&VulkanTutorial::destroy_swapchain_objects, this));
|
||||
|
||||
auto const subdiv_count = 4;
|
||||
|
||||
vertices.resize(6 * Cell::vertex_count(subdiv_count));
|
||||
indices.resize(6 * 3 * Cell::triangle_count(subdiv_count));
|
||||
|
||||
Vector3AV positions(
|
||||
vertices.size(), vertices.data(),
|
||||
sizeof(Vertex), offsetof(Vertex, position)
|
||||
);
|
||||
|
||||
Vector3AV positions2(
|
||||
vertices.size(), vertices.data(),
|
||||
sizeof(Vertex), offsetof(Vertex, position2)
|
||||
);
|
||||
|
||||
Vector3AV normals(
|
||||
vertices.size(), vertices.data(),
|
||||
sizeof(Vertex), offsetof(Vertex, normal)
|
||||
);
|
||||
|
||||
Vector3AV colors(
|
||||
vertices.size(), vertices.data(),
|
||||
sizeof(Vertex), offsetof(Vertex, color)
|
||||
);
|
||||
|
||||
TriangleAV triangles(
|
||||
indices.size() / 3,
|
||||
indices.data()
|
||||
);
|
||||
|
||||
Planet planet;
|
||||
planet.build_mesh(positions, positions2, normals, triangles, subdiv_count);
|
||||
|
||||
for (size_t vertex_index = 0; vertex_index < vertices.size(); vertex_index += 1) {
|
||||
colors[vertex_index] =
|
||||
positions[vertex_index] / 2.0f + Vector3::Constant(0.5f);
|
||||
}
|
||||
|
||||
// for(size_t vi = 0; vi < vertices.size(); vi += 1)
|
||||
// logger.debug() << "v" << vi << ": "
|
||||
// << vertices[vi].position.transpose();
|
||||
// for(size_t ii = 0; 3 * ii < indices.size(); ii += 1)
|
||||
// logger.debug() << "i" << ii << ": "
|
||||
// << indices[3 * ii + 0] << ", "
|
||||
// << indices[3 * ii + 1] << ", "
|
||||
// << indices[3 * ii + 2];
|
||||
// abort();
|
||||
}
|
||||
|
||||
VulkanTutorial::~VulkanTutorial() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void VulkanTutorial::initialize(SDL_Window* window) {
|
||||
auto const context_settings = vk::ContextSettings()
|
||||
#if defined(VKEXPE_ENABLE_VALIDATION) || !defined(NDEBUG)
|
||||
.with_debug(true)
|
||||
#endif
|
||||
.with_queue(GRAPHIC_QUEUE, VK_QUEUE_GRAPHICS_BIT)
|
||||
.with_window(window);
|
||||
m_context.initialize(context_settings);
|
||||
|
||||
create_command_pool();
|
||||
create_descriptor_set_layout();
|
||||
create_vertex_buffer();
|
||||
create_index_buffer();
|
||||
|
||||
auto const swapchain_settings = vk::SwapchainSettings(&m_context)
|
||||
.with_queue(m_context.queue_family(GRAPHIC_QUEUE));
|
||||
m_swapchain.initialize(swapchain_settings);
|
||||
}
|
||||
|
||||
void VulkanTutorial::shutdown() {
|
||||
if(!m_context.instance())
|
||||
return;
|
||||
|
||||
vkDeviceWaitIdle(m_context.device());
|
||||
|
||||
destroy_swapchain_objects();
|
||||
|
||||
m_command_pool.destroy();
|
||||
m_index_buffer.destroy();
|
||||
m_vertex_buffer.destroy();
|
||||
m_context.destroy_descriptor_set_layout(m_descriptor_set_layout);
|
||||
|
||||
m_render_done.clear();
|
||||
|
||||
m_swapchain.shutdown();
|
||||
m_context.shutdown();
|
||||
}
|
||||
|
||||
void VulkanTutorial::set_camera(const Vector3& camera_position, const Vector3& camera_z, const Vector3& camera_y) {
|
||||
m_camera_position = camera_position;
|
||||
m_camera_z = camera_z;
|
||||
m_camera_y = camera_y;
|
||||
}
|
||||
|
||||
void VulkanTutorial::draw_frame() {
|
||||
m_swapchain.begin_frame();
|
||||
auto const image_index = m_swapchain.current_image_index();
|
||||
|
||||
const auto now = Clock::now();
|
||||
if(m_last_frame_time.time_since_epoch() != Duration(0)) {
|
||||
m_time += now - m_last_frame_time;
|
||||
}
|
||||
m_last_frame_time = now;
|
||||
|
||||
Matrix3 linear_view;
|
||||
linear_view <<
|
||||
m_camera_y.cross(m_camera_z).transpose(),
|
||||
m_camera_y.transpose(),
|
||||
m_camera_z.transpose();
|
||||
Matrix4 view;
|
||||
view << linear_view, linear_view * -m_camera_position,
|
||||
Vector4::UnitW().transpose();
|
||||
|
||||
const float fov = M_PI / 3.0f;
|
||||
const float near = 0.1f;
|
||||
const float far = 10.0f;
|
||||
const float hx = near * std::tan(fov / 2.0f);
|
||||
const float hy = hx * m_swapchain.extent().height / m_swapchain.extent().width;
|
||||
const Eigen::Matrix4f proj = projection_matrix(
|
||||
-hx, hx,
|
||||
-hy, hy,
|
||||
near, far
|
||||
);
|
||||
|
||||
Transform model = Transform::Identity();
|
||||
|
||||
const Uniforms uniforms = {
|
||||
.scene_from_model = model.matrix(),
|
||||
.projection_from_scene = proj * view,
|
||||
.half_screen_size = {
|
||||
0.5 * m_swapchain.extent().width,
|
||||
0.5 * m_swapchain.extent().height,
|
||||
},
|
||||
// .lod = std::cos(alpha) * 0.5f + 0.5f,
|
||||
};
|
||||
|
||||
void* uniform_buffer = m_uniform_buffer_memory.map(
|
||||
m_context,
|
||||
m_uniform_buffer_offsets[image_index],
|
||||
sizeof(Uniforms)
|
||||
);
|
||||
std::memcpy(uniform_buffer, &uniforms, sizeof(Uniforms));
|
||||
m_uniform_buffer_memory.unmap(m_context);
|
||||
|
||||
VkSemaphore wait_semaphores[] = {
|
||||
m_swapchain.ready_to_render(),
|
||||
};
|
||||
VkSemaphore done_semaphores[] = {
|
||||
m_render_done[image_index],
|
||||
};
|
||||
VkPipelineStageFlags stages[] = {
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
};
|
||||
VkCommandBuffer command_buffers[] = {
|
||||
m_command_buffers[image_index],
|
||||
};
|
||||
VkSubmitInfo submit_info {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = 1,
|
||||
.pWaitSemaphores = wait_semaphores,
|
||||
.pWaitDstStageMask = stages,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = command_buffers,
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = done_semaphores,
|
||||
};
|
||||
|
||||
if(vkQueueSubmit(
|
||||
m_context.queue(GRAPHIC_QUEUE),
|
||||
1, &submit_info,
|
||||
m_swapchain.render_done()
|
||||
))
|
||||
throw std::runtime_error("failed to submit draw command buffer");
|
||||
|
||||
m_swapchain.swap_buffers(done_semaphores);
|
||||
}
|
||||
|
||||
void VulkanTutorial::invalidate_swapchain() {
|
||||
m_swapchain.invalidate();
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_swapchain_objects(uint32_t image_count) {
|
||||
create_render_pass();
|
||||
create_framebuffers();
|
||||
create_uniform_buffer();
|
||||
create_descriptor_pool();
|
||||
create_descriptor_sets();
|
||||
create_graphic_pipeline();
|
||||
create_command_buffers();
|
||||
|
||||
m_render_done.clear();
|
||||
for(size_t index = 0; index < m_swapchain.image_count(); index += 1) {
|
||||
m_render_done.emplace_back(m_context);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTutorial::destroy_swapchain_objects() {
|
||||
if(m_command_pool == VK_NULL_HANDLE)
|
||||
return;
|
||||
|
||||
m_command_buffers.clear();
|
||||
|
||||
for(auto& buffer: m_uniform_buffers)
|
||||
buffer.destroy();
|
||||
m_uniform_buffers.clear();
|
||||
m_uniform_buffer_memory.free();
|
||||
|
||||
m_context.destroy_descriptor_pool(m_descriptor_pool);
|
||||
|
||||
m_framebuffers.clear();
|
||||
|
||||
m_pipeline.destroy();
|
||||
m_context.destroy_pipeline_layout(m_pipeline_layout);
|
||||
m_render_pass.destroy();
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_render_pass() {
|
||||
VkAttachmentDescription color_attachment {
|
||||
.format = m_context.surface_format().format,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
||||
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
||||
.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||
.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
};
|
||||
|
||||
VkAttachmentReference color_attachment_ref = {
|
||||
.attachment = 0,
|
||||
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||
};
|
||||
|
||||
VkSubpassDescription subpass {
|
||||
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &color_attachment_ref,
|
||||
};
|
||||
|
||||
VkSubpassDependency subpass_dep = {
|
||||
.srcSubpass = VK_SUBPASS_EXTERNAL,
|
||||
.dstSubpass = 0,
|
||||
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
.srcAccessMask = 0,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
};
|
||||
|
||||
VkRenderPassCreateInfo render_pass_info {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &color_attachment,
|
||||
.subpassCount = 1,
|
||||
.pSubpasses = &subpass,
|
||||
.dependencyCount = 1,
|
||||
.pDependencies = &subpass_dep,
|
||||
};
|
||||
|
||||
m_render_pass = vk::RenderPass(m_context, render_pass_info);
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_descriptor_set_layout() {
|
||||
VkDescriptorSetLayoutBinding binding {
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT,
|
||||
};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo layout_info {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = 1,
|
||||
.pBindings = &binding,
|
||||
};
|
||||
if(vkCreateDescriptorSetLayout(
|
||||
m_context.device(),
|
||||
&layout_info,
|
||||
nullptr,
|
||||
&m_descriptor_set_layout
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create descriptor set layout");
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_graphic_pipeline() {
|
||||
auto vertex_shader_module = vk::ShaderModule(m_context, "shaders/shader.vert.spv");
|
||||
auto geometry_shader_module = vk::ShaderModule(m_context, "shaders/shader.geom.spv");
|
||||
auto fragment_shader_module = vk::ShaderModule(m_context, "shaders/shader.frag.spv");
|
||||
|
||||
VkPipelineShaderStageCreateInfo shader_stage_infos[] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = vertex_shader_module,
|
||||
.pName = "main",
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_GEOMETRY_BIT,
|
||||
.module = geometry_shader_module,
|
||||
.pName = "main",
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
.module = fragment_shader_module,
|
||||
.pName = "main",
|
||||
},
|
||||
};
|
||||
|
||||
const VkVertexInputBindingDescription vertex_bindings[] = {
|
||||
vertex_binding_description(),
|
||||
};
|
||||
const auto vertex_attributes = vertex_attributes_description();
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = 1,
|
||||
.pVertexBindingDescriptions = vertex_bindings,
|
||||
.vertexAttributeDescriptionCount = uint32_t(vertex_attributes.size()),
|
||||
.pVertexAttributeDescriptions = vertex_attributes.data(),
|
||||
};
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo assembly_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
.primitiveRestartEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
VkViewport viewport {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = float(m_swapchain.extent().width),
|
||||
.height = float(m_swapchain.extent().height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
|
||||
VkRect2D scissor = {
|
||||
.offset = { 0, 0 },
|
||||
.extent = m_swapchain.extent(),
|
||||
};
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.viewportCount = 1,
|
||||
.pViewports = &viewport,
|
||||
.scissorCount = 1,
|
||||
.pScissors = &scissor,
|
||||
};
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.depthClampEnable = VK_FALSE,
|
||||
.rasterizerDiscardEnable = VK_FALSE,
|
||||
.polygonMode = VK_POLYGON_MODE_FILL,
|
||||
// .cullMode = VK_CULL_MODE_NONE,
|
||||
.cullMode = VK_CULL_MODE_BACK_BIT,
|
||||
.frontFace = VK_FRONT_FACE_CLOCKWISE,
|
||||
.depthBiasEnable = VK_FALSE,
|
||||
.depthBiasConstantFactor = 0.0f,
|
||||
.depthBiasClamp = 0.0f,
|
||||
.depthBiasSlopeFactor = 0.0f,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisample_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.sampleShadingEnable = VK_FALSE,
|
||||
.minSampleShading = 1.0f,
|
||||
.pSampleMask = nullptr,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.alphaToOneEnable = VK_FALSE,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendAttachmentState color_blend_attachment {
|
||||
.blendEnable = VK_FALSE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.colorBlendOp = VK_BLEND_OP_ADD,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
.alphaBlendOp = VK_BLEND_OP_ADD,
|
||||
.colorWriteMask =
|
||||
VK_COLOR_COMPONENT_R_BIT |
|
||||
VK_COLOR_COMPONENT_G_BIT |
|
||||
VK_COLOR_COMPONENT_B_BIT |
|
||||
VK_COLOR_COMPONENT_A_BIT,
|
||||
};
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.logicOpEnable = VK_FALSE,
|
||||
.logicOp = VK_LOGIC_OP_COPY,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &color_blend_attachment,
|
||||
.blendConstants = { 0.0f, 0.0f, 0.0f, 0.0f },
|
||||
};
|
||||
|
||||
VkPipelineLayoutCreateInfo layout_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = &m_descriptor_set_layout,
|
||||
.pushConstantRangeCount = 0,
|
||||
.pPushConstantRanges = nullptr,
|
||||
};
|
||||
|
||||
if(vkCreatePipelineLayout(
|
||||
m_context.device(),
|
||||
&layout_info,
|
||||
nullptr,
|
||||
&m_pipeline_layout
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create pipeline layout");
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_info {
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.stageCount = 3,
|
||||
.pStages = shader_stage_infos,
|
||||
.pVertexInputState = &vertex_info,
|
||||
.pInputAssemblyState = &assembly_info,
|
||||
.pViewportState = &viewport_info,
|
||||
.pRasterizationState = &rasterization_info,
|
||||
.pMultisampleState = &multisample_info,
|
||||
.pDepthStencilState = nullptr,
|
||||
.pColorBlendState = &color_blend_info,
|
||||
.pDynamicState = nullptr,
|
||||
.layout = m_pipeline_layout,
|
||||
.renderPass = m_render_pass,
|
||||
.subpass = 0,
|
||||
.basePipelineHandle = VK_NULL_HANDLE,
|
||||
.basePipelineIndex = -1,
|
||||
};
|
||||
|
||||
m_pipeline = vk::Pipeline(m_context, pipeline_info);
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_framebuffers() {
|
||||
m_framebuffers.clear();
|
||||
for(size_t index = 0; index < m_swapchain.image_count(); index += 1) {
|
||||
VkImageView view = m_swapchain.image_view(index);
|
||||
m_framebuffers.emplace_back(
|
||||
m_context,
|
||||
m_render_pass,
|
||||
&view,
|
||||
m_swapchain.extent()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_command_pool() {
|
||||
if(!m_command_pool.is_null())
|
||||
return;
|
||||
|
||||
m_command_pool = vk::CommandPool(
|
||||
m_context,
|
||||
m_context.queue_family(GRAPHIC_QUEUE)
|
||||
);
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_vertex_buffer() {
|
||||
if(m_vertex_buffer)
|
||||
return;
|
||||
|
||||
VkDeviceSize size = sizeof(vertices[0]) * vertices.size();
|
||||
m_vertex_buffer = vk::Buffer(
|
||||
m_context,
|
||||
size,
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||
);
|
||||
m_vertex_buffer.upload(size, vertices.data(), m_context.queue_family(GRAPHIC_QUEUE));
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_index_buffer() {
|
||||
if(m_index_buffer)
|
||||
return;
|
||||
|
||||
VkDeviceSize size = sizeof(indices[0]) * indices.size();
|
||||
m_index_buffer = vk::Buffer(
|
||||
m_context,
|
||||
size,
|
||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT
|
||||
| VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
|
||||
);
|
||||
m_index_buffer.upload(size, indices.data(), m_context.queue_family(GRAPHIC_QUEUE));
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_uniform_buffer() {
|
||||
m_uniform_buffers.resize(m_swapchain.image_count());
|
||||
for(size_t index = 0; index < m_uniform_buffers.size(); index += 1) {
|
||||
m_uniform_buffers[index] = vk::Buffer(
|
||||
m_context,
|
||||
sizeof(Uniforms),
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT
|
||||
);
|
||||
}
|
||||
|
||||
VkMemoryRequirements memory_requirements =
|
||||
m_uniform_buffers[0].memory_requirements();
|
||||
|
||||
m_uniform_buffer_memory = m_context.allocator().allocate(
|
||||
memory_requirements.size * m_swapchain.image_count(),
|
||||
memory_requirements.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||
);
|
||||
|
||||
m_uniform_buffer_offsets.resize(m_uniform_buffers.size());
|
||||
for(size_t index = 0; index < m_uniform_buffers.size(); index += 1) {
|
||||
m_uniform_buffer_offsets[index] = index * memory_requirements.size;
|
||||
m_uniform_buffers[index].bind_memory(
|
||||
m_uniform_buffer_memory,
|
||||
m_uniform_buffer_offsets[index]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_descriptor_pool() {
|
||||
VkDescriptorPoolSize pool_size {
|
||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = uint32_t(m_swapchain.image_count()),
|
||||
};
|
||||
|
||||
VkDescriptorPoolCreateInfo pool_info {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.maxSets = uint32_t(m_swapchain.image_count()),
|
||||
.poolSizeCount = 1,
|
||||
.pPoolSizes = &pool_size,
|
||||
};
|
||||
if(vkCreateDescriptorPool(
|
||||
m_context.device(),
|
||||
&pool_info,
|
||||
nullptr,
|
||||
&m_descriptor_pool
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create descriptor pool");
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_descriptor_sets() {
|
||||
const std::vector<VkDescriptorSetLayout> layouts(
|
||||
m_swapchain.image_count(), m_descriptor_set_layout);
|
||||
|
||||
VkDescriptorSetAllocateInfo alloc_info {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = m_descriptor_pool,
|
||||
.descriptorSetCount = uint32_t(layouts.size()),
|
||||
.pSetLayouts = layouts.data(),
|
||||
};
|
||||
|
||||
m_descriptor_sets.resize(m_swapchain.image_count());
|
||||
if(vkAllocateDescriptorSets(
|
||||
m_context.device(),
|
||||
&alloc_info,
|
||||
m_descriptor_sets.data()
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to allocate descriptor sets");
|
||||
|
||||
for(size_t index = 0; index < m_swapchain.image_count(); index += 1) {
|
||||
VkDescriptorBufferInfo buffer_info {
|
||||
.buffer = m_uniform_buffers[index],
|
||||
.offset = 0,
|
||||
.range = sizeof(Uniforms),
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet write {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = m_descriptor_sets[index],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.pBufferInfo = &buffer_info,
|
||||
};
|
||||
vkUpdateDescriptorSets(
|
||||
m_context.device(),
|
||||
1, &write,
|
||||
0, nullptr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTutorial::create_command_buffers() {
|
||||
m_command_buffers.clear();
|
||||
for(size_t index = 0; index < m_framebuffers.size(); index += 1) {
|
||||
m_command_buffers.emplace_back(m_context, m_command_pool);
|
||||
VkCommandBuffer command_buffer = m_command_buffers[index];
|
||||
|
||||
VkCommandBufferBeginInfo begin_info {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = 0,
|
||||
.pInheritanceInfo = nullptr,
|
||||
};
|
||||
|
||||
logger.debug() << "record command buffer " << index << " (" << command_buffer << ")";
|
||||
|
||||
if(vkBeginCommandBuffer(
|
||||
command_buffer,
|
||||
&begin_info
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to begin command buffer");
|
||||
|
||||
VkClearValue clear_color = {
|
||||
.color = {
|
||||
.float32 = { 0.0f, 0.0f, 0.0f, 1.0f }
|
||||
}
|
||||
};
|
||||
|
||||
VkRenderPassBeginInfo pass_info {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = m_render_pass,
|
||||
.framebuffer = m_framebuffers[index],
|
||||
.renderArea = {
|
||||
.offset = { 0, 0 },
|
||||
.extent = m_swapchain.extent(),
|
||||
},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clear_color,
|
||||
};
|
||||
|
||||
vkCmdBeginRenderPass(
|
||||
command_buffer,
|
||||
&pass_info,
|
||||
VK_SUBPASS_CONTENTS_INLINE
|
||||
);
|
||||
|
||||
vkCmdBindPipeline(
|
||||
command_buffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_pipeline
|
||||
);
|
||||
|
||||
VkBuffer vertex_buffers[] = {
|
||||
m_vertex_buffer,
|
||||
};
|
||||
VkDeviceSize offsets[] = {
|
||||
0,
|
||||
};
|
||||
vkCmdBindVertexBuffers(
|
||||
command_buffer,
|
||||
0,
|
||||
1,
|
||||
vertex_buffers,
|
||||
offsets
|
||||
);
|
||||
|
||||
vkCmdBindIndexBuffer(
|
||||
command_buffer,
|
||||
m_index_buffer,
|
||||
0,
|
||||
VK_INDEX_TYPE_UINT32
|
||||
);
|
||||
|
||||
vkCmdBindDescriptorSets(
|
||||
command_buffer,
|
||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_pipeline_layout,
|
||||
0, 1, &m_descriptor_sets[index],
|
||||
0, nullptr
|
||||
);
|
||||
|
||||
vkCmdDrawIndexed(
|
||||
command_buffer,
|
||||
uint32_t(indices.size()),
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
);
|
||||
|
||||
vkCmdEndRenderPass(command_buffer);
|
||||
|
||||
if(vkEndCommandBuffer(command_buffer) != VK_SUCCESS)
|
||||
throw("failed to record command buffer");
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <vk/Swapchain.h>
|
||||
#include <vk/Buffer.h>
|
||||
#include <vk/Pipeline.h>
|
||||
#include <vk/Framebuffer.h>
|
||||
#include <vk/RenderPass.h>
|
||||
#include <vk/Semaphore.h>
|
||||
#include <vk/CommandBuffer.h>
|
||||
#include <vk/CommandPool.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <core/math.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
VkVertexInputBindingDescription vertex_binding_description();
|
||||
std::array<VkVertexInputAttributeDescription, 4> vertex_attributes_description();
|
||||
|
||||
|
||||
class VulkanTutorial {
|
||||
public:
|
||||
enum QueueIndex {
|
||||
GRAPHIC_QUEUE,
|
||||
};
|
||||
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
using Duration = Clock::duration;
|
||||
using SecondsD = std::chrono::duration<double>;
|
||||
|
||||
public:
|
||||
VulkanTutorial();
|
||||
VulkanTutorial(const VulkanTutorial&) = delete;
|
||||
~VulkanTutorial();
|
||||
|
||||
VulkanTutorial& operator=(const VulkanTutorial&) = delete;
|
||||
|
||||
void initialize(SDL_Window* window);
|
||||
void shutdown();
|
||||
|
||||
void set_camera(const Vector3& camera_position, const Vector3& camera_z, const Vector3& camera_y);
|
||||
|
||||
void draw_frame();
|
||||
void invalidate_swapchain();
|
||||
|
||||
private:
|
||||
void create_swapchain_objects(uint32_t image_count);
|
||||
void destroy_swapchain_objects();
|
||||
|
||||
void create_render_pass();
|
||||
void create_descriptor_set_layout();
|
||||
void create_graphic_pipeline();
|
||||
void create_framebuffers();
|
||||
void create_command_pool();
|
||||
void create_vertex_buffer();
|
||||
void create_index_buffer();
|
||||
void create_uniform_buffer();
|
||||
void create_descriptor_pool();
|
||||
void create_descriptor_sets();
|
||||
void create_command_buffers();
|
||||
|
||||
private:
|
||||
vk::Context m_context;
|
||||
vk::Swapchain m_swapchain;
|
||||
VkQueue m_graphic_queue = nullptr;
|
||||
VkQueue m_presentation_queue = nullptr;
|
||||
|
||||
vk::RenderPass m_render_pass;
|
||||
VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
|
||||
vk::Pipeline m_pipeline;
|
||||
std::vector<vk::Framebuffer> m_framebuffers;
|
||||
vk::CommandPool m_command_pool;
|
||||
vk::Buffer m_vertex_buffer;
|
||||
vk::Buffer m_index_buffer;
|
||||
std::vector<vk::Buffer> m_uniform_buffers;
|
||||
std::vector<VkDeviceSize> m_uniform_buffer_offsets;
|
||||
vk::MemoryBlock m_uniform_buffer_memory;
|
||||
VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE;
|
||||
std::vector<VkDescriptorSet> m_descriptor_sets;
|
||||
std::vector<vk::CommandBuffer> m_command_buffers;
|
||||
std::vector<vk::Semaphore> m_render_done;
|
||||
|
||||
Vector3 m_camera_position = Vector3(0.0f, 0.0f, -3.0f);
|
||||
Vector3 m_camera_z = Vector3(0.0f, 0.0f, 1.0f);
|
||||
Vector3 m_camera_y = Vector3(0.0f, 1.0f, 0.0f);
|
||||
|
||||
TimePoint m_last_frame_time;
|
||||
Duration m_time = Duration(0);
|
||||
};
|
||||
@@ -11,13 +11,13 @@ Eigen::Matrix4f projection_matrix(float x_min, float x_max, float y_min, float y
|
||||
auto const dy = y_max - y_min;
|
||||
auto const dz = z_max - z_min;
|
||||
|
||||
auto const pz = z_max * z_min;
|
||||
auto const sz = z_max / dz;
|
||||
|
||||
return (Eigen::Matrix4f() <<
|
||||
2.0f * z_min / dx, 0.0f, -cx / dx, 0.0f,
|
||||
0.0f, 2.0f * z_min / dy, -cy / dy, 0.0f,
|
||||
0.0f, 0.0f, cz / dz, -2.0f * pz / dz,
|
||||
0.0f, 0.0f, 1.0f, 0.0f
|
||||
2.0f * z_min / dx, 0.0f, -cx / dx, 0.0f,
|
||||
0.0f, 2.0f * z_min / dy, -cy / dy, 0.0f,
|
||||
0.0f, 0.0f, sz, (1 - sz) * z_max,
|
||||
0.0f, 0.0f, 1.0f, 0.0f
|
||||
).finished();
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,14 @@ Guard<T> make_guard(T&& teardown) {
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
T& recreate_object(T& object) {
|
||||
object.~T();
|
||||
new(&object) T;
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
template<typename... Args>
|
||||
std::string cat(Args&&... args) {
|
||||
std::ostringstream ostream;
|
||||
@@ -57,10 +65,10 @@ public:
|
||||
assert((size == 0 && m_data == nullptr) || (size != 0 && m_data != nullptr));
|
||||
}
|
||||
|
||||
Array(T* item)
|
||||
: m_size(1)
|
||||
, m_data(item)
|
||||
{}
|
||||
// Array(T* item)
|
||||
// : m_size(1)
|
||||
// , m_data(item)
|
||||
// {}
|
||||
|
||||
template<size_t Size>
|
||||
Array(T (&array)[Size])
|
||||
|
||||
@@ -21,7 +21,7 @@ Buffer::Buffer(
|
||||
VkDeviceSize size,
|
||||
VkBufferUsageFlags usage
|
||||
)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(*m_context);
|
||||
|
||||
@@ -51,29 +51,19 @@ Buffer::Buffer(
|
||||
allocate_and_bind_memory(memory_properties);
|
||||
}
|
||||
|
||||
Buffer::Buffer(Buffer&& other) noexcept
|
||||
: m_context(other.m_context)
|
||||
, m_buffer(other.m_buffer)
|
||||
, m_memory(std::move(other.m_memory))
|
||||
{
|
||||
other.m_context = nullptr;
|
||||
other.m_buffer = VK_NULL_HANDLE;
|
||||
Buffer::Buffer(Buffer&& other) noexcept {
|
||||
swap(other);
|
||||
}
|
||||
|
||||
Buffer::~Buffer() noexcept {
|
||||
if(!is_null()) {
|
||||
logger.warning() << "Buffer deleted before being destroyed";
|
||||
if(!is_null())
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Buffer& Buffer::operator=(Buffer&& other) noexcept {
|
||||
if(&other != this) {
|
||||
using std::swap;
|
||||
swap(m_context, other.m_context);
|
||||
swap(m_buffer, other.m_buffer);
|
||||
swap(m_memory, other.m_memory);
|
||||
}
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Memory.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <core/utils.h>
|
||||
|
||||
@@ -15,7 +16,7 @@
|
||||
namespace vk {
|
||||
|
||||
|
||||
class Buffer {
|
||||
class Buffer: public Wrapper {
|
||||
public:
|
||||
Buffer() noexcept;
|
||||
Buffer(
|
||||
@@ -29,11 +30,11 @@ public:
|
||||
VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags memory_properties
|
||||
);
|
||||
Buffer(const Buffer&) = delete;
|
||||
Buffer(const Buffer&) = default;
|
||||
Buffer(Buffer&& other) noexcept;
|
||||
~Buffer() noexcept;
|
||||
|
||||
Buffer& operator=(const Buffer&) = delete;
|
||||
Buffer& operator=(const Buffer&) = default;
|
||||
Buffer& operator=(Buffer&& other) noexcept;
|
||||
|
||||
inline explicit operator bool() const {
|
||||
@@ -44,14 +45,6 @@ public:
|
||||
return m_buffer == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline operator VkBuffer() const noexcept {
|
||||
return m_buffer;
|
||||
}
|
||||
@@ -76,10 +69,20 @@ public:
|
||||
|
||||
void upload(size_t size, void* src_buffer, uint32_t dst_queue_family);
|
||||
|
||||
inline void swap(Buffer& other) noexcept {
|
||||
using std::swap;
|
||||
Wrapper::swap(other);
|
||||
swap(m_buffer, other.m_buffer);
|
||||
swap(m_memory, other.m_memory);
|
||||
}
|
||||
|
||||
friend inline void swap(Buffer& buffer_0, Buffer& buffer_1) noexcept {
|
||||
buffer_0.swap(buffer_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkBuffer m_buffer = VK_NULL_HANDLE;
|
||||
MemoryBlock m_memory;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ CommandBuffer::CommandBuffer() noexcept {
|
||||
}
|
||||
|
||||
CommandBuffer::CommandBuffer(Context& context, VkCommandPool command_pool, VkCommandBufferLevel level)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
, m_command_pool(command_pool)
|
||||
{
|
||||
assert(m_context);
|
||||
@@ -35,7 +35,7 @@ CommandBuffer::CommandBuffer(Context& context, VkCommandPool command_pool, VkCom
|
||||
|
||||
CommandBuffer::CommandBuffer(CommandBuffer&& other) noexcept
|
||||
{
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
}
|
||||
|
||||
CommandBuffer::~CommandBuffer() noexcept {
|
||||
@@ -45,7 +45,7 @@ CommandBuffer::~CommandBuffer() noexcept {
|
||||
|
||||
|
||||
CommandBuffer& CommandBuffer::operator=(CommandBuffer&& other) noexcept {
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
@@ -9,15 +10,15 @@
|
||||
namespace vk {
|
||||
|
||||
|
||||
class CommandBuffer {
|
||||
class CommandBuffer: public Wrapper {
|
||||
public:
|
||||
CommandBuffer() noexcept;
|
||||
CommandBuffer(Context& context, VkCommandPool command_pool, VkCommandBufferLevel level=VK_COMMAND_BUFFER_LEVEL_PRIMARY);
|
||||
CommandBuffer(const CommandBuffer&) = delete;
|
||||
CommandBuffer(const CommandBuffer&) = default;
|
||||
CommandBuffer(CommandBuffer&& other) noexcept;
|
||||
~CommandBuffer() noexcept;
|
||||
|
||||
CommandBuffer& operator=(const CommandBuffer&) = delete;
|
||||
CommandBuffer& operator=(const CommandBuffer&) = default;
|
||||
CommandBuffer& operator=(CommandBuffer&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
@@ -28,14 +29,6 @@ public:
|
||||
return m_command_buffer == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline const VkCommandPool command_pool() const noexcept {
|
||||
return m_command_pool;
|
||||
}
|
||||
@@ -52,17 +45,20 @@ public:
|
||||
return m_command_buffer;
|
||||
}
|
||||
|
||||
friend inline void swap(CommandBuffer& command_buffer_0, CommandBuffer& command_buffer_1) noexcept {
|
||||
inline void swap(CommandBuffer& other) noexcept {
|
||||
using std::swap;
|
||||
swap(command_buffer_0.m_context, command_buffer_1.m_context);
|
||||
swap(command_buffer_0.m_command_pool, command_buffer_1.m_command_pool);
|
||||
swap(command_buffer_0.m_command_buffer, command_buffer_1.m_command_buffer);
|
||||
Wrapper::swap(other);
|
||||
swap(m_command_pool, other.m_command_pool);
|
||||
swap(m_command_buffer, other.m_command_buffer);
|
||||
}
|
||||
|
||||
friend inline void swap(CommandBuffer& command_buffer_0, CommandBuffer& command_buffer_1) noexcept {
|
||||
command_buffer_0.swap(command_buffer_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkCommandPool m_command_pool = VK_NULL_HANDLE;
|
||||
VkCommandBuffer m_command_buffer = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ CommandPool::CommandPool() noexcept {
|
||||
}
|
||||
|
||||
CommandPool::CommandPool(Context& context, uint32_t queue_family, VkCommandPoolCreateFlags flags)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
@@ -33,7 +33,7 @@ CommandPool::CommandPool(Context& context, uint32_t queue_family, VkCommandPoolC
|
||||
|
||||
CommandPool::CommandPool(CommandPool&& other) noexcept
|
||||
{
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
}
|
||||
|
||||
CommandPool::~CommandPool() noexcept {
|
||||
@@ -43,7 +43,7 @@ CommandPool::~CommandPool() noexcept {
|
||||
|
||||
|
||||
CommandPool& CommandPool::operator=(CommandPool&& other) noexcept {
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
@@ -9,15 +10,15 @@
|
||||
namespace vk {
|
||||
|
||||
|
||||
class CommandPool {
|
||||
class CommandPool: public Wrapper {
|
||||
public:
|
||||
CommandPool() noexcept;
|
||||
CommandPool(Context& context, uint32_t queue_family, VkCommandPoolCreateFlags flags=0);
|
||||
CommandPool(const CommandPool&) = delete;
|
||||
CommandPool(const CommandPool&) = default;
|
||||
CommandPool(CommandPool&& other) noexcept;
|
||||
~CommandPool() noexcept;
|
||||
|
||||
CommandPool& operator=(const CommandPool&) = delete;
|
||||
CommandPool& operator=(const CommandPool&) = default;
|
||||
CommandPool& operator=(CommandPool&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
@@ -28,14 +29,6 @@ public:
|
||||
return m_command_pool == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline operator VkCommandPool() noexcept {
|
||||
return m_command_pool;
|
||||
}
|
||||
@@ -44,16 +37,19 @@ public:
|
||||
return m_command_pool;
|
||||
}
|
||||
|
||||
friend inline void swap(CommandPool& command_pool_0, CommandPool& command_pool_1) noexcept {
|
||||
inline void swap(CommandPool& other) noexcept {
|
||||
using std::swap;
|
||||
swap(command_pool_0.m_context, command_pool_1.m_context);
|
||||
swap(command_pool_0.m_command_pool, command_pool_1.m_command_pool);
|
||||
Wrapper::swap(other);
|
||||
swap(m_command_pool, other.m_command_pool);
|
||||
}
|
||||
|
||||
friend inline void swap(CommandPool& command_pool_0, CommandPool& command_pool_1) noexcept {
|
||||
command_pool_0.swap(command_pool_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkCommandPool m_command_pool = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
@@ -562,7 +562,7 @@ void Context::create_instance(const ContextSettings& settings) {
|
||||
if(vkCreateInstance(&instance_info, nullptr, &m_instance) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create vulkan instance");
|
||||
|
||||
initialize_extension_functions();
|
||||
initialize_instance_extension_functions();
|
||||
|
||||
if(settings.debug()) {
|
||||
createDebugUtilsMessenger(
|
||||
@@ -733,7 +733,9 @@ void Context::create_device(const ContextSettings& settings) {
|
||||
.geometryShader = true,
|
||||
};
|
||||
|
||||
std::vector<const char*> extensions;
|
||||
std::vector<const char*> extensions {
|
||||
VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME,
|
||||
};
|
||||
if(m_window) {
|
||||
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
};
|
||||
@@ -756,6 +758,8 @@ void Context::create_device(const ContextSettings& settings) {
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create logical device");
|
||||
|
||||
initialize_device_extension_functions();
|
||||
|
||||
m_queues.resize(m_queue_families.size());
|
||||
for(size_t index = 0; index < m_queue_families.size(); ++index) {
|
||||
vkGetDeviceQueue(
|
||||
@@ -777,10 +781,10 @@ void Context::create_internal_objects() {
|
||||
m_transfer_fence = Fence(*this);
|
||||
}
|
||||
|
||||
void Context::initialize_extension_functions() {
|
||||
void Context::initialize_instance_extension_functions() {
|
||||
uint32_t errors_count = 0;
|
||||
|
||||
#define GET_PROC_ADDR(ptr_name, func_name) \
|
||||
#define GET_INSTANCE_PROC_ADDR(ptr_name, func_name) \
|
||||
ptr_name = (PFN_vk ## func_name)vkGetInstanceProcAddr( \
|
||||
m_instance, "vk" #func_name); \
|
||||
if(ptr_name == nullptr) \
|
||||
@@ -789,9 +793,27 @@ void Context::initialize_extension_functions() {
|
||||
errors_count += 1; \
|
||||
}
|
||||
|
||||
GET_PROC_ADDR(createDebugUtilsMessenger, CreateDebugUtilsMessengerEXT)
|
||||
GET_PROC_ADDR(destroyDebugUtilsMessenger, DestroyDebugUtilsMessengerEXT)
|
||||
GET_PROC_ADDR(setDebugUtilsObjectName, SetDebugUtilsObjectNameEXT)
|
||||
GET_INSTANCE_PROC_ADDR(createDebugUtilsMessenger, CreateDebugUtilsMessengerEXT)
|
||||
GET_INSTANCE_PROC_ADDR(destroyDebugUtilsMessenger, DestroyDebugUtilsMessengerEXT)
|
||||
GET_INSTANCE_PROC_ADDR(setDebugUtilsObjectName, SetDebugUtilsObjectNameEXT)
|
||||
|
||||
if(errors_count != 0)
|
||||
throw std::runtime_error("failed to load extensions");
|
||||
}
|
||||
|
||||
void Context::initialize_device_extension_functions() {
|
||||
uint32_t errors_count = 0;
|
||||
|
||||
#define GET_DEVICE_PROC_ADDR(ptr_name, func_name) \
|
||||
ptr_name = (PFN_vk ## func_name)vkGetDeviceProcAddr( \
|
||||
m_device, "vk" #func_name); \
|
||||
if(ptr_name == nullptr) \
|
||||
{ \
|
||||
logger.error() << "failed to load extension function 'vk" #func_name "'"; \
|
||||
errors_count += 1; \
|
||||
}
|
||||
|
||||
GET_DEVICE_PROC_ADDR(cmdDrawIndexedIndirectCount, CmdDrawIndexedIndirectCountKHR)
|
||||
|
||||
if(errors_count != 0)
|
||||
throw std::runtime_error("failed to load extensions");
|
||||
@@ -803,6 +825,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL Context::log_debug_message(
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* data,
|
||||
void* user_data
|
||||
) {
|
||||
if(data->messageIdNumber == 0) // Loader message
|
||||
return VK_FALSE;
|
||||
|
||||
{
|
||||
auto stream = [severity]() {
|
||||
switch(severity) {
|
||||
@@ -834,7 +859,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL Context::log_debug_message(
|
||||
}
|
||||
|
||||
if(severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
|
||||
&& data->messageIdNumber != 2094043421 // wrong swapchain extent
|
||||
&& (
|
||||
data->messageIdNumber != 2094043421 // wrong swapchain extent
|
||||
)
|
||||
)
|
||||
abort();
|
||||
|
||||
|
||||
@@ -154,6 +154,8 @@ public:
|
||||
PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessenger = nullptr;
|
||||
PFN_vkSetDebugUtilsObjectNameEXT setDebugUtilsObjectName = nullptr;
|
||||
|
||||
PFN_vkCmdDrawIndexedIndirectCountKHR cmdDrawIndexedIndirectCount = nullptr;
|
||||
|
||||
private:
|
||||
void create_instance(const ContextSettings& settings);
|
||||
void create_surface(const ContextSettings& settings);
|
||||
@@ -165,7 +167,8 @@ private:
|
||||
void create_device(const ContextSettings& settings);
|
||||
void create_internal_objects();
|
||||
|
||||
void initialize_extension_functions();
|
||||
void initialize_instance_extension_functions();
|
||||
void initialize_device_extension_functions();
|
||||
|
||||
static VKAPI_ATTR VkBool32 VKAPI_CALL log_debug_message(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
|
||||
74
src/vk/DescriptorPool.cpp
Normal file
74
src/vk/DescriptorPool.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
|
||||
#include <vk/DescriptorPool.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
DescriptorPool::DescriptorPool() noexcept {
|
||||
}
|
||||
|
||||
DescriptorPool::DescriptorPool(
|
||||
Context& context,
|
||||
uint32_t max_sets,
|
||||
Array<const VkDescriptorPoolSize> pool_sizes,
|
||||
VkDescriptorPoolCreateFlags flags
|
||||
)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
VkDescriptorPoolCreateInfo create_info {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.flags = flags,
|
||||
.maxSets = max_sets,
|
||||
.poolSizeCount = uint32_t(pool_sizes.size()),
|
||||
.pPoolSizes = pool_sizes.data(),
|
||||
};
|
||||
if(vkCreateDescriptorPool(
|
||||
context.device(),
|
||||
&create_info,
|
||||
nullptr,
|
||||
&m_descriptor_pool
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create descriptor pool");
|
||||
}
|
||||
|
||||
DescriptorPool::DescriptorPool(DescriptorPool&& other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
DescriptorPool::~DescriptorPool() noexcept {
|
||||
if(!is_null())
|
||||
destroy();
|
||||
}
|
||||
|
||||
|
||||
DescriptorPool& DescriptorPool::operator=(DescriptorPool&& other) noexcept {
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void DescriptorPool::destroy() noexcept {
|
||||
assert(!is_null());
|
||||
assert(m_context);
|
||||
|
||||
vkDestroyDescriptorPool(
|
||||
m_context->device(),
|
||||
m_descriptor_pool,
|
||||
nullptr
|
||||
);
|
||||
|
||||
m_descriptor_pool = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
64
src/vk/DescriptorPool.h
Normal file
64
src/vk/DescriptorPool.h
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <core/utils.h>
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
class DescriptorPool: public Wrapper {
|
||||
public:
|
||||
DescriptorPool() noexcept;
|
||||
DescriptorPool(
|
||||
Context& context,
|
||||
uint32_t max_sets,
|
||||
Array<const VkDescriptorPoolSize> pool_sizes,
|
||||
VkDescriptorPoolCreateFlags flags=0
|
||||
);
|
||||
DescriptorPool(const DescriptorPool&) = default;
|
||||
DescriptorPool(DescriptorPool&& other) noexcept;
|
||||
~DescriptorPool() noexcept;
|
||||
|
||||
DescriptorPool& operator=(const DescriptorPool&) = default;
|
||||
DescriptorPool& operator=(DescriptorPool&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
return !is_null();
|
||||
}
|
||||
|
||||
inline bool is_null() const noexcept {
|
||||
return m_descriptor_pool == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline operator VkDescriptorPool() noexcept {
|
||||
return m_descriptor_pool;
|
||||
}
|
||||
|
||||
inline VkDescriptorPool descriptor_pool() noexcept {
|
||||
return m_descriptor_pool;
|
||||
}
|
||||
|
||||
inline void swap(DescriptorPool& other) noexcept {
|
||||
using std::swap;
|
||||
Wrapper::swap(other);
|
||||
swap(m_descriptor_pool, other.m_descriptor_pool);
|
||||
}
|
||||
|
||||
friend inline void swap(DescriptorPool& descriptor_pool_0, DescriptorPool& descriptor_pool_1) noexcept {
|
||||
descriptor_pool_0.swap(descriptor_pool_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
76
src/vk/DescriptorSet.cpp
Normal file
76
src/vk/DescriptorSet.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
|
||||
#include <vk/DescriptorSet.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
DescriptorSet::DescriptorSet() noexcept {
|
||||
}
|
||||
|
||||
DescriptorSet::DescriptorSet(
|
||||
Context& context,
|
||||
VkDescriptorPool descriptor_pool,
|
||||
const VkDescriptorSetLayout& set_layout
|
||||
)
|
||||
: Wrapper(context, DontOwnUnderlyingObject)
|
||||
, m_descriptor_pool(descriptor_pool)
|
||||
{
|
||||
assert(m_descriptor_pool != VK_NULL_HANDLE);
|
||||
|
||||
VkDescriptorSetAllocateInfo create_info {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = m_descriptor_pool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &set_layout,
|
||||
};
|
||||
if(vkAllocateDescriptorSets(
|
||||
context.device(),
|
||||
&create_info,
|
||||
&m_descriptor_set
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create descriptor_set");
|
||||
}
|
||||
|
||||
DescriptorSet::DescriptorSet(DescriptorSet&& other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
DescriptorSet::~DescriptorSet() noexcept {
|
||||
if(!is_null())
|
||||
destroy();
|
||||
}
|
||||
|
||||
|
||||
DescriptorSet& DescriptorSet::operator=(DescriptorSet&& other) noexcept {
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void DescriptorSet::destroy() noexcept {
|
||||
assert(!is_null());
|
||||
assert(m_context);
|
||||
|
||||
if (!own_underlying_object())
|
||||
return;
|
||||
|
||||
vkFreeDescriptorSets(
|
||||
m_context->device(),
|
||||
m_descriptor_pool,
|
||||
1,
|
||||
&m_descriptor_set
|
||||
);
|
||||
|
||||
m_descriptor_set = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
71
src/vk/DescriptorSet.h
Normal file
71
src/vk/DescriptorSet.h
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
class DescriptorSet: public Wrapper {
|
||||
public:
|
||||
DescriptorSet() noexcept;
|
||||
DescriptorSet(
|
||||
Context& context,
|
||||
VkDescriptorPool descriptor_pool,
|
||||
const VkDescriptorSetLayout& set_layout
|
||||
);
|
||||
DescriptorSet(const DescriptorSet&) = default;
|
||||
DescriptorSet(DescriptorSet&& other) noexcept;
|
||||
~DescriptorSet() noexcept;
|
||||
|
||||
DescriptorSet& operator=(const DescriptorSet&) = default;
|
||||
DescriptorSet& operator=(DescriptorSet&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
return !is_null();
|
||||
}
|
||||
|
||||
inline bool is_null() const noexcept {
|
||||
return m_descriptor_set == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline const VkDescriptorPool descriptor_pool() const noexcept {
|
||||
return m_descriptor_pool;
|
||||
}
|
||||
|
||||
inline VkDescriptorPool descriptor_pool() noexcept {
|
||||
return m_descriptor_pool;
|
||||
}
|
||||
|
||||
inline operator VkDescriptorSet() noexcept {
|
||||
return m_descriptor_set;
|
||||
}
|
||||
|
||||
inline VkDescriptorSet descriptor_set() noexcept {
|
||||
return m_descriptor_set;
|
||||
}
|
||||
|
||||
inline void swap(DescriptorSet& other) noexcept {
|
||||
using std::swap;
|
||||
Wrapper::swap(other);
|
||||
swap(m_descriptor_pool, other.m_descriptor_pool);
|
||||
swap(m_descriptor_set, other.m_descriptor_set);
|
||||
}
|
||||
|
||||
friend inline void swap(DescriptorSet& descriptor_set_0, DescriptorSet& descriptor_set_1) noexcept {
|
||||
descriptor_set_0.swap(descriptor_set_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE;
|
||||
VkDescriptorSet m_descriptor_set = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
67
src/vk/DescriptorSetLayout.cpp
Normal file
67
src/vk/DescriptorSetLayout.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
|
||||
#include <vk/DescriptorSetLayout.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
DescriptorSetLayout::DescriptorSetLayout() noexcept {
|
||||
}
|
||||
|
||||
DescriptorSetLayout::DescriptorSetLayout(Context& context, Array<const VkDescriptorSetLayoutBinding> bindings)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo create_info {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = uint32_t(bindings.size()),
|
||||
.pBindings = bindings.data(),
|
||||
};
|
||||
if(vkCreateDescriptorSetLayout(
|
||||
context.device(),
|
||||
&create_info,
|
||||
nullptr,
|
||||
&m_descriptor_set_layout
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create descriptor set layout");
|
||||
}
|
||||
|
||||
DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout&& other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
DescriptorSetLayout::~DescriptorSetLayout() noexcept {
|
||||
if(!is_null())
|
||||
destroy();
|
||||
}
|
||||
|
||||
|
||||
DescriptorSetLayout& DescriptorSetLayout::operator=(DescriptorSetLayout&& other) noexcept {
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void DescriptorSetLayout::destroy() noexcept {
|
||||
assert(!is_null());
|
||||
assert(m_context);
|
||||
|
||||
vkDestroyDescriptorSetLayout(
|
||||
m_context->device(),
|
||||
m_descriptor_set_layout,
|
||||
nullptr
|
||||
);
|
||||
|
||||
m_descriptor_set_layout = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
59
src/vk/DescriptorSetLayout.h
Normal file
59
src/vk/DescriptorSetLayout.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <core/utils.h>
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
class DescriptorSetLayout: public Wrapper {
|
||||
public:
|
||||
DescriptorSetLayout() noexcept;
|
||||
DescriptorSetLayout(Context& context, Array<const VkDescriptorSetLayoutBinding> bindings);
|
||||
DescriptorSetLayout(const DescriptorSetLayout&) = default;
|
||||
DescriptorSetLayout(DescriptorSetLayout&& other) noexcept;
|
||||
~DescriptorSetLayout() noexcept;
|
||||
|
||||
DescriptorSetLayout& operator=(const DescriptorSetLayout&) = default;
|
||||
DescriptorSetLayout& operator=(DescriptorSetLayout&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
return !is_null();
|
||||
}
|
||||
|
||||
inline bool is_null() const noexcept {
|
||||
return m_descriptor_set_layout == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline operator VkDescriptorSetLayout() noexcept {
|
||||
return m_descriptor_set_layout;
|
||||
}
|
||||
|
||||
inline VkDescriptorSetLayout descriptor_set_layout() noexcept {
|
||||
return m_descriptor_set_layout;
|
||||
}
|
||||
|
||||
inline void swap(DescriptorSetLayout& other) noexcept {
|
||||
using std::swap;
|
||||
Wrapper::swap(other);
|
||||
swap(m_descriptor_set_layout, other.m_descriptor_set_layout);
|
||||
}
|
||||
|
||||
friend inline void swap(DescriptorSetLayout& descriptor_set_layout_0, DescriptorSetLayout& descriptor_set_layout_1) noexcept {
|
||||
descriptor_set_layout_0.swap(descriptor_set_layout_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@ Fence::Fence() noexcept {
|
||||
}
|
||||
|
||||
Fence::Fence(Context& context, VkFenceCreateFlags flags)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
@@ -32,7 +32,7 @@ Fence::Fence(Context& context, VkFenceCreateFlags flags)
|
||||
|
||||
Fence::Fence(Fence&& other) noexcept
|
||||
{
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
}
|
||||
|
||||
Fence::~Fence() noexcept {
|
||||
@@ -42,7 +42,7 @@ Fence::~Fence() noexcept {
|
||||
|
||||
|
||||
Fence& Fence::operator=(Fence&& other) noexcept {
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <core/ArrayView.h>
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
@@ -11,15 +12,15 @@
|
||||
namespace vk {
|
||||
|
||||
|
||||
class Fence {
|
||||
class Fence: public Wrapper {
|
||||
public:
|
||||
Fence() noexcept;
|
||||
Fence(Context& context, VkFenceCreateFlags flags=0);
|
||||
Fence(const Fence&) = delete;
|
||||
Fence(const Fence&) = default;
|
||||
Fence(Fence&& other) noexcept;
|
||||
~Fence() noexcept;
|
||||
|
||||
Fence& operator=(const Fence&) = delete;
|
||||
Fence& operator=(const Fence&) = default;
|
||||
Fence& operator=(Fence&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
@@ -30,14 +31,6 @@ public:
|
||||
return m_fence == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline operator VkFence() noexcept {
|
||||
return m_fence;
|
||||
}
|
||||
@@ -50,16 +43,19 @@ public:
|
||||
void reset();
|
||||
void wait(uint64_t timeout=UINT64_MAX) const;
|
||||
|
||||
friend inline void swap(Fence& fence_0, Fence& fence_1) noexcept {
|
||||
inline void swap(Fence& other) noexcept {
|
||||
using std::swap;
|
||||
swap(fence_0.m_context, fence_1.m_context);
|
||||
swap(fence_0.m_fence, fence_1.m_fence);
|
||||
Wrapper::swap(other);
|
||||
swap(m_fence, other.m_fence);
|
||||
}
|
||||
|
||||
friend inline void swap(Fence& fence_0, Fence& fence_1) noexcept {
|
||||
fence_0.swap(fence_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkFence m_fence = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Framebuffer::Framebuffer(
|
||||
Array<VkImageView> attachments,
|
||||
uint32_t width, uint32_t height, uint32_t layers
|
||||
)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
assert(render_pass);
|
||||
@@ -52,7 +52,7 @@ Framebuffer::Framebuffer(
|
||||
|
||||
Framebuffer::Framebuffer(Framebuffer&& other) noexcept
|
||||
{
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
}
|
||||
|
||||
Framebuffer::~Framebuffer() noexcept {
|
||||
@@ -62,7 +62,7 @@ Framebuffer::~Framebuffer() noexcept {
|
||||
|
||||
|
||||
Framebuffer& Framebuffer::operator=(Framebuffer&& other) noexcept {
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <core/utils.h>
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
@@ -11,7 +12,7 @@
|
||||
namespace vk {
|
||||
|
||||
|
||||
class Framebuffer {
|
||||
class Framebuffer: public Wrapper {
|
||||
public:
|
||||
Framebuffer() noexcept;
|
||||
Framebuffer(
|
||||
@@ -26,11 +27,11 @@ public:
|
||||
Array<VkImageView> attachments,
|
||||
VkExtent2D extent, uint32_t layers=1
|
||||
);
|
||||
Framebuffer(const Framebuffer&) = delete;
|
||||
Framebuffer(const Framebuffer&) = default;
|
||||
Framebuffer(Framebuffer&& other) noexcept;
|
||||
~Framebuffer() noexcept;
|
||||
|
||||
Framebuffer& operator=(const Framebuffer&) = delete;
|
||||
Framebuffer& operator=(const Framebuffer&) = default;
|
||||
Framebuffer& operator=(Framebuffer&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
@@ -41,14 +42,6 @@ public:
|
||||
return m_framebuffer == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline operator VkFramebuffer() noexcept {
|
||||
return m_framebuffer;
|
||||
}
|
||||
@@ -57,16 +50,19 @@ public:
|
||||
return m_framebuffer;
|
||||
}
|
||||
|
||||
friend inline void swap(Framebuffer& framebuffer_0, Framebuffer& framebuffer_1) noexcept {
|
||||
inline void swap(Framebuffer& other) noexcept {
|
||||
using std::swap;
|
||||
swap(framebuffer_0.m_context, framebuffer_1.m_context);
|
||||
swap(framebuffer_0.m_framebuffer, framebuffer_1.m_framebuffer);
|
||||
Wrapper::swap(other);
|
||||
swap(m_framebuffer, other.m_framebuffer);
|
||||
}
|
||||
|
||||
friend inline void swap(Framebuffer& framebuffer_0, Framebuffer& framebuffer_1) noexcept {
|
||||
framebuffer_0.swap(framebuffer_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkFramebuffer m_framebuffer = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
124
src/vk/Image.cpp
Normal file
124
src/vk/Image.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
|
||||
#include <vk/Image.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
Image::Image() noexcept {
|
||||
}
|
||||
|
||||
Image::Image(Context& context, VkImageCreateInfo create_info)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
if(vkCreateImage(
|
||||
context.device(),
|
||||
&create_info,
|
||||
nullptr,
|
||||
&m_image
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create image");
|
||||
}
|
||||
|
||||
Image::Image(Context& context, VkImageCreateInfo create_info, VkMemoryPropertyFlags memory_properties)
|
||||
: Image(context, create_info)
|
||||
{
|
||||
allocate_and_bind_memory(memory_properties);
|
||||
}
|
||||
|
||||
Image::Image(Image&& other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
Image::~Image() noexcept {
|
||||
if(!is_null())
|
||||
destroy();
|
||||
}
|
||||
|
||||
|
||||
Image& Image::operator=(Image&& other) noexcept {
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
VkMemoryRequirements Image::memory_requirements() const noexcept {
|
||||
assert(!is_null());
|
||||
assert(*m_context);
|
||||
|
||||
VkMemoryRequirements memory_requirements;
|
||||
vkGetImageMemoryRequirements(
|
||||
m_context->device(),
|
||||
m_image,
|
||||
&memory_requirements
|
||||
);
|
||||
|
||||
return memory_requirements;
|
||||
}
|
||||
|
||||
|
||||
void Image::bind_memory(const MemoryBlock& memory_block, VkDeviceSize offset) {
|
||||
assert(!is_null());
|
||||
assert(*m_context);
|
||||
assert(memory_block);
|
||||
|
||||
// m_memory = std::move(memory_block);
|
||||
if(vkBindImageMemory(
|
||||
m_context->device(),
|
||||
m_image,
|
||||
memory_block.device_memory(),
|
||||
memory_block.offset() + offset
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to bind image memory");
|
||||
}
|
||||
|
||||
void Image::bind_memory(MemoryBlock&& memory_block) {
|
||||
bind_memory(memory_block);
|
||||
m_memory = std::move(memory_block);
|
||||
}
|
||||
|
||||
void Image::allocate_and_bind_memory(VkMemoryPropertyFlags memory_properties) {
|
||||
assert(!is_null());
|
||||
assert(*m_context);
|
||||
|
||||
const auto memory_requirements = this->memory_requirements();
|
||||
m_memory = m_context->allocator().allocate(
|
||||
memory_requirements.size,
|
||||
memory_requirements.memoryTypeBits,
|
||||
memory_properties
|
||||
);
|
||||
|
||||
bind_memory(m_memory);
|
||||
}
|
||||
|
||||
|
||||
void Image::destroy() noexcept {
|
||||
assert(!is_null());
|
||||
assert(m_context);
|
||||
|
||||
if(m_memory) {
|
||||
m_memory.free();
|
||||
m_memory = MemoryBlock();
|
||||
}
|
||||
|
||||
vkDestroyImage(
|
||||
m_context->device(),
|
||||
m_image,
|
||||
nullptr
|
||||
);
|
||||
|
||||
m_context = nullptr;
|
||||
m_image = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
66
src/vk/Image.h
Normal file
66
src/vk/Image.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
#include <vk/Memory.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
class Image: public Wrapper {
|
||||
public:
|
||||
Image() noexcept;
|
||||
Image(Context& context, VkImageCreateInfo create_info);
|
||||
Image(Context& context, VkImageCreateInfo create_info, VkMemoryPropertyFlags memory_properties);
|
||||
Image(const Image&) = default;
|
||||
Image(Image&& other) noexcept;
|
||||
~Image() noexcept;
|
||||
|
||||
Image& operator=(const Image&) = default;
|
||||
Image& operator=(Image&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
return !is_null();
|
||||
}
|
||||
|
||||
inline bool is_null() const noexcept {
|
||||
return m_image == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline operator VkImage() noexcept {
|
||||
return m_image;
|
||||
}
|
||||
|
||||
inline VkImage image() noexcept {
|
||||
return m_image;
|
||||
}
|
||||
|
||||
VkMemoryRequirements memory_requirements() const noexcept;
|
||||
|
||||
void bind_memory(const MemoryBlock& memory_block, VkDeviceSize offset=0);
|
||||
void bind_memory(MemoryBlock&& memory_block);
|
||||
void allocate_and_bind_memory(VkMemoryPropertyFlags memory_properties);
|
||||
|
||||
inline void swap(Image& other) noexcept {
|
||||
using std::swap;
|
||||
Wrapper::swap(other);
|
||||
swap(m_image, other.m_image);
|
||||
}
|
||||
|
||||
friend inline void swap(Image& image_0, Image& image_1) noexcept {
|
||||
image_0.swap(image_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
VkImage m_image = VK_NULL_HANDLE;
|
||||
MemoryBlock m_memory;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
62
src/vk/ImageView.cpp
Normal file
62
src/vk/ImageView.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
|
||||
#include <vk/ImageView.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
ImageView::ImageView() noexcept {
|
||||
}
|
||||
|
||||
ImageView::ImageView(Context& context, const VkImageViewCreateInfo& create_info)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
if(vkCreateImageView(
|
||||
context.device(),
|
||||
&create_info,
|
||||
nullptr,
|
||||
&m_image_view
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create image view");
|
||||
}
|
||||
|
||||
ImageView::ImageView(ImageView&& other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
ImageView::~ImageView() noexcept {
|
||||
if(!is_null())
|
||||
destroy();
|
||||
}
|
||||
|
||||
|
||||
ImageView& ImageView::operator=(ImageView&& other) noexcept {
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void ImageView::destroy() noexcept {
|
||||
assert(!is_null());
|
||||
assert(m_context);
|
||||
|
||||
vkDestroyImageView(
|
||||
m_context->device(),
|
||||
m_image_view,
|
||||
nullptr
|
||||
);
|
||||
|
||||
m_image_view = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
57
src/vk/ImageView.h
Normal file
57
src/vk/ImageView.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
class ImageView: public Wrapper {
|
||||
public:
|
||||
ImageView() noexcept;
|
||||
ImageView(Context& context, const VkImageViewCreateInfo& create_info);
|
||||
ImageView(const ImageView&) = default;
|
||||
ImageView(ImageView&& other) noexcept;
|
||||
~ImageView() noexcept;
|
||||
|
||||
ImageView& operator=(const ImageView&) = default;
|
||||
ImageView& operator=(ImageView&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
return !is_null();
|
||||
}
|
||||
|
||||
inline bool is_null() const noexcept {
|
||||
return m_image_view == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline operator VkImageView() noexcept {
|
||||
return m_image_view;
|
||||
}
|
||||
|
||||
inline VkImageView image_view() noexcept {
|
||||
return m_image_view;
|
||||
}
|
||||
|
||||
inline void swap(ImageView& other) noexcept {
|
||||
using std::swap;
|
||||
Wrapper::swap(other);
|
||||
swap(m_image_view, other.m_image_view);
|
||||
}
|
||||
|
||||
friend inline void swap(ImageView& image_view_0, ImageView& image_view_1) noexcept {
|
||||
image_view_0.swap(image_view_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
VkImageView m_image_view = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -42,10 +42,8 @@ MemoryBlock::MemoryBlock(MemoryBlock&& other) noexcept
|
||||
}
|
||||
|
||||
MemoryBlock::~MemoryBlock() noexcept {
|
||||
if(!is_null()) {
|
||||
logger.warning() << "MemoryBlock deleted before being freed";
|
||||
if(!is_null())
|
||||
free();
|
||||
}
|
||||
}
|
||||
|
||||
MemoryBlock& MemoryBlock::operator=(MemoryBlock&& other) noexcept {
|
||||
@@ -82,11 +80,11 @@ void MemoryBlock::free() noexcept {
|
||||
}
|
||||
|
||||
|
||||
void* MemoryBlock::map(Context& context) {
|
||||
Byte* MemoryBlock::map(Context& context) {
|
||||
return map(context, 0, m_size);
|
||||
}
|
||||
|
||||
void* MemoryBlock::map(Context& context, VkDeviceSize offset, VkDeviceSize size) {
|
||||
Byte* MemoryBlock::map(Context& context, VkDeviceSize offset, VkDeviceSize size) {
|
||||
assert(!is_null());
|
||||
assert(context);
|
||||
assert(offset + size <= m_size);
|
||||
@@ -102,7 +100,7 @@ void* MemoryBlock::map(Context& context, VkDeviceSize offset, VkDeviceSize size)
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to map memory");
|
||||
|
||||
return ptr;
|
||||
return reinterpret_cast<Byte*>(ptr);
|
||||
}
|
||||
|
||||
void MemoryBlock::unmap(Context& context) noexcept {
|
||||
@@ -176,6 +174,8 @@ MemoryBlock MemoryPage::allocate(VkDeviceSize size) noexcept {
|
||||
if(block_it == m_blocks.end())
|
||||
return MemoryBlock();
|
||||
|
||||
const auto offset = block_it->offset;
|
||||
|
||||
block_it[0].is_free = false;
|
||||
if (block_it[0].offset != block_it[1].offset) {
|
||||
m_blocks.emplace(std::next(block_it), Block{
|
||||
@@ -183,7 +183,7 @@ MemoryBlock MemoryPage::allocate(VkDeviceSize size) noexcept {
|
||||
});
|
||||
}
|
||||
|
||||
return MemoryBlock(m_device_memory, size, block_it[0].offset, this, m_memory_type);
|
||||
return MemoryBlock(m_device_memory, size, offset, this, m_memory_type);
|
||||
}
|
||||
|
||||
void MemoryPage::p_free(MemoryBlock& block) noexcept {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
|
||||
#include <vk/forward.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
@@ -61,8 +63,8 @@ public:
|
||||
|
||||
void free() noexcept;
|
||||
|
||||
void* map(Context& context);
|
||||
void* map(Context& context, VkDeviceSize offset, VkDeviceSize size);
|
||||
Byte* map(Context& context);
|
||||
Byte* map(Context& context, VkDeviceSize offset, VkDeviceSize size);
|
||||
void unmap(Context& context) noexcept;
|
||||
|
||||
void flush(Context& context);
|
||||
|
||||
@@ -13,7 +13,7 @@ Pipeline::Pipeline() noexcept {
|
||||
}
|
||||
|
||||
Pipeline::Pipeline(Context& context, VkPipeline pipeline)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
, m_pipeline(pipeline)
|
||||
{
|
||||
assert(m_context);
|
||||
@@ -21,7 +21,7 @@ Pipeline::Pipeline(Context& context, VkPipeline pipeline)
|
||||
}
|
||||
|
||||
Pipeline::Pipeline(Context& context, VkGraphicsPipelineCreateInfo create_info)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
@@ -37,7 +37,7 @@ Pipeline::Pipeline(Context& context, VkGraphicsPipelineCreateInfo create_info)
|
||||
|
||||
Pipeline::Pipeline(Pipeline&& other) noexcept
|
||||
{
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
}
|
||||
|
||||
Pipeline::~Pipeline() noexcept {
|
||||
@@ -47,7 +47,7 @@ Pipeline::~Pipeline() noexcept {
|
||||
|
||||
|
||||
Pipeline& Pipeline::operator=(Pipeline&& other) noexcept {
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
@@ -9,16 +10,16 @@
|
||||
namespace vk {
|
||||
|
||||
|
||||
class Pipeline {
|
||||
class Pipeline: public Wrapper {
|
||||
public:
|
||||
Pipeline() noexcept;
|
||||
Pipeline(Context& context, VkPipeline pipeline);
|
||||
Pipeline(Context& context, VkGraphicsPipelineCreateInfo create_info);
|
||||
Pipeline(const Pipeline&) = delete;
|
||||
Pipeline(const Pipeline&) = default;
|
||||
Pipeline(Pipeline&& other) noexcept;
|
||||
~Pipeline() noexcept;
|
||||
|
||||
Pipeline& operator=(const Pipeline&) = delete;
|
||||
Pipeline& operator=(const Pipeline&) = default;
|
||||
Pipeline& operator=(Pipeline&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
@@ -29,14 +30,6 @@ public:
|
||||
return m_pipeline == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline operator VkPipeline() noexcept {
|
||||
return m_pipeline;
|
||||
}
|
||||
@@ -45,16 +38,19 @@ public:
|
||||
return m_pipeline;
|
||||
}
|
||||
|
||||
friend inline void swap(Pipeline& pipeline_0, Pipeline& pipeline_1) noexcept {
|
||||
inline void swap(Pipeline& other) noexcept {
|
||||
using std::swap;
|
||||
swap(pipeline_0.m_context, pipeline_1.m_context);
|
||||
swap(pipeline_0.m_pipeline, pipeline_1.m_pipeline);
|
||||
Wrapper::swap(other);
|
||||
swap(m_pipeline, other.m_pipeline);
|
||||
}
|
||||
|
||||
friend inline void swap(Pipeline& pipeline_0, Pipeline& pipeline_1) noexcept {
|
||||
pipeline_0.swap(pipeline_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
73
src/vk/PipelineLayout.cpp
Normal file
73
src/vk/PipelineLayout.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
|
||||
#include <vk/PipelineLayout.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
PipelineLayout::PipelineLayout() noexcept {
|
||||
}
|
||||
|
||||
PipelineLayout::PipelineLayout(
|
||||
Context& context,
|
||||
Array<const VkDescriptorSetLayout> set_layouts,
|
||||
Array<const VkPushConstantRange> push_constant_ranges
|
||||
)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
VkPipelineLayoutCreateInfo create_info {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = uint32_t(set_layouts.size()),
|
||||
.pSetLayouts = set_layouts.data(),
|
||||
.pushConstantRangeCount = uint32_t(push_constant_ranges.size()),
|
||||
.pPushConstantRanges = push_constant_ranges.data(),
|
||||
};
|
||||
if(vkCreatePipelineLayout(
|
||||
context.device(),
|
||||
&create_info,
|
||||
nullptr,
|
||||
&m_pipeline_layout
|
||||
) != VK_SUCCESS)
|
||||
throw std::runtime_error("failed to create pipeline layout");
|
||||
}
|
||||
|
||||
PipelineLayout::PipelineLayout(PipelineLayout&& other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
PipelineLayout::~PipelineLayout() noexcept {
|
||||
if(!is_null())
|
||||
destroy();
|
||||
}
|
||||
|
||||
|
||||
PipelineLayout& PipelineLayout::operator=(PipelineLayout&& other) noexcept {
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void PipelineLayout::destroy() noexcept {
|
||||
assert(!is_null());
|
||||
assert(m_context);
|
||||
|
||||
vkDestroyPipelineLayout(
|
||||
m_context->device(),
|
||||
m_pipeline_layout,
|
||||
nullptr
|
||||
);
|
||||
|
||||
m_pipeline_layout = nullptr;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
63
src/vk/PipelineLayout.h
Normal file
63
src/vk/PipelineLayout.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <core/utils.h>
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
class PipelineLayout: public Wrapper {
|
||||
public:
|
||||
PipelineLayout() noexcept;
|
||||
PipelineLayout(
|
||||
Context& context,
|
||||
Array<const VkDescriptorSetLayout> set_layouts,
|
||||
Array<const VkPushConstantRange> push_constant_ranges={}
|
||||
);
|
||||
PipelineLayout(const PipelineLayout&) = default;
|
||||
PipelineLayout(PipelineLayout&& other) noexcept;
|
||||
~PipelineLayout() noexcept;
|
||||
|
||||
PipelineLayout& operator=(const PipelineLayout&) = default;
|
||||
PipelineLayout& operator=(PipelineLayout&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
return !is_null();
|
||||
}
|
||||
|
||||
inline bool is_null() const noexcept {
|
||||
return m_pipeline_layout == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline operator VkPipelineLayout() noexcept {
|
||||
return m_pipeline_layout;
|
||||
}
|
||||
|
||||
inline VkPipelineLayout pipeline_layout() noexcept {
|
||||
return m_pipeline_layout;
|
||||
}
|
||||
|
||||
inline void swap(PipelineLayout& other) noexcept {
|
||||
using std::swap;
|
||||
Wrapper::swap(other);
|
||||
swap(m_pipeline_layout, other.m_pipeline_layout);
|
||||
}
|
||||
|
||||
friend inline void swap(PipelineLayout& pipeline_layout_0, PipelineLayout& pipeline_layout_1) noexcept {
|
||||
pipeline_layout_0.swap(pipeline_layout_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@ RenderPass::RenderPass() noexcept {
|
||||
}
|
||||
|
||||
RenderPass::RenderPass(Context& context, const VkRenderPassCreateInfo& create_info)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
@@ -28,7 +28,7 @@ RenderPass::RenderPass(Context& context, const VkRenderPassCreateInfo& create_in
|
||||
|
||||
RenderPass::RenderPass(RenderPass&& other) noexcept
|
||||
{
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
}
|
||||
|
||||
RenderPass::~RenderPass() noexcept {
|
||||
@@ -38,7 +38,7 @@ RenderPass::~RenderPass() noexcept {
|
||||
|
||||
|
||||
RenderPass& RenderPass::operator=(RenderPass&& other) noexcept {
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
@@ -9,15 +10,15 @@
|
||||
namespace vk {
|
||||
|
||||
|
||||
class RenderPass {
|
||||
class RenderPass: public Wrapper {
|
||||
public:
|
||||
RenderPass() noexcept;
|
||||
RenderPass(Context& context, const VkRenderPassCreateInfo& create_info);
|
||||
RenderPass(const RenderPass&) = delete;
|
||||
RenderPass(const RenderPass&) = default;
|
||||
RenderPass(RenderPass&& other) noexcept;
|
||||
~RenderPass() noexcept;
|
||||
|
||||
RenderPass& operator=(const RenderPass&) = delete;
|
||||
RenderPass& operator=(const RenderPass&) = default;
|
||||
RenderPass& operator=(RenderPass&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
@@ -36,16 +37,19 @@ public:
|
||||
return m_render_pass;
|
||||
}
|
||||
|
||||
friend inline void swap(RenderPass& render_pass_0, RenderPass& render_pass_1) noexcept {
|
||||
inline void swap(RenderPass& other) noexcept {
|
||||
using std::swap;
|
||||
swap(render_pass_0.m_context, render_pass_1.m_context);
|
||||
swap(render_pass_0.m_render_pass, render_pass_1.m_render_pass);
|
||||
Wrapper::swap(other);
|
||||
swap(m_render_pass, other.m_render_pass);
|
||||
}
|
||||
|
||||
friend inline void swap(RenderPass& render_pass_0, RenderPass& render_pass_1) noexcept {
|
||||
render_pass_0.swap(render_pass_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkRenderPass m_render_pass = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Semaphore::Semaphore() noexcept {
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(Context& context)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
@@ -31,7 +31,7 @@ Semaphore::Semaphore(Context& context)
|
||||
|
||||
Semaphore::Semaphore(Semaphore&& other) noexcept
|
||||
{
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore() noexcept {
|
||||
@@ -41,7 +41,7 @@ Semaphore::~Semaphore() noexcept {
|
||||
|
||||
|
||||
Semaphore& Semaphore::operator=(Semaphore&& other) noexcept {
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
@@ -9,15 +10,15 @@
|
||||
namespace vk {
|
||||
|
||||
|
||||
class Semaphore {
|
||||
class Semaphore: public Wrapper {
|
||||
public:
|
||||
Semaphore() noexcept;
|
||||
Semaphore(Context& context);
|
||||
Semaphore(const Semaphore&) = delete;
|
||||
Semaphore(const Semaphore&) = default;
|
||||
Semaphore(Semaphore&& other) noexcept;
|
||||
~Semaphore() noexcept;
|
||||
|
||||
Semaphore& operator=(const Semaphore&) = delete;
|
||||
Semaphore& operator=(const Semaphore&) = default;
|
||||
Semaphore& operator=(Semaphore&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
@@ -28,14 +29,6 @@ public:
|
||||
return m_semaphore == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline operator VkSemaphore() noexcept {
|
||||
return m_semaphore;
|
||||
}
|
||||
@@ -44,16 +37,19 @@ public:
|
||||
return m_semaphore;
|
||||
}
|
||||
|
||||
friend inline void swap(Semaphore& semaphore_0, Semaphore& semaphore_1) noexcept {
|
||||
inline void swap(Semaphore& other) noexcept {
|
||||
using std::swap;
|
||||
swap(semaphore_0.m_context, semaphore_1.m_context);
|
||||
swap(semaphore_0.m_semaphore, semaphore_1.m_semaphore);
|
||||
Wrapper::swap(other);
|
||||
swap(m_semaphore, other.m_semaphore);
|
||||
}
|
||||
|
||||
friend inline void swap(Semaphore& semaphore_0, Semaphore& semaphore_1) noexcept {
|
||||
semaphore_0.swap(semaphore_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkSemaphore m_semaphore = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ ShaderModule::ShaderModule() noexcept {
|
||||
}
|
||||
|
||||
ShaderModule::ShaderModule(Context& context, const std::vector<char>& code)
|
||||
: m_context(&context)
|
||||
: Wrapper(context)
|
||||
{
|
||||
assert(m_context);
|
||||
|
||||
@@ -37,7 +37,7 @@ ShaderModule::ShaderModule(Context& context, const char* path)
|
||||
|
||||
ShaderModule::ShaderModule(ShaderModule&& other) noexcept
|
||||
{
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
}
|
||||
|
||||
ShaderModule::~ShaderModule() noexcept {
|
||||
@@ -47,7 +47,7 @@ ShaderModule::~ShaderModule() noexcept {
|
||||
|
||||
|
||||
ShaderModule& ShaderModule::operator=(ShaderModule&& other) noexcept {
|
||||
swap(*this, other);
|
||||
swap(other);
|
||||
if(other)
|
||||
other.destroy();
|
||||
return *this;
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <core/utils.h>
|
||||
|
||||
#include <vk/forward.h>
|
||||
#include <vk/Wrapper.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
class ShaderModule {
|
||||
class ShaderModule: public Wrapper {
|
||||
public:
|
||||
ShaderModule() noexcept;
|
||||
ShaderModule(Context& context, const std::vector<char>& code);
|
||||
ShaderModule(Context& context, const char* path);
|
||||
ShaderModule(const ShaderModule&) = delete;
|
||||
ShaderModule(const ShaderModule&) = default;
|
||||
ShaderModule(ShaderModule&& other) noexcept;
|
||||
~ShaderModule() noexcept;
|
||||
|
||||
ShaderModule& operator=(const ShaderModule&) = delete;
|
||||
ShaderModule& operator=(const ShaderModule&) = default;
|
||||
ShaderModule& operator=(ShaderModule&& other) noexcept;
|
||||
|
||||
explicit inline operator bool() const noexcept {
|
||||
@@ -31,14 +32,6 @@ public:
|
||||
return m_shader_module == VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline operator VkShaderModule() noexcept {
|
||||
return m_shader_module;
|
||||
}
|
||||
@@ -47,16 +40,19 @@ public:
|
||||
return m_shader_module;
|
||||
}
|
||||
|
||||
friend inline void swap(ShaderModule& shader_module_0, ShaderModule& shader_module_1) noexcept {
|
||||
inline void swap(ShaderModule& other) noexcept {
|
||||
using std::swap;
|
||||
swap(shader_module_0.m_context, shader_module_1.m_context);
|
||||
swap(shader_module_0.m_shader_module, shader_module_1.m_shader_module);
|
||||
Wrapper::swap(other);
|
||||
swap(m_shader_module, other.m_shader_module);
|
||||
}
|
||||
|
||||
friend inline void swap(ShaderModule& shader_module_0, ShaderModule& shader_module_1) noexcept {
|
||||
shader_module_0.swap(shader_module_1);
|
||||
}
|
||||
|
||||
void destroy() noexcept;
|
||||
|
||||
private:
|
||||
Context* m_context = nullptr;
|
||||
VkShaderModule m_shader_module = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
|
||||
@@ -137,9 +137,9 @@ void Swapchain::begin_frame() {
|
||||
}
|
||||
}
|
||||
|
||||
logger.info() << "begin frame " << m_frame_index
|
||||
<< ": image " << m_current_image_index
|
||||
<< ", frame: " << m_frame_resources_index;
|
||||
// logger.info() << "begin frame " << m_frame_index
|
||||
// << ": image " << m_current_image_index
|
||||
// << ", frame: " << m_frame_resources_index;
|
||||
|
||||
auto& image_resources = m_image_resources[m_current_image_index];
|
||||
|
||||
@@ -293,7 +293,7 @@ void Swapchain::create() {
|
||||
|
||||
image_resources.image = swapchain_images[index];
|
||||
|
||||
VkImageViewCreateInfo view_info {
|
||||
image_resources.view = ImageView(*m_context, VkImageViewCreateInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.image = image_resources.image,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
@@ -311,16 +311,7 @@ void Swapchain::create() {
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
|
||||
if(vkCreateImageView(
|
||||
m_context->device(),
|
||||
&view_info,
|
||||
nullptr,
|
||||
&image_resources.view
|
||||
) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create swapchain image view");
|
||||
}
|
||||
});
|
||||
|
||||
image_resources.render_done = Fence(*m_context, VK_FENCE_CREATE_SIGNALED_BIT);
|
||||
}
|
||||
@@ -333,7 +324,7 @@ void Swapchain::create() {
|
||||
}
|
||||
|
||||
for(auto& callback: m_creation_callbacks)
|
||||
callback(image_count);
|
||||
callback();
|
||||
}
|
||||
|
||||
void Swapchain::destroy() {
|
||||
@@ -341,11 +332,7 @@ void Swapchain::destroy() {
|
||||
callback();
|
||||
|
||||
m_frame_resources.clear();
|
||||
|
||||
for(auto& image_resources: m_image_resources) {
|
||||
image_resources.render_done.destroy();
|
||||
m_context->destroy_image_view(image_resources.view);
|
||||
}
|
||||
m_image_resources.clear();
|
||||
|
||||
m_context->destroy_swapchain(m_swapchain);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <vk/Semaphore.h>
|
||||
#include <vk/Context.h>
|
||||
#include <vk/Semaphore.h>
|
||||
#include <vk/ImageView.h>
|
||||
|
||||
#include <core/utils.h>
|
||||
|
||||
@@ -41,7 +42,7 @@ public:
|
||||
static constexpr uint32_t CURRENT_IMAGE_INDEX = UINT32_MAX;
|
||||
static constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
|
||||
|
||||
using CreationCallback = std::function<void(uint32_t)>;
|
||||
using CreationCallback = std::function<void()>;
|
||||
using DestructionCallback = std::function<void()>;
|
||||
|
||||
public:
|
||||
@@ -76,7 +77,7 @@ public:
|
||||
private:
|
||||
struct ImageResources {
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkImageView view = VK_NULL_HANDLE;
|
||||
ImageView view;
|
||||
Fence render_done;
|
||||
};
|
||||
|
||||
|
||||
49
src/vk/Wrapper.cpp
Normal file
49
src/vk/Wrapper.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
|
||||
#include <vk/Wrapper.h>
|
||||
#include <vk/Context.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
Wrapper::Wrapper() noexcept {
|
||||
}
|
||||
|
||||
Wrapper::Wrapper(Context& context, Flags flags) noexcept
|
||||
: m_context(&context)
|
||||
, m_flags(flags)
|
||||
{
|
||||
assert(m_context);
|
||||
}
|
||||
|
||||
Wrapper::Wrapper(const Wrapper& other) noexcept
|
||||
: m_context(other.m_context)
|
||||
{}
|
||||
|
||||
Wrapper::Wrapper(Wrapper&& other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
Wrapper::~Wrapper() noexcept {
|
||||
}
|
||||
|
||||
|
||||
Wrapper& Wrapper::operator=(const Wrapper& other) noexcept {
|
||||
if (&other != this) {
|
||||
m_context = other.m_context;
|
||||
m_flags = other.m_flags & ~OwnUnderlyingObject;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Wrapper& Wrapper::operator=(Wrapper&& other) noexcept {
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
65
src/vk/Wrapper.h
Normal file
65
src/vk/Wrapper.h
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2022 Simon Boyé
|
||||
#pragma once
|
||||
|
||||
#include <vk/forward.h>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
||||
class Wrapper {
|
||||
public:
|
||||
enum Flag {
|
||||
DontOwnUnderlyingObject = 0x00,
|
||||
OwnUnderlyingObject = 0x01,
|
||||
};
|
||||
using Flags = unsigned;
|
||||
|
||||
public:
|
||||
Wrapper() noexcept;
|
||||
Wrapper(Context& context, Flags flags=OwnUnderlyingObject) noexcept;
|
||||
Wrapper(const Wrapper& other) noexcept;
|
||||
Wrapper(Wrapper&& other) noexcept;
|
||||
~Wrapper() noexcept;
|
||||
|
||||
Wrapper& operator=(const Wrapper& other) noexcept;
|
||||
Wrapper& operator=(Wrapper&& other) noexcept;
|
||||
|
||||
inline const Context* context() const noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline Context* context() noexcept {
|
||||
return m_context;
|
||||
}
|
||||
|
||||
inline bool own_underlying_object() const noexcept {
|
||||
return (m_flags & OwnUnderlyingObject) != 0;
|
||||
}
|
||||
|
||||
inline void set_own_underlying_object(bool own_underlying_object) noexcept {
|
||||
if(own_underlying_object)
|
||||
m_flags |= OwnUnderlyingObject;
|
||||
else
|
||||
m_flags &= ~OwnUnderlyingObject;
|
||||
}
|
||||
|
||||
inline void swap(Wrapper& other) noexcept {
|
||||
using std::swap;
|
||||
swap(m_context, other.m_context);
|
||||
swap(m_flags, other.m_flags);
|
||||
}
|
||||
|
||||
friend inline void swap(Wrapper& wrapper_0, Wrapper& wrapper_1) noexcept {
|
||||
wrapper_0.swap(wrapper_1);
|
||||
}
|
||||
|
||||
protected:
|
||||
Context* m_context = nullptr;
|
||||
Flags m_flags = 0;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user