Compare commits
23 Commits
33531a6eec
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7213c85fdf | |||
| 9d4b85c940 | |||
| 3ce7962d4c | |||
| 5b363a8c57 | |||
| b8a15406da | |||
| 46b2698abb | |||
| 8358558ddb | |||
| f15ade11b7 | |||
| b18f3719ed | |||
| b96155f1b8 | |||
| ad5e382d3a | |||
| db9fdf5a7f | |||
| b4cb6ebbbc | |||
| c73d773845 | |||
| 63a87edc29 | |||
| d06458454d | |||
| f03a0bd98b | |||
| 14caf36c53 | |||
| 5b9be907ba | |||
| f19e59f819 | |||
| 906290edfa | |||
| 6f4a8461a9 | |||
| 803a3cf87e |
@@ -1,3 +1,5 @@
|
|||||||
|
# Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.0.0)
|
cmake_minimum_required(VERSION 3.0.0)
|
||||||
project(vk_expe VERSION 0.1.0)
|
project(vk_expe VERSION 0.1.0)
|
||||||
|
|
||||||
@@ -30,18 +32,41 @@ function(add_shaders TARGET)
|
|||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
add_executable(vk_expe
|
add_executable(vk_expe
|
||||||
src/Vulkan/Context.cpp
|
src/core/math.cpp
|
||||||
src/Vulkan/Swapchain.cpp
|
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/main.cpp
|
src/main.cpp
|
||||||
src/utils.cpp
|
src/Camera.cpp
|
||||||
src/Logger.cpp
|
src/Simplex.cpp
|
||||||
src/Planet.cpp
|
src/Planet.cpp
|
||||||
src/VkExpe.cpp
|
src/VkExpe.cpp
|
||||||
src/VulkanTutorial.cpp
|
src/Renderer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_shaders(vk_expe
|
add_shaders(vk_expe
|
||||||
shaders/shader.vert
|
shaders/shader.vert
|
||||||
|
shaders/shader.geom
|
||||||
shaders/shader.frag
|
shaders/shader.frag
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,29 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(location = 0) in vec3 fragColor;
|
layout(location = 1) in vec3 in_normal;
|
||||||
|
layout(location = 2) in vec3 in_color;
|
||||||
|
layout(location = 3) in vec3 in_edge_dist;
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
outColor = vec4(fragColor, 1.0);
|
vec3 edge_dist = vec3(0.75) - in_edge_dist;
|
||||||
|
float edge = clamp(
|
||||||
|
max(edge_dist[0], max(edge_dist[1], edge_dist[2])),
|
||||||
|
0.0, 1.0
|
||||||
|
);
|
||||||
|
|
||||||
|
vec3 edge_color = vec3(1.0);
|
||||||
|
vec3 light_dir = normalize(vec3(-0.2, -0.1, -1.0));
|
||||||
|
|
||||||
|
vec3 color = in_color;
|
||||||
|
|
||||||
|
// vec3 normal = normalize(in_normal);
|
||||||
|
// color *= clamp(
|
||||||
|
// dot(normal, light_dir),
|
||||||
|
// 0.0, 1.0
|
||||||
|
// );
|
||||||
|
|
||||||
|
out_color = vec4(mix(color, edge_color, edge), 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
50
shaders/shader.geom
Normal file
50
shaders/shader.geom
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(triangles) in;
|
||||||
|
layout(triangle_strip, max_vertices=3) out;
|
||||||
|
|
||||||
|
layout(binding = 0) uniform Uniforms {
|
||||||
|
mat4 scene_from_model;
|
||||||
|
mat4 projection_from_scene;
|
||||||
|
vec2 half_screen_size;
|
||||||
|
float lod;
|
||||||
|
} uniforms;
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 in_position[3];
|
||||||
|
layout(location = 1) in vec3 in_normal[3];
|
||||||
|
layout(location = 2) in vec3 in_color[3];
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 out_position;
|
||||||
|
layout(location = 1) out vec3 out_normal;
|
||||||
|
layout(location = 2) out vec3 out_color;
|
||||||
|
layout(location = 3) noperspective out vec3 out_edge_dist;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 positions[3] = {
|
||||||
|
uniforms.half_screen_size * gl_in[0].gl_Position.xy / gl_in[0].gl_Position.w,
|
||||||
|
uniforms.half_screen_size * gl_in[1].gl_Position.xy / gl_in[1].gl_Position.w,
|
||||||
|
uniforms.half_screen_size * gl_in[2].gl_Position.xy / gl_in[2].gl_Position.w,
|
||||||
|
};
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i += 1) {
|
||||||
|
gl_Position = gl_in[i].gl_Position;
|
||||||
|
out_position = in_position[i];
|
||||||
|
out_normal = in_normal[i];
|
||||||
|
out_color = in_color[i];
|
||||||
|
out_edge_dist = vec3(0.0);
|
||||||
|
|
||||||
|
vec2 p0 = positions[(i + 0) % 3];
|
||||||
|
vec2 p1 = positions[(i + 1) % 3];
|
||||||
|
vec2 p2 = positions[(i + 2) % 3];
|
||||||
|
|
||||||
|
// vec2 v21 = p2 - p1;
|
||||||
|
// out_edge_dist[i] = (p1.x * v21.x + p1.y * v21.y) / dot(v21, v21);
|
||||||
|
|
||||||
|
vec2 v0 = p0 - p2;
|
||||||
|
vec2 v1 = p1 - p2;
|
||||||
|
out_edge_dist[i] = abs(determinant(mat2(v0, v1))) / length(v1);
|
||||||
|
|
||||||
|
EmitVertex();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,35 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(binding = 0) uniform Uniforms {
|
layout(binding = 0) uniform Uniforms {
|
||||||
mat4 scene_from_model;
|
mat4 scene_from_model;
|
||||||
mat4 projection_from_scene;
|
mat4 projection_from_scene;
|
||||||
|
vec2 screen_size;
|
||||||
|
float lod;
|
||||||
} uniforms;
|
} uniforms;
|
||||||
|
|
||||||
layout(location = 0) in vec3 inPosition;
|
layout(location = 0) in vec3 in_position;
|
||||||
layout(location = 1) in vec3 inColor;
|
layout(location = 1) in vec3 in_position2;
|
||||||
|
layout(location = 2) in vec3 in_normal;
|
||||||
|
layout(location = 3) in vec3 in_color;
|
||||||
|
|
||||||
layout(location = 0) out vec3 fragColor;
|
layout(location = 0) out vec4 out_position;
|
||||||
|
layout(location = 1) out vec3 out_normal;
|
||||||
|
layout(location = 2) out vec3 out_color;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position =
|
// 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.projection_from_scene *
|
||||||
uniforms.scene_from_model *
|
uniforms.scene_from_model *
|
||||||
vec4(inPosition, 1.0);
|
vec4(position, 1.0);
|
||||||
fragColor = inColor;
|
out_normal = in_normal;
|
||||||
|
out_color = in_color;
|
||||||
|
|
||||||
|
gl_Position = out_position;
|
||||||
}
|
}
|
||||||
|
|||||||
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;
|
||||||
|
};
|
||||||
264
src/Planet.cpp
264
src/Planet.cpp
@@ -1,16 +1,13 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
#include <Planet.h>
|
#include <Planet.h>
|
||||||
#include <Logger.h>
|
|
||||||
|
#include <core/Logger.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
Vector3 lerp(Real factor, const Vector3& v0, const Vector3& v1) {
|
|
||||||
return (Real(1) - factor) * v0 + factor * v1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Cell::Cell(
|
Cell::Cell(
|
||||||
const Vector3& corner00,
|
const Vector3& corner00,
|
||||||
const Vector3& corner01,
|
const Vector3& corner01,
|
||||||
@@ -18,13 +15,13 @@ Cell::Cell(
|
|||||||
const Vector3& corner11
|
const Vector3& corner11
|
||||||
)
|
)
|
||||||
: m_corners{ corner00, corner01, corner10, corner11 }
|
: m_corners{ corner00, corner01, corner10, corner11 }
|
||||||
, m_heightFactor(heightFactor(m_corners))
|
, m_height_factor(height_factor(m_corners))
|
||||||
, m_cells{ nullptr, nullptr, nullptr, nullptr }
|
, m_cells{ nullptr, nullptr, nullptr, nullptr }
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Cell& Cell::cell(uint32_t cellIndex) {
|
Cell& Cell::cell(uint32_t cell_index) {
|
||||||
assert(cellIndex < CellCount);
|
assert(cell_index < CellCount);
|
||||||
auto& cell = m_cells[cellIndex];
|
auto& cell = m_cells[cell_index];
|
||||||
if(!cell)
|
if(!cell)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
@@ -32,119 +29,204 @@ Cell& Cell::cell(uint32_t cellIndex) {
|
|||||||
return *cell;
|
return *cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::writeMesh(
|
size_t Cell::vertex_count(uint32_t subdiv_count) {
|
||||||
Vertex*& vertices, const Vertex* verticesEnd,
|
auto const s2 = 1 << subdiv_count;
|
||||||
uint32_t*& indices, const uint32_t* indicesEnd,
|
return s2 * s2 + 2 * s2 + 1;
|
||||||
uint32_t subdivCount
|
}
|
||||||
|
|
||||||
|
size_t Cell::triangle_count(uint32_t subdiv_count) {
|
||||||
|
auto const s2 = 1 << subdiv_count;
|
||||||
|
return 2 * s2 * s2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cell::build_mesh(
|
||||||
|
Vector3AV positions, Vector3AV positions2, Vector3AV normals, TriangleAV triangles,
|
||||||
|
uint32_t subdiv_count, Index index_offset
|
||||||
) const {
|
) const {
|
||||||
auto const sideEdgeCount = (1u << subdivCount);
|
auto const side_edge_count = (1u << subdiv_count);
|
||||||
auto const sideVertCount = sideEdgeCount + 1;
|
auto const side_vert_count = side_edge_count + 1;
|
||||||
|
|
||||||
auto const quadCount = sideEdgeCount * sideEdgeCount;
|
auto const quad_count = side_edge_count * side_edge_count;
|
||||||
auto const triCount = 2 * quadCount;
|
auto const tri_count = 2 * quad_count;
|
||||||
auto const vertCount = sideVertCount * sideVertCount;
|
auto const vert_count = side_vert_count * side_vert_count;
|
||||||
|
|
||||||
assert(vertices + vertCount <= verticesEnd);
|
assert(positions.size() == vert_count);
|
||||||
assert(indices + (3 * triCount) <= indicesEnd);
|
assert(triangles.size() == tri_count);
|
||||||
|
|
||||||
auto const index = [sideVertCount](uint32_t x, uint32_t y) -> uint32_t {
|
auto const index = [side_vert_count](uint32_t x, uint32_t y) -> uint32_t {
|
||||||
return x + y * sideVertCount;
|
return x + y * side_vert_count;
|
||||||
};
|
};
|
||||||
auto const vertex = [vertices, &index](uint32_t x, uint32_t y) -> Vertex& {
|
auto const vertex = [&normals, &index](uint32_t x, uint32_t y) -> Vector3& {
|
||||||
return vertices[index(x, y)];
|
return normals[index(x, y)];
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector3 c00(0.0f, 0.0f, 1.0f);
|
vertex( 0, 0) = m_corners[0];
|
||||||
Vector3 c01(1.0f, 0.0f, 1.0f);
|
vertex(side_edge_count, 0) = m_corners[1];
|
||||||
Vector3 c10(0.0f, 1.0f, 1.0f);
|
vertex( 0, side_edge_count) = m_corners[2];
|
||||||
Vector3 c11(1.0f, 1.0f, 1.0f);
|
vertex(side_edge_count, side_edge_count) = m_corners[3];
|
||||||
|
|
||||||
auto const color = [&c00, &c01, &c10, &c11, sideEdgeCount](uint32_t x, uint32_t y) -> Vector3 {
|
|
||||||
const auto c0 = lerp(Real(x) / Real(sideEdgeCount), c00, c01);
|
|
||||||
const auto c1 = lerp(Real(x) / Real(sideEdgeCount), c10, c11);
|
|
||||||
return lerp(Real(y) / Real(sideEdgeCount), c0, c1);
|
|
||||||
};
|
|
||||||
|
|
||||||
auto const setVertex = [&vertex, &color](uint32_t x, uint32_t y, const Vector3& pos) {
|
|
||||||
vertex(x, y) = Vertex {
|
|
||||||
pos,
|
|
||||||
color(x, y)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
setVertex( 0, 0, m_corners[0]);
|
|
||||||
setVertex(sideEdgeCount, 0, m_corners[1]);
|
|
||||||
setVertex( 0, sideEdgeCount, m_corners[2]);
|
|
||||||
setVertex(sideEdgeCount, sideEdgeCount, m_corners[3]);
|
|
||||||
|
|
||||||
struct QuadCell {
|
struct QuadCell {
|
||||||
uint32_t x0, x1, y0, y1;
|
uint32_t x0, x1, y0, y1;
|
||||||
};
|
};
|
||||||
std::vector<QuadCell> stack;
|
std::vector<QuadCell> stack;
|
||||||
stack.reserve(quadCount / 4);
|
stack.reserve(quad_count / 4);
|
||||||
if(subdivCount > 0) {
|
if(subdiv_count > 0) {
|
||||||
stack.emplace_back(QuadCell {
|
stack.emplace_back(QuadCell {
|
||||||
0, sideEdgeCount,
|
0, side_edge_count,
|
||||||
0, sideEdgeCount,
|
0, side_edge_count,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!stack.empty()) {
|
while(!stack.empty()) {
|
||||||
const auto quadCell = stack.back();
|
const auto quad_cell = stack.back();
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
|
|
||||||
auto const mx = (quadCell.x0 + quadCell.x1) / 2;
|
auto const mx = (quad_cell.x0 + quad_cell.x1) / 2;
|
||||||
auto const my = (quadCell.y0 + quadCell.y1) / 2;
|
auto const my = (quad_cell.y0 + quad_cell.y1) / 2;
|
||||||
|
|
||||||
auto const& v00 = vertex(quadCell.x0, quadCell.y0).position;
|
auto const& v00 = vertex(quad_cell.x0, quad_cell.y0);
|
||||||
auto const& v01 = vertex(quadCell.x1, quadCell.y0).position;
|
auto const& v01 = vertex(quad_cell.x1, quad_cell.y0);
|
||||||
auto const& v10 = vertex(quadCell.x0, quadCell.y1).position;
|
auto const& v10 = vertex(quad_cell.x0, quad_cell.y1);
|
||||||
auto const& v11 = vertex(quadCell.x1, quadCell.y1).position;
|
auto const& v11 = vertex(quad_cell.x1, quad_cell.y1);
|
||||||
|
|
||||||
setVertex( mx, quadCell.y0,
|
vertex( mx, quad_cell.y0) =
|
||||||
(v00 + v01).normalized()
|
(v00 + v01).normalized();
|
||||||
);
|
vertex(quad_cell.x0, my) =
|
||||||
setVertex(quadCell.x0, my,
|
(v00 + v10).normalized();
|
||||||
(v00 + v10).normalized()
|
vertex(mx, my) =
|
||||||
);
|
(v00 + v01 + v10 + v11).normalized();
|
||||||
setVertex(mx, my,
|
vertex(quad_cell.x1, my) =
|
||||||
(v00 + v01 + v10 + v11).normalized()
|
(v01 + v11).normalized();
|
||||||
);
|
vertex( mx, quad_cell.y1) =
|
||||||
setVertex(quadCell.x1, my,
|
(v10 + v11).normalized();
|
||||||
(v01 + v11).normalized()
|
|
||||||
);
|
|
||||||
setVertex( mx, quadCell.y1,
|
|
||||||
(v10 + v11).normalized()
|
|
||||||
);
|
|
||||||
|
|
||||||
if(quadCell.x0 + 1 < mx) {
|
if(quad_cell.x0 + 1 < mx) {
|
||||||
stack.emplace_back(QuadCell { mx, quadCell.x1, my, quadCell.y1 });
|
stack.emplace_back(QuadCell { mx, quad_cell.x1, my, quad_cell.y1 });
|
||||||
stack.emplace_back(QuadCell { quadCell.x0, mx, my, quadCell.y1 });
|
stack.emplace_back(QuadCell { quad_cell.x0, mx, my, quad_cell.y1 });
|
||||||
stack.emplace_back(QuadCell { mx, quadCell.x1, quadCell.y0, my });
|
stack.emplace_back(QuadCell { mx, quad_cell.x1, quad_cell.y0, my });
|
||||||
stack.emplace_back(QuadCell { quadCell.x0, mx, quadCell.y0, my });
|
stack.emplace_back(QuadCell { quad_cell.x0, mx, quad_cell.y0, my });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vertices += vertCount;
|
|
||||||
|
|
||||||
for(uint32_t y = 0; y < sideEdgeCount; y += 1) {
|
for (Index index = 0; index < normals.size(); index += 1) {
|
||||||
for(uint32_t x = 0; x < sideEdgeCount; x += 1) {
|
positions[index] = normals[index];
|
||||||
const auto i00 = index(x, y);
|
}
|
||||||
|
|
||||||
|
int x_offset = (
|
||||||
|
m_corners[0][0] < m_corners[1][0] ||
|
||||||
|
m_corners[0][1] < m_corners[1][1] ||
|
||||||
|
m_corners[0][2] < m_corners[1][2]
|
||||||
|
)? -1: 1;
|
||||||
|
int y_offset = (
|
||||||
|
m_corners[0][0] < m_corners[2][0] ||
|
||||||
|
m_corners[0][1] < m_corners[2][1] ||
|
||||||
|
m_corners[0][2] < m_corners[2][2]
|
||||||
|
)? -1: 1;
|
||||||
|
for(Index y = 0; y < side_vert_count; y += 1) {
|
||||||
|
for(Index x = 0; x < side_vert_count; x += 1) {
|
||||||
|
Index x2 = x + (x & 0x01) * x_offset;
|
||||||
|
Index y2 = y + (y & 0x01) * y_offset;
|
||||||
|
|
||||||
|
positions2[index(x, y)] = positions[index(x2, y2)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto triangleIndex = 0;
|
||||||
|
bool flip = false; // (x_offset > 0) ^ (y_offset > 0);
|
||||||
|
for(Index y = 0; y < side_edge_count; y += 1) {
|
||||||
|
for(Index x = 0; x < side_edge_count; x += 1) {
|
||||||
|
const auto i00 = index_offset + index(x, y);
|
||||||
const auto i01 = i00 + 1;
|
const auto i01 = i00 + 1;
|
||||||
const auto i10 = i00 + sideVertCount;
|
const auto i10 = i00 + side_vert_count;
|
||||||
const auto i11 = i10 + 1;
|
const auto i11 = i10 + 1;
|
||||||
|
|
||||||
*(indices++) = i00;
|
if (flip) {
|
||||||
*(indices++) = i01;
|
triangles[triangleIndex++] = Triangle { i00, i01, i11 };
|
||||||
*(indices++) = i10;
|
triangles[triangleIndex++] = Triangle { i00, i11, i10 };
|
||||||
|
}
|
||||||
*(indices++) = i10;
|
else {
|
||||||
*(indices++) = i01;
|
triangles[triangleIndex++] = Triangle { i00, i01, i10 };
|
||||||
*(indices++) = i11;
|
triangles[triangleIndex++] = Triangle { i10, i01, i11 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Real Cell::heightFactor(Vector3 corners[CornerCount]) {
|
Real Cell::height_factor(Vector3 corners[CornerCount]) {
|
||||||
Vector3 c = (corners[0] + corners[1] + corners[2] + corners[3]).normalized();
|
Vector3 c = (corners[0] + corners[1] + corners[2] + corners[3]).normalized();
|
||||||
return Real(1) / corners[0].dot(c);
|
return Real(1) / corners[0].dot(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Planet::Planet()
|
||||||
|
: m_cells {
|
||||||
|
{
|
||||||
|
Vector3(-1.0, -1.0, -1.0).normalized(),
|
||||||
|
Vector3( 1.0, -1.0, -1.0).normalized(),
|
||||||
|
Vector3(-1.0, 1.0, -1.0).normalized(),
|
||||||
|
Vector3( 1.0, 1.0, -1.0).normalized(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector3( 1.0, -1.0, -1.0).normalized(),
|
||||||
|
Vector3( 1.0, -1.0, 1.0).normalized(),
|
||||||
|
Vector3( 1.0, 1.0, -1.0).normalized(),
|
||||||
|
Vector3( 1.0, 1.0, 1.0).normalized(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector3( 1.0, -1.0, 1.0).normalized(),
|
||||||
|
Vector3(-1.0, -1.0, 1.0).normalized(),
|
||||||
|
Vector3( 1.0, 1.0, 1.0).normalized(),
|
||||||
|
Vector3(-1.0, 1.0, 1.0).normalized(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector3(-1.0, -1.0, 1.0).normalized(),
|
||||||
|
Vector3(-1.0, -1.0, -1.0).normalized(),
|
||||||
|
Vector3(-1.0, 1.0, 1.0).normalized(),
|
||||||
|
Vector3(-1.0, 1.0, -1.0).normalized(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector3(-1.0, -1.0, 1.0).normalized(),
|
||||||
|
Vector3( 1.0, -1.0, 1.0).normalized(),
|
||||||
|
Vector3(-1.0, -1.0, -1.0).normalized(),
|
||||||
|
Vector3( 1.0, -1.0, -1.0).normalized(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Vector3(-1.0, 1.0, -1.0).normalized(),
|
||||||
|
Vector3( 1.0, 1.0, -1.0).normalized(),
|
||||||
|
Vector3(-1.0, 1.0, 1.0).normalized(),
|
||||||
|
Vector3( 1.0, 1.0, 1.0).normalized(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Planet::build_mesh(
|
||||||
|
Vector3AV positions, Vector3AV positions2, Vector3AV normals, TriangleAV triangles,
|
||||||
|
uint32_t subdiv_count, Index index_offset
|
||||||
|
) const {
|
||||||
|
auto const vertex_count = Cell::vertex_count(subdiv_count);
|
||||||
|
auto const index_count = Cell::triangle_count(subdiv_count);
|
||||||
|
|
||||||
|
for(Index cell_index = 0; cell_index < CellCount; cell_index += 1) {
|
||||||
|
auto sub_positions = positions.slice(
|
||||||
|
cell_index * vertex_count, vertex_count
|
||||||
|
);
|
||||||
|
auto sub_positions2 = positions2.slice(
|
||||||
|
cell_index * vertex_count, vertex_count
|
||||||
|
);
|
||||||
|
auto sub_normals = normals.slice(
|
||||||
|
cell_index * vertex_count, vertex_count
|
||||||
|
);
|
||||||
|
auto sub_triangles = triangles.slice(
|
||||||
|
cell_index * index_count, index_count
|
||||||
|
);
|
||||||
|
cell(cell_index).build_mesh(
|
||||||
|
sub_positions,
|
||||||
|
sub_positions2,
|
||||||
|
sub_normals,
|
||||||
|
sub_triangles,
|
||||||
|
subdiv_count,
|
||||||
|
index_offset + cell_index * vertex_count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
46
src/Planet.h
46
src/Planet.h
@@ -1,26 +1,25 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Eigen/Dense>
|
#include <core/math.h>
|
||||||
|
#include <core/ArrayView.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
using Real = float;
|
|
||||||
using Vector3 = Eigen::Matrix<Real, 3, 1>;
|
|
||||||
|
|
||||||
class Cell;
|
class Cell;
|
||||||
using CellUP = std::unique_ptr<Cell>;
|
using CellUP = std::unique_ptr<Cell>;
|
||||||
|
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
Vector3 position;
|
Vector3 position;
|
||||||
|
Vector3 position2;
|
||||||
|
Vector3 normal;
|
||||||
Vector3 color;
|
Vector3 color;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Vector3 lerp(Real factor, const Vector3& v0, const Vector3& v1);
|
|
||||||
|
|
||||||
|
|
||||||
class Cell {
|
class Cell {
|
||||||
public:
|
public:
|
||||||
static constexpr uint32_t CornerCount = 4;
|
static constexpr uint32_t CornerCount = 4;
|
||||||
@@ -40,20 +39,22 @@ public:
|
|||||||
Cell& operator=(const Cell&) = delete;
|
Cell& operator=(const Cell&) = delete;
|
||||||
Cell& operator=(Cell&&) = default;
|
Cell& operator=(Cell&&) = default;
|
||||||
|
|
||||||
Cell& cell(uint32_t cellIndex);
|
Cell& cell(uint32_t cell_index);
|
||||||
|
|
||||||
void writeMesh(
|
static size_t vertex_count(uint32_t subdiv_count);
|
||||||
Vertex*& vertices, const Vertex* verticesEnd,
|
static size_t triangle_count(uint32_t subdiv_count);
|
||||||
uint32_t*& indices, const uint32_t* indicesEnd,
|
|
||||||
uint32_t subdivCount = 4
|
void build_mesh(
|
||||||
|
Vector3AV positions, Vector3AV positions2, Vector3AV normals, TriangleAV triangles,
|
||||||
|
uint32_t subdiv_count=4, Index index_offset=0
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Real heightFactor(Vector3 corners[CornerCount]);
|
static Real height_factor(Vector3 corners[CornerCount]);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector3 m_corners[CornerCount];
|
Vector3 m_corners[CornerCount];
|
||||||
Real m_heightFactor;
|
Real m_height_factor;
|
||||||
CellUP m_cells[CellCount];
|
CellUP m_cells[CellCount];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,8 +72,21 @@ public:
|
|||||||
Planet& operator=(const Planet&) = delete;
|
Planet& operator=(const Planet&) = delete;
|
||||||
Planet& operator=(Planet&&) = default;
|
Planet& operator=(Planet&&) = default;
|
||||||
|
|
||||||
Cell& cell(uint32_t cellIndex);
|
inline const Cell& cell(uint32_t cell_index) const {
|
||||||
|
assert(cell_index < CellCount);
|
||||||
|
return m_cells[cell_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Cell& cell(uint32_t cell_index) {
|
||||||
|
assert(cell_index < CellCount);
|
||||||
|
return m_cells[cell_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void build_mesh(
|
||||||
|
Vector3AV positions, Vector3AV positions2, Vector3AV normals, TriangleAV triangles,
|
||||||
|
uint32_t subdiv_count=4, Index index_offset=0
|
||||||
|
) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CellUP m_cells[CellCount];
|
Cell m_cells[CellCount];
|
||||||
};
|
};
|
||||||
|
|||||||
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;
|
||||||
|
};
|
||||||
142
src/Simplex.cpp
Normal file
142
src/Simplex.cpp
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <Simplex.h>
|
||||||
|
|
||||||
|
|
||||||
|
Vector3 polyhedron_support(Vector3AV polyhedron, const Vector3& direction) {
|
||||||
|
assert(polyhedron.size() > 0);
|
||||||
|
|
||||||
|
Index best = 0;
|
||||||
|
Real best_dot = polyhedron[0].dot(direction);
|
||||||
|
|
||||||
|
for (Index index = 1; index < polyhedron.size(); index += 1) {
|
||||||
|
Real dot = polyhedron[index].dot(direction);
|
||||||
|
if (dot > best_dot) {
|
||||||
|
best = index;
|
||||||
|
best_dot = dot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return polyhedron[best];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Simplex::Simplex() = default;
|
||||||
|
Simplex::Simplex(const Vector3& point)
|
||||||
|
: m_points{ point, Vector3{}, Vector3{}, Vector3{} }
|
||||||
|
, m_point_count(1)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
Gjk::Gjk(Vector3AV polyhedron0, Vector3AV polyhedron1, const Vector3& direction)
|
||||||
|
: m_polyhedron0(polyhedron0)
|
||||||
|
, m_polyhedron1(polyhedron1)
|
||||||
|
, m_direction(direction)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Gjk::~Gjk() = default;
|
||||||
|
|
||||||
|
const Vector3& Gjk::direction() const {
|
||||||
|
return m_direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Gjk::operator()() {
|
||||||
|
while (true) {
|
||||||
|
m_simplex.push(support(m_direction));
|
||||||
|
|
||||||
|
if (m_simplex.last().dot(m_direction) < Real(0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (nextSimplex())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Gjk::nextSimplex() {
|
||||||
|
switch(m_simplex.point_count()) {
|
||||||
|
case 1: return nextFromPoint();
|
||||||
|
case 2: return nextFromLine();
|
||||||
|
case 3: return nextFromTriangle();
|
||||||
|
case 4: return nextFromTetrahedron();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Gjk::nextFromPoint() {
|
||||||
|
m_direction = -m_simplex.last();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Gjk::nextFromLine() {
|
||||||
|
Vector3 v01 = m_simplex.point(1) - m_simplex.point(0);
|
||||||
|
Vector3 v1o = -m_simplex.point(1);
|
||||||
|
|
||||||
|
m_direction = v01.cross(v1o).cross(v01);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Gjk::nextFromTriangle() {
|
||||||
|
Vector3 v20 = m_simplex.point(0) - m_simplex.point(2);
|
||||||
|
Vector3 v21 = m_simplex.point(1) - m_simplex.point(2);
|
||||||
|
Vector3 v2o = -m_simplex.point(2);
|
||||||
|
|
||||||
|
Vector3 n = v20.cross(v21);
|
||||||
|
Vector3 n0 = n.cross(v21);
|
||||||
|
Vector3 n1 = v20.cross(n);
|
||||||
|
|
||||||
|
if(n0.dot(v2o) > Real(0)) {
|
||||||
|
m_simplex.pop(0);
|
||||||
|
m_direction = n0;
|
||||||
|
}
|
||||||
|
else if(n1.dot(v2o) > Real(0)) {
|
||||||
|
m_simplex.pop(1);
|
||||||
|
m_direction = n1;
|
||||||
|
}
|
||||||
|
else if(n.dot(v2o) > Real(0)) {
|
||||||
|
m_direction = n;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_simplex.swap(0, 1);
|
||||||
|
m_direction = -n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Gjk::nextFromTetrahedron() {
|
||||||
|
Vector3 v30 = m_simplex.point(0) - m_simplex.point(3);
|
||||||
|
Vector3 v31 = m_simplex.point(1) - m_simplex.point(3);
|
||||||
|
Vector3 v32 = m_simplex.point(2) - m_simplex.point(3);
|
||||||
|
Vector3 v3o = -m_simplex.point(3);
|
||||||
|
|
||||||
|
Vector3 n[3] = {
|
||||||
|
v31.cross(v32),
|
||||||
|
v32.cross(v30),
|
||||||
|
v30.cross(v31),
|
||||||
|
};
|
||||||
|
|
||||||
|
Real d[3] = {
|
||||||
|
n[0].dot(v3o),
|
||||||
|
n[1].dot(v3o),
|
||||||
|
n[2].dot(v3o),
|
||||||
|
};
|
||||||
|
|
||||||
|
Index best = (d[0] > d[1])? ((d[0] > d[2])? 0: 2):
|
||||||
|
((d[1] > d[2])? 1: 2);
|
||||||
|
|
||||||
|
if(d[best] <= Real(0))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
m_simplex.pop(best);
|
||||||
|
m_direction = n[best];
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 Gjk::support(const Vector3& direction) const
|
||||||
|
{
|
||||||
|
return polyhedron_support(m_polyhedron0, direction)
|
||||||
|
- polyhedron_support(m_polyhedron1, -direction);
|
||||||
|
}
|
||||||
77
src/Simplex.h
Normal file
77
src/Simplex.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <core/math.h>
|
||||||
|
#include <core/ArrayView.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Simplex {
|
||||||
|
public:
|
||||||
|
Simplex();
|
||||||
|
Simplex(const Vector3& point);
|
||||||
|
|
||||||
|
inline Index point_count() const {
|
||||||
|
return m_point_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const Vector3& point(Index index) const {
|
||||||
|
assert(index < m_point_count);
|
||||||
|
return m_points[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const Vector3& last() const {
|
||||||
|
assert(m_point_count > 0);
|
||||||
|
return point(m_point_count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void push(const Vector3& point) {
|
||||||
|
assert(m_point_count < MaxPointCount);
|
||||||
|
m_points[m_point_count] = point;
|
||||||
|
m_point_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void pop(Index index) {
|
||||||
|
assert(index < m_point_count);
|
||||||
|
for(Index i = index; i + 1 < m_point_count; i += 1)
|
||||||
|
m_points[i] = m_points[i + 1];
|
||||||
|
m_point_count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(Index index0, Index index1) {
|
||||||
|
using std::swap;
|
||||||
|
swap(m_points[index0], m_points[index1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr Index MaxPointCount = 4;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector3 m_points[MaxPointCount];
|
||||||
|
Index m_point_count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Gjk {
|
||||||
|
public:
|
||||||
|
Gjk(Vector3AV polyhedron0, Vector3AV polyhedron1, const Vector3& direction=Vector3::UnitX());
|
||||||
|
~Gjk();
|
||||||
|
|
||||||
|
const Vector3& direction() const;
|
||||||
|
|
||||||
|
bool operator()();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool nextSimplex();
|
||||||
|
bool nextFromPoint();
|
||||||
|
bool nextFromLine();
|
||||||
|
bool nextFromTriangle();
|
||||||
|
bool nextFromTetrahedron();
|
||||||
|
|
||||||
|
Vector3 support(const Vector3& direction) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector3AV m_polyhedron0;
|
||||||
|
Vector3AV m_polyhedron1;
|
||||||
|
Simplex m_simplex;
|
||||||
|
Vector3 m_direction;
|
||||||
|
};
|
||||||
111
src/VkExpe.cpp
111
src/VkExpe.cpp
@@ -1,10 +1,12 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#include <VkExpe.h>
|
#include <VkExpe.h>
|
||||||
|
|
||||||
#include <utils.h>
|
#include <core/utils.h>
|
||||||
#include <Logger.h>
|
#include <core/Logger.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
|
||||||
void SdlWindowDeleter::operator()(SDL_Window* window) const {
|
void SdlWindowDeleter::operator()(SDL_Window* window) const {
|
||||||
@@ -13,6 +15,12 @@ void SdlWindowDeleter::operator()(SDL_Window* window) const {
|
|||||||
|
|
||||||
|
|
||||||
VkExpe::VkExpe(int argc, char** argv) {
|
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() {
|
VkExpe::~VkExpe() {
|
||||||
@@ -25,7 +33,7 @@ void VkExpe::initialize() {
|
|||||||
auto const window = SDL_CreateWindow(
|
auto const window = SDL_CreateWindow(
|
||||||
"vk_expe",
|
"vk_expe",
|
||||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
800, 600,
|
1920, 1080,
|
||||||
SDL_WINDOW_VULKAN
|
SDL_WINDOW_VULKAN
|
||||||
| SDL_WINDOW_ALLOW_HIGHDPI
|
| SDL_WINDOW_ALLOW_HIGHDPI
|
||||||
| SDL_WINDOW_SHOWN
|
| SDL_WINDOW_SHOWN
|
||||||
@@ -41,7 +49,7 @@ void VkExpe::initialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VkExpe::shutdown() {
|
void VkExpe::shutdown() {
|
||||||
m_vulkan.shutdown();
|
recreate_object(m_vulkan);
|
||||||
|
|
||||||
m_window.reset();
|
m_window.reset();
|
||||||
|
|
||||||
@@ -60,9 +68,24 @@ void VkExpe::run() {
|
|||||||
m_running = false;
|
m_running = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
SDL_Event event;
|
auto last_time = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
// SDL_Event event;
|
||||||
|
std::vector<SDL_Event> events;
|
||||||
|
events.reserve(128);
|
||||||
while(m_running) {
|
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) {
|
switch(event.type) {
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
m_running = false;
|
m_running = false;
|
||||||
@@ -72,17 +95,91 @@ void VkExpe::run() {
|
|||||||
m_running = false;
|
m_running = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SDL_MOUSEMOTION: {
|
||||||
|
m_mouse_offset += Vector2(
|
||||||
|
event.motion.xrel,
|
||||||
|
event.motion.yrel
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SDL_WINDOWEVENT: {
|
case SDL_WINDOWEVENT: {
|
||||||
switch(event.window.event) {
|
switch(event.window.event) {
|
||||||
case SDL_WINDOWEVENT_RESIZED: {
|
case SDL_WINDOWEVENT_RESIZED: {
|
||||||
m_vulkan.invalidate_swapchain();
|
m_vulkan.invalidate_swapchain();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SDL_WINDOWEVENT_FOCUS_GAINED: {
|
||||||
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SDL_WINDOWEVENT_FOCUS_LOST: {
|
||||||
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto new_time = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto elapsed = new_time - last_time;
|
||||||
|
last_time = new_time;
|
||||||
|
|
||||||
|
update(std::chrono::duration<double>(elapsed).count());
|
||||||
|
|
||||||
|
m_vulkan.set_camera(m_camera);
|
||||||
m_vulkan.draw_frame();
|
m_vulkan.draw_frame();
|
||||||
// m_running = false;
|
|
||||||
|
m_mouse_offset = Vector2::Zero();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VkExpe::update(double elapsed) {
|
||||||
|
int key_count = 0;
|
||||||
|
const auto keys = SDL_GetKeyboardState(&key_count);
|
||||||
|
|
||||||
|
const auto test_key = [key_count, keys](int scan_code) {
|
||||||
|
return scan_code < key_count && keys[scan_code];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!m_mouse_offset.isZero()) {
|
||||||
|
const Real x_sensi = 0.001;
|
||||||
|
const Real y_sensi = -0.001;
|
||||||
|
|
||||||
|
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] * camera_y +
|
||||||
|
y_sensi * m_mouse_offset[1] * camera_x;
|
||||||
|
Real rot_norm = axis.norm();
|
||||||
|
AngleAxis rot(rot_norm, axis / rot_norm);
|
||||||
|
m_camera.set_down(rot * camera_y);
|
||||||
|
m_camera.set_direction(rot * camera_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 walk_direction = Vector3::Zero();
|
||||||
|
if(test_key(SDL_SCANCODE_W))
|
||||||
|
walk_direction += Vector3::UnitZ();
|
||||||
|
if(test_key(SDL_SCANCODE_S))
|
||||||
|
walk_direction -= Vector3::UnitZ();
|
||||||
|
if(test_key(SDL_SCANCODE_A))
|
||||||
|
walk_direction -= Vector3::UnitX();
|
||||||
|
if(test_key(SDL_SCANCODE_D))
|
||||||
|
walk_direction += Vector3::UnitX();
|
||||||
|
if(test_key(SDL_SCANCODE_SPACE))
|
||||||
|
walk_direction -= Vector3::UnitY();
|
||||||
|
if(test_key(SDL_SCANCODE_LCTRL))
|
||||||
|
walk_direction += Vector3::UnitY();
|
||||||
|
|
||||||
|
if(!walk_direction.isZero()) {
|
||||||
|
walk_direction.normalize();
|
||||||
|
const Real base_velocity = 1;
|
||||||
|
|
||||||
|
const Matrix3 basis = m_camera.basis();
|
||||||
|
m_camera.set_position(
|
||||||
|
m_camera.position() + elapsed * base_velocity * (basis * walk_direction)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/VkExpe.h
15
src/VkExpe.h
@@ -1,6 +1,10 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <VulkanTutorial.h>
|
#include <Renderer.h>
|
||||||
|
#include <Camera.h>
|
||||||
|
|
||||||
|
#include <core/math.h>
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
@@ -26,8 +30,15 @@ public:
|
|||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
void update(double elapsed);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VulkanTutorial m_vulkan;
|
Renderer m_vulkan;
|
||||||
|
|
||||||
|
Camera m_camera;
|
||||||
|
|
||||||
WindowUP m_window;
|
WindowUP m_window;
|
||||||
bool m_running = false;
|
bool m_running = false;
|
||||||
|
|
||||||
|
Vector2 m_mouse_offset = Vector2::Zero();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,844 +0,0 @@
|
|||||||
#include <VulkanTutorial.h>
|
|
||||||
|
|
||||||
#include <utils.h>
|
|
||||||
#include <Logger.h>
|
|
||||||
#include <Planet.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, 2> vertex_attributes_description() {
|
|
||||||
return {
|
|
||||||
VkVertexInputAttributeDescription {
|
|
||||||
.location = 0,
|
|
||||||
.binding = 0,
|
|
||||||
.format = VK_FORMAT_R32G32B32_SFLOAT,
|
|
||||||
.offset = offsetof(Vertex, position),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.location = 1,
|
|
||||||
.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;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
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 subdivCount = 2;
|
|
||||||
auto const sideEdgeCount = (1u << subdivCount);
|
|
||||||
auto const sideVertCount = sideEdgeCount + 1;
|
|
||||||
auto const quadCount = sideEdgeCount * sideEdgeCount;
|
|
||||||
auto const indexCount = 6 * quadCount;
|
|
||||||
auto const vertCount = sideVertCount * sideVertCount;
|
|
||||||
|
|
||||||
vertices.assign(vertCount, Vertex{
|
|
||||||
Vector3::Constant(std::numeric_limits<Real>::quiet_NaN()),
|
|
||||||
Vector3::Zero()
|
|
||||||
});
|
|
||||||
indices.assign(indexCount, UINT32_MAX);
|
|
||||||
|
|
||||||
Cell cell(
|
|
||||||
Vector3(-1.0f, -1.0f, 1.0f).normalized(),
|
|
||||||
Vector3( 1.0f, -1.0f, 1.0f).normalized(),
|
|
||||||
Vector3(-1.0f, -1.0f, -1.0f).normalized(),
|
|
||||||
Vector3( 1.0f, -1.0f, -1.0f).normalized()
|
|
||||||
);
|
|
||||||
|
|
||||||
auto vertex = &*vertices.begin();
|
|
||||||
const auto verticesEnd = &*vertices.end();
|
|
||||||
auto index = &*indices.begin();
|
|
||||||
const auto indicesEnd = &*indices.end();
|
|
||||||
cell.writeMesh(
|
|
||||||
vertex, verticesEnd,
|
|
||||||
index, indicesEnd,
|
|
||||||
subdivCount
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.info() << "Vertices:";
|
|
||||||
for(auto v = &*vertices.begin(); v < vertex; v += 1)
|
|
||||||
logger.debug() << (v - &*vertices.begin()) << ": "
|
|
||||||
<< v->position.transpose() << " - "
|
|
||||||
<< v->color.transpose();
|
|
||||||
|
|
||||||
logger.info() << "Indices:";
|
|
||||||
for(auto i = &*indices.begin(); i < index; i += 6)
|
|
||||||
logger.debug() << (i - &*indices.begin()) << ": "
|
|
||||||
<< i[0] << ", " << i[1] << ", " << i[2] << " - "
|
|
||||||
<< i[3] << ", " << i[4] << ", " << i[5];
|
|
||||||
|
|
||||||
// abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
VulkanTutorial::~VulkanTutorial() {
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanTutorial::initialize(SDL_Window* window) {
|
|
||||||
auto const context_settings = Vulkan::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 = Vulkan::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_context.free_memory(m_vertex_buffer_memory);
|
|
||||||
m_context.destroy_command_pool(m_command_pool);
|
|
||||||
m_context.free_memory(m_index_buffer_memory);
|
|
||||||
m_context.destroy_buffer(m_index_buffer);
|
|
||||||
m_context.free_memory(m_vertex_buffer_memory);
|
|
||||||
m_context.destroy_buffer(m_vertex_buffer);
|
|
||||||
m_context.destroy_descriptor_set_layout(m_descriptor_set_layout);
|
|
||||||
|
|
||||||
for(VkSemaphore semaphore: m_render_done)
|
|
||||||
m_context.destroy_semaphore(semaphore);
|
|
||||||
m_render_done.clear();
|
|
||||||
|
|
||||||
m_swapchain.shutdown();
|
|
||||||
m_context.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
const float alpha = SecondsD(m_time).count() * (2.0 * M_PI) / 3.0;
|
|
||||||
const float dist = 2.0f;
|
|
||||||
const Eigen::Matrix4f view = look_at_matrix(
|
|
||||||
// Eigen::Vector3f(0.0f, 0.0f, -dist),
|
|
||||||
Eigen::Vector3f(0.0f, -dist, -dist),
|
|
||||||
// dist * Eigen::Vector3f(std::cos(alpha), std::sin(alpha), -1.0),
|
|
||||||
Eigen::Vector3f::Zero(),
|
|
||||||
-Eigen::Vector3f::UnitY()
|
|
||||||
);
|
|
||||||
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
using Transform = Eigen::Transform<float, 3, Eigen::Affine>;
|
|
||||||
Transform model = Transform::Identity();
|
|
||||||
model.rotate(Eigen::AngleAxisf(
|
|
||||||
alpha,
|
|
||||||
Eigen::Vector3f::UnitY()
|
|
||||||
));
|
|
||||||
|
|
||||||
const Uniforms uniforms = {
|
|
||||||
.scene_from_model = model.matrix(),
|
|
||||||
.projection_from_scene = proj * view,
|
|
||||||
};
|
|
||||||
|
|
||||||
void* uniform_buffer;
|
|
||||||
vkMapMemory(
|
|
||||||
m_context.device(),
|
|
||||||
m_uniform_buffer_memory,
|
|
||||||
m_uniform_buffer_offsets[image_index],
|
|
||||||
sizeof(Uniforms),
|
|
||||||
0,
|
|
||||||
&uniform_buffer
|
|
||||||
);
|
|
||||||
std::memcpy(uniform_buffer, &uniforms, sizeof(Uniforms));
|
|
||||||
vkUnmapMemory(m_context.device(), m_uniform_buffer_memory);
|
|
||||||
|
|
||||||
VkSemaphore wait_semaphores[] = {
|
|
||||||
m_swapchain.ready_to_render(),
|
|
||||||
};
|
|
||||||
VkPipelineStageFlags stages[] = {
|
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
||||||
};
|
|
||||||
VkSubmitInfo submit_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
||||||
.waitSemaphoreCount = 1,
|
|
||||||
.pWaitSemaphores = wait_semaphores,
|
|
||||||
.pWaitDstStageMask = stages,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &m_command_buffers[image_index],
|
|
||||||
.signalSemaphoreCount = 1,
|
|
||||||
.pSignalSemaphores = &m_render_done[image_index],
|
|
||||||
};
|
|
||||||
|
|
||||||
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({1, &m_render_done[image_index]});
|
|
||||||
}
|
|
||||||
|
|
||||||
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.resize(m_swapchain.image_count());
|
|
||||||
|
|
||||||
VkSemaphoreCreateInfo semaphore_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
|
||||||
};
|
|
||||||
|
|
||||||
for(size_t index = 0; index < m_render_done.size(); index += 1) {
|
|
||||||
if(vkCreateSemaphore(
|
|
||||||
m_context.device(),
|
|
||||||
&semaphore_info,
|
|
||||||
nullptr,
|
|
||||||
&m_render_done[index]
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create semaphore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanTutorial::destroy_swapchain_objects() {
|
|
||||||
if(m_command_pool == VK_NULL_HANDLE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
vkFreeCommandBuffers(
|
|
||||||
m_context.device(),
|
|
||||||
m_command_pool,
|
|
||||||
uint32_t(m_command_buffers.size()),
|
|
||||||
m_command_buffers.data()
|
|
||||||
);
|
|
||||||
m_command_buffers.clear();
|
|
||||||
|
|
||||||
for(VkBuffer buffer: m_uniform_buffers)
|
|
||||||
m_context.destroy_buffer(buffer);
|
|
||||||
m_uniform_buffers.clear();
|
|
||||||
m_context.free_memory(m_uniform_buffer_memory);
|
|
||||||
|
|
||||||
m_context.destroy_descriptor_pool(m_descriptor_pool);
|
|
||||||
|
|
||||||
for(VkFramebuffer framebuffer: m_framebuffers)
|
|
||||||
m_context.destroy_framebuffer(framebuffer);
|
|
||||||
m_framebuffers.clear();
|
|
||||||
|
|
||||||
m_context.destroy_pipeline(m_pipeline);
|
|
||||||
m_context.destroy_pipeline_layout(m_pipeline_layout);
|
|
||||||
m_context.destroy_render_pass(m_render_pass);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(vkCreateRenderPass(
|
|
||||||
m_context.device(),
|
|
||||||
&render_pass_info,
|
|
||||||
nullptr,
|
|
||||||
&m_render_pass
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create render pass");
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanTutorial::create_descriptor_set_layout() {
|
|
||||||
VkDescriptorSetLayoutBinding binding {
|
|
||||||
.binding = 0,
|
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = VK_SHADER_STAGE_VERTEX_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 const vertex_shader_module =
|
|
||||||
m_context.create_shader_module_from_file("shaders/shader.vert.spv");
|
|
||||||
auto const vertex_shader_guard = make_guard([&]{
|
|
||||||
vkDestroyShaderModule(m_context.device(), vertex_shader_module, nullptr);
|
|
||||||
});
|
|
||||||
|
|
||||||
auto const fragment_shader_module =
|
|
||||||
m_context.create_shader_module_from_file("shaders/shader.frag.spv");
|
|
||||||
auto const fragment_shader_guard = make_guard([&]{
|
|
||||||
vkDestroyShaderModule(m_context.device(), fragment_shader_module, nullptr);
|
|
||||||
});
|
|
||||||
|
|
||||||
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_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 = 2,
|
|
||||||
.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,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(vkCreateGraphicsPipelines(
|
|
||||||
m_context.device(),
|
|
||||||
VK_NULL_HANDLE,
|
|
||||||
1, &pipeline_info,
|
|
||||||
nullptr,
|
|
||||||
&m_pipeline
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create graphic pipeline");
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanTutorial::create_framebuffers() {
|
|
||||||
m_framebuffers.resize(m_swapchain.image_count());
|
|
||||||
|
|
||||||
for(size_t index = 0; index < m_framebuffers.size(); index += 1) {
|
|
||||||
VkImageView view = m_swapchain.image_view(index);
|
|
||||||
|
|
||||||
VkFramebufferCreateInfo framebuffer_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
||||||
.renderPass = m_render_pass,
|
|
||||||
.attachmentCount = 1,
|
|
||||||
.pAttachments = &view,
|
|
||||||
.width = m_swapchain.extent().width,
|
|
||||||
.height = m_swapchain.extent().height,
|
|
||||||
.layers = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(vkCreateFramebuffer(
|
|
||||||
m_context.device(),
|
|
||||||
&framebuffer_info,
|
|
||||||
nullptr,
|
|
||||||
&m_framebuffers[index]
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create framebuffer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanTutorial::create_command_pool() {
|
|
||||||
if(m_command_pool != VK_NULL_HANDLE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
VkCommandPoolCreateInfo pool_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
||||||
.flags = 0,
|
|
||||||
.queueFamilyIndex = m_context.queue_family(GRAPHIC_QUEUE),
|
|
||||||
};
|
|
||||||
|
|
||||||
if(vkCreateCommandPool(
|
|
||||||
m_context.device(),
|
|
||||||
&pool_info,
|
|
||||||
nullptr,
|
|
||||||
&m_command_pool
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create command pool");
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanTutorial::create_vertex_buffer() {
|
|
||||||
if(m_vertex_buffer != VK_NULL_HANDLE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
VkDeviceSize size = sizeof(vertices[0]) * vertices.size();
|
|
||||||
std::tie(m_vertex_buffer, m_vertex_buffer_memory) = m_context.create_buffer(
|
|
||||||
size, (char*)vertices.data(),
|
|
||||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
||||||
m_context.queue_family(GRAPHIC_QUEUE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanTutorial::create_index_buffer() {
|
|
||||||
if(m_index_buffer != VK_NULL_HANDLE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
VkDeviceSize size = sizeof(indices[0]) * indices.size();
|
|
||||||
std::tie(m_index_buffer, m_index_buffer_memory) = m_context.create_buffer(
|
|
||||||
size, (char*)indices.data(),
|
|
||||||
VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
||||||
m_context.queue_family(GRAPHIC_QUEUE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanTutorial::create_uniform_buffer() {
|
|
||||||
m_uniform_buffers.assign(m_swapchain.image_count(), VK_NULL_HANDLE);
|
|
||||||
for(size_t index = 0; index < m_uniform_buffers.size(); index += 1) {
|
|
||||||
VkBufferCreateInfo buffer_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.size = sizeof(Uniforms),
|
|
||||||
.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(vkCreateBuffer(
|
|
||||||
m_context.device(),
|
|
||||||
&buffer_info,
|
|
||||||
nullptr,
|
|
||||||
&m_uniform_buffers[index]
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
VkMemoryRequirements memory_requirements;
|
|
||||||
vkGetBufferMemoryRequirements(
|
|
||||||
m_context.device(),
|
|
||||||
m_uniform_buffers[0],
|
|
||||||
&memory_requirements
|
|
||||||
);
|
|
||||||
|
|
||||||
m_uniform_buffer_memory = m_context.allocate_memory(
|
|
||||||
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;
|
|
||||||
vkBindBufferMemory(
|
|
||||||
m_context.device(),
|
|
||||||
m_uniform_buffers[index],
|
|
||||||
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.resize(m_framebuffers.size());
|
|
||||||
|
|
||||||
VkCommandBufferAllocateInfo allocate_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
||||||
.commandPool = m_command_pool,
|
|
||||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
||||||
.commandBufferCount = uint32_t(m_command_buffers.size()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if(vkAllocateCommandBuffers(
|
|
||||||
m_context.device(),
|
|
||||||
&allocate_info,
|
|
||||||
m_command_buffers.data()
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to allocate command buffers");
|
|
||||||
|
|
||||||
for(size_t index = 0; index < m_command_buffers.size(); index += 1) {
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
vkCmdBindDescriptorSets(
|
|
||||||
command_buffer,
|
|
||||||
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
||||||
m_pipeline_layout,
|
|
||||||
0, 1, &m_descriptor_sets[index],
|
|
||||||
0, nullptr
|
|
||||||
);
|
|
||||||
|
|
||||||
vkCmdBindIndexBuffer(
|
|
||||||
command_buffer,
|
|
||||||
m_index_buffer,
|
|
||||||
0,
|
|
||||||
VK_INDEX_TYPE_UINT32
|
|
||||||
);
|
|
||||||
|
|
||||||
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,83 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Vulkan/Context.h>
|
|
||||||
#include <Vulkan/Swapchain.h>
|
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include <Eigen/Dense>
|
|
||||||
#include <vector>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
|
|
||||||
VkVertexInputBindingDescription vertex_binding_description();
|
|
||||||
std::array<VkVertexInputAttributeDescription, 2> 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 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:
|
|
||||||
Vulkan::Context m_context;
|
|
||||||
Vulkan::Swapchain m_swapchain;
|
|
||||||
VkQueue m_graphic_queue = nullptr;
|
|
||||||
VkQueue m_presentation_queue = nullptr;
|
|
||||||
|
|
||||||
VkRenderPass m_render_pass = VK_NULL_HANDLE;
|
|
||||||
VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;
|
|
||||||
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
|
|
||||||
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
|
||||||
std::vector<VkFramebuffer> m_framebuffers;
|
|
||||||
VkCommandPool m_command_pool = VK_NULL_HANDLE;
|
|
||||||
VkBuffer m_vertex_buffer = VK_NULL_HANDLE;
|
|
||||||
VkDeviceMemory m_vertex_buffer_memory = VK_NULL_HANDLE;
|
|
||||||
VkBuffer m_index_buffer = VK_NULL_HANDLE;
|
|
||||||
VkDeviceMemory m_index_buffer_memory = VK_NULL_HANDLE;
|
|
||||||
std::vector<VkBuffer> m_uniform_buffers;
|
|
||||||
std::vector<VkDeviceSize> m_uniform_buffer_offsets;
|
|
||||||
VkDeviceMemory m_uniform_buffer_memory = VK_NULL_HANDLE;
|
|
||||||
VkDescriptorPool m_descriptor_pool = VK_NULL_HANDLE;
|
|
||||||
std::vector<VkDescriptorSet> m_descriptor_sets;
|
|
||||||
std::vector<VkCommandBuffer> m_command_buffers;
|
|
||||||
std::vector<VkSemaphore> m_render_done;
|
|
||||||
|
|
||||||
TimePoint m_last_frame_time;
|
|
||||||
Duration m_time = Duration(0);
|
|
||||||
};
|
|
||||||
80
src/core/ArrayView.h
Normal file
80
src/core/ArrayView.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <core/types.h>
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class ArrayView {
|
||||||
|
public:
|
||||||
|
ArrayView() = default;
|
||||||
|
ArrayView(const ArrayView&) = default;
|
||||||
|
ArrayView(ArrayView&&) = default;
|
||||||
|
~ArrayView() = default;
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
ArrayView(Index size, U* data, Index stride=sizeof(T), Index byte_offset=0)
|
||||||
|
: m_data(reinterpret_cast<Byte*>(data) + byte_offset)
|
||||||
|
, m_size(size)
|
||||||
|
, m_stride(stride)
|
||||||
|
{
|
||||||
|
assert(m_data != nullptr || m_size == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit ArrayView(std::vector<T>& vector)
|
||||||
|
: m_data(vector.data())
|
||||||
|
, m_data(vector.size())
|
||||||
|
{}
|
||||||
|
|
||||||
|
ArrayView& operator=(const ArrayView&) = default;
|
||||||
|
ArrayView& operator=(ArrayView&&) = default;
|
||||||
|
|
||||||
|
explicit operator bool() const {
|
||||||
|
return m_size != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Index size() const {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Index stride() const {
|
||||||
|
return m_stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_dense() const {
|
||||||
|
return m_stride == sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* data() const {
|
||||||
|
return reinterpret_cast<const T*>(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
T* data() {
|
||||||
|
return reinterpret_cast<T*>(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& operator[](Index index) const {
|
||||||
|
assert(index < m_size);
|
||||||
|
return *reinterpret_cast<T*>(m_data + index * m_stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator[](Index index) {
|
||||||
|
return const_cast<T&>(const_cast<const ArrayView&>(*this)[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayView slice(Index start, Index count, Index step=1) {
|
||||||
|
assert(step > 0);
|
||||||
|
assert(start + count * step <= m_size);
|
||||||
|
return ArrayView(
|
||||||
|
count,
|
||||||
|
m_data + start * m_stride,
|
||||||
|
m_stride * step
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Byte* m_data = nullptr;
|
||||||
|
Index m_size = 0;
|
||||||
|
Index m_stride = sizeof(T);
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include <Logger.h>
|
// Copyright 2022 Simon Boyé
|
||||||
|
#include <core/Logger.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
@@ -1,28 +1,6 @@
|
|||||||
#include <utils.h>
|
// Copyright 2022 Simon Boyé
|
||||||
|
#include <core/math.h>
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
|
|
||||||
Uuid make_uuid(uint8_t const* bytes) {
|
|
||||||
Uuid uuid;
|
|
||||||
std::memcpy(uuid.data(), bytes, UUID_SIZE);
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> read_binary_file(const char* path) {
|
|
||||||
std::ifstream istream(path, std::ios_base::ate | std::ios_base::binary);
|
|
||||||
if(!istream.is_open())
|
|
||||||
throw std::runtime_error(cat("failed to open '", path, "'"));
|
|
||||||
|
|
||||||
std::vector<char> buffer(size_t(istream.tellg()));
|
|
||||||
|
|
||||||
istream.seekg(0);
|
|
||||||
istream.read(buffer.data(), buffer.size());
|
|
||||||
istream.close();
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
Eigen::Matrix4f projection_matrix(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) {
|
Eigen::Matrix4f projection_matrix(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) {
|
||||||
auto const cx = x_max + x_min;
|
auto const cx = x_max + x_min;
|
||||||
@@ -33,12 +11,12 @@ Eigen::Matrix4f projection_matrix(float x_min, float x_max, float y_min, float y
|
|||||||
auto const dy = y_max - y_min;
|
auto const dy = y_max - y_min;
|
||||||
auto const dz = z_max - z_min;
|
auto const dz = z_max - z_min;
|
||||||
|
|
||||||
auto const pz = z_max * z_min;
|
auto const sz = z_max / dz;
|
||||||
|
|
||||||
return (Eigen::Matrix4f() <<
|
return (Eigen::Matrix4f() <<
|
||||||
2.0f * z_min / dx, 0.0f, -cx / dx, 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, 2.0f * z_min / dy, -cy / dy, 0.0f,
|
||||||
0.0f, 0.0f, cz / dz, -2.0f * pz / dz,
|
0.0f, 0.0f, sz, (1 - sz) * z_max,
|
||||||
0.0f, 0.0f, 1.0f, 0.0f
|
0.0f, 0.0f, 1.0f, 0.0f
|
||||||
).finished();
|
).finished();
|
||||||
}
|
}
|
||||||
60
src/core/math.h
Normal file
60
src/core/math.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <core/types.h>
|
||||||
|
#include <core/ArrayView.h>
|
||||||
|
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
|
||||||
|
using Real = float;
|
||||||
|
|
||||||
|
using Vector2 = Eigen::Matrix<Real, 2, 1>;
|
||||||
|
using Vector3 = Eigen::Matrix<Real, 3, 1>;
|
||||||
|
using Vector4 = Eigen::Matrix<Real, 4, 1>;
|
||||||
|
|
||||||
|
using Matrix2 = Eigen::Matrix<Real, 2, 2>;
|
||||||
|
using Matrix3 = Eigen::Matrix<Real, 3, 3>;
|
||||||
|
using Matrix4 = Eigen::Matrix<Real, 4, 4>;
|
||||||
|
|
||||||
|
using Transform = Eigen::Transform<Real, 3, Eigen::Affine>;
|
||||||
|
using AngleAxis = Eigen::AngleAxis<Real>;
|
||||||
|
using Quaternion = Eigen::Quaternion<Real>;
|
||||||
|
|
||||||
|
using Triangle = Eigen::Array<Index, 3, 1>;
|
||||||
|
|
||||||
|
|
||||||
|
using Vector3AV = ArrayView<Vector3>;
|
||||||
|
using TriangleAV = ArrayView<Triangle>;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Scalar, typename Derived0, typename Derived1>
|
||||||
|
auto lerp(
|
||||||
|
Scalar factor,
|
||||||
|
const Eigen::MatrixBase<Derived0>& m0,
|
||||||
|
const Eigen::MatrixBase<Derived1>& m1
|
||||||
|
) -> std::enable_if_t<
|
||||||
|
Derived0::RowsAtCompileTime == Derived1::RowsAtCompileTime &&
|
||||||
|
Derived0::ColsAtCompileTime == Derived1::ColsAtCompileTime,
|
||||||
|
Eigen::Matrix<
|
||||||
|
std::common_type_t<
|
||||||
|
Scalar,
|
||||||
|
typename Derived0::Scalar,
|
||||||
|
typename Derived1::Scalar
|
||||||
|
>,
|
||||||
|
Derived0::RowsAtCompileTime,
|
||||||
|
Derived0::ColsAtCompileTime
|
||||||
|
>
|
||||||
|
> {
|
||||||
|
return (Scalar(1) - factor) * m0 + factor * m1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Eigen::Matrix4f projection_matrix(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max);
|
||||||
|
Eigen::Matrix4f look_at_matrix(
|
||||||
|
const Eigen::Vector3f& cam_pos,
|
||||||
|
const Eigen::Vector3f& look_at,
|
||||||
|
const Eigen::Vector3f& up
|
||||||
|
);
|
||||||
11
src/core/types.h
Normal file
11
src/core/types.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
|
||||||
|
using Byte = unsigned char;
|
||||||
|
using Index = uint32_t;
|
||||||
26
src/core/utils.cpp
Normal file
26
src/core/utils.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#include <core/utils.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
Uuid make_uuid(uint8_t const* bytes) {
|
||||||
|
Uuid uuid;
|
||||||
|
std::memcpy(uuid.data(), bytes, UUID_SIZE);
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> read_binary_file(const char* path) {
|
||||||
|
std::ifstream istream(path, std::ios_base::ate | std::ios_base::binary);
|
||||||
|
if(!istream.is_open())
|
||||||
|
throw std::runtime_error(cat("failed to open '", path, "'"));
|
||||||
|
|
||||||
|
std::vector<char> buffer(size_t(istream.tellg()));
|
||||||
|
|
||||||
|
istream.seekg(0);
|
||||||
|
istream.read(buffer.data(), buffer.size());
|
||||||
|
istream.close();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Eigen/Dense>
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -33,6 +32,15 @@ Guard<T> make_guard(T&& teardown) {
|
|||||||
return Guard<T>(std::move(teardown));
|
return Guard<T>(std::move(teardown));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T& recreate_object(T& object) {
|
||||||
|
object.~T();
|
||||||
|
new(&object) T;
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
std::string cat(Args&&... args) {
|
std::string cat(Args&&... args) {
|
||||||
std::ostringstream ostream;
|
std::ostringstream ostream;
|
||||||
@@ -40,8 +48,10 @@ std::string cat(Args&&... args) {
|
|||||||
return ostream.str();
|
return ostream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<char> read_binary_file(const char* path);
|
std::vector<char> read_binary_file(const char* path);
|
||||||
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Array {
|
class Array {
|
||||||
public:
|
public:
|
||||||
@@ -55,6 +65,17 @@ public:
|
|||||||
assert((size == 0 && m_data == nullptr) || (size != 0 && m_data != nullptr));
|
assert((size == 0 && m_data == nullptr) || (size != 0 && m_data != nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Array(T* item)
|
||||||
|
// : m_size(1)
|
||||||
|
// , m_data(item)
|
||||||
|
// {}
|
||||||
|
|
||||||
|
template<size_t Size>
|
||||||
|
Array(T (&array)[Size])
|
||||||
|
: m_size(Size)
|
||||||
|
, m_data(array)
|
||||||
|
{}
|
||||||
|
|
||||||
Array(const std::vector<T>& vector)
|
Array(const std::vector<T>& vector)
|
||||||
: m_size(vector.size())
|
: m_size(vector.size())
|
||||||
, m_data(vector.data())
|
, m_data(vector.data())
|
||||||
@@ -76,10 +97,3 @@ private:
|
|||||||
size_t m_size = 0;
|
size_t m_size = 0;
|
||||||
T* m_data = nullptr;
|
T* m_data = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
Eigen::Matrix4f projection_matrix(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max);
|
|
||||||
Eigen::Matrix4f look_at_matrix(
|
|
||||||
const Eigen::Vector3f& cam_pos,
|
|
||||||
const Eigen::Vector3f& look_at,
|
|
||||||
const Eigen::Vector3f& up
|
|
||||||
);
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#include <Logger.h>
|
// Copyright 2022 Simon Boyé
|
||||||
#include <VkExpe.h>
|
#include <VkExpe.h>
|
||||||
|
|
||||||
|
#include <core/Logger.h>
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
VkExpe vk_expe(argc, argv);
|
VkExpe vk_expe(argc, argv);
|
||||||
|
|||||||
179
src/vk/Buffer.cpp
Normal file
179
src/vk/Buffer.cpp
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#include <vk/Buffer.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <core/Logger.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
Buffer::Buffer() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Buffer(
|
||||||
|
Context& context,
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkBufferUsageFlags usage
|
||||||
|
)
|
||||||
|
: Wrapper(context)
|
||||||
|
{
|
||||||
|
assert(*m_context);
|
||||||
|
|
||||||
|
VkBufferCreateInfo create_info {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
|
.size = size,
|
||||||
|
.usage = usage,
|
||||||
|
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
|
};
|
||||||
|
if(vkCreateBuffer(
|
||||||
|
m_context->device(),
|
||||||
|
&create_info,
|
||||||
|
nullptr,
|
||||||
|
&m_buffer
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to create buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Buffer(
|
||||||
|
Context& context,
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkBufferUsageFlags usage,
|
||||||
|
VkMemoryPropertyFlags memory_properties
|
||||||
|
)
|
||||||
|
: Buffer(context, size, usage)
|
||||||
|
{
|
||||||
|
allocate_and_bind_memory(memory_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::Buffer(Buffer&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::~Buffer() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer& Buffer::operator=(Buffer&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkMemoryRequirements Buffer::memory_requirements() const noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(*m_context);
|
||||||
|
|
||||||
|
VkMemoryRequirements memory_requirements;
|
||||||
|
vkGetBufferMemoryRequirements(
|
||||||
|
m_context->device(),
|
||||||
|
m_buffer,
|
||||||
|
&memory_requirements
|
||||||
|
);
|
||||||
|
|
||||||
|
return memory_requirements;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Buffer::bind_memory(const MemoryBlock& memory_block, VkDeviceSize offset) {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(*m_context);
|
||||||
|
assert(memory_block);
|
||||||
|
|
||||||
|
// m_memory = std::move(memory_block);
|
||||||
|
if(vkBindBufferMemory(
|
||||||
|
m_context->device(),
|
||||||
|
m_buffer,
|
||||||
|
memory_block.device_memory(),
|
||||||
|
memory_block.offset() + offset
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to bind buffer memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::bind_memory(MemoryBlock&& memory_block) {
|
||||||
|
bind_memory(memory_block);
|
||||||
|
m_memory = std::move(memory_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Buffer::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 Buffer::upload(size_t size, void* src_buffer, uint32_t dst_queue_family) {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(*m_context);
|
||||||
|
|
||||||
|
const bool use_staging_buffer =
|
||||||
|
(m_memory.memory_type_info(*m_context).propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0;
|
||||||
|
|
||||||
|
if(use_staging_buffer) {
|
||||||
|
auto staging_buffer = Buffer(
|
||||||
|
*m_context,
|
||||||
|
size,
|
||||||
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||||
|
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
||||||
|
);
|
||||||
|
|
||||||
|
void* dst_buffer = staging_buffer.memory().map(*m_context);
|
||||||
|
std::memcpy(dst_buffer, src_buffer, size);
|
||||||
|
staging_buffer.memory().unmap(*m_context);
|
||||||
|
|
||||||
|
m_context->copy_buffer(
|
||||||
|
m_buffer,
|
||||||
|
staging_buffer,
|
||||||
|
dst_queue_family,
|
||||||
|
size
|
||||||
|
);
|
||||||
|
|
||||||
|
staging_buffer.destroy();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
void* dst_buffer = m_memory.map(*m_context);
|
||||||
|
std::memcpy(dst_buffer, src_buffer, size);
|
||||||
|
m_memory.unmap(*m_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Buffer::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(*m_context);
|
||||||
|
|
||||||
|
if(m_memory) {
|
||||||
|
m_memory.free();
|
||||||
|
m_memory = MemoryBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
vkDestroyBuffer(
|
||||||
|
m_context->device(),
|
||||||
|
m_buffer,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_context = nullptr;
|
||||||
|
m_buffer = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
91
src/vk/Buffer.h
Normal file
91
src/vk/Buffer.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vk/forward.h>
|
||||||
|
#include <vk/Memory.h>
|
||||||
|
#include <vk/Wrapper.h>
|
||||||
|
|
||||||
|
#include <core/utils.h>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
class Buffer: public Wrapper {
|
||||||
|
public:
|
||||||
|
Buffer() noexcept;
|
||||||
|
Buffer(
|
||||||
|
Context& context,
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkBufferUsageFlags usage
|
||||||
|
);
|
||||||
|
Buffer(
|
||||||
|
Context& context,
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkBufferUsageFlags usage,
|
||||||
|
VkMemoryPropertyFlags memory_properties
|
||||||
|
);
|
||||||
|
Buffer(const Buffer&) = default;
|
||||||
|
Buffer(Buffer&& other) noexcept;
|
||||||
|
~Buffer() noexcept;
|
||||||
|
|
||||||
|
Buffer& operator=(const Buffer&) = default;
|
||||||
|
Buffer& operator=(Buffer&& other) noexcept;
|
||||||
|
|
||||||
|
inline explicit operator bool() const {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const {
|
||||||
|
return m_buffer == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkBuffer() const noexcept {
|
||||||
|
return m_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkBuffer buffer() const noexcept {
|
||||||
|
return m_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const MemoryBlock& memory() const noexcept {
|
||||||
|
return m_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MemoryBlock& memory() noexcept {
|
||||||
|
return m_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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:
|
||||||
|
VkBuffer m_buffer = VK_NULL_HANDLE;
|
||||||
|
MemoryBlock m_memory;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
70
src/vk/CommandBuffer.cpp
Normal file
70
src/vk/CommandBuffer.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <vk/CommandBuffer.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
CommandBuffer::CommandBuffer() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandBuffer::CommandBuffer(Context& context, VkCommandPool command_pool, VkCommandBufferLevel level)
|
||||||
|
: Wrapper(context)
|
||||||
|
, m_command_pool(command_pool)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
assert(m_command_pool != VK_NULL_HANDLE);
|
||||||
|
|
||||||
|
VkCommandBufferAllocateInfo allocate_info {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||||
|
.commandPool = m_command_pool,
|
||||||
|
.level = level,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
};
|
||||||
|
if(vkAllocateCommandBuffers(
|
||||||
|
context.device(),
|
||||||
|
&allocate_info,
|
||||||
|
&m_command_buffer
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to allocate command pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandBuffer::CommandBuffer(CommandBuffer&& other) noexcept
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandBuffer::~CommandBuffer() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CommandBuffer& CommandBuffer::operator=(CommandBuffer&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CommandBuffer::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
vkFreeCommandBuffers(
|
||||||
|
m_context->device(),
|
||||||
|
m_command_pool,
|
||||||
|
1,
|
||||||
|
&m_command_buffer
|
||||||
|
);
|
||||||
|
|
||||||
|
m_command_buffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
67
src/vk/CommandBuffer.h
Normal file
67
src/vk/CommandBuffer.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vk/forward.h>
|
||||||
|
#include <vk/Wrapper.h>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
class CommandBuffer: public Wrapper {
|
||||||
|
public:
|
||||||
|
CommandBuffer() noexcept;
|
||||||
|
CommandBuffer(Context& context, VkCommandPool command_pool, VkCommandBufferLevel level=VK_COMMAND_BUFFER_LEVEL_PRIMARY);
|
||||||
|
CommandBuffer(const CommandBuffer&) = default;
|
||||||
|
CommandBuffer(CommandBuffer&& other) noexcept;
|
||||||
|
~CommandBuffer() noexcept;
|
||||||
|
|
||||||
|
CommandBuffer& operator=(const CommandBuffer&) = default;
|
||||||
|
CommandBuffer& operator=(CommandBuffer&& other) noexcept;
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_command_buffer == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const VkCommandPool command_pool() const noexcept {
|
||||||
|
return m_command_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkCommandPool command_pool() noexcept {
|
||||||
|
return m_command_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkCommandBuffer() noexcept {
|
||||||
|
return m_command_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkCommandBuffer command_buffer() noexcept {
|
||||||
|
return m_command_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(CommandBuffer& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
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:
|
||||||
|
VkCommandPool m_command_pool = VK_NULL_HANDLE;
|
||||||
|
VkCommandBuffer m_command_buffer = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
67
src/vk/CommandPool.cpp
Normal file
67
src/vk/CommandPool.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <vk/CommandPool.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
CommandPool::CommandPool() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandPool::CommandPool(Context& context, uint32_t queue_family, VkCommandPoolCreateFlags flags)
|
||||||
|
: Wrapper(context)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
VkCommandPoolCreateInfo create_info {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||||
|
.flags = flags,
|
||||||
|
.queueFamilyIndex = queue_family,
|
||||||
|
};
|
||||||
|
if(vkCreateCommandPool(
|
||||||
|
context.device(),
|
||||||
|
&create_info,
|
||||||
|
nullptr,
|
||||||
|
&m_command_pool
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to create command pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandPool::CommandPool(CommandPool&& other) noexcept
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandPool::~CommandPool() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CommandPool& CommandPool::operator=(CommandPool&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CommandPool::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
vkDestroyCommandPool(
|
||||||
|
m_context->device(),
|
||||||
|
m_command_pool,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_command_pool = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
57
src/vk/CommandPool.h
Normal file
57
src/vk/CommandPool.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 CommandPool: public Wrapper {
|
||||||
|
public:
|
||||||
|
CommandPool() noexcept;
|
||||||
|
CommandPool(Context& context, uint32_t queue_family, VkCommandPoolCreateFlags flags=0);
|
||||||
|
CommandPool(const CommandPool&) = default;
|
||||||
|
CommandPool(CommandPool&& other) noexcept;
|
||||||
|
~CommandPool() noexcept;
|
||||||
|
|
||||||
|
CommandPool& operator=(const CommandPool&) = default;
|
||||||
|
CommandPool& operator=(CommandPool&& other) noexcept;
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_command_pool == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkCommandPool() noexcept {
|
||||||
|
return m_command_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkCommandPool command_pool() noexcept {
|
||||||
|
return m_command_pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(CommandPool& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
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:
|
||||||
|
VkCommandPool m_command_pool = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
#include <Vulkan/Context.h>
|
// Copyright 2022 Simon Boyé
|
||||||
|
#include <vk/Context.h>
|
||||||
|
#include <vk/Memory.h>
|
||||||
|
#include <vk/CommandBuffer.h>
|
||||||
|
|
||||||
#include <utils.h>
|
#include <core/utils.h>
|
||||||
#include <Logger.h>
|
#include <core/Logger.h>
|
||||||
|
|
||||||
#include <SDL2/SDL_vulkan.h>
|
#include <SDL2/SDL_vulkan.h>
|
||||||
|
|
||||||
@@ -11,7 +14,7 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
ContextSettings::ContextSettings() {
|
ContextSettings::ContextSettings() {
|
||||||
@@ -122,6 +125,8 @@ void Context::initialize(const ContextSettings& settings) {
|
|||||||
choose_physical_device(settings);
|
choose_physical_device(settings);
|
||||||
create_device(settings);
|
create_device(settings);
|
||||||
create_internal_objects();
|
create_internal_objects();
|
||||||
|
|
||||||
|
m_allocator.reset(new Allocator(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::shutdown() {
|
void Context::shutdown() {
|
||||||
@@ -133,8 +138,11 @@ void Context::shutdown() {
|
|||||||
for(auto& callback: m_context_destruction_callbacks)
|
for(auto& callback: m_context_destruction_callbacks)
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
destroy_fence(m_transfer_fence);
|
m_allocator->free_all_pages();
|
||||||
destroy_command_pool(m_transfer_command_pool);
|
m_allocator.reset();
|
||||||
|
|
||||||
|
m_transfer_fence.destroy();
|
||||||
|
m_transfer_command_pool.destroy();
|
||||||
|
|
||||||
destroy_device(m_device);
|
destroy_device(m_device);
|
||||||
destroy_surface(m_surface);
|
destroy_surface(m_surface);
|
||||||
@@ -274,137 +282,8 @@ VkShaderModule Context::create_shader_module_from_file(const char* path) {
|
|||||||
return VK_NULL_HANDLE;
|
return VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t Context::find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags properties) {
|
|
||||||
for(uint32_t type_index = 0; type_index < m_memory_properties.memoryTypeCount; type_index += 1) {
|
|
||||||
if(((1 << type_index) & type_filter) &&
|
|
||||||
(m_memory_properties.memoryTypes[type_index].propertyFlags & properties) == properties)
|
|
||||||
return type_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkDeviceMemory Context::allocate_memory(VkDeviceSize size, uint32_t type_filter, VkMemoryPropertyFlags properties) {
|
|
||||||
uint32_t memory_type = find_memory_type(type_filter, properties);
|
|
||||||
|
|
||||||
VkMemoryAllocateInfo malloc_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
|
||||||
.allocationSize = size,
|
|
||||||
.memoryTypeIndex = memory_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
|
||||||
if(vkAllocateMemory(m_device, &malloc_info, nullptr, &memory) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to allocate device memory");
|
|
||||||
|
|
||||||
return memory;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<VkBuffer, VkDeviceMemory> Context::create_buffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memory_properties) {
|
|
||||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
|
||||||
VkBuffer buffer = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
VkBufferCreateInfo buffer_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
|
||||||
.size = size,
|
|
||||||
.usage = usage,
|
|
||||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(vkCreateBuffer(
|
|
||||||
m_device,
|
|
||||||
&buffer_info,
|
|
||||||
nullptr,
|
|
||||||
&buffer
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create buffer");
|
|
||||||
|
|
||||||
VkMemoryRequirements memory_requirements;
|
|
||||||
vkGetBufferMemoryRequirements(
|
|
||||||
m_device,
|
|
||||||
buffer,
|
|
||||||
&memory_requirements
|
|
||||||
);
|
|
||||||
|
|
||||||
memory = allocate_memory(
|
|
||||||
memory_requirements.size,
|
|
||||||
memory_requirements.memoryTypeBits,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
||||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
|
||||||
);
|
|
||||||
|
|
||||||
vkBindBufferMemory(m_device, buffer, memory, 0);
|
|
||||||
|
|
||||||
return { buffer, memory };
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<VkBuffer, VkDeviceMemory> Context::create_buffer(
|
|
||||||
VkDeviceSize size, char* data,
|
|
||||||
VkBufferUsageFlags usage,
|
|
||||||
VkMemoryPropertyFlags memory_properties,
|
|
||||||
uint32_t dst_queue_family
|
|
||||||
) {
|
|
||||||
VkBuffer tmp_buffer = VK_NULL_HANDLE;
|
|
||||||
VkDeviceMemory tmp_buffer_memory = VK_NULL_HANDLE;
|
|
||||||
std::tie(tmp_buffer, tmp_buffer_memory) = create_buffer(
|
|
||||||
size,
|
|
||||||
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
|
||||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
|
||||||
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
|
|
||||||
);
|
|
||||||
auto const tmp_buffer_guard = make_guard([&] {
|
|
||||||
free_memory(tmp_buffer_memory);
|
|
||||||
destroy_buffer(tmp_buffer);
|
|
||||||
});
|
|
||||||
|
|
||||||
VkBuffer buffer = VK_NULL_HANDLE;
|
|
||||||
VkDeviceMemory buffer_memory = VK_NULL_HANDLE;
|
|
||||||
std::tie(buffer, buffer_memory) = create_buffer(
|
|
||||||
size,
|
|
||||||
usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
|
||||||
memory_properties
|
|
||||||
);
|
|
||||||
|
|
||||||
void* device_buffer = nullptr;
|
|
||||||
vkMapMemory(
|
|
||||||
m_device,
|
|
||||||
tmp_buffer_memory,
|
|
||||||
0,
|
|
||||||
size,
|
|
||||||
0,
|
|
||||||
&device_buffer
|
|
||||||
);
|
|
||||||
|
|
||||||
memcpy(device_buffer, data, size_t(size));
|
|
||||||
|
|
||||||
vkUnmapMemory(
|
|
||||||
m_device,
|
|
||||||
tmp_buffer_memory
|
|
||||||
);
|
|
||||||
|
|
||||||
copy_buffer(
|
|
||||||
buffer,
|
|
||||||
tmp_buffer,
|
|
||||||
dst_queue_family,
|
|
||||||
size
|
|
||||||
);
|
|
||||||
|
|
||||||
return std::make_tuple(buffer, buffer_memory);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Context::copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family, VkDeviceSize size) {
|
void Context::copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family, VkDeviceSize size) {
|
||||||
VkCommandBufferAllocateInfo alloc_info {
|
CommandBuffer command_buffer(*this, m_transfer_command_pool);
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
||||||
.commandPool = m_transfer_command_pool,
|
|
||||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkCommandBuffer command_buffer;
|
|
||||||
vkAllocateCommandBuffers(m_device, &alloc_info, &command_buffer);
|
|
||||||
auto const command_buffer_guard = make_guard([this, command_buffer]() {
|
|
||||||
vkFreeCommandBuffers(m_device, m_transfer_command_pool, 1, &command_buffer);
|
|
||||||
});
|
|
||||||
|
|
||||||
VkCommandBufferBeginInfo begin_info {
|
VkCommandBufferBeginInfo begin_info {
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||||
@@ -441,19 +320,22 @@ void Context::copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family,
|
|||||||
|
|
||||||
vkEndCommandBuffer(command_buffer);
|
vkEndCommandBuffer(command_buffer);
|
||||||
|
|
||||||
vkResetFences(m_device, 1, &m_transfer_fence);
|
m_transfer_fence.reset();
|
||||||
|
|
||||||
|
VkCommandBuffer command_buffers[] = {
|
||||||
|
command_buffer
|
||||||
|
};
|
||||||
VkSubmitInfo submit_info {
|
VkSubmitInfo submit_info {
|
||||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
.commandBufferCount = 1,
|
.commandBufferCount = 1,
|
||||||
.pCommandBuffers = &command_buffer,
|
.pCommandBuffers = command_buffers,
|
||||||
};
|
};
|
||||||
vkQueueSubmit(
|
vkQueueSubmit(
|
||||||
queue(m_transfer_queue_index),
|
queue(m_transfer_queue_index),
|
||||||
1, &submit_info,
|
1, &submit_info,
|
||||||
m_transfer_fence
|
m_transfer_fence
|
||||||
);
|
);
|
||||||
vkWaitForFences(m_device, 1, &m_transfer_fence, VK_TRUE, UINT64_MAX);
|
m_transfer_fence.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -680,7 +562,7 @@ void Context::create_instance(const ContextSettings& settings) {
|
|||||||
if(vkCreateInstance(&instance_info, nullptr, &m_instance) != VK_SUCCESS)
|
if(vkCreateInstance(&instance_info, nullptr, &m_instance) != VK_SUCCESS)
|
||||||
throw std::runtime_error("failed to create vulkan instance");
|
throw std::runtime_error("failed to create vulkan instance");
|
||||||
|
|
||||||
initialize_extension_functions();
|
initialize_instance_extension_functions();
|
||||||
|
|
||||||
if(settings.debug()) {
|
if(settings.debug()) {
|
||||||
createDebugUtilsMessenger(
|
createDebugUtilsMessenger(
|
||||||
@@ -737,6 +619,13 @@ std::optional<VkPhysicalDeviceProperties> Context::select_physical_device(
|
|||||||
VkPhysicalDeviceProperties device_properties;
|
VkPhysicalDeviceProperties device_properties;
|
||||||
vkGetPhysicalDeviceProperties(physical_device, &device_properties);
|
vkGetPhysicalDeviceProperties(physical_device, &device_properties);
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures available_features;
|
||||||
|
vkGetPhysicalDeviceFeatures(physical_device, &available_features);
|
||||||
|
|
||||||
|
if (!available_features.geometryShader)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
|
||||||
m_transfer_queue_index = settings.queues().size();
|
m_transfer_queue_index = settings.queues().size();
|
||||||
m_queue_families.assign(settings.queues().size() + 1, INVALID_QUEUE_FAMILY);
|
m_queue_families.assign(settings.queues().size() + 1, INVALID_QUEUE_FAMILY);
|
||||||
m_presentation_queue_family = INVALID_QUEUE_FAMILY;
|
m_presentation_queue_family = INVALID_QUEUE_FAMILY;
|
||||||
@@ -841,9 +730,12 @@ void Context::create_device(const ContextSettings& settings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkPhysicalDeviceFeatures const device_features {
|
VkPhysicalDeviceFeatures const device_features {
|
||||||
|
.geometryShader = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<const char*> extensions;
|
std::vector<const char*> extensions {
|
||||||
|
VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME,
|
||||||
|
};
|
||||||
if(m_window) {
|
if(m_window) {
|
||||||
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||||
};
|
};
|
||||||
@@ -866,6 +758,8 @@ void Context::create_device(const ContextSettings& settings) {
|
|||||||
) != VK_SUCCESS)
|
) != VK_SUCCESS)
|
||||||
throw std::runtime_error("failed to create logical device");
|
throw std::runtime_error("failed to create logical device");
|
||||||
|
|
||||||
|
initialize_device_extension_functions();
|
||||||
|
|
||||||
m_queues.resize(m_queue_families.size());
|
m_queues.resize(m_queue_families.size());
|
||||||
for(size_t index = 0; index < m_queue_families.size(); ++index) {
|
for(size_t index = 0; index < m_queue_families.size(); ++index) {
|
||||||
vkGetDeviceQueue(
|
vkGetDeviceQueue(
|
||||||
@@ -879,31 +773,18 @@ void Context::create_device(const ContextSettings& settings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Context::create_internal_objects() {
|
void Context::create_internal_objects() {
|
||||||
VkCommandPoolCreateInfo pool_info {
|
m_transfer_command_pool = CommandPool(
|
||||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
*this,
|
||||||
.flags = 0,
|
queue_family(m_transfer_queue_index),
|
||||||
.queueFamilyIndex = queue_family(m_transfer_queue_index),
|
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT
|
||||||
};
|
);
|
||||||
if(vkCreateCommandPool(
|
m_transfer_fence = Fence(*this);
|
||||||
m_device,
|
|
||||||
&pool_info,
|
|
||||||
nullptr,
|
|
||||||
&m_transfer_command_pool
|
|
||||||
) != VK_SUCCESS) {
|
|
||||||
throw std::runtime_error("failed to create command pool");
|
|
||||||
}
|
|
||||||
|
|
||||||
VkFenceCreateInfo fence_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
||||||
};
|
|
||||||
if(vkCreateFence(m_device, &fence_info, nullptr, &m_transfer_fence) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create fence");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::initialize_extension_functions() {
|
void Context::initialize_instance_extension_functions() {
|
||||||
uint32_t errors_count = 0;
|
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( \
|
ptr_name = (PFN_vk ## func_name)vkGetInstanceProcAddr( \
|
||||||
m_instance, "vk" #func_name); \
|
m_instance, "vk" #func_name); \
|
||||||
if(ptr_name == nullptr) \
|
if(ptr_name == nullptr) \
|
||||||
@@ -912,9 +793,27 @@ void Context::initialize_extension_functions() {
|
|||||||
errors_count += 1; \
|
errors_count += 1; \
|
||||||
}
|
}
|
||||||
|
|
||||||
GET_PROC_ADDR(createDebugUtilsMessenger, CreateDebugUtilsMessengerEXT)
|
GET_INSTANCE_PROC_ADDR(createDebugUtilsMessenger, CreateDebugUtilsMessengerEXT)
|
||||||
GET_PROC_ADDR(destroyDebugUtilsMessenger, DestroyDebugUtilsMessengerEXT)
|
GET_INSTANCE_PROC_ADDR(destroyDebugUtilsMessenger, DestroyDebugUtilsMessengerEXT)
|
||||||
GET_PROC_ADDR(setDebugUtilsObjectName, SetDebugUtilsObjectNameEXT)
|
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)
|
if(errors_count != 0)
|
||||||
throw std::runtime_error("failed to load extensions");
|
throw std::runtime_error("failed to load extensions");
|
||||||
@@ -926,6 +825,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL Context::log_debug_message(
|
|||||||
const VkDebugUtilsMessengerCallbackDataEXT* data,
|
const VkDebugUtilsMessengerCallbackDataEXT* data,
|
||||||
void* user_data
|
void* user_data
|
||||||
) {
|
) {
|
||||||
|
if(data->messageIdNumber == 0) // Loader message
|
||||||
|
return VK_FALSE;
|
||||||
|
|
||||||
{
|
{
|
||||||
auto stream = [severity]() {
|
auto stream = [severity]() {
|
||||||
switch(severity) {
|
switch(severity) {
|
||||||
@@ -957,7 +859,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL Context::log_debug_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
|
if(severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
|
||||||
&& data->messageIdNumber != 2094043421 // wrong swapchain extent
|
&& (
|
||||||
|
data->messageIdNumber != 2094043421 // wrong swapchain extent
|
||||||
|
)
|
||||||
)
|
)
|
||||||
abort();
|
abort();
|
||||||
|
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <utils.h>
|
#include <vk/forward.h>
|
||||||
#include <Logger.h>
|
#include <vk/CommandPool.h>
|
||||||
|
#include <vk/Fence.h>
|
||||||
|
|
||||||
|
#include <core/utils.h>
|
||||||
|
#include <core/Logger.h>
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -12,7 +18,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
class Context;
|
class Context;
|
||||||
@@ -61,6 +67,14 @@ public:
|
|||||||
|
|
||||||
Context& operator=(const Context&) = delete;
|
Context& operator=(const Context&) = delete;
|
||||||
|
|
||||||
|
explicit inline operator bool() const {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const {
|
||||||
|
return m_instance == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
VkInstance instance();
|
VkInstance instance();
|
||||||
VkPhysicalDevice physical_device();
|
VkPhysicalDevice physical_device();
|
||||||
VkDevice device();
|
VkDevice device();
|
||||||
@@ -70,11 +84,19 @@ public:
|
|||||||
VkQueue queue(size_t queue_index);
|
VkQueue queue(size_t queue_index);
|
||||||
VkQueue presentation_queue();
|
VkQueue presentation_queue();
|
||||||
|
|
||||||
|
inline VkPhysicalDeviceMemoryProperties memory_properties() const noexcept {
|
||||||
|
return m_memory_properties;
|
||||||
|
}
|
||||||
|
|
||||||
SDL_Window* window();
|
SDL_Window* window();
|
||||||
VkSurfaceKHR surface();
|
VkSurfaceKHR surface();
|
||||||
VkSurfaceFormatKHR surface_format() const;
|
VkSurfaceFormatKHR surface_format() const;
|
||||||
VkPresentModeKHR present_mode() const;
|
VkPresentModeKHR present_mode() const;
|
||||||
|
|
||||||
|
inline Allocator& allocator() noexcept {
|
||||||
|
return *m_allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void initialize(const ContextSettings& settings);
|
void initialize(const ContextSettings& settings);
|
||||||
void shutdown();
|
void shutdown();
|
||||||
@@ -100,18 +122,6 @@ public:
|
|||||||
VkShaderModule create_shader_module(const std::vector<char> bytecode);
|
VkShaderModule create_shader_module(const std::vector<char> bytecode);
|
||||||
VkShaderModule create_shader_module_from_file(const char* path);
|
VkShaderModule create_shader_module_from_file(const char* path);
|
||||||
|
|
||||||
int32_t find_memory_type(uint32_t type_filter, VkMemoryPropertyFlags properties);
|
|
||||||
VkDeviceMemory allocate_memory(VkDeviceSize size, uint32_t type_filter, VkMemoryPropertyFlags properties);
|
|
||||||
|
|
||||||
std::tuple<VkBuffer, VkDeviceMemory> create_buffer(
|
|
||||||
VkDeviceSize size,
|
|
||||||
VkBufferUsageFlags usage,
|
|
||||||
VkMemoryPropertyFlags memory_properties);
|
|
||||||
std::tuple<VkBuffer, VkDeviceMemory> create_buffer(
|
|
||||||
VkDeviceSize size, char* buffer,
|
|
||||||
VkBufferUsageFlags usage,
|
|
||||||
VkMemoryPropertyFlags memory_properties,
|
|
||||||
uint32_t dst_queue_family);
|
|
||||||
void copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family, VkDeviceSize size);
|
void copy_buffer(VkBuffer dst, VkBuffer src, uint32_t dst_queue_family, VkDeviceSize size);
|
||||||
|
|
||||||
|
|
||||||
@@ -144,6 +154,8 @@ public:
|
|||||||
PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessenger = nullptr;
|
PFN_vkDestroyDebugUtilsMessengerEXT destroyDebugUtilsMessenger = nullptr;
|
||||||
PFN_vkSetDebugUtilsObjectNameEXT setDebugUtilsObjectName = nullptr;
|
PFN_vkSetDebugUtilsObjectNameEXT setDebugUtilsObjectName = nullptr;
|
||||||
|
|
||||||
|
PFN_vkCmdDrawIndexedIndirectCountKHR cmdDrawIndexedIndirectCount = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void create_instance(const ContextSettings& settings);
|
void create_instance(const ContextSettings& settings);
|
||||||
void create_surface(const ContextSettings& settings);
|
void create_surface(const ContextSettings& settings);
|
||||||
@@ -155,7 +167,8 @@ private:
|
|||||||
void create_device(const ContextSettings& settings);
|
void create_device(const ContextSettings& settings);
|
||||||
void create_internal_objects();
|
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(
|
static VKAPI_ATTR VkBool32 VKAPI_CALL log_debug_message(
|
||||||
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||||
@@ -185,10 +198,12 @@ private:
|
|||||||
VkPresentModeKHR m_present_mode;
|
VkPresentModeKHR m_present_mode;
|
||||||
|
|
||||||
uint32_t m_transfer_queue_index = UINT32_MAX;
|
uint32_t m_transfer_queue_index = UINT32_MAX;
|
||||||
VkCommandPool m_transfer_command_pool = VK_NULL_HANDLE;
|
CommandPool m_transfer_command_pool;
|
||||||
VkFence m_transfer_fence = VK_NULL_HANDLE;
|
Fence m_transfer_fence;
|
||||||
|
|
||||||
std::vector<ContextDestructionCallback> m_context_destruction_callbacks;
|
std::vector<ContextDestructionCallback> m_context_destruction_callbacks;
|
||||||
|
|
||||||
|
std::unique_ptr<Allocator> m_allocator;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
123
src/vk/Fence.cpp
Normal file
123
src/vk/Fence.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <vk/Fence.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
Fence::Fence() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
Fence::Fence(Context& context, VkFenceCreateFlags flags)
|
||||||
|
: Wrapper(context)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
VkFenceCreateInfo create_info {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
||||||
|
.flags = flags,
|
||||||
|
};
|
||||||
|
if(vkCreateFence(
|
||||||
|
context.device(),
|
||||||
|
&create_info,
|
||||||
|
nullptr,
|
||||||
|
&m_fence
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to create fence");
|
||||||
|
}
|
||||||
|
|
||||||
|
Fence::Fence(Fence&& other) noexcept
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fence::~Fence() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Fence& Fence::operator=(Fence&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkResult Fence::get_status() const {
|
||||||
|
assert(m_context);
|
||||||
|
assert(!is_null());
|
||||||
|
|
||||||
|
VkResult result = vkGetFenceStatus(
|
||||||
|
m_context->device(),
|
||||||
|
m_fence
|
||||||
|
);
|
||||||
|
|
||||||
|
if(result < 0)
|
||||||
|
throw std::runtime_error("failed to get fence status");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fence::reset() {
|
||||||
|
assert(m_context);
|
||||||
|
assert(!is_null());
|
||||||
|
|
||||||
|
if(vkResetFences(
|
||||||
|
m_context->device(),
|
||||||
|
1,
|
||||||
|
&m_fence
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to reset fence");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fence::wait(uint64_t timeout) const {
|
||||||
|
assert(m_context);
|
||||||
|
assert(!is_null());
|
||||||
|
|
||||||
|
wait_for_fences(
|
||||||
|
m_context->device(),
|
||||||
|
ArrayView<VkFence>(1, const_cast<VkFence*>(&m_fence)),
|
||||||
|
true,
|
||||||
|
timeout
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Fence::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
vkDestroyFence(
|
||||||
|
m_context->device(),
|
||||||
|
m_fence,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_fence = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void wait_for_fences(VkDevice device, ArrayView<VkFence> fences, bool wait_all, uint64_t timeout) {
|
||||||
|
assert(fences.is_dense());
|
||||||
|
|
||||||
|
if(fences.size() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(vkWaitForFences(
|
||||||
|
device,
|
||||||
|
fences.size(),
|
||||||
|
fences.data(),
|
||||||
|
wait_all,
|
||||||
|
timeout
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to wait for fences");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
65
src/vk/Fence.h
Normal file
65
src/vk/Fence.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <core/ArrayView.h>
|
||||||
|
|
||||||
|
#include <vk/forward.h>
|
||||||
|
#include <vk/Wrapper.h>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
class Fence: public Wrapper {
|
||||||
|
public:
|
||||||
|
Fence() noexcept;
|
||||||
|
Fence(Context& context, VkFenceCreateFlags flags=0);
|
||||||
|
Fence(const Fence&) = default;
|
||||||
|
Fence(Fence&& other) noexcept;
|
||||||
|
~Fence() noexcept;
|
||||||
|
|
||||||
|
Fence& operator=(const Fence&) = default;
|
||||||
|
Fence& operator=(Fence&& other) noexcept;
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_fence == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkFence() noexcept {
|
||||||
|
return m_fence;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkFence fence() noexcept {
|
||||||
|
return m_fence;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult get_status() const;
|
||||||
|
void reset();
|
||||||
|
void wait(uint64_t timeout=UINT64_MAX) const;
|
||||||
|
|
||||||
|
inline void swap(Fence& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
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:
|
||||||
|
VkFence m_fence = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
void wait_for_fences(VkDevice device, ArrayView<VkFence> fences, bool wait_all=true, uint64_t timeout=UINT64_MAX);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
86
src/vk/Framebuffer.cpp
Normal file
86
src/vk/Framebuffer.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <vk/Framebuffer.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
Framebuffer::Framebuffer() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer::Framebuffer(
|
||||||
|
Context& context,
|
||||||
|
VkRenderPass render_pass,
|
||||||
|
Array<VkImageView> attachments,
|
||||||
|
uint32_t width, uint32_t height, uint32_t layers
|
||||||
|
)
|
||||||
|
: Wrapper(context)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
assert(render_pass);
|
||||||
|
|
||||||
|
VkFramebufferCreateInfo create_info {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||||
|
.renderPass = render_pass,
|
||||||
|
.attachmentCount = uint32_t(attachments.size()),
|
||||||
|
.pAttachments = attachments.data(),
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.layers = layers,
|
||||||
|
};
|
||||||
|
if(vkCreateFramebuffer(
|
||||||
|
context.device(),
|
||||||
|
&create_info,
|
||||||
|
nullptr,
|
||||||
|
&m_framebuffer
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to create framebuffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer::Framebuffer(
|
||||||
|
Context& context,
|
||||||
|
VkRenderPass render_pass,
|
||||||
|
Array<VkImageView> attachments,
|
||||||
|
VkExtent2D extent, uint32_t layers
|
||||||
|
)
|
||||||
|
: Framebuffer(context, render_pass, attachments, extent.width, extent.height, layers)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Framebuffer::Framebuffer(Framebuffer&& other) noexcept
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer::~Framebuffer() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Framebuffer& Framebuffer::operator=(Framebuffer&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Framebuffer::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
vkDestroyFramebuffer(
|
||||||
|
m_context->device(),
|
||||||
|
m_framebuffer,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_framebuffer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
70
src/vk/Framebuffer.h
Normal file
70
src/vk/Framebuffer.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// 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 Framebuffer: public Wrapper {
|
||||||
|
public:
|
||||||
|
Framebuffer() noexcept;
|
||||||
|
Framebuffer(
|
||||||
|
Context& context,
|
||||||
|
VkRenderPass render_pass,
|
||||||
|
Array<VkImageView> attachments,
|
||||||
|
uint32_t width, uint32_t height, uint32_t layers=1
|
||||||
|
);
|
||||||
|
Framebuffer(
|
||||||
|
Context& context,
|
||||||
|
VkRenderPass render_pass,
|
||||||
|
Array<VkImageView> attachments,
|
||||||
|
VkExtent2D extent, uint32_t layers=1
|
||||||
|
);
|
||||||
|
Framebuffer(const Framebuffer&) = default;
|
||||||
|
Framebuffer(Framebuffer&& other) noexcept;
|
||||||
|
~Framebuffer() noexcept;
|
||||||
|
|
||||||
|
Framebuffer& operator=(const Framebuffer&) = default;
|
||||||
|
Framebuffer& operator=(Framebuffer&& other) noexcept;
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_framebuffer == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkFramebuffer() noexcept {
|
||||||
|
return m_framebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkFramebuffer framebuffer() noexcept {
|
||||||
|
return m_framebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(Framebuffer& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
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:
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
376
src/vk/Memory.cpp
Normal file
376
src/vk/Memory.cpp
Normal file
@@ -0,0 +1,376 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#include <vk/Memory.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
MemoryBlock::MemoryBlock() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBlock::MemoryBlock(
|
||||||
|
VkDeviceMemory device_memory,
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkDeviceSize offset,
|
||||||
|
MemoryPage* memory_page,
|
||||||
|
uint32_t memory_type
|
||||||
|
) noexcept
|
||||||
|
: m_size(size)
|
||||||
|
, m_offset(offset)
|
||||||
|
, m_device_memory(device_memory)
|
||||||
|
, m_memory_page(memory_page)
|
||||||
|
, m_memory_type(memory_type)
|
||||||
|
{}
|
||||||
|
|
||||||
|
MemoryBlock::MemoryBlock(MemoryBlock&& other) noexcept
|
||||||
|
: m_size(other.m_size)
|
||||||
|
, m_offset(other.m_offset)
|
||||||
|
, m_device_memory(other.m_device_memory)
|
||||||
|
, m_memory_page(other.m_memory_page)
|
||||||
|
, m_memory_type(other.m_memory_type)
|
||||||
|
{
|
||||||
|
other.m_size = 0;
|
||||||
|
other.m_offset = 0;
|
||||||
|
other.m_device_memory = VK_NULL_HANDLE;
|
||||||
|
other.m_memory_page = nullptr;
|
||||||
|
other.m_memory_type = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBlock::~MemoryBlock() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
free();
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBlock& MemoryBlock::operator=(MemoryBlock&& other) noexcept {
|
||||||
|
if(&other != this) {
|
||||||
|
using std::swap;
|
||||||
|
swap(m_size, other.m_size);
|
||||||
|
swap(m_offset, other.m_offset);
|
||||||
|
swap(m_device_memory, other.m_device_memory);
|
||||||
|
swap(m_memory_page, other.m_memory_page);
|
||||||
|
swap(m_memory_type, other.m_memory_type);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkMemoryType MemoryBlock::memory_type_info(Context& context) const noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(context);
|
||||||
|
|
||||||
|
return context.memory_properties().memoryTypes[m_memory_type];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MemoryBlock::free() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
|
||||||
|
m_memory_page->p_free(*this);
|
||||||
|
|
||||||
|
m_size = 0;
|
||||||
|
m_offset = 0;
|
||||||
|
m_device_memory = VK_NULL_HANDLE;
|
||||||
|
m_memory_page = nullptr;
|
||||||
|
m_memory_type = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Byte* MemoryBlock::map(Context& context) {
|
||||||
|
return map(context, 0, m_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Byte* MemoryBlock::map(Context& context, VkDeviceSize offset, VkDeviceSize size) {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(context);
|
||||||
|
assert(offset + size <= m_size);
|
||||||
|
|
||||||
|
void* ptr;
|
||||||
|
if(vkMapMemory(
|
||||||
|
context.device(),
|
||||||
|
m_device_memory,
|
||||||
|
m_offset + offset,
|
||||||
|
size,
|
||||||
|
0,
|
||||||
|
&ptr
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to map memory");
|
||||||
|
|
||||||
|
return reinterpret_cast<Byte*>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryBlock::unmap(Context& context) noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
|
||||||
|
vkUnmapMemory(
|
||||||
|
context.device(),
|
||||||
|
m_device_memory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MemoryBlock::flush(Context& context) {
|
||||||
|
assert(!is_null());
|
||||||
|
|
||||||
|
VkMappedMemoryRange range {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
|
||||||
|
.memory = m_device_memory,
|
||||||
|
.offset = m_offset,
|
||||||
|
.size = m_size,
|
||||||
|
};
|
||||||
|
if(vkFlushMappedMemoryRanges(
|
||||||
|
context.device(),
|
||||||
|
1,
|
||||||
|
&range
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
std::runtime_error("failed to flush memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryBlock::invalidate(Context& context) {
|
||||||
|
assert(!is_null());
|
||||||
|
|
||||||
|
VkMappedMemoryRange range {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
|
||||||
|
.memory = m_device_memory,
|
||||||
|
.offset = m_offset,
|
||||||
|
.size = m_size,
|
||||||
|
};
|
||||||
|
if(vkInvalidateMappedMemoryRanges(
|
||||||
|
context.device(),
|
||||||
|
1,
|
||||||
|
&range
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
std::runtime_error("failed to invalidate memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MemoryPage::MemoryPage(
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkDeviceMemory device_memory,
|
||||||
|
uint32_t memory_type
|
||||||
|
) noexcept
|
||||||
|
: m_size(size)
|
||||||
|
, m_device_memory(device_memory)
|
||||||
|
, m_blocks{
|
||||||
|
Block{ 0, true },
|
||||||
|
Block{ m_size, false },
|
||||||
|
}
|
||||||
|
, m_memory_type(memory_type)
|
||||||
|
{}
|
||||||
|
|
||||||
|
MemoryPage::MemoryPage(MemoryPage&&) = default;
|
||||||
|
MemoryPage::~MemoryPage() = default;
|
||||||
|
|
||||||
|
MemoryPage& MemoryPage::operator=(MemoryPage&&) = default;
|
||||||
|
|
||||||
|
MemoryBlock MemoryPage::allocate(VkDeviceSize size) noexcept {
|
||||||
|
const auto block_it = find_free_block(size);
|
||||||
|
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{
|
||||||
|
block_it[0].offset + size, true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return MemoryBlock(m_device_memory, size, offset, this, m_memory_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryPage::p_free(MemoryBlock& block) noexcept {
|
||||||
|
assert(block.device_memory() == m_device_memory);
|
||||||
|
|
||||||
|
const auto offset = block.offset();
|
||||||
|
const auto block_it = std::lower_bound(
|
||||||
|
m_blocks.begin(),
|
||||||
|
m_blocks.end(),
|
||||||
|
Block{ offset, false },
|
||||||
|
[] (const Block& lhs, const Block& rhs) {
|
||||||
|
return lhs.offset < rhs.offset;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert(block_it != m_blocks.end() && block_it->offset == offset);
|
||||||
|
|
||||||
|
// Merge with next block if also free
|
||||||
|
if (block_it[1].is_free) {
|
||||||
|
m_blocks.erase(std::next(block_it));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge with previous block if also free
|
||||||
|
if (block_it != m_blocks.begin() && block_it[-1].is_free) {
|
||||||
|
m_blocks.erase(block_it);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
block_it->is_free = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryPage::free_device_memory(Context& context) noexcept {
|
||||||
|
vkFreeMemory(
|
||||||
|
context.device(),
|
||||||
|
m_device_memory,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_size = 0;
|
||||||
|
m_device_memory = VK_NULL_HANDLE;
|
||||||
|
m_blocks.clear();
|
||||||
|
m_memory_type = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MemoryPage::BlockList::iterator MemoryPage::find_free_block(VkDeviceSize size) {
|
||||||
|
const auto block_end = std::prev(m_blocks.end());
|
||||||
|
for(auto block_it = m_blocks.begin(); block_it != block_end; ++block_it) {
|
||||||
|
if(block_it[0].is_free && block_it[1].offset - block_it[0].offset >= size)
|
||||||
|
return block_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_blocks.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Allocator::Allocator(Context* context) noexcept
|
||||||
|
: m_context(context)
|
||||||
|
, m_memory_properties{}
|
||||||
|
, m_page_map{}
|
||||||
|
{
|
||||||
|
vkGetPhysicalDeviceMemoryProperties(
|
||||||
|
m_context->physical_device(),
|
||||||
|
&m_memory_properties
|
||||||
|
);
|
||||||
|
|
||||||
|
for(auto& next_page_size: m_next_page_sizes)
|
||||||
|
next_page_size = BasePageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
Allocator::~Allocator() = default;
|
||||||
|
|
||||||
|
|
||||||
|
int32_t Allocator::find_memory_type(
|
||||||
|
uint32_t type_mask,
|
||||||
|
VkMemoryPropertyFlags required_properties
|
||||||
|
) noexcept {
|
||||||
|
for(uint32_t type_index = 0;
|
||||||
|
type_index < m_memory_properties.memoryTypeCount;
|
||||||
|
type_index += 1
|
||||||
|
) {
|
||||||
|
if((type_mask & (1 << type_index)) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto memory_properties =
|
||||||
|
m_memory_properties.memoryTypes[type_index].propertyFlags;
|
||||||
|
if((memory_properties & required_properties) == required_properties)
|
||||||
|
return type_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t Allocator::find_memory_type(
|
||||||
|
uint32_t type_mask,
|
||||||
|
std::initializer_list<VkMemoryPropertyFlags> properties_list
|
||||||
|
) noexcept {
|
||||||
|
for(const auto& properties: properties_list) {
|
||||||
|
const auto memory_type = find_memory_type(type_mask, properties);
|
||||||
|
if(memory_type >= 0)
|
||||||
|
return memory_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBlock Allocator::allocate(
|
||||||
|
VkDeviceSize size,
|
||||||
|
uint32_t memory_type
|
||||||
|
) noexcept {
|
||||||
|
assert(memory_type < VK_MAX_MEMORY_TYPES);
|
||||||
|
|
||||||
|
auto& pages = m_page_map[memory_type];
|
||||||
|
|
||||||
|
for(auto& page: pages) {
|
||||||
|
auto block = page.allocate(size);
|
||||||
|
if(block)
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkDeviceSize new_page_size = std::max(
|
||||||
|
size,
|
||||||
|
m_next_page_sizes[memory_type]
|
||||||
|
);
|
||||||
|
|
||||||
|
VkMemoryAllocateInfo allocate_info {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||||
|
.allocationSize = new_page_size,
|
||||||
|
.memoryTypeIndex = memory_type,
|
||||||
|
};
|
||||||
|
VkDeviceMemory device_memory;
|
||||||
|
vkAllocateMemory(
|
||||||
|
m_context->device(),
|
||||||
|
&allocate_info,
|
||||||
|
nullptr,
|
||||||
|
&device_memory
|
||||||
|
);
|
||||||
|
|
||||||
|
pages.emplace_back(
|
||||||
|
new_page_size,
|
||||||
|
device_memory,
|
||||||
|
memory_type
|
||||||
|
);
|
||||||
|
|
||||||
|
if(new_page_size == m_next_page_sizes[memory_type])
|
||||||
|
m_next_page_sizes[memory_type] *= 2;
|
||||||
|
|
||||||
|
return pages.back().allocate(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBlock Allocator::allocate(
|
||||||
|
VkDeviceSize size,
|
||||||
|
uint32_t type_mask,
|
||||||
|
VkMemoryPropertyFlags properties
|
||||||
|
) noexcept {
|
||||||
|
const auto memory_type = find_memory_type(
|
||||||
|
type_mask,
|
||||||
|
properties
|
||||||
|
);
|
||||||
|
if(memory_type < 0)
|
||||||
|
return MemoryBlock();
|
||||||
|
|
||||||
|
return allocate(size, uint32_t(memory_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBlock Allocator::allocate(
|
||||||
|
VkDeviceSize size,
|
||||||
|
uint32_t type_mask,
|
||||||
|
std::initializer_list<VkMemoryPropertyFlags> properties_list
|
||||||
|
) noexcept {
|
||||||
|
const auto memory_type = find_memory_type(
|
||||||
|
type_mask,
|
||||||
|
properties_list
|
||||||
|
);
|
||||||
|
if(memory_type < 0)
|
||||||
|
return MemoryBlock();
|
||||||
|
|
||||||
|
return allocate(size, uint32_t(memory_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Allocator::free_all_pages() noexcept {
|
||||||
|
for(uint32_t memory_type = 0; memory_type < VK_MAX_MEMORY_TYPES; memory_type += 1) {
|
||||||
|
for(auto& page: m_page_map[memory_type])
|
||||||
|
page.free_device_memory(*m_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
188
src/vk/Memory.h
Normal file
188
src/vk/Memory.h
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <core/types.h>
|
||||||
|
|
||||||
|
#include <vk/forward.h>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryBlock {
|
||||||
|
public:
|
||||||
|
MemoryBlock() noexcept;
|
||||||
|
MemoryBlock(
|
||||||
|
VkDeviceMemory device_memory,
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkDeviceSize offset,
|
||||||
|
MemoryPage* memory_page,
|
||||||
|
uint32_t memory_type
|
||||||
|
) noexcept;
|
||||||
|
MemoryBlock(const MemoryBlock&) = delete;
|
||||||
|
MemoryBlock(MemoryBlock&& other) noexcept;
|
||||||
|
~MemoryBlock() noexcept;
|
||||||
|
|
||||||
|
MemoryBlock& operator=(const MemoryBlock&) = delete;
|
||||||
|
MemoryBlock& operator=(MemoryBlock&& other) noexcept;
|
||||||
|
|
||||||
|
inline explicit operator bool() const noexcept {
|
||||||
|
return m_size != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkDeviceSize size() const noexcept {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkDeviceSize offset() const noexcept {
|
||||||
|
return m_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkDeviceMemory device_memory() const noexcept {
|
||||||
|
return m_device_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t memory_type() const noexcept {
|
||||||
|
return m_memory_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MemoryPage* memory_page() const noexcept {
|
||||||
|
return m_memory_page;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkMemoryType memory_type_info(Context& context) const noexcept;
|
||||||
|
|
||||||
|
void free() noexcept;
|
||||||
|
|
||||||
|
Byte* map(Context& context);
|
||||||
|
Byte* map(Context& context, VkDeviceSize offset, VkDeviceSize size);
|
||||||
|
void unmap(Context& context) noexcept;
|
||||||
|
|
||||||
|
void flush(Context& context);
|
||||||
|
void invalidate(Context& context);
|
||||||
|
|
||||||
|
private:
|
||||||
|
VkDeviceSize m_size = 0;
|
||||||
|
VkDeviceSize m_offset = 0;
|
||||||
|
VkDeviceMemory m_device_memory = VK_NULL_HANDLE;
|
||||||
|
MemoryPage* m_memory_page = nullptr;
|
||||||
|
uint32_t m_memory_type = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryPage {
|
||||||
|
public:
|
||||||
|
MemoryPage(
|
||||||
|
VkDeviceSize size,
|
||||||
|
VkDeviceMemory device_memory,
|
||||||
|
uint32_t memory_type
|
||||||
|
) noexcept;
|
||||||
|
MemoryPage(const MemoryPage&) = delete;
|
||||||
|
MemoryPage(MemoryPage&&);
|
||||||
|
~MemoryPage();
|
||||||
|
|
||||||
|
MemoryPage& operator=(const MemoryPage&) = delete;
|
||||||
|
MemoryPage& operator=(MemoryPage&&);
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_size == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkDeviceSize size() const noexcept {
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkDeviceMemory device_memory() const noexcept {
|
||||||
|
return m_device_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t memory_type() const noexcept {
|
||||||
|
return m_memory_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBlock allocate(VkDeviceSize size) noexcept;
|
||||||
|
void p_free(MemoryBlock& block) noexcept;
|
||||||
|
|
||||||
|
void free_device_memory(Context& context) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Block {
|
||||||
|
VkDeviceSize offset;
|
||||||
|
bool is_free;
|
||||||
|
};
|
||||||
|
using BlockList = std::vector<Block>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlockList::iterator find_free_block(VkDeviceSize size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
VkDeviceSize m_size = 0;
|
||||||
|
VkDeviceMemory m_device_memory = VK_NULL_HANDLE;
|
||||||
|
BlockList m_blocks;
|
||||||
|
uint32_t m_memory_type = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Allocator {
|
||||||
|
public:
|
||||||
|
Allocator(Context* context) noexcept;
|
||||||
|
Allocator(const Allocator&) = delete;
|
||||||
|
Allocator(Allocator&&) = delete;
|
||||||
|
~Allocator();
|
||||||
|
|
||||||
|
Allocator& operator=(const Allocator&) = delete;
|
||||||
|
Allocator& operator=(Allocator&) = delete;
|
||||||
|
|
||||||
|
int32_t find_memory_type(
|
||||||
|
uint32_t type_mask,
|
||||||
|
VkMemoryPropertyFlags properties
|
||||||
|
) noexcept;
|
||||||
|
int32_t find_memory_type(
|
||||||
|
uint32_t type_mask,
|
||||||
|
std::initializer_list<VkMemoryPropertyFlags> properties_list
|
||||||
|
) noexcept;
|
||||||
|
|
||||||
|
MemoryBlock allocate(
|
||||||
|
VkDeviceSize size,
|
||||||
|
uint32_t memory_type
|
||||||
|
) noexcept;
|
||||||
|
MemoryBlock allocate(
|
||||||
|
VkDeviceSize size,
|
||||||
|
uint32_t type_mask,
|
||||||
|
VkMemoryPropertyFlags properties
|
||||||
|
) noexcept;
|
||||||
|
MemoryBlock allocate(
|
||||||
|
VkDeviceSize size,
|
||||||
|
uint32_t type_mask,
|
||||||
|
std::initializer_list<VkMemoryPropertyFlags> properties_list
|
||||||
|
) noexcept;
|
||||||
|
|
||||||
|
void free_all_pages() noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using PageList = std::vector<MemoryPage>;
|
||||||
|
|
||||||
|
static constexpr VkDeviceSize BasePageSize = 1 << 24;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Context* m_context = nullptr;
|
||||||
|
VkPhysicalDeviceMemoryProperties m_memory_properties;
|
||||||
|
PageList m_page_map[VK_MAX_MEMORY_TYPES];
|
||||||
|
VkDeviceSize m_next_page_sizes[VK_MAX_MEMORY_TYPES];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
71
src/vk/Pipeline.cpp
Normal file
71
src/vk/Pipeline.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <vk/Pipeline.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
Pipeline::Pipeline() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline::Pipeline(Context& context, VkPipeline pipeline)
|
||||||
|
: Wrapper(context)
|
||||||
|
, m_pipeline(pipeline)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
assert(m_pipeline != VK_NULL_HANDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline::Pipeline(Context& context, VkGraphicsPipelineCreateInfo create_info)
|
||||||
|
: Wrapper(context)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
if(vkCreateGraphicsPipelines(
|
||||||
|
m_context->device(),
|
||||||
|
VK_NULL_HANDLE,
|
||||||
|
1, &create_info,
|
||||||
|
nullptr,
|
||||||
|
&m_pipeline
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to create graphic pipeline");
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline::Pipeline(Pipeline&& other) noexcept
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline::~Pipeline() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pipeline& Pipeline::operator=(Pipeline&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Pipeline::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
vkDestroyPipeline(
|
||||||
|
m_context->device(),
|
||||||
|
m_pipeline,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_pipeline = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
58
src/vk/Pipeline.h
Normal file
58
src/vk/Pipeline.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vk/forward.h>
|
||||||
|
#include <vk/Wrapper.h>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
class Pipeline: public Wrapper {
|
||||||
|
public:
|
||||||
|
Pipeline() noexcept;
|
||||||
|
Pipeline(Context& context, VkPipeline pipeline);
|
||||||
|
Pipeline(Context& context, VkGraphicsPipelineCreateInfo create_info);
|
||||||
|
Pipeline(const Pipeline&) = default;
|
||||||
|
Pipeline(Pipeline&& other) noexcept;
|
||||||
|
~Pipeline() noexcept;
|
||||||
|
|
||||||
|
Pipeline& operator=(const Pipeline&) = default;
|
||||||
|
Pipeline& operator=(Pipeline&& other) noexcept;
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_pipeline == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkPipeline() noexcept {
|
||||||
|
return m_pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkPipeline pipeline() noexcept {
|
||||||
|
return m_pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(Pipeline& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
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:
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
63
src/vk/RenderPass.cpp
Normal file
63
src/vk/RenderPass.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <vk/RenderPass.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
RenderPass::RenderPass() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderPass::RenderPass(Context& context, const VkRenderPassCreateInfo& create_info)
|
||||||
|
: Wrapper(context)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
if(vkCreateRenderPass(
|
||||||
|
context.device(),
|
||||||
|
&create_info,
|
||||||
|
nullptr,
|
||||||
|
&m_render_pass
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to create render pass");
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderPass::RenderPass(RenderPass&& other) noexcept
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderPass::~RenderPass() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RenderPass& RenderPass::operator=(RenderPass&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RenderPass::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
vkDestroyRenderPass(
|
||||||
|
m_context->device(),
|
||||||
|
m_render_pass,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_render_pass = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
57
src/vk/RenderPass.h
Normal file
57
src/vk/RenderPass.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 RenderPass: public Wrapper {
|
||||||
|
public:
|
||||||
|
RenderPass() noexcept;
|
||||||
|
RenderPass(Context& context, const VkRenderPassCreateInfo& create_info);
|
||||||
|
RenderPass(const RenderPass&) = default;
|
||||||
|
RenderPass(RenderPass&& other) noexcept;
|
||||||
|
~RenderPass() noexcept;
|
||||||
|
|
||||||
|
RenderPass& operator=(const RenderPass&) = default;
|
||||||
|
RenderPass& operator=(RenderPass&& other) noexcept;
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_render_pass == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkRenderPass() noexcept {
|
||||||
|
return m_render_pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkRenderPass render_pass() noexcept {
|
||||||
|
return m_render_pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(RenderPass& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
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:
|
||||||
|
VkRenderPass m_render_pass = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
65
src/vk/Semaphore.cpp
Normal file
65
src/vk/Semaphore.cpp
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <vk/Semaphore.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
Semaphore::Semaphore() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
Semaphore::Semaphore(Context& context)
|
||||||
|
: Wrapper(context)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
VkSemaphoreCreateInfo create_info {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||||
|
};
|
||||||
|
if(vkCreateSemaphore(
|
||||||
|
context.device(),
|
||||||
|
&create_info,
|
||||||
|
nullptr,
|
||||||
|
&m_semaphore
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to create semaphore");
|
||||||
|
}
|
||||||
|
|
||||||
|
Semaphore::Semaphore(Semaphore&& other) noexcept
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
Semaphore::~Semaphore() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Semaphore& Semaphore::operator=(Semaphore&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Semaphore::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
vkDestroySemaphore(
|
||||||
|
m_context->device(),
|
||||||
|
m_semaphore,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_semaphore = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
57
src/vk/Semaphore.h
Normal file
57
src/vk/Semaphore.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 Semaphore: public Wrapper {
|
||||||
|
public:
|
||||||
|
Semaphore() noexcept;
|
||||||
|
Semaphore(Context& context);
|
||||||
|
Semaphore(const Semaphore&) = default;
|
||||||
|
Semaphore(Semaphore&& other) noexcept;
|
||||||
|
~Semaphore() noexcept;
|
||||||
|
|
||||||
|
Semaphore& operator=(const Semaphore&) = default;
|
||||||
|
Semaphore& operator=(Semaphore&& other) noexcept;
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_semaphore == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkSemaphore() noexcept {
|
||||||
|
return m_semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkSemaphore semaphore() noexcept {
|
||||||
|
return m_semaphore;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(Semaphore& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
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:
|
||||||
|
VkSemaphore m_semaphore = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
71
src/vk/ShaderModule.cpp
Normal file
71
src/vk/ShaderModule.cpp
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <vk/ShaderModule.h>
|
||||||
|
#include <vk/Context.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
ShaderModule::ShaderModule() noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderModule::ShaderModule(Context& context, const std::vector<char>& code)
|
||||||
|
: Wrapper(context)
|
||||||
|
{
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
VkShaderModuleCreateInfo create_info {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||||
|
.codeSize = code.size(),
|
||||||
|
.pCode = (uint32_t*)code.data(),
|
||||||
|
};
|
||||||
|
if(vkCreateShaderModule(
|
||||||
|
context.device(),
|
||||||
|
&create_info,
|
||||||
|
nullptr,
|
||||||
|
&m_shader_module
|
||||||
|
) != VK_SUCCESS)
|
||||||
|
throw std::runtime_error("failed to create shader module");
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderModule::ShaderModule(Context& context, const char* path)
|
||||||
|
: ShaderModule(context, read_binary_file(path))
|
||||||
|
{}
|
||||||
|
|
||||||
|
ShaderModule::ShaderModule(ShaderModule&& other) noexcept
|
||||||
|
{
|
||||||
|
swap(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderModule::~ShaderModule() noexcept {
|
||||||
|
if(!is_null())
|
||||||
|
destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ShaderModule& ShaderModule::operator=(ShaderModule&& other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
if(other)
|
||||||
|
other.destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ShaderModule::destroy() noexcept {
|
||||||
|
assert(!is_null());
|
||||||
|
assert(m_context);
|
||||||
|
|
||||||
|
vkDestroyShaderModule(
|
||||||
|
m_context->device(),
|
||||||
|
m_shader_module,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
m_shader_module = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
60
src/vk/ShaderModule.h
Normal file
60
src/vk/ShaderModule.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vk/forward.h>
|
||||||
|
#include <vk/Wrapper.h>
|
||||||
|
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
class ShaderModule: public Wrapper {
|
||||||
|
public:
|
||||||
|
ShaderModule() noexcept;
|
||||||
|
ShaderModule(Context& context, const std::vector<char>& code);
|
||||||
|
ShaderModule(Context& context, const char* path);
|
||||||
|
ShaderModule(const ShaderModule&) = default;
|
||||||
|
ShaderModule(ShaderModule&& other) noexcept;
|
||||||
|
~ShaderModule() noexcept;
|
||||||
|
|
||||||
|
ShaderModule& operator=(const ShaderModule&) = default;
|
||||||
|
ShaderModule& operator=(ShaderModule&& other) noexcept;
|
||||||
|
|
||||||
|
explicit inline operator bool() const noexcept {
|
||||||
|
return !is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_null() const noexcept {
|
||||||
|
return m_shader_module == VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator VkShaderModule() noexcept {
|
||||||
|
return m_shader_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline VkShaderModule shader_module() noexcept {
|
||||||
|
return m_shader_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(ShaderModule& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
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:
|
||||||
|
VkShaderModule m_shader_module = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <Vulkan/Swapchain.h>
|
// Copyright 2022 Simon Boyé
|
||||||
|
#include <vk/Swapchain.h>
|
||||||
|
|
||||||
#include <Logger.h>
|
#include <core/Logger.h>
|
||||||
|
|
||||||
#include <SDL2/SDL_vulkan.h>
|
#include <SDL2/SDL_vulkan.h>
|
||||||
|
|
||||||
@@ -10,7 +11,7 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
SwapchainSettings::SwapchainSettings(Context* context)
|
SwapchainSettings::SwapchainSettings(Context* context)
|
||||||
@@ -79,12 +80,12 @@ VkImageView Swapchain::image_view(size_t image_index) {
|
|||||||
return m_image_resources[image_index].view;
|
return m_image_resources[image_index].view;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSemaphore Swapchain::ready_to_render() {
|
Semaphore& Swapchain::ready_to_render() {
|
||||||
return m_frame_resources[m_frame_resources_index].ready_to_render;
|
return m_frame_resources[m_frame_resources_index].ready_to_render;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFence Swapchain::render_done() {
|
Fence& Swapchain::render_done() {
|
||||||
return m_frame_resources[m_frame_resources_index].render_done;
|
return *m_frame_resources[m_frame_resources_index].render_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::initialize(const SwapchainSettings& settings) {
|
void Swapchain::initialize(const SwapchainSettings& settings) {
|
||||||
@@ -111,13 +112,8 @@ void Swapchain::begin_frame() {
|
|||||||
// frame_resources are used cyclically. We wait for the current one to be
|
// frame_resources are used cyclically. We wait for the current one to be
|
||||||
// done rendering to not render more that MAX_FRAMES_IN_FLIGHT frames at
|
// done rendering to not render more that MAX_FRAMES_IN_FLIGHT frames at
|
||||||
// the same time.
|
// the same time.
|
||||||
if(frame_resources.render_done != VK_NULL_HANDLE) {
|
if(frame_resources.render_done) {
|
||||||
vkWaitForFences(
|
frame_resources.render_done->wait();
|
||||||
m_context->device(),
|
|
||||||
1, &frame_resources.render_done,
|
|
||||||
VK_TRUE,
|
|
||||||
UINT64_MAX
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool acquired_image = false;
|
bool acquired_image = false;
|
||||||
@@ -141,27 +137,22 @@ void Swapchain::begin_frame() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info() << "begin frame " << m_frame_index
|
// logger.info() << "begin frame " << m_frame_index
|
||||||
<< ": image " << m_current_image_index
|
// << ": image " << m_current_image_index
|
||||||
<< ", frame: " << m_frame_resources_index;
|
// << ", frame: " << m_frame_resources_index;
|
||||||
|
|
||||||
auto& image_resources = m_image_resources[m_current_image_index];
|
auto& image_resources = m_image_resources[m_current_image_index];
|
||||||
|
|
||||||
// In case vkAcquireNextImageKHR doesn't return images in-order, we wait
|
// In case vkAcquireNextImageKHR doesn't return images in-order, we wait
|
||||||
// again to be sure.
|
// again to be sure.
|
||||||
if(image_resources.render_done != VK_NULL_HANDLE) {
|
if(image_resources.render_done != VK_NULL_HANDLE) {
|
||||||
vkWaitForFences(
|
image_resources.render_done.wait();
|
||||||
m_context->device(),
|
|
||||||
1, &image_resources.render_done,
|
|
||||||
VK_TRUE,
|
|
||||||
UINT64_MAX
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember the right frame to wait for.
|
// Remember the right frame to wait for.
|
||||||
frame_resources.render_done = image_resources.render_done;
|
frame_resources.render_done = &image_resources.render_done;
|
||||||
|
|
||||||
vkResetFences(m_context->device(), 1, &frame_resources.render_done);
|
frame_resources.render_done->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::swap_buffers(Array<VkSemaphore> wait_semaphores) {
|
void Swapchain::swap_buffers(Array<VkSemaphore> wait_semaphores) {
|
||||||
@@ -296,13 +287,13 @@ void Swapchain::create() {
|
|||||||
swapchain_images
|
swapchain_images
|
||||||
);
|
);
|
||||||
|
|
||||||
m_image_resources.resize(image_count, {});
|
|
||||||
for(size_t index = 0; index < image_count; index += 1) {
|
for(size_t index = 0; index < image_count; index += 1) {
|
||||||
auto& image_resources = m_image_resources[index];
|
m_image_resources.emplace_back();
|
||||||
|
auto& image_resources = m_image_resources.back();
|
||||||
|
|
||||||
image_resources.image = swapchain_images[index];
|
image_resources.image = swapchain_images[index];
|
||||||
|
|
||||||
VkImageViewCreateInfo view_info {
|
image_resources.view = ImageView(*m_context, VkImageViewCreateInfo {
|
||||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||||
.image = image_resources.image,
|
.image = image_resources.image,
|
||||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||||
@@ -320,66 +311,28 @@ void Swapchain::create() {
|
|||||||
.baseArrayLayer = 0,
|
.baseArrayLayer = 0,
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
|
|
||||||
if(vkCreateImageView(
|
image_resources.render_done = Fence(*m_context, VK_FENCE_CREATE_SIGNALED_BIT);
|
||||||
m_context->device(),
|
|
||||||
&view_info,
|
|
||||||
nullptr,
|
|
||||||
&image_resources.view
|
|
||||||
) != VK_SUCCESS) {
|
|
||||||
throw std::runtime_error("failed to create swapchain image view");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFenceCreateInfo fence_info {
|
m_frame_resources.clear();
|
||||||
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
for(int index = 0; index < MAX_FRAMES_IN_FLIGHT; index += 1) {
|
||||||
.flags = VK_FENCE_CREATE_SIGNALED_BIT,
|
m_frame_resources.emplace_back(FrameResources {
|
||||||
};
|
.ready_to_render = Semaphore(*m_context),
|
||||||
if(vkCreateFence(
|
});
|
||||||
m_context->device(),
|
|
||||||
&fence_info,
|
|
||||||
nullptr,
|
|
||||||
&image_resources.render_done
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create fence");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_frame_resources.resize(MAX_FRAMES_IN_FLIGHT, {});
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
for(auto& frame_resources: m_frame_resources) {
|
|
||||||
VkSemaphoreCreateInfo semaphore_info {
|
|
||||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
|
||||||
};
|
|
||||||
if(vkCreateSemaphore(
|
|
||||||
m_context->device(),
|
|
||||||
&semaphore_info,
|
|
||||||
nullptr,
|
|
||||||
&frame_resources.ready_to_render
|
|
||||||
) != VK_SUCCESS)
|
|
||||||
throw std::runtime_error("failed to create semaphore");
|
|
||||||
|
|
||||||
index += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& callback: m_creation_callbacks)
|
for(auto& callback: m_creation_callbacks)
|
||||||
callback(image_count);
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Swapchain::destroy() {
|
void Swapchain::destroy() {
|
||||||
for(auto& callback: m_destruction_callbacks)
|
for(auto& callback: m_destruction_callbacks)
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
for(auto& frame_resources: m_frame_resources) {
|
m_frame_resources.clear();
|
||||||
m_context->destroy_semaphore(frame_resources.ready_to_render);
|
m_image_resources.clear();
|
||||||
// This is a reference to an ImageResources.render_done
|
|
||||||
frame_resources.render_done = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto& image_resources: m_image_resources) {
|
|
||||||
m_context->destroy_fence(image_resources.render_done);
|
|
||||||
m_context->destroy_image_view(image_resources.view);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_context->destroy_swapchain(m_swapchain);
|
m_context->destroy_swapchain(m_swapchain);
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Vulkan/Context.h>
|
#include <vk/Context.h>
|
||||||
#include <utils.h>
|
#include <vk/Semaphore.h>
|
||||||
|
#include <vk/ImageView.h>
|
||||||
|
|
||||||
|
#include <core/utils.h>
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -12,7 +17,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
class Swapchain;
|
class Swapchain;
|
||||||
@@ -37,7 +42,7 @@ public:
|
|||||||
static constexpr uint32_t CURRENT_IMAGE_INDEX = UINT32_MAX;
|
static constexpr uint32_t CURRENT_IMAGE_INDEX = UINT32_MAX;
|
||||||
static constexpr uint32_t MAX_FRAMES_IN_FLIGHT = 2;
|
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()>;
|
using DestructionCallback = std::function<void()>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -55,8 +60,8 @@ public:
|
|||||||
VkImage image(size_t image_index);
|
VkImage image(size_t image_index);
|
||||||
VkImageView image_view();
|
VkImageView image_view();
|
||||||
VkImageView image_view(size_t image_index);
|
VkImageView image_view(size_t image_index);
|
||||||
VkSemaphore ready_to_render();
|
Semaphore& ready_to_render();
|
||||||
VkFence render_done();
|
Fence& render_done();
|
||||||
|
|
||||||
void initialize(const SwapchainSettings& settings);
|
void initialize(const SwapchainSettings& settings);
|
||||||
void shutdown();
|
void shutdown();
|
||||||
@@ -72,13 +77,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
struct ImageResources {
|
struct ImageResources {
|
||||||
VkImage image = VK_NULL_HANDLE;
|
VkImage image = VK_NULL_HANDLE;
|
||||||
VkImageView view = VK_NULL_HANDLE;
|
ImageView view;
|
||||||
VkFence render_done = VK_NULL_HANDLE;
|
Fence render_done;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FrameResources {
|
struct FrameResources {
|
||||||
VkSemaphore ready_to_render = VK_NULL_HANDLE;
|
Semaphore ready_to_render;
|
||||||
VkFence render_done = VK_NULL_HANDLE;
|
Fence* render_done = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
23
src/vk/forward.h
Normal file
23
src/vk/forward.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2022 Simon Boyé
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
namespace vk {
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryPage;
|
||||||
|
class MemoryBlock;
|
||||||
|
class Allocator;
|
||||||
|
|
||||||
|
using MemoryPageSP = std::shared_ptr<MemoryBlock>;
|
||||||
|
|
||||||
|
class Context;
|
||||||
|
class ContextSettings;
|
||||||
|
|
||||||
|
class Buffer;
|
||||||
|
class RenderPass;
|
||||||
|
class Fence;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user