From f1263d46c71576fd68e25c2937f6c1e75ca84267 Mon Sep 17 00:00:00 2001 From: Argon Date: Thu, 15 Dec 2022 10:28:00 +0800 Subject: [PATCH] first commit --- .gitignore | 11 + basic_model.hpp | 317 ++++++++ build.bat | 4 + image.cpp | 31 + image.hpp | 15 + main.cpp | 510 +++++++++++++ model_object.hpp | 329 +++++++++ rs_iter.hpp | 500 +++++++++++++ rusty.hpp | 258 +++++++ shader/shader.frag | 40 ++ shader/shader.vert | 40 ++ tmath.hpp | 253 +++++++ vulkan.cpp | 1707 ++++++++++++++++++++++++++++++++++++++++++++ vulkan.hpp | 373 ++++++++++ 14 files changed, 4388 insertions(+) create mode 100644 .gitignore create mode 100644 basic_model.hpp create mode 100644 build.bat create mode 100644 image.cpp create mode 100644 image.hpp create mode 100644 main.cpp create mode 100644 model_object.hpp create mode 100644 rs_iter.hpp create mode 100644 rusty.hpp create mode 100644 shader/shader.frag create mode 100644 shader/shader.vert create mode 100644 tmath.hpp create mode 100644 vulkan.cpp create mode 100644 vulkan.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb11b75 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.vscode/ +.idea/ +build/ +deps/ +res/ +*.obj +*.pdb +*.dll +*.exe +*.spv +*.ilk diff --git a/basic_model.hpp b/basic_model.hpp new file mode 100644 index 0000000..bf0b713 --- /dev/null +++ b/basic_model.hpp @@ -0,0 +1,317 @@ +#ifndef _BASIC_MODEL_HPP_ +#define _BASIC_MODEL_HPP_ + +#include + +#include "tmath.hpp" +#include "rusty.hpp" + +struct Vertex { + tmath::fvec3 position; + tmath::fvec3 normal; + tmath::fvec2 texcoord; + // 0 - attribute 1 - texture + tmath::ivec2 index; +}; + +struct VertexAttribute { + tmath::fmat4 matrix = tmath::fmat4::identity(); + tmath::fmat4 nmatrix = tmath::fmat4::identity(); + tmath::fvec4 color = {1.0f, 1.0f, 1.0f, 1.0f}; + tmath::fvec4 mixcolor = {0.0f, 0.0f, 0.0f, 0.0f}; +}; + +struct ModelUnit { + uint32_t vert_offset; + uint32_t vert_count; + uint32_t index_offset; + uint32_t index_count; +}; + +class VertCollection { +public: + ModelUnit gen_square(float tx = 0.0f, float ty = 0.0f, float tw = 1.0f, float th = 1.0f) { + uint32_t offset = out_v.size(); + uint32_t ioffset = out_i.size(); + // vert_count = 4; + out_v.push_back({{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {tx, ty}}); + out_v.push_back({{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {tx, ty + th}}); + out_v.push_back({{1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {tx + tw, ty + th}}); + out_v.push_back({{1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {tx + tw, ty}}); + out_i.insert(out_i.end(), {0 + offset, 1 + offset, 2 + offset, 2 + offset, 3 + offset, 0 + offset}); + return ModelUnit{offset, (uint32_t)out_v.size() - offset, ioffset, (uint32_t)out_i.size() - ioffset}; + } + + ModelUnit gen_texture(uint32_t tilew, uint32_t tileh, float posx, float posy, float texw, float texh) { + uint32_t offset = out_v.size(); + uint32_t ioffset = out_i.size(); + float tx = 1.0f / tilew; + float ty = 1.0f / tileh; + float mx = texw * tx; + float my = texh * ty; + // vert_count = (tilew + 1) * (tileyh + 1); + for(uint32_t y = 0; y <= tileh; ++y) { + for(uint32_t x = 0; x <= tilew; ++x) + out_v.push_back({{posx + x * mx, posy + y * my, 0.0f}, {0.0f, 0.0f, 1.0f}, {x * tx, y * ty}}); + } + for(uint32_t y = 0; y < tileh; ++y) { + for(uint32_t x = 0; x < tilew; ++x) { + uint32_t idx = y * (tileh + 1) + x + offset; + out_i.insert(out_i.end(), {idx, idx + tileh + 1, idx + 1, idx + 1, idx + tileh + 1, idx + tileh + 2}); + } + } + return ModelUnit{offset, (uint32_t)out_v.size() - offset, ioffset, (uint32_t)out_i.size() - ioffset}; + } + + ModelUnit gen_cube() { + uint32_t offset = out_v.size(); + uint32_t ioffset = out_i.size(); + // vert_count = 24; + out_v.push_back({{1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}); + out_v.push_back({{1.0f, 1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}); + out_v.push_back({{1.0f, -1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 1.0f}}); + out_v.push_back({{1.0f, -1.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}); + out_i.insert(out_i.end(), {0 + offset, 1 + offset, 2 + offset, 2 + offset, 3 + offset, 0 + offset}); + out_v.push_back({{1.0f, -1.0f, 1.0f}, {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f}}); + out_v.push_back({{1.0f, -1.0f, -1.0f}, {0.0f, -1.0f, 0.0f}, {0.0f, 1.0f}}); + out_v.push_back({{-1.0f, -1.0f, -1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 1.0f}}); + out_v.push_back({{-1.0f, -1.0f, 1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.0f}}); + out_i.insert(out_i.end(), {4 + offset, 5 + offset, 6 + offset, 6 + offset, 7 + offset, 4 + offset}); + out_v.push_back({{-1.0f, -1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}); + out_v.push_back({{-1.0f, -1.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}}); + out_v.push_back({{-1.0f, 1.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}, {1.0f, 1.0f}}); + out_v.push_back({{-1.0f, 1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}}); + out_i.insert(out_i.end(), {8 + offset, 9 + offset, 10 + offset, 10 + offset, 11 + offset, 8 + offset}); + out_v.push_back({{-1.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}); + out_v.push_back({{-1.0f, 1.0f, -1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f}}); + out_v.push_back({{1.0f, 1.0f, -1.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}}); + out_v.push_back({{1.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}); + out_i.insert(out_i.end(), {12 + offset, 13 + offset, 14 + offset, 14 + offset, 15 + offset, 12 + offset}); + out_v.push_back({{-1.0f, -1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}); + out_v.push_back({{-1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}); + out_v.push_back({{1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}); + out_v.push_back({{1.0f, -1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}}); + out_i.insert(out_i.end(), {16 + offset, 17 + offset, 18 + offset, 18 + offset, 19 + offset, 16 + offset}); + out_v.push_back({{-1.0f, 1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f}}); + out_v.push_back({{-1.0f, -1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f}}); + out_v.push_back({{1.0f, -1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f}}); + out_v.push_back({{1.0f, 1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 0.0f}}); + out_i.insert(out_i.end(), {20 + offset, 21 + offset, 22 + offset, 22 + offset, 23 + offset, 20 + offset}); + return ModelUnit{offset, (uint32_t)out_v.size() - offset, ioffset, (uint32_t)out_i.size() - ioffset}; + } + + ModelUnit gen_octahedron() { + const float nv = .5773503f; // 1.0 / sqrt(3) + uint32_t offset = out_v.size(); + uint32_t ioffset = out_i.size(); + // vert_count = 24; + out_v.push_back({{1.0f, 0.0f, 0.0f}, {nv, nv, nv}}); + out_v.push_back({{0.0f, 0.0f, 1.0f}, {nv, nv, nv}}); + out_v.push_back({{0.0f, 1.0f, 0.0f}, {nv, nv, nv}}); + out_i.insert(out_i.end(), {0 + offset, 1 + offset, 2 + offset}); + out_v.push_back({{0.0f, -1.0f, 0.0f}, {nv, -nv, nv}}); + out_v.push_back({{0.0f, 0.0f, 1.0f}, {nv, -nv, nv}}); + out_v.push_back({{1.0f, 0.0f, 0.0f}, {nv, -nv, nv}}); + out_i.insert(out_i.end(), {3 + offset, 4 + offset, 5 + offset}); + out_v.push_back({{-1.0f, 0.0f, 0.0f}, {-nv, -nv, nv}}); + out_v.push_back({{0.0f, 0.0f, 1.0f}, {-nv, -nv, nv}}); + out_v.push_back({{0.0f, -1.0f, 0.0f}, {-nv, -nv, nv}}); + out_i.insert(out_i.end(), {6 + offset, 7 + offset, 8 + offset}); + out_v.push_back({{0.0f, 1.0f, 0.0f}, {-nv, nv, nv}}); + out_v.push_back({{0.0f, 0.0f, 1.0f}, {-nv, nv, nv}}); + out_v.push_back({{-1.0f, 0.0f, 0.0f}, {-nv, nv, nv}}); + out_i.insert(out_i.end(), {9 + offset, 10 + offset, 11 + offset}); + out_v.push_back({{1.0f, 0.0f, 0.0f}, {nv, nv, -nv}}); + out_v.push_back({{0.0f, 1.0f, 0.0f}, {nv, nv, -nv}}); + out_v.push_back({{0.0f, 0.0f, -1.0f}, {nv, nv, -nv}}); + out_i.insert(out_i.end(), {12 + offset, 13 + offset, 14 + offset}); + out_v.push_back({{0.0f, -1.0f, 0.0f}, {nv, -nv, -nv}}); + out_v.push_back({{1.0f, 0.0f, 0.0f}, {nv, -nv, -nv}}); + out_v.push_back({{0.0f, 0.0f, -1.0f}, {nv, -nv, -nv}}); + out_i.insert(out_i.end(), {15 + offset, 16 + offset, 17 + offset}); + out_v.push_back({{-1.0f, 0.0f, 0.0f}, {-nv, -nv, -nv}}); + out_v.push_back({{0.0f, -1.0f, 0.0f}, {-nv, -nv, -nv}}); + out_v.push_back({{0.0f, 0.0f, -1.0f}, {-nv, -nv, -nv}}); + out_i.insert(out_i.end(), {18 + offset, 19 + offset, 20 + offset}); + out_v.push_back({{0.0f, 1.0f, 0.0f}, {-nv, nv, -nv}}); + out_v.push_back({{-1.0f, 0.0f, 0.0f}, {-nv, nv, -nv}}); + out_v.push_back({{0.0f, 0.0f, -1.0f}, {-nv, nv, -nv}}); + out_i.insert(out_i.end(), {21 + offset, 22 + offset, 23 + offset}); + return ModelUnit{offset, (uint32_t)out_v.size() - offset, ioffset, (uint32_t)out_i.size() - ioffset}; + } + + ModelUnit gen_sphere(float radius, uint32_t slice, uint32_t stack) { + uint32_t offset = out_v.size(); + uint32_t ioffset = out_i.size(); + // vert_count = 2 + (stack - 1) * slice; + // vertices + out_v.push_back({{0.0f, 0.0f, radius}, {0.0f, 0.0f, 1.0f}, {0.5f, 0.0f}}); + out_v.push_back({{0.0f, 0.0f, -radius}, {0.0f, 0.0f, -1.0f}, {0.5f, 1.0f}}); + float za = pi / stack; + for(uint32_t i = 1; i < stack; ++i) { + float z = radius * std::cos(za); + float r = radius * std::sin(za); + float a = 0; + for(uint32_t s = 0; s < slice; ++s) { + float x = r * std::cos(a); + float y = r * std::sin(a); + tmath::fvec3 position = {x, y, z}; + out_v.push_back({position, position * (1.0f / radius), {(x + 1.0f) * 0.5f, (1.0f - z / radius) * 0.5f}}); + a += pi2 / slice; + } + za += pi / stack; + } + // draw indices + for(uint32_t i = 2; i < slice + 1; ++i) + out_i.insert(out_i.end(), {0, i + 1, i}); + out_i.insert(out_i.end(), {0, 2, slice + 1}); + for(uint32_t s = 0; s < stack - 2; ++s) { + uint32_t start = 2 + slice * s; + for(uint32_t i = start; i < start + slice - 1; ++i) + out_i.insert(out_i.end(), {i, i + 1, i + slice, i + slice, i + 1, i + slice + 1}); + out_i.insert(out_i.end(), {start + slice - 1, start, start + slice * 2 - 1, start + slice * 2 - 1, start, start + slice}); + } + uint32_t start = 2 + slice * (stack - 2); + for(uint32_t i = start; i < start + slice - 1; ++i) + out_i.insert(out_i.end(), {i, i + 1, 1}); + out_i.insert(out_i.end(), {start + slice - 1, start, 1}); + // fix offset + for(uint32_t i = ioffset; i < out_i.size(); ++i) + out_i[i] += offset; + return ModelUnit{offset, (uint32_t)out_v.size() - offset, ioffset, (uint32_t)out_i.size() - ioffset}; + } + + ModelUnit gen_cylinder(float base, float top, float height, uint32_t slice, uint32_t stack) { + uint32_t offset = out_v.size(); + uint32_t ioffset = out_i.size(); + // vert_count = 2 + (stack + 3) * (slice + 1); + // vertices + out_v.push_back({{0.0f, 0.0f, height}, {0.0f, 0.0f, height}, {0.5f, 0.0f}}); + out_v.push_back({{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.5f, 1.0f}}); + for(uint32_t i = 0; i < slice + 1; ++i) { + float a = (float)i / slice; + out_v.push_back({{std::cos(a * pi2) * top, std::sin(a * pi2) * top, height}, {0.0f, 0.0f, 1.0f}, {a, 0.0f}}); + } + for(uint32_t i = 0; i < slice + 1; ++i) { + float a = (float)i / slice; + out_v.push_back({{std::cos(a * pi2) * base, std::sin(a * pi2) * base, 0.0f}, {0.0f, 0.0f, -1.0f}, {a, 1.0f}}); + } + float z = height; + tmath::fvec3 norm = tmath::fvec3{top - base, 0, height}.cross(tmath::fvec3{0.0f, -1.0f, 0.0f}).normalize(); + for(uint32_t zi = 0; zi < stack + 1; ++zi) { + float r = top + (base - top) * zi / stack; + for(uint32_t i = 0; i < slice + 1; ++i) { + float a = (float)i / slice; + float x = std::cos(a * pi2); + float y = std::sin(a * pi2); + out_v.push_back({{x * r, y * r, z}, {x * norm[0] - y * norm[1], y * norm[0] + x * norm[1], norm[2]}, {a, 1.0f - z / height}}); + } + z -= height / stack; + } + // draw indices + for(uint32_t i = 2; i < 2 + slice; ++i) + out_i.insert(out_i.end(), {0, i + 1, i}); + for(uint32_t i = 2 + slice + 1; i < 2 + slice * 2 + 1; ++i) + out_i.insert(out_i.end(), {1, i, i + 1}); + for(uint32_t zi = 0; zi < stack; ++zi) { + uint32_t st = 2 + (zi + 2) * (slice + 1); + for(uint32_t i = st; i < st + slice; ++i) + out_i.insert(out_i.end(), {i, i + 1, i + slice + 1, i + slice + 1, i + 1, i + slice + 2}); + } + // fix offset + for(uint32_t i = ioffset; i < out_i.size(); ++i) + out_i[i] += offset; + return ModelUnit{offset, (uint32_t)out_v.size() - offset, ioffset, (uint32_t)out_i.size() - ioffset}; + } + + // radius = f(z) + template + ModelUnit gen_solid_of_revolution(float zb, float zt, const F& f, uint32_t slice, uint32_t stack) { + uint32_t offset = out_v.size(); + uint32_t ioffset = out_i.size(); + // vert_count = 2 + (stack + 3) * (slice + 1); + // vertices + out_v.push_back({{0.0f, 0.0f, zt}, {0.0f, 0.0f, zt}, {0.5f, 0.0f}}); + out_v.push_back({{0.0f, 0.0f, zb}, {0.0f, 0.0f, zb}, {0.5f, 1.0f}}); + float top = f(zt); + for(uint32_t i = 0; i < slice + 1; ++i) { + float a = (float)i / slice; + out_v.push_back({{std::cos(a * pi2) * top, std::sin(a * pi2) * top, zt}, {0.0f, 0.0f, 1.0f}, {a, 0.0f}}); + } + float base = f(zb); + for(uint32_t i = 0; i < slice + 1; ++i) { + float a = (float)i / slice; + out_v.push_back({{std::cos(a * pi2) * base, std::sin(a * pi2) * base, 0.0f}, {0.0f, 0.0f, -1.0f}, {a, 1.0f}}); + } + rs::vector rsample(stack + 1); + rs::vector norms(stack + 1); + float deltaz = (zb - zt) / stack; + for(uint32_t i = 0; i < stack + 1; ++i) + rsample[i] = f(zt + deltaz * i); + norms[0] = tmath::fvec3{rsample[0] - rsample[1], 0, -deltaz}.cross(tmath::fvec3{0.0f, -1.0f, 0.0f}).normalize(); + for(uint32_t i = 1; i < stack; ++i) + norms[i] = tmath::fvec3{rsample[i - 1] - rsample[i + 1], 0, -deltaz * 2.0f}.cross(tmath::fvec3{0.0f, -1.0f, 0.0f}).normalize(); + norms[stack] = tmath::fvec3{rsample[stack - 1] - rsample[stack], 0, -deltaz}.cross(tmath::fvec3{0.0f, -1.0f, 0.0f}).normalize(); + // tmath::fvec3 norm = tmath::fvec3{top - base, 0, height}.cross(tmath::fvec3{0.0f, -1.0f, 0.0f}).normalize(); + float z = zt; + for(uint32_t zi = 0; zi < stack + 1; ++zi) { + float r = rsample[zi]; + tmath::fvec3& norm = norms[zi]; + for(uint32_t i = 0; i < slice + 1; ++i) { + float a = (float)i / slice; + float x = std::cos(a * pi2); + float y = std::sin(a * pi2); + out_v.push_back({{x * r, y * r, z}, {x * norm[0] - y * norm[1], y * norm[0] + x * norm[1], norm[2]}, {a, 1.0f - deltaz * zi / (zb - zt)}}); + } + z += deltaz; + } + // draw indices + for(uint32_t i = 2; i < 2 + slice; ++i) + out_i.insert(out_i.end(), {0, i + 1, i}); + for(uint32_t i = 2 + slice + 1; i < 2 + slice * 2 + 1; ++i) + out_i.insert(out_i.end(), {1, i, i + 1}); + for(uint32_t zi = 0; zi < stack; ++zi) { + uint32_t st = 2 + (zi + 2) * (slice + 1); + for(uint32_t i = st; i < st + slice; ++i) + out_i.insert(out_i.end(), {i, i + 1, i + slice + 1, i + slice + 1, i + 1, i + slice + 2}); + } + // fix offset + for(uint32_t i = ioffset; i < out_i.size(); ++i) + out_i[i] += offset; + return ModelUnit{offset, (uint32_t)out_v.size() - offset, ioffset, (uint32_t)out_i.size() - ioffset}; + } + + uint32_t allocate_attribute_index() { + if(empty_indices.empty()) { + attrs.emplace_back(); + return max_index++; + } else { + auto index = empty_indices.back(); + empty_indices.pop_back(); + return index; + } + } + + ModelUnit clone_unit(ModelUnit pu) { + uint32_t new_voffset = out_v.size(); + uint32_t new_ioffset = out_i.size(); + out_v.insert(out_v.end(), &out_v[pu.vert_offset], &out_v[pu.vert_offset + pu.vert_count]); + out_i.insert(out_i.end(), &out_i[pu.index_offset], &out_i[pu.index_offset + pu.index_count]); + uint32_t diff = new_ioffset - pu.index_offset; + for(uint32_t i = new_ioffset; i < out_i.size(); ++i) + out_i[i] -= diff; + } + + rs::vector& all_vertices() { return out_v; } + rs::vector& all_indices() { return out_i; } + rs::vector& all_attributes() { return attrs; } + +private: + rs::vector out_v; + rs::vector out_i; + rs::vector empty_indices; + uint32_t max_index = 0; + rs::vector attrs; +}; + +#endif diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..bfaa351 --- /dev/null +++ b/build.bat @@ -0,0 +1,4 @@ +cl /std:c++17 /EHsc /Zi -I%1/include -I%2/Include /c /Fd:build/main.pdb /Fo:build/main.obj main.cpp +cl /std:c++17 /EHsc /Zi -I%1/include -I%2/Include /c /Fd:build/vulkan.pdb /Fo:build/vulkan.obj vulkan.cpp +cl /std:c++17 /EHsc /Zi -I%1/include -I%2/Include /c /Fd:build/image.pdb /Fo:build/image.obj image.cpp +link /DEBUG:FULL /SUBSYSTEM:windows /ENTRY:mainCRTStartup /LIBPATH:%1/lib-vc2019 /LIBPATH:%2/Lib build/main.obj build/vulkan.obj build/image.obj glfw3dll.lib vulkan-1.lib shaderc_combined.lib /OUT:tacraft.exe diff --git a/image.cpp b/image.cpp new file mode 100644 index 0000000..5261240 --- /dev/null +++ b/image.cpp @@ -0,0 +1,31 @@ +#define STB_IMAGE_IMPLEMENTATION +#include "deps/stb_image.h" + +#include "image.hpp" + +class ImageImpl : public Image { +public: + ImageImpl(unsigned char* data, int x, int y) : data(data), width(x), height(y) {} + ImageImpl(const ImageImpl&) = delete; + ~ImageImpl() { + if(data) + stbi_image_free(data); + } + std::pair img_size() override { + return std::make_pair(width, height); + } + const unsigned char* img_data() override { + return data; + } + +private: + unsigned char* data = nullptr; + int32_t width; + int32_t height; +}; + +std::shared_ptr Image::load_from_file(const char* filename) { + int x, y, n; + auto data = stbi_load(filename, &x, &y, &n, STBI_rgb_alpha); + return std::make_shared(data, x, y); +} diff --git a/image.hpp b/image.hpp new file mode 100644 index 0000000..deecce2 --- /dev/null +++ b/image.hpp @@ -0,0 +1,15 @@ +#ifndef _IMAGE_HPP_ +#define _IMAGE_HPP_ + +#include + +class Image { +public: + static std::shared_ptr load_from_file(const char* filename); + + virtual ~Image() = default; + virtual std::pair img_size() = 0; + virtual const unsigned char* img_data() = 0; +}; + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..23cc3f7 --- /dev/null +++ b/main.cpp @@ -0,0 +1,510 @@ +#include +#include + +#define GLFW_INCLUDE_NONE +#define GLFW_INCLUDE_VULKAN +#include + +#include "rusty.hpp" +#include "vulkan.hpp" +#include "tmath.hpp" +#include "image.hpp" +#include "model_object.hpp" + +const uint32_t object_matrix_count = 2048; + +struct GlobalVars { + tmath::fmat4 proj_view; + alignas(16) tmath::fvec3 light; + alignas(16) tmath::fvec3 eye; +}; + +class Application { +public: + ~Application() { + if(device) + device->wait_idle(); + swapchain.reset(); + } + void init(GLFWwindow* window) { + mdl_objects.push_back(std::make_shared(verts)); + field = std::make_shared(verts); + mdl_objects.push_back(field); + auto u1 = std::make_shared(verts); + u1->set_pos(0, 0); + mdl_objects.push_back(u1); + auto u4 = std::make_shared(verts); + u4->set_pos(4, 0); + u1->attack(*u4, 0.4f); + mdl_objects.push_back(u4); + this->window = window; + uint32_t ext_count; + const char** ext_names = glfwGetRequiredInstanceExtensions(&ext_count); + instance = vk::Instance::begin_build().set_application_name("Tacraft").set_enging_name("tacraft") + .set_layers({"VK_LAYER_LUNARG_standard_validation"}) + .set_extensions(rs::vector(ext_names, ext_names + ext_count)) + .end(); + physical_device = instance->enumerate_physical_device().nth(0).value(); + auto maxsample = std::min(physical_device->properties()->limits.framebufferColorSampleCounts, + physical_device->properties()->limits.framebufferDepthSampleCounts); + + VkSurfaceKHR raw_surface; + glfwCreateWindowSurface(instance->handle(), window, nullptr, &raw_surface); + surface = vk::Surface::from_raw_surface(raw_surface, *instance, *physical_device); + auto findex = physical_device->enumerate_queue_families().enumerate().filter([&](size_t idx, const VkQueueFamilyProperties* p) -> bool { + VkBool32 support_present = false; + vkGetPhysicalDeviceSurfaceSupportKHR(physical_device->handle(), idx, raw_surface, &support_present); + return (p->queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0 && support_present + && glfwGetPhysicalDevicePresentationSupport(instance->handle(), physical_device->handle(), idx); + }).unzip().first.nth(0).value(); + + device = vk::Device::begin_build(*physical_device).set_extensions({"VK_KHR_swapchain"}) + .add_queue_family(findex, 1.0f).add_queue_family(findex, 1.0f) + .enable_features(*physical_device->supported_features()) + .end(); + auto gqueue = device->enumerate_queues().next().value(); + auto pqueue = device->enumerate_queues().next().value_or(gqueue); + graphic_queue = *gqueue; + present_queue = *pqueue; + + vert_shader = vk::ShaderModule::load_from_src(*device, "./shader/shader.vert", vk::ShaderModule::shader_type_vertex); + if(!vert_shader) { + std::cout << "vertex shader null!" << std::endl; + return; + } + frag_shader = vk::ShaderModule::load_from_src(*device, "./shader/shader.frag", vk::ShaderModule::shader_type_fragment); + if(!frag_shader) { + std::cout << "fragment shader null!" << std::endl; + return; + } + render_pass = vk::RenderPass::begin_build(*device) + .set_samples(sample_count) + .end(); + if(!render_pass) { + std::cout << "renderpass null!" << std::endl; + return; + } + auto& vertices = verts.all_vertices(); + auto& vert_indices = verts.all_indices(); + vertex_buffer = vk::Buffer::begin_build(*device, *physical_device).alloc_size(vertices.size() * sizeof(Vertex)) + .set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) + .set_cpu_accessible() + .end(); + vertex_buffer->update_buffer(vertices.data(), vertices.size() * sizeof(Vertex)); + index_buffer = vk::Buffer::begin_build(*device, *physical_device).alloc_size(vert_indices.size() * sizeof(uint32_t)) + .set_usage(VK_BUFFER_USAGE_INDEX_BUFFER_BIT) + .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) + .set_cpu_accessible() + .end(); + index_buffer->update_buffer(vert_indices.data(), vert_indices.size() * sizeof(uint32_t)); + + auto image = Image::load_from_file("res/snow.tga"); + auto [imgx, imgy] = image->img_size(); + img_buffer = vk::Buffer::begin_build(*device, *physical_device).alloc_size(imgx * imgy * 4 + 64) + .set_usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT) + .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) + .set_cpu_accessible() + .end(); + img_buffer->update_buffer(image->img_data(), imgx * imgy * 4); + memset(img_buffer->map(imgx * imgy * 4, 64), 0xff, 64); + img_buffer->unmap(); + teximage = vk::Image::begin_build(*device, *physical_device) + .set_device_local() + .set_format(VK_FORMAT_R8G8B8A8_UNORM) + .set_extent({(uint32_t)imgx, (uint32_t)imgy, 1}) + .end(); + teximage_view = teximage->create_view(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); + whiteimage = vk::Image::begin_build(*device, *physical_device) + .set_device_local() + .set_format(VK_FORMAT_R8G8B8A8_UNORM) + .set_extent({(uint32_t)4, (uint32_t)4, 1}) + .end(); + whiteimage_view = whiteimage->create_view(VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT); + + sampler = vk::Sampler::begin_build(*device).set_anisotrophy(16.0f).end(); + descriptor_set_layout = vk::DescriptorSetLayout::begin_build(*device) + .add_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER).set_count(1).set_stage(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT) + .add_binding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER).set_count(1).set_stage(VK_SHADER_STAGE_VERTEX_BIT) + .add_binding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER).set_count(2).set_stage(VK_SHADER_STAGE_FRAGMENT_BIT) + .end(); + descriptor_pool = vk::DescriptorPool::begin_build(*device).set_max_sets(2) + .add_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 16) + .add_pool_size(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 16) + .add_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 16) + .end(); + descriptor_sets = descriptor_pool->alloc_descriptor_set(*descriptor_set_layout, 2); + uint32_t buffer_size = 0x100 + sizeof(VertexAttribute) * object_matrix_count; + for(uint32_t i = 0; i < 2; ++i) { + uniform_buffers.emplace_back(vk::Buffer::begin_build(*device, *physical_device).alloc_size(buffer_size) + .set_usage(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) + .set_sharing_mode(VK_SHARING_MODE_EXCLUSIVE) + .set_cpu_accessible() + .end()); + for(auto& ds : descriptor_sets) { + ds->update_write(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, 0) + .update_write_buffer_info(*uniform_buffers.back(), 0, sizeof(GlobalVars), 1) + .update_end(); + ds->update_write(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, 0) + .update_write_buffer_info(*uniform_buffers.back(), 0x100, sizeof(VertexAttribute) * object_matrix_count, 1) + .update_write(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2, 0) + .update_write_image_info(*whiteimage_view, *sampler) + .update_write_image_info(*teximage_view, *sampler) + .update_end(); + } + } + + draw_command_pool = vk::CommandPool::begin_build(*device) + .set_queue_family_index(graphic_queue.queue_family_index) + .set_pool_flag(VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) + .end(); + onetime_command_pool = vk::CommandPool::begin_build(*device) + .set_queue_family_index(graphic_queue.queue_family_index) + .set_pool_flag(VK_COMMAND_POOL_CREATE_TRANSIENT_BIT) + .end(); + draw_command_pool->alloc_command_buffer(command_buffers, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 2); + renderer = vk::Renderer::create_renderer(*device, 2); + + onetime_command_pool->alloc_command_buffer(onetime_commands, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 2); + onetime_commands[0]->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) + .begin_pipeline_barrior(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT) + .add_image_memory_barrior(*teximage) + .image_set_layout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) + .image_set_access_mask(0, VK_ACCESS_TRANSFER_WRITE_BIT) + .end_pipeline_barrior() + .begin_copy_buffer_to_image(*img_buffer, *teximage) + .add_region(0, {0, 0, 0}, {(uint32_t)imgx, (uint32_t)imgy, 1u}) + .end_buffer_image_copy() + .begin_pipeline_barrior(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) + .add_image_memory_barrior(*teximage) + .image_set_layout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) + .image_set_access_mask(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) + .end_pipeline_barrior() + .end_command(); + onetime_commands[1]->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) + .begin_pipeline_barrior(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT) + .add_image_memory_barrior(*whiteimage) + .image_set_layout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) + .image_set_access_mask(0, VK_ACCESS_TRANSFER_WRITE_BIT) + .end_pipeline_barrior() + .begin_copy_buffer_to_image(*img_buffer, *whiteimage) + .add_region(imgx * imgy * 4, {0, 0, 0}, {4u, 4u, 1u}) + .end_buffer_image_copy() + .begin_pipeline_barrior(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) + .add_image_memory_barrior(*whiteimage) + .image_set_layout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) + .image_set_access_mask(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT) + .end_pipeline_barrior() + .end_command(); + recreate_sized_object(); + } + + void draw_frame() { + if(skipdraw) + return; + if(recreate || renderer->out_of_date()) { + recreate_sized_object(); + recreate = false; + } + uint32_t frame_index = renderer->prepare_for_next_frame(); + uint32_t image_index = renderer->accquire_next_image(*swapchain); + if(renderer->out_of_date()) + return; + // update attributes + // global_vars.light = tmath::fmat4::identity().translate(5.0f, 0.0f, 8.0f).rotatex(glfwGetTime() * 0.5f).rotatez(pi * 0.25f) * tmath::fvec3{0.0f, 0.0f, 0.0f}; + for(auto& mdo : mdl_objects) + mdo->update(glfwGetTime()); + auto ptr = uniform_buffers[frame_index]->map(0, 0x100 + sizeof(VertexAttribute) * object_matrix_count); + memcpy(ptr, &global_vars, sizeof(GlobalVars)); + auto att_ptr = reinterpret_cast((uintptr_t)ptr + 0x100); + memcpy(att_ptr, verts.all_attributes().data(), verts.all_attributes().size() * sizeof(VertexAttribute)); + uniform_buffers[frame_index]->unmap(); + + if(!onetime_commands.empty()) { + renderer->submit_onetime_command(graphic_queue, onetime_commands).wait_idle(graphic_queue); + onetime_commands.clear(); + } + command_buffers[frame_index]->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) + .begin_render_pass(*render_pass, *framebuffers[image_index], VkClearValue{0.0, 0.0, 0.0, 0.8}) + .bind_pipeline_and_descriptor_sets(*pipeline, *descriptor_sets[frame_index]) + .draw_indexed(*vertex_buffer, *index_buffer, verts.all_indices().size()) + .end_render_pass() + .end_command(); + renderer->submit_draw_command(graphic_queue, *command_buffers[frame_index]); + renderer->present(present_queue, *swapchain, image_index); + } + + void recreate_sized_object() { + device->wait_idle(); + swapchain = nullptr; + framebuffers.clear(); + glfwGetFramebufferSize(window, &width, &height); + surface->refresh(); + const VkSurfaceFormatKHR* format = surface->default_format(); + auto caps = surface->capabilities(); + swapchain = vk::Swapchain::begin_build(*device) + .set_surface(*surface) + .set_min_image_count(caps->maxImageCount) + .set_format(format->format) + .set_extent({(uint32_t)width, (uint32_t)height}) + .set_image_usage(caps->supportedUsageFlags) + .set_queue_family_indices(device->queue_family_in_use()) + .set_transform(caps->currentTransform) + .end(); + auto swapchain_images = swapchain->get_swapchain_images(); + + depthimage = vk::Image::begin_build(*device, *physical_device) + .set_device_local() + .set_samples(sample_count) + .set_usage(VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) + .set_format(VK_FORMAT_D24_UNORM_S8_UINT) + .set_tilling(VK_IMAGE_TILING_OPTIMAL) + .set_extent({(uint32_t)width, (uint32_t)height, 1u}) + .end(); + depthimage_view = depthimage->create_view(VK_FORMAT_D24_UNORM_S8_UINT, VK_IMAGE_ASPECT_DEPTH_BIT); + onetime_command_pool->alloc_command_buffer(onetime_commands, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); + onetime_commands.back()->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) + .begin_pipeline_barrior(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT) + .add_image_memory_barrior(*depthimage) + .image_set_layout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) + .image_set_aspect_mask(VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) + .image_set_access_mask(0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) + .end_pipeline_barrior() + .end_command(); + + if(sample_count != VK_SAMPLE_COUNT_1_BIT) { + msaaimage = vk::Image::begin_build(*device, *physical_device) + .set_device_local() + .set_usage(VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) + .set_samples(sample_count) + .set_format(format->format) + .set_tilling(VK_IMAGE_TILING_OPTIMAL) + .set_extent({(uint32_t)width, (uint32_t)height, 1u}) + .end(); + msaaimage_view = msaaimage->create_view(format->format, VK_IMAGE_ASPECT_COLOR_BIT); + onetime_command_pool->alloc_command_buffer(onetime_commands, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1); + onetime_commands.back()->begin_command(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT) + .begin_pipeline_barrior(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT) + .add_image_memory_barrior(*msaaimage) + .image_set_layout(VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) + .image_set_aspect_mask(VK_IMAGE_ASPECT_COLOR_BIT) + .image_set_access_mask(0, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) + .end_pipeline_barrior() + .end_command(); + } + + framebuffers.clear(); + for(auto view : swapchain_images) { + if(sample_count == VK_SAMPLE_COUNT_1_BIT) { + framebuffers.emplace_back(vk::Framebuffer::begin_build(*device) + .set_render_pass(*render_pass) + .set_framebuffer_size(width, height) + .add_image_view(*view) + .add_image_view(*depthimage_view) + .end() + ); + } else { + framebuffers.emplace_back(vk::Framebuffer::begin_build(*device) + .set_render_pass(*render_pass) + .set_framebuffer_size(width, height) + .add_image_view(*msaaimage_view) + .add_image_view(*depthimage_view) + .add_image_view(*view) + .end() + ); + } + } + pipeline = vk::Pipeline::begin_build(*device) + .set_render_pass(*render_pass) + .set_vertex_shader(*vert_shader) + .set_fragment_shader(*frag_shader) + .set_cull_mode(VK_CULL_MODE_BACK_BIT) + .normal_viewport((float)width, (float)height) + .normal_scissor((uint32_t)width, (uint32_t)height) + .add_descriptor_set_layout(*descriptor_set_layout) + .set_samples(sample_count) + .bind_vertex_size(sizeof(Vertex)) + .add_binding_attribute(VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, position)) + .add_binding_attribute(VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)) + .add_binding_attribute(VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, texcoord)) + .add_binding_attribute(VK_FORMAT_R32G32_SINT, offsetof(Vertex, index)) + .end(); + global_vars.proj_view = tmath::fmat4::perspective_fov(3.1415926f / 3.0f, (float)width / (float)height, 0.1f, 100.0f).scale(1.0f, -1.0f, 1.0f) + * tmath::fmat4::look_at({8.0f, 20.0f, 10.0f}, {8.0f, 10.0f, 0.0f}, {0.0f, 0.0f, 1.0f}); + global_vars.light = {8.0f, 24.0f, 20.0f}; + global_vars.eye = {8.0f, 20.0f, 10.0f}; + } + + void copy_buffer_to_image() { + + } + void post_window_resized(bool skip = false) { skipdraw = skip; recreate = true; } + void mouse_move(float x, float y) { + auto& m = global_vars.proj_view; + float w = x * 2.0f / width - 1.0f; + float h = y * 2.0f / height - 1.0f; + float a1 = m[0] - w * m[3]; + float b1 = m[4] - w * m[7]; + float c1 = m[12] - w * m[15]; + float a2 = m[1] - h * m[3]; + float b2 = m[5] - h * m[7]; + float c2 = m[13] - h * m[15]; + float d = a1 * b2 - a2 * b1; + float zx = (b1 * c2 - b2 * c1) / d; + float zy = (c1 * a2 - c2 * a1) / d; + + field->mouse_point(zx, zy); + } + +private: + bool recreate = false; + bool skipdraw = false; + GLFWwindow* window = nullptr; + std::shared_ptr instance; + const vk::PhysicalDevice* physical_device; + std::shared_ptr device; + std::shared_ptr swapchain; + std::shared_ptr surface; + rs::vector> framebuffers; + std::shared_ptr vert_shader; + std::shared_ptr frag_shader; + std::shared_ptr render_pass; + std::shared_ptr pipeline; + std::shared_ptr draw_command_pool; + std::shared_ptr onetime_command_pool; + rs::vector> command_buffers; + rs::vector> onetime_commands; + std::shared_ptr renderer; + vk::Queue graphic_queue; + vk::Queue present_queue; + std::shared_ptr vertex_buffer; + std::shared_ptr index_buffer; + rs::vector> uniform_buffers; + std::shared_ptr descriptor_set_layout; + std::shared_ptr descriptor_pool; + rs::vector> descriptor_sets; + std::shared_ptr img_buffer; + std::shared_ptr teximage; + std::shared_ptr whiteimage; + std::shared_ptr depthimage; + std::shared_ptr msaaimage; + std::shared_ptr teximage_view; + std::shared_ptr whiteimage_view; + std::shared_ptr depthimage_view; + std::shared_ptr msaaimage_view; + std::shared_ptr sampler; + GlobalVars global_vars; + VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_4_BIT; + VertCollection verts; + rs::vector> mdl_objects; + std::shared_ptr field; + int32_t width; + int32_t height; +}; + +Application app; + +void show_mat4(tmath::fmat4& m) { + printf("| %f %f %f %f |\n", m[0], m[4], m[8], m[12]); + printf("| %f %f %f %f |\n", m[1], m[5], m[9], m[13]); + printf("| %f %f %f %f |\n", m[2], m[6], m[10], m[14]); + printf("| %f %f %f %f |\n", m[3], m[7], m[11], m[15]); +} + +GLFWwindow* window; +int dx, dy, dwx, dwy; +bool window_moving = false; + +int main(int argc, char *argv[]) { + if (!glfwInit()) + exit(EXIT_FAILURE); + if (!glfwVulkanSupported()) { + std::cout << "Vulkan not supported" << std::endl; + exit(EXIT_FAILURE); + } + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); + glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); + window = glfwCreateWindow(1280, 800, "Tacraft!", NULL, NULL); + if (!window) { + glfwTerminate(); + exit(EXIT_FAILURE); + } + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + int monitorX, monitorY; + glfwGetMonitorPos(monitor, &monitorX, &monitorY); + int windowWidth, windowHeight; + glfwGetWindowSize(window, &windowWidth, &windowHeight); + glfwSetWindowPos(window, monitorX + (mode->width - windowWidth) / 2, monitorY + (mode->height - windowHeight) / 2); + float xscale, yscale; + glfwGetWindowContentScale(window, &xscale, &yscale); + + app.init(window); + + glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods) { + if(action == GLFW_PRESS) { + if(key == GLFW_KEY_ESCAPE) + glfwSetWindowShouldClose(window, GLFW_TRUE); + } else { + + } + }); + glfwSetWindowSizeCallback(window, [](GLFWwindow* window, int x, int y) { + app.post_window_resized(x == 0 && y == 0); + }); + glfwSetMouseButtonCallback(window, [](GLFWwindow*,int button,int action,int mods){ + if(action == GLFW_PRESS) { + if(button == GLFW_MOUSE_BUTTON_RIGHT) { + double mx, my; + glfwGetWindowPos(window, &dwx, &dwy); + glfwGetCursorPos(window, &mx, &my); + dx = dwx + (int)mx; + dy = dwy + (int)my; + window_moving = true; + } + } else { + if(button == GLFW_MOUSE_BUTTON_RIGHT) { + window_moving = false; + } + } + }); + glfwSetCursorPosCallback(window, [](GLFWwindow*,double x,double y) { + if(window_moving) { + int wx, wy, delx, dely; + glfwGetWindowPos(window, &wx, &wy); + delx = wx + (int)x - dx; + dely = wy + (int)y - dy; + glfwSetWindowPos(window, dwx + delx, dwy + dely); + } + app.mouse_move(x, y); + }); + glfwSetTime(0.0); + double prev_time = 0.0; + double time_check= 0.0; + uint32_t frame = 0; + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + app.draw_frame(); + frame++; + double curtime = glfwGetTime(); + time_check += (curtime - prev_time); + prev_time = curtime; + if(time_check > 1.0) { + time_check -= 1.0; + char buffer[50]; + sprintf(buffer, "Tacraft! %d", frame); + glfwSetWindowTitle(window, buffer); + frame = 0; + } + } + glfwDestroyWindow(window); + glfwTerminate(); + exit(EXIT_SUCCESS); + + return 0; +}; diff --git a/model_object.hpp b/model_object.hpp new file mode 100644 index 0000000..cac1ac1 --- /dev/null +++ b/model_object.hpp @@ -0,0 +1,329 @@ +#ifndef _MODEL_OBJECT_HPP_ +#define _MODEL_OBJECT_HPP_ + +#include + +#include "rusty.hpp" +#include "basic_model.hpp" + +class SceneMgr { + +}; + +class ModelBasic { +public: + ModelBasic(ModelUnit u, VertCollection& v) : unit(u), vc(v) {} + + const VertexAttribute& get_attribute() { return vc.all_attributes()[index[0]]; } + ModelBasic& set_attribute_index(uint32_t idx) { + index[0] = idx; + auto& vertices = vc.all_vertices(); + for(uint32_t i = unit.vert_offset; i < unit.vert_offset + unit.vert_count; ++i) + vertices[i].index[0] = idx; + return *this; + } + ModelBasic& set_texture_index(uint32_t idx) { + index[1] = idx; + auto& vertices = vc.all_vertices(); + for(uint32_t i = unit.vert_offset; i < unit.vert_offset + unit.vert_count; ++i) + vertices[i].index[1] = idx; + return *this; + } + uint32_t att_index() { return index[0]; } + uint32_t tex_index() { return index[1]; } + ModelUnit model_info() { return unit; } + + ModelBasic& set_matrix(tmath::fmat4 mat) { + vc.all_attributes()[index[0]].matrix = mat; + vc.all_attributes()[index[0]].nmatrix = mat.homo_inverse().transpose(); + return *this; + } + ModelBasic& set_matrix_no_normal(tmath::fmat4 mat) { + vc.all_attributes()[index[0]].matrix = mat; + return *this; + } + ModelBasic& set_color(tmath::fvec4 color) { vc.all_attributes()[index[0]].color = color; return *this; } + ModelBasic& set_mixcolor(tmath::fvec4 color) { vc.all_attributes()[index[0]].mixcolor = color; return *this; } + +private: + ModelUnit unit; + uint32_t index[2]; + VertCollection& vc; +}; + +class ModelObject { +public: + virtual bool update(double tm) = 0; + +}; + +namespace mdo { + +class MDLAxis : public ModelObject { +public: + MDLAxis(VertCollection& collection) { + auto m1 = collection.gen_cylinder(0.05f, 0.05f, 1.0f, 16, 1); + auto ptr1 = std::make_shared(m1, collection); + ptr1->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix(tmath::fmat4::identity().rotatey(pi * 0.5f)) + .set_color({1.0f, 0.0f, 0.0f, 1.0f}); + units.push_back(ptr1); + + auto m2 = collection.gen_cylinder(0.05f, 0.05f, 1.0f, 16, 1); + auto ptr2 = std::make_shared(m2, collection); + ptr2->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix(tmath::fmat4::identity().rotatex(pi * -0.5f)) + .set_color({0.0f, 0.85f, 0.3f, 1.0f}); + units.push_back(ptr2); + + auto m3 = collection.gen_cylinder(0.05f, 0.05f, 1.0f, 16, 1); + auto ptr3 = std::make_shared(m3, collection); + ptr3->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix(tmath::fmat4::identity()) + .set_color({0.0f, 0.2f, 1.0f, 1.0f}); + units.push_back(ptr3); + + auto m4 = collection.gen_cylinder(0.1f, 0.0f, 0.2f, 16, 1); + auto ptr4 = std::make_shared(m4, collection); + ptr4->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix(tmath::fmat4::identity().rotatey(pi * 0.5f).translate(1.0f, 0.0f, 0.0f)) + .set_color({1.0f, 0.0f, 0.0f, 1.0f}); + units.push_back(ptr4); + + auto m5 = collection.gen_cylinder(0.1f, 0.0f, 0.2f, 16, 1); + auto ptr5 = std::make_shared(m5, collection); + ptr5->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix(tmath::fmat4::identity().rotatex(pi * -0.5f).translate(0.0f, 1.0f, 0.0f)) + .set_color({0.0f, 0.85f, 0.3f, 1.0f}); + units.push_back(ptr5); + + auto m6 = collection.gen_cylinder(0.1f, 0.0f, 0.2f, 16, 1); + auto ptr6 = std::make_shared(m6, collection); + ptr6->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix(tmath::fmat4::identity().translate(0.0f, 0.0f, 1.0f)) + .set_color({0.0f, 0.2f, 1.0f, 1.0f}); + units.push_back(ptr6); + + } + + bool update(double tm) override { return false; } + +private: + std::vector> units; +}; + +class MDLUnits : public ModelObject { +public: + static tmath::fvec3 pos_center(int32_t x, int32_t y) { + return tmath::fvec3{0.25f + x * 0.5f, 0.25f + (31 - y) * 0.5f, 0.0f}; + } + + std::pair pos() { return std::make_pair(posx, posy); } + std::pair target_pos() { return std::make_pair(tposx, tposy); } + void set_pos(int32_t x, int32_t y) { + posx = x; + posy = y; + auto pc = pos_center(x, y); + self_matrix = tmath::fmat4::identity().translate(pc[0], pc[1], pc[2]); + self_matrix_updated = true; + } + void set_target_pos(int32_t x, int32_t y) { + tposx = x; + tposy = y; + } + + virtual void move_to(int32_t x, int32_t y, float tm) = 0; + virtual void attack(MDLUnits&, float tm) = 0; + virtual void damage(MDLUnits&, float tm) = 0; + + bool update(double tm) override { + last_update = tm; + self_matrix_updated = false; + return false; + } + +protected: + int32_t posx; + int32_t posy; + int32_t tposx; + int32_t tposy; + float last_update = 0.0f; + bool self_matrix_updated = false; + tmath::fmat4 self_matrix; +}; + +class MDLField : public ModelObject { +public: + MDLField(VertCollection& collection) { + float tw = 1.0f / 32; + float th = 1.0f / 32; + for(uint32_t y = 0; y < 32; ++y) { + for(uint32_t x = 0; x < 32; ++x) { + auto m = collection.gen_square(tw * x, th * y, tw, th); + auto ptr = std::make_shared(m, collection); + ptr->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix_no_normal(tmath::fmat4::identity().scale(0.25f, 0.25f, 1.0f).translate(0.25f + x * 0.5f, 0.25f + y * 0.5f, 0.0f)) + .set_texture_index(1); + units.push_back(ptr); + } + } + } + + bool update(double tm) override { + if(hindex != -1) { + units[hindex]->set_mixcolor({1.0f, 1.0f, 1.0f, std::sin((float)tm * 32.0f / pi) * 0.4f + 0.5f}); + } + auto iter = status.begin(); + while(iter != status.end()) { + auto cur = iter++; + float delta = (tm - cur->second) * 2.0f; + auto mc = units[cur->first]->get_attribute().mixcolor; + if(mc[3] > delta) { + mc[3] = mc[3] - delta; + units[cur->first]->set_mixcolor(mc); + cur->second = tm; + } else { + mc[3] = 0.0f; + units[cur->first]->set_mixcolor(mc); + status.erase(cur); + } + } + last_update = tm; + return false; + } + + void mouse_point(float x, float y) { + int32_t idx = -1; + if(x > 0.0f && x < 16.0f && y > 0.0f && y < 16.0f) { + int32_t bx = (int32_t)(x * 2.0f); + int32_t by = (int32_t)(y * 2.0f); + idx = bx + by * 32; + } + if(hindex == idx) + return; + status.erase(idx); + if(hindex != -1) { + status[hindex] = last_update; + units[hindex]->set_mixcolor({1.0f, 1.0f, 1.0f, 0.9f}); + } + hindex = idx; + } + + void mouse_press(bool up) { + + } + + void add_object(std::weak_ptr obj, int32_t x, int32_t y) { + auto fid = uindex(x, y); + if(objects[fid].lock() != nullptr) + return; + objects[fid] = obj; + } + + std::shared_ptr get_unit(int32_t x, int32_t y) { + return objects[uindex(x, y)].lock(); + } + + static int32_t uindex(int32_t x, int32_t y) { return x + (31 - y) * 32; } + +private: + int32_t hindex = -1; + float last_update = 0.0f; + rs::unordered_map status; + std::vector> units; + std::weak_ptr objects[1024]; +}; + +class MDLUnit1 : public MDLUnits { +public: + MDLUnit1(VertCollection& collection) { + auto m1 = collection.gen_solid_of_revolution(0.0f, 1.0f, [](float z){ return (z - 0.55f) * (z - 0.55f)* 0.5f + 0.1f; }, 32, 32); + auto ptr1 = std::make_shared(m1, collection); + ptr1->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix(tmath::fmat4::identity().scale(1.0f, 1.0f, 0.5f)) + .set_color({0.0f, 0.8f, 0.3f, 1.0f}); + units.push_back(ptr1); + + auto m2 = collection.gen_octahedron(); + auto ptr2 = std::make_shared(m2, collection); + ptr2->set_attribute_index(collection.allocate_attribute_index()) + .set_matrix(tmath::fmat4::identity().scale(0.2f, 0.2f, 0.4f).translate(0.0f, 0.0f, 1.0f)) + .set_color({0.7f, 0.1f, 0.3f, 0.8f}); + units.push_back(ptr2); + } + + bool update(double tm) override { + bool updated = self_matrix_updated; + MDLUnits::update(tm); + for(auto iter = updator.begin(); iter != updator.end();) { + if((*iter)(tm)) + updator.erase(iter++); + else + iter++; + } + if(updated) + units[0]->set_matrix(self_matrix * tmath::fmat4::identity().scale(0.6f, 0.6f, 0.5f)); + units[1]->set_matrix(self_matrix * tmath::fmat4::identity().scale(0.15f, 0.15f, 0.2f).rotatez(tm * 2.0f).translate(0.0f, 0.0f, 0.8f)); + return !updator.empty(); + } + + void move_to(int32_t x, int32_t y, float tm) { + auto cur = pos_center(posx, posy); + auto end = pos_center(x, y); + posx = x; + posy = y; + add_move(cur, end, tm, {0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, nullptr); + } + + void attack(MDLUnits& u, float tm) { + auto [x, y] = u.pos(); + auto cur = pos_center(posx, posy); + auto src = pos_center(x, y); + auto end = cur + (src - cur) * 0.8f; + add_move(cur, end, tm * 0.5f, {0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, [this, &u, tm, end, cur](){ + u.damage(*this, 0.2f); + add_move(end, cur, tm * 0.5f, {0.0f, 0.0f, 0.0f}, 0.0f, 0.0f, nullptr); + }); + } + + void damage(MDLUnits& u, float tm) { + auto [fx, fy] = u.pos(); + auto cur = pos_center(posx, posy); + auto src = pos_center(fx, fy); + auto end = cur + (cur - src) * 0.25f; + add_move(cur, end, tm * 0.5f, {1.0f, 1.0f, 1.0f}, 0.0f, 0.9f, [this, tm, end, cur](){ + add_move(end, cur, tm * 0.5f, {1.0f, 1.0f, 1.0f}, 0.9f, 0.0f, nullptr); + }); + } + + void add_move(tmath::fvec3 from, tmath::fvec3 to, float tm, tmath::fvec3 mc, float a1, float a2, const std::function& end_cb) { + updator.push_back([this, from, to, st = last_update, tm, mc, a1, a2, end_cb](float t){ + auto h = std::sqrt((to[0] - from[0]) * (to[0] - from[0]) + (to[1] - from[1]) * (to[1] - from[1]))* 1.0f; + if(t >= st + tm) { + if(end_cb) + end_cb(); + for(auto& m : units) + m->set_mixcolor({mc[0], mc[1], mc[2], a2}); + self_matrix = tmath::fmat4::identity().translate(to[0], to[1], to[2]); + self_matrix_updated = true; + return true; + } else { + float factor = (t - st) / tm; + auto pos = from + (to - from) * factor; + for(auto& m : units) + m->set_mixcolor({mc[0], mc[1], mc[2], a1 + (a2 - a1) * factor}); + self_matrix = tmath::fmat4::identity().translate(pos[0], pos[1], (factor - 0.5f) * (0.5f - factor) * h + h * 0.25f); + self_matrix_updated = true; + return false; + } + }); + } + +private: + std::vector> units; + rs::list> updator; +}; + +} + +#endif diff --git a/rs_iter.hpp b/rs_iter.hpp new file mode 100644 index 0000000..6816798 --- /dev/null +++ b/rs_iter.hpp @@ -0,0 +1,500 @@ +#ifndef _RC_ITER_HPP_ +#define _RC_ITER_HPP_ + +#include +#include +#include +#include + +namespace rs { + +template +struct InvokeType { + template + auto Call(F f, ARGS... args) { return f(std::forward(args)...); } + template + void CallNoReturn(F f, ARGS... args) { f(std::forward(args)...); } + template + auto CallFold(F f, S s, ARGS... args) { return f(std::forward(s), std::forward(args)...); } +}; + +template +struct InvokeType> { + template + auto Call(F f, std::pair arg) { return f(arg.first, arg.second); } + template + void CallNoReturn(F f, std::pair arg) { f(arg.first, arg.second); } + template + auto CallFold(F f, S s, std::pair arg) { return f(std::forward(s), arg.first, arg.second); } +}; + +template +struct InvokeType> { + using sequence_type = std::index_sequence_for; + template + auto Call(F f, std::tuple arg) { return UnpackCall(std::forward(f), arg, sequence_type{}); } + template + void CallNoReturn(F f, std::tuple arg) { UnpackCallNoReturn(std::forward(f), arg, sequence_type{}); } + template + auto CallFold(F f, S s, std::tuple arg) { return UnpackCallFold(std::forward(f), std::forward(s), arg, sequence_type{}); } + + template + auto UnpackCall(F f, std::tuple& arg, std::index_sequence) { return f(std::get(arg)...); } + template + void UnpackCallNoReturn(F f, std::tuple& arg, std::index_sequence) { f(std::get(arg)...); } + template + auto UnpackCallFold(F f, S s, std::tuple& arg, std::index_sequence) { return f(s, std::get(arg)...); } +}; + +template +struct StdFuncType { + using type = void; + using return_type = void; +}; +template +struct StdFuncType { + using type = std::function; + using return_type = RET; +}; +template +struct StdFuncType { + using type = std::function; + using return_type = RET; +}; + +template +struct FunctionType { + using type = typename StdFuncType::type; + using return_type = typename StdFuncType::return_type; +}; +template +struct FunctionType { + using type = std::function; + using return_type = RET; +}; +template +struct FunctionType { + using type = std::function; + using return_type = RET; +}; + +template typename TP> +struct TemplateChecker { + static constexpr bool value = false; +}; +template typename TP, typename... ARGS> +struct TemplateChecker, TP> { + static constexpr bool value = true; +}; + +template +struct iterator { + using value_type = T; + + std::optional next() { + return iter(); + } + + size_t count() { + size_t result = 0; + for (auto n = next(); n.has_value(); n = next()) + result++; + return result; + } + + std::optional nth(size_t n) { + while (true) { + std::optional value = next(); + if (n-- == 0 || !value.has_value()) + return value; + } + } + + std::optional last() { + std::optional value; + for (auto n = next(); n.has_value(); n = next()) + value = n; + return value; + } + + auto step_by(int32_t s) { + return iterator{[s, c = s, it = std::move(iter)]() mutable { + for (; c < s; c++) + it(); + c = 1; + return it(); + }}; + } + + auto chain(iterator&& i) { + return iterator{[it1 = std::move(iter), it2 = std::move(i.iter), s = true]() mutable { + if (s) { + auto v = it1(true); + if (!v.has_value()) { + s = false; + return it2(true); + } + } else + return it2(true); + }}; + } + + template + auto zip(iterator&& i) { + return iterator>{[it1 = std::move(iter), it2 = std::move(i.iter)]() mutable { + if(auto v1 = it1(); v1.has_value()) { + if(auto v2 = it2(); v2.has_value()) + return std::optional>(std::make_pair(v1.value(), v2.value())); + } + return std::optional>(); + }}; + } + + template + void for_each(F f) { + for(auto v = next(); v.has_value(); v = next()) { + InvokeType().CallNoReturn(std::ref(f), v.value()); + } + } + + template + auto filter(F f) { + return iterator{[f, it = std::move(iter)]() mutable { + auto value = it(); + while(value.has_value() && !InvokeType().Call(std::ref(f), value.value())) + value = it(); + return value; + }}; + } + + template + auto map(M m) { + using RET = typename FunctionType::return_type; + return iterator{[m, it = std::move(iter)]() { + auto t = it(); + if (t.has_value()) + return std::optional(InvokeType().Call(std::ref(m), t.value())); + return std::optional(); + }}; + } + + template + auto filter_map(FM fm) { + using OPT = typename FunctionType::return_type; + static_assert(TemplateChecker::value, "filter_map: filter must return an optional value."); + using RET = typename OPT::value_type; + return iterator{[fm, it = std::move(iter)]() { + auto value = it(); + while (value.has_value()) { + if(auto mvalue = InvokeType().Call(std::ref(fm), value.value()); mvalue.has_value()) + return mvalue; + value = it(); + } + return std::optional(); + }}; + } + + auto enumerate() { + return iterator>{[it = std::move(iter), i = 0]() mutable { + if(auto v = it(); v.has_value()) + return std::optional>(std::make_pair(i++, v.value())); + return std::optional>(); + }}; + } + + auto skip(size_t n) { + return iterator{[n, it = std::move(iter)]() mutable { + while (n > 0) { + n--; + it(); + } + return it(); + }}; + } + + auto take(size_t n) { + return iterator{[n, it = std::move(iter)]() mutable { + if (n > 0) { + n--; + return it(); + } + return std::optional(); + }}; + } + + template + auto skip_while(F f) { + return iterator{[f, it = std::move(iter), res = false]() mutable { + auto v = it(); + if(res) + return v; + while(v.has_value() && InvokeType().Call(std::ref(f), v.value())) + v = it(); + res = true; + return v; + }}; + } + + template + auto take_while(F f) { + return iterator{[f, it = std::move(iter), res = true]() mutable { + if (res) { + if(auto v = it(); v.has_value() && InvokeType().Call(std::ref(f), v.value())) + return v; + else + res = false; + } + return std::optional(); + }}; + } + + template + auto scan(S s, M m) { + using RET = typename FunctionType::return_type; + return iterator{[s, m, it = std::move(iter)]() mutable { + auto t = it(); + if(t.has_value()) + return std::optional(InvokeType().CallFold(std::ref(m), std::ref(s), t.value())); + return std::optional(); + }}; + } + + template + auto flat_map(M m) { + return map(m).flatten(); + } + + auto flatten() { + static_assert(TemplateChecker::value, "flatten: value type can only be iterator type."); + using NT = typename T::value_type; + return iterator{[inited = false, cur = std::optional>(), it = std::move(iter)]() mutable { + if(!inited) { + cur = it(); + inited = true; + } + if(!cur.has_value()) + return std::optional(); + auto v = cur.value().next(); + while(!v.has_value() && cur.has_value()) { + cur = it(); + if(cur.has_value()) + v = cur.value().next(); + } + return v; + }}; + } + + auto fuse() { + return iterator{[end = false, it = std::move(iter)]() mutable { + if (end) + return std::optional(); + auto v = it(); + if(!it.has_value()) + end = true; + return v; + }}; + } + + template + auto inspect(F f) { + return iterator{[f, it = std::move(iter)]() { + auto v = it(); + if(v.has_value()) + InvokeType().CallNoReturn(std::ref(f), v.value()); + return v; + }}; + } + + template + auto collect() { + C container; + for (auto v = next(); v.has_value(); v = next()) + container.collect_item(v.value()); + return container; + } + + template + C& collect(C& container) { + for (auto v = next(); v.has_value(); v = next()) + container.collect_item(v.value()); + return container; + } + + template + auto partition(F f) { + std::pair result; + for (auto v = next(); v.has_value(); v = next()) { + if(InvokeType().CallNoReturn(std::ref(f), v.value())) + result.first.collect_item(v.value()); + else + result.second.collect_item(v.value()); + } + return result; + } + + template + auto fold(S s, F f) { + using RET = typename FunctionType::return_type; + static_assert(std::is_same::value, "fold: function should return same type as value."); + for (auto v = next(); v.has_value(); v = next()) { + s = InvokeType().CallFold(std::ref(f), s, v.value()); + } + return s; + } + + template + bool all(F f) { + for (auto v = next(); v.has_value(); v = next()) { + if(!InvokeType().Call(std::ref(f), v.value())) + return false; + } + return true; + } + + template + bool any(F f) { + for (auto v = next(); v.has_value(); v = next()) { + if(InvokeType().Call(std::ref(f), v.value())) + return true; + } + return false; + } + + template + auto find(F f) { + for (auto v = next(); v.has_value(); v = next()) { + if(InvokeType().Call(std::ref(f), v.value())) + return v; + } + return std::optional(); + } + + template + auto find_map(F f) { + using OPT = typename FunctionType::return_type; + static_assert(TemplateChecker::value, "find_map: filter must return an optional value."); + using RET = typename OPT::value_type; + for (auto v = next(); v.has_value(); v = next()) { + if(auto mv = InvokeType().Call(std::ref(f), v.value()); mv.has_value()) + return mv; + } + return std::optional(); + } + + template + auto position(F f) { + size_t i = 0; + for (auto v = next(); v.has_value(); v = next(), i++) { + if(InvokeType().Call(std::ref(f), v.value())) + return std::optional(i); + } + return std::optional(); + } + + auto max() { + auto res = next(); + if(!res.has_value()) + return res; + for (auto v = next(); v.has_value(); v = next()) { + if(v.value() >= res.value()) + res = v; + } + return res; + } + + auto min() { + auto res = next(); + if(!res.has_value()) + return res; + for (auto v = next(); v.has_value(); v = next()) { + if(v.value() < res.value()) + res = v; + } + return res; + } + + template + auto max_by_key(F f) { + auto res = next(); + if(!res.has_value()) + return res; + for (auto v = next(); v.has_value(); v = next()) { + if(InvokeType().Call(std::ref(f), v.value()) >= InvokeType().Call(std::ref(f), res.value())) + res = v; + } + return res; + } + + template + auto min_by_key(F f) { + auto res = next(); + if(!res.has_value()) + return res; + for (auto v = next(); v.has_value(); v = next()) { + if(InvokeType().Call(std::ref(f), v.value()) < InvokeType().Call(std::ref(f), res.value())) + res = v; + } + return res; + } + + auto unzip() { + static_assert(TemplateChecker::value, "unzip: iterator type must be pair"); + using TP1 = typename T::first_type; + using TP2 = typename T::second_type; + std::pair, iterator> result; + result.first.iter = [it = iter](){ + auto v = it(); + return v.has_value() ? std::optional(v.value().first): std::optional(); + }; + result.second.iter = [it = iter](){ + auto v = it(); + return v.has_value() ? std::optional(v.value().second): std::optional(); + }; + return result; + } + + auto cycle() { + return iterator{[it = iter, it2 = iter]() mutable { + auto v = it(false); + if(v.has_value()) + return v; + it = it2; + return it(false); + }}; + } + + auto sum() { + auto res = T(0); + for (auto v = next(); v.has_value(); v = next()) + res += v.value(); + return res; + } + + auto product() { + auto res = T(1); + for (auto v = next(); v.has_value(); v = next()) + res *= v.value(); + return res; + } + + std::function()> iter; +}; + +template +auto range(T start, T end) { + return iterator{[start, end]() mutable { + if(start >= end) + return std::optional(); + return std::optional(start++); + }}; +} + +template +auto repeat(T value) { + return iterator{[value]() { + return std::optional(value); + }}; +} + +} // namespace rs +#endif diff --git a/rusty.hpp b/rusty.hpp new file mode 100644 index 0000000..09f3d05 --- /dev/null +++ b/rusty.hpp @@ -0,0 +1,258 @@ +#ifndef _RC_HPP_ +#define _RC_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rs_iter.hpp" + +namespace rs { + +template +class linear_container : public P { +public: + using P::P; + + template + rs::iterator into_iter() const { + if constexpr (!REV) { + return rs::iterator{[this, iter = this->begin()] () mutable { + if(iter == this->end()) + return std::optional(); + return std::optional(*iter++); + }}; + } else { + return rs::iterator{[this, riter = this->rbegin()] () mutable { + if(riter == this->rend()) + return std::optional(); + return std::optional(*riter++); + }}; + } + } + + template + rs::iterator into_iter_ptr() const { + if constexpr (!REV) { + return rs::iterator{[this, iter = this->cbegin()] () mutable { + if(iter == this->cend()) + return std::optional(); + return std::optional(&*iter++); + }}; + } else { + return rs::iterator{[this, riter = this->crbegin()] () mutable { + if(riter == this->crend()) + return std::optional(); + return std::optional(&*riter++); + }}; + } + } + + template + rs::iterator into_iter_mptr() { + if constexpr (!REV) { + return rs::iterator{[this, iter = this->begin()] () mutable { + if(iter == this->end()) + return std::optional(); + return std::optional(&*iter++); + }}; + } else { + return rs::iterator{[this, riter = this->rbegin()] () mutable { + if(riter == this->rend()) + return std::optional(); + return std::optional(&*riter++); + }}; + } + } + + void collect_item(const TP& v) { + this->push_back(v); + } +}; + +template > +using vector = linear_container, TP>; +template > +using list = linear_container, TP>; +template > +using forward_list = linear_container, TP>; +template > +using deque = linear_container, TP>; +template +using array = linear_container, TP>; + +template +class set_container : public S { +public: + using S::S; + + template + rs::iterator into_iter() const { + if constexpr (!REV || !CanRev) { + return rs::iterator{[this, iter = this->begin()] () mutable { + if(iter == this->end()) + return std::optional(); + return std::optional(*iter++); + }}; + } else { + return rs::iterator{[this, riter = this->rbegin()] () mutable { + if(riter == this->rend()) + return std::optional(); + return std::optional(*riter++); + }}; + } + } + + template + rs::iterator into_iter_ptr() const { + if constexpr (!REV || !CanRev) { + return rs::iterator{[this, iter = this->cbegin()] () mutable { + if(iter == this->cend()) + return std::optional(); + return std::optional(&*iter++); + }}; + } else { + return rs::iterator{[this, riter = this->crbegin()] () mutable { + if(riter == this->crend()) + return std::optional(); + return std::optional(&*riter++); + }}; + } + } + + void collect_item(const KEY& value) { + this->insert(value); + } + +}; + +template, + typename ALLOC = std::allocator> +using set = set_container, KEY, true>; +template, + typename ALLOC = std::allocator> +using multiset = set_container, KEY, true>; +template, + typename PRED = std::equal_to, + typename ALLOC = std::allocator> +using unordered_set = set_container, KEY, false>; +template, + typename PRED = std::equal_to, + typename ALLOC = std::allocator> +using unordered_multiset = set_container, KEY, false>; + +template +class map_container : public M { +public: + using M::M; + + template + rs::iterator> into_iter() const { + if constexpr (!REV || !CanRev) { + return rs::iterator>{[this, iter = this->begin()] () mutable { + if(iter == this->end()) + return std::optional>(); + return std::optional>(*iter++); + }}; + } else { + return rs::iterator>{[this, riter = this->rbegin()] () mutable { + if(riter == this->rend()) + return std::optional>(); + return std::optional>(*riter++); + }}; + } + } + + template + rs::iterator> into_iter_ptr() const { + if constexpr (!REV || !CanRev) { + return rs::iterator>{[this, iter = this->cbegin()] (bool rev) mutable { + if(iter == this->cend()) + return std::optional>(); + auto ptr = &*iter++; + return std::optional>(std::make_pair(&ptr->first, &ptr->second)); + }}; + } else { + return rs::iterator>{[this, riter = this->crbegin()] (bool rev) mutable { + if(riter == this->crend()) + return std::optional>(); + auto ptr = &*riter++; + return std::optional>(std::make_pair(&ptr->first, &ptr->second)); + }}; + } + } + + template + rs::iterator> into_iter_mptr() { + if constexpr (!REV || !CanRev) { + return rs::iterator>{[this, iter = this->begin()] (bool rev) mutable { + if(iter == this->end()) + return std::optional>(); + auto ptr = &*iter++; + return std::optional>(std::make_pair(&ptr->first, &ptr->second)); + }}; + } else { + return rs::iterator>{[this, riter = this->rbegin()] (bool rev) mutable { + if(riter == this->rend()) + return std::optional>(); + auto ptr = &*riter++; + return std::optional>(std::make_pair(&ptr->first, &ptr->second)); + }}; + } + } + + rs::iterator keys() const { + return into_iter().unzip().first; + } + + rs::iterator keys_ptr() const { + return into_iter_ptr().unzip().first; + } + + rs::iterator values() const { + return into_iter().unzip().second; + } + + rs::iterator values_ptr() const { + return into_iter_ptr().unzip().second; + } + + void collect_item(const std::pair& value) { + this->insert(value); + } + +}; + +template, + typename ALLOC = std::allocator>> +using map = map_container, KEY, TP, true>; +template, + typename ALLOC = std::allocator>> +using multimap = map_container, KEY, TP, true>; +template, + typename PRED = std::equal_to, + typename ALLOC = std::allocator>> +using unordered_map = map_container, KEY, TP, false>; +template, + typename PRED = std::equal_to, + typename ALLOC = std::allocator>> +using unordered_multimap = map_container, KEY, TP, false>; + +} + +#endif diff --git a/shader/shader.frag b/shader/shader.frag new file mode 100644 index 0000000..61e6810 --- /dev/null +++ b/shader/shader.frag @@ -0,0 +1,40 @@ +#version 450 + +layout(location = 0) in vec4 frag_color; +layout(location = 1) in vec4 frag_mcolor; +layout(location = 2) in vec2 frag_texcoord; +layout(location = 3) in flat int frag_texindex; +layout(location = 4) in vec3 frag_pos; +layout(location = 5) in vec3 frag_normal; + +layout(location = 0) out vec4 outColor; + +layout(binding = 0) uniform GlobalVars { + mat4 proj_view; + vec3 light; + vec3 eye; +} global; + +layout(binding = 2) uniform sampler2D teximage[2]; + +void main() { + // vec3 light_pos = vec3(5.0, 5.0, -5.0); + // vec3 view_pos = vec3(3.0, 3.0, 3.0); + + float ambiant = 0.2; + + vec3 light_dir = normalize(global.light - frag_pos); + float diff = max(dot(frag_normal, light_dir), 0.0); + + float specular_strength = 0.7; + + vec3 view_dir = normalize(global.eye - frag_pos); + vec3 reflect_dir = reflect(-light_dir, frag_normal); + float spec = pow(max(dot(view_dir, reflect_dir), 0.0), 64); + float specular = specular_strength * spec; + + float factor = ambiant + diff + specular; + vec4 pre_color = frag_color * texture(teximage[frag_texindex], frag_texcoord); + pre_color = vec4(mix((factor * pre_color).rgb, frag_mcolor.rgb, frag_mcolor.a), pre_color.a); + outColor = pre_color; +} diff --git a/shader/shader.vert b/shader/shader.vert new file mode 100644 index 0000000..2924c99 --- /dev/null +++ b/shader/shader.vert @@ -0,0 +1,40 @@ +#version 450 + +layout(location = 0) in vec3 position; +layout(location = 1) in vec3 normal; +layout(location = 2) in vec2 texcoord; +layout(location = 3) in ivec2 index; + +layout(location = 0) out vec4 frag_color; +layout(location = 1) out vec4 frag_mcolor; +layout(location = 2) out vec2 frag_texcoord; +layout(location = 3) out int frag_texindex; +layout(location = 4) out vec3 frag_pos; +layout(location = 5) out vec3 frag_normal; + +struct VertexAttribute { + mat4 matrix; + mat4 nmatrix; + vec4 color; + vec4 mixcolor; +}; + +layout(binding = 0) uniform GlobalVars { + mat4 proj_view; + vec3 light; + vec3 eye; +} global; + +layout (std430, binding = 1) buffer AttributeBlock { + VertexAttribute atts[2048]; +} units; + +void main() { + gl_Position = global.proj_view * units.atts[index[0]].matrix * vec4(position,1.0); + frag_color = units.atts[index[0]].color; + frag_mcolor = units.atts[index[0]].mixcolor; + frag_texcoord = texcoord; + frag_texindex = index[1]; + frag_pos = vec3(units.atts[index[0]].matrix * vec4(position,1.0)); + frag_normal = normalize(vec3(units.atts[index[0]].nmatrix * vec4(normal,1.0))); +} diff --git a/tmath.hpp b/tmath.hpp new file mode 100644 index 0000000..6c827d7 --- /dev/null +++ b/tmath.hpp @@ -0,0 +1,253 @@ +#ifndef _TMATH_HPP_ +#define _TMATH_HPP_ + +#include + +const float pi = 3.141593f; +const float pi2 = 6.283185f; +const float sr2 = 1.414214f; +const float isr2 = 0.7071068f; + +namespace tmath { + +template +struct vec2 { + T val[2]; + + T& operator [] (int32_t idx) { return val[idx]; } + const T& operator [] (int32_t idx) const { return val[idx]; } + vec2 operator + (const vec2& m) const { return vec2{val[0] + m.val[0], val[1] + m.val[1]}; } + vec2 operator - (const vec2& m) const { return vec2{val[0] - m.val[0], val[1] - m.val[1]}; } + vec2 operator * (T scalar) const { return vec2{val[0] * scalar, val[1] * scalar}; } + T dot(const vec2& m) const { return val[0] * m.val[0] + val[1] * m.val[1]; } +}; + +template +struct vec3 { + T val[3]; + + T& operator [] (int32_t idx) { return val[idx]; } + const T& operator [] (int32_t idx) const { return val[idx]; } + vec3 operator + (const vec3& m) const { return vec3{val[0] + m.val[0], val[1] + m.val[1], val[2] + m.val[2]}; } + vec3 operator - (const vec3& m) const { return vec3{val[0] - m.val[0], val[1] - m.val[1], val[2] - m.val[2]}; } + vec3 operator * (T scalar) const { return vec3{val[0] * scalar, val[1] * scalar, val[2] * scalar}; } + T dot(const vec3& m) const { return val[0] * m.val[0] + val[1] * m.val[1] + val[2] * m.val[2]; } + vec3 cross(const vec3& m) const { + return vec3{ + val[1] * m.val[2] - val[2] * m.val[1], + val[2] * m.val[0] - val[0] * m.val[2], + val[0] * m.val[1] - val[1] * m.val[0] + }; + } + vec3 normalize() const { + T factor = std::sqrt(val[0] * val[0] + val[1] * val[1] + val[2] * val[2]); + return vec3{val[0] / factor, val[1] / factor, val[2] / factor}; + } +}; + +template +struct vec4 { + T val[4]; + + T& operator [] (int32_t idx) { return val[idx]; } + const T& operator [] (int32_t idx) const { return val[idx]; } +}; + +// | 0 4 8 12 | +// | 1 5 9 13 | +// | 2 6 10 14 | +// | 3 7 11 15 | +template +struct mat4 { + T val[16]; + + T& operator [] (int32_t idx) { return val[idx]; } + const T& operator [] (int32_t idx) const { return val[idx]; } + + mat4 operator + (const mat4& m) const { + return mat4 { + val[ 0] + m.val[ 0], val[ 1] + m.val[ 1], val[ 2] + m.val[ 2], val[ 3] + m.val[ 3], + val[ 4] + m.val[ 4], val[ 5] + m.val[ 5], val[ 6] + m.val[ 6], val[ 7] + m.val[ 7], + val[ 8] + m.val[ 8], val[ 9] + m.val[ 9], val[10] + m.val[10], val[11] + m.val[11], + val[12] + m.val[12], val[13] + m.val[13], val[14] + m.val[14], val[15] + m.val[15] + }; + } + + vec3 operator * (const vec3& m) const { + T fx = val[ 0] * m.val[0] + val[ 4] * m.val[1] + val[ 8] * m.val[2] + val[12]; + T fy = val[ 1] * m.val[0] + val[ 5] * m.val[1] + val[ 9] * m.val[2] + val[13]; + T fz = val[ 2] * m.val[0] + val[ 6] * m.val[1] + val[10] * m.val[2] + val[14]; + T fw = val[ 3] * m.val[0] + val[ 7] * m.val[1] + val[11] * m.val[2] + val[15]; + return vec3{ fx / fw, fy / fw, fz / fw}; + } + + mat4 operator * (const mat4& m) const { + return mat4 { + val[ 0] * m.val[ 0] + val[ 4] * m.val[ 1] + val[ 8] * m.val[ 2] + val[12] * m.val[ 3], + val[ 1] * m.val[ 0] + val[ 5] * m.val[ 1] + val[ 9] * m.val[ 2] + val[13] * m.val[ 3], + val[ 2] * m.val[ 0] + val[ 6] * m.val[ 1] + val[10] * m.val[ 2] + val[14] * m.val[ 3], + val[ 3] * m.val[ 0] + val[ 7] * m.val[ 1] + val[11] * m.val[ 2] + val[15] * m.val[ 3], + val[ 0] * m.val[ 4] + val[ 4] * m.val[ 5] + val[ 8] * m.val[ 6] + val[12] * m.val[ 7], + val[ 1] * m.val[ 4] + val[ 5] * m.val[ 5] + val[ 9] * m.val[ 6] + val[13] * m.val[ 7], + val[ 2] * m.val[ 4] + val[ 6] * m.val[ 5] + val[10] * m.val[ 6] + val[14] * m.val[ 7], + val[ 3] * m.val[ 4] + val[ 7] * m.val[ 5] + val[11] * m.val[ 6] + val[15] * m.val[ 7], + val[ 0] * m.val[ 8] + val[ 4] * m.val[ 9] + val[ 8] * m.val[10] + val[12] * m.val[11], + val[ 1] * m.val[ 8] + val[ 5] * m.val[ 9] + val[ 9] * m.val[10] + val[13] * m.val[11], + val[ 2] * m.val[ 8] + val[ 6] * m.val[ 9] + val[10] * m.val[10] + val[14] * m.val[11], + val[ 3] * m.val[ 8] + val[ 7] * m.val[ 9] + val[11] * m.val[10] + val[15] * m.val[11], + val[ 0] * m.val[12] + val[ 4] * m.val[13] + val[ 8] * m.val[14] + val[12] * m.val[15], + val[ 1] * m.val[12] + val[ 5] * m.val[13] + val[ 9] * m.val[14] + val[13] * m.val[15], + val[ 2] * m.val[12] + val[ 6] * m.val[13] + val[10] * m.val[14] + val[14] * m.val[15], + val[ 3] * m.val[12] + val[ 7] * m.val[13] + val[11] * m.val[14] + val[15] * m.val[15] + }; + } + + mat4 translate(T tx, T ty, T tz) const { + return mat4{ + val[ 0] + val[ 3] * tx, val[ 1] + val[ 3] * ty, val[ 2] + val[ 3] * tz, val[ 3], + val[ 4] + val[ 7] * tx, val[ 5] + val[ 7] * ty, val[ 6] + val[ 7] * tz, val[ 7], + val[ 8] + val[11] * tx, val[ 9] + val[11] * ty, val[10] + val[11] * tz, val[11], + val[12] + val[15] * tx, val[13] + val[15] * ty, val[14] + val[15] * tz, val[15] + }; + } + + mat4 scale(T sx, T sy, T sz) const { + return mat4{ + val[ 0] * sx, val[ 1] * sy, val[ 2] * sz, val[ 3], + val[ 4] * sx, val[ 5] * sy, val[ 6] * sz, val[ 7], + val[ 8] * sx, val[ 9] * sy, val[10] * sz, val[11], + val[12] * sx, val[13] * sy, val[14] * sz, val[15] + }; + } + + mat4 rotatex(T rx) const { + T sv = std::sin(rx); + T cv = std::cos(rx); + return mat4{ + val[ 0], val[ 1] * cv - val[ 2] * sv, val[ 1] * sv + val[ 2] * cv, val[ 3], + val[ 4], val[ 5] * cv - val[ 6] * sv, val[ 5] * sv + val[ 6] * cv, val[ 7], + val[ 8], val[ 9] * cv - val[10] * sv, val[ 9] * sv + val[10] * cv, val[11], + val[12], val[13] * cv - val[14] * sv, val[13] * sv + val[14] * cv, val[15] + }; + } + + mat4 rotatey(T ry) const { + T sv = std::sin(ry); + T cv = std::cos(ry); + return mat4{ + val[ 0] * cv + val[ 2] * sv, val[ 1], -val[ 0] * sv + val[ 2] * cv, val[ 3], + val[ 4] * cv + val[ 6] * sv, val[ 5], -val[ 4] * sv + val[ 6] * cv, val[ 7], + val[ 8] * cv + val[10] * sv, val[ 9], -val[ 8] * sv + val[10] * cv, val[11], + val[12] * cv + val[14] * sv, val[13], -val[12] * sv + val[14] * cv, val[15] + }; + } + + mat4 rotatez(T rz) const { + T sv = std::sin(rz); + T cv = std::cos(rz); + return mat4{ + val[ 0] * cv - val[ 1] * sv, val[ 0] * sv + val[ 1] * cv, val[ 2], val[ 3], + val[ 4] * cv - val[ 5] * sv, val[ 4] * sv + val[ 5] * cv, val[ 6], val[ 7], + val[ 8] * cv - val[ 9] * sv, val[ 8] * sv + val[ 9] * cv, val[10], val[11], + val[12] * cv - val[13] * sv, val[12] * sv + val[13] * cv, val[14], val[15] + }; + } + + mat4 homo_inverse() const { + T det = val[0] * val[5] * val[10] + val[4] * val[9] * val[2] + val[8] * val[1] * val[6] + -val[0] * val[6] * val[9] - val[1] * val[4] * val[10] - val[2] * val[5] * val[8]; + T idet = (T)1.0 / det; + T adj0 = (val[5] * val[10] - val[6] * val[9]) * idet; + T adj1 = (val[4] * val[10] - val[6] * val[8]) * -idet; + T adj2 = (val[4] * val[9] - val[5] * val[8]) * idet; + T adj3 = (val[1] * val[10] - val[2] * val[9]) * -idet; + T adj4 = (val[0] * val[10] - val[2] * val[8]) * idet; + T adj5 = (val[0] * val[9] - val[1] * val[8]) * -idet; + T adj6 = (val[1] * val[6] - val[2] * val[5]) * idet; + T adj7 = (val[0] * val[6] - val[2] * val[4]) * -idet; + T adj8 = (val[0] * val[5] - val[1] * val[4]) * idet; + T d1 = -adj0 * val[12] - adj1 * val[13] - adj2 * val[14]; + T d2 = -adj3 * val[12] - adj4 * val[13] - adj5 * val[14]; + T d3 = -adj6 * val[12] - adj7 * val[13] - adj8 * val[14]; + return mat4{ + adj0, adj3, adj6, (T)0, + adj1, adj4, adj7, (T)0, + adj2, adj5, adj8, (T)0, + d1, d2, d3, (T)1 + }; + } + + mat4 transpose() const { + return mat4{ + val[0], val[4], val[ 8], val[12], + val[1], val[5], val[ 9], val[13], + val[2], val[6], val[10], val[14], + val[3], val[7], val[11], val[15], + }; + } + + static mat4 identity() { + return mat4{(T)1, (T)0, (T)0, (T)0, (T)0, (T)1, (T)0, (T)0, (T)0, (T)0, (T)1, (T)0, (T)0, (T)0, (T)0, (T)1}; + } + + static mat4 perspective(T width, T height, T near, T far, bool lh = true) { + T factor = lh ? (T)1 : (T)-1; + T val = far / (far - near); + return mat4{ + (T)2 * near / width, (T)0, (T)0, (T)0, + (T)0, (T)2 * near / height, (T)0, (T)0, + (T)0, (T)0, val * factor, (T)factor, + (T)0, (T)0, -near * val, (T)0 + }; + } + + // same as glm::perspectiveNO, mapping z-axis to [-1, 1] (OpenGL clip volume definition) + static mat4 perspective_fov(T fov, T aspect, T near, T far, bool lh = true) { + T factor = lh ? (T)1 : (T)-1; + T yscale = (T)1 / std::tan(fov * (T)0.5); + T xscale = yscale / aspect; + T val = far / (far - near); + return mat4{ + xscale, (T)0, (T)0, (T)0, + (T)0, yscale, (T)0, (T)0, + (T)0, (T)0, (far + near) / (far - near) * factor, (T)factor, + (T)0, (T)0, -(T)2 * far * near / (far - near), (T)0 + }; + } + + static mat4 orthogonal(T width, T height, T near, T far, bool lh = true) { + T factor = lh ? (T)1 : (T)-1; + T dist = far - near; + return mat4{ + (T)2 / width, (T)0, (T)0, (T)0, + (T)0, (T)2 / height, (T)0, (T)0, + (T)0, (T)0, factor / dist, -near / dist, + (T)0, (T)0, (T)0, (T)1 + } + } + + static mat4 look_at(vec3 eye, vec3 at, vec3 up, bool lh = true) { + T factor = lh ? (T)1 : (T)-1; + vec3 zaxis = (at - eye).normalize() * factor; + vec3 xaxis = up.cross(zaxis).normalize(); + vec3 yaxis = zaxis.cross(xaxis); + return mat4{ + xaxis[0], yaxis[0], zaxis[0], (T)0, + xaxis[1], yaxis[1], zaxis[1], (T)0, + xaxis[2], yaxis[2], zaxis[2], (T)0, + -xaxis.dot(eye) * factor, -yaxis.dot(eye) * factor, -zaxis.dot(eye) * factor, (T)1 + }; + } +}; + +using ivec2 = vec2; +using ivec3 = vec3; +using ivec4 = vec4; +using fvec2 = vec2; +using fvec3 = vec3; +using fvec4 = vec4; +using fmat4 = mat4; + +} + +#endif diff --git a/vulkan.cpp b/vulkan.cpp new file mode 100644 index 0000000..54791d6 --- /dev/null +++ b/vulkan.cpp @@ -0,0 +1,1707 @@ +#include +#include +#include + +#include "vulkan.hpp" + +namespace vk { + +template +class defer { +public: + defer(const T& t) : ft(t) {} + ~defer() { ft(); } + T ft; +}; + +class PhysicalDeviceImpl : public PhysicalDevice { +public: + PhysicalDeviceImpl(VkPhysicalDevice handle) : physical_device(handle) { + vkGetPhysicalDeviceProperties(handle, &props); + vkGetPhysicalDeviceMemoryProperties(handle, &mem_props); + uint32_t extension_count = 0; + vkEnumerateDeviceExtensionProperties(handle, nullptr, &extension_count, nullptr); + rs::vector extension_props(extension_count); + vkEnumerateDeviceExtensionProperties(handle, nullptr, &extension_count, extension_props.data()); + uint32_t queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(handle, &queue_family_count, nullptr); + queue_family_props.resize(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(handle, &queue_family_count, queue_family_props.data()); + vkGetPhysicalDeviceFeatures(handle, &features); + } + PhysicalDeviceImpl(const PhysicalDeviceImpl&) = delete; + PhysicalDeviceImpl(PhysicalDeviceImpl&& pdev) { + physical_device = pdev.physical_device; + props = pdev.props; + mem_props = pdev.mem_props; + extension_props = std::move(pdev.extension_props); + queue_family_props = std::move(pdev.queue_family_props); + features = pdev.features; + }; + ~PhysicalDeviceImpl() {} + + VkPhysicalDevice handle() const override { return physical_device;} + const VkPhysicalDeviceProperties* properties() const override { return &props; } + const VkPhysicalDeviceMemoryProperties* mem_properties() const override { return &mem_props; } + const VkPhysicalDeviceFeatures* supported_features() const override { return &features; } + rs::iterator enumerate_extensions() const override{ return extension_props.into_iter_ptr(); } + rs::iterator enumerate_queue_families() const override { return queue_family_props.into_iter_ptr(); } + +private: + VkPhysicalDevice physical_device; + VkPhysicalDeviceProperties props; + VkPhysicalDeviceMemoryProperties mem_props; + rs::vector extension_props; + rs::vector queue_family_props; + VkPhysicalDeviceFeatures features; +}; + +class InstanceImpl : public Instance { +public: + InstanceImpl(VkInstance handle) : instance(handle) { + uint32_t device_count = 0; + vkEnumeratePhysicalDevices(instance, &device_count, nullptr); + rs::vector devices(device_count); + vkEnumeratePhysicalDevices(instance, &device_count, devices.data()); + physical_devices.reserve(device_count); + for(auto p : devices) + physical_devices.emplace_back(p); + } + InstanceImpl(const InstanceImpl&) = delete; + ~InstanceImpl() { + vkDestroyInstance(instance, nullptr); + } + + rs::iterator enumerate_physical_device() const override { + return physical_devices.into_iter_ptr().map([](const PhysicalDeviceImpl* p) { + return static_cast(p); + }); + } + + VkInstance handle() const override { return instance; } + +private: + VkInstance instance; + rs::vector physical_devices; +}; + +class SurfaceImpl : public Surface { +public: + SurfaceImpl(VkSurfaceKHR s, const Instance& inst, const PhysicalDevice& dev) : surface(s) { + instance = inst.handle(); + physical_device = dev.handle(); + refresh(); + } + SurfaceImpl(const SurfaceImpl&) = delete; + ~SurfaceImpl() { + vkDestroySurfaceKHR(instance, surface, nullptr); + } + + VkSurfaceKHR handle() const override { return surface; } + void refresh() override { + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_capabilities); + uint32_t surface_format_count = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &surface_format_count, nullptr); + supported_formats.resize(surface_format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &surface_format_count, supported_formats.data()); + } + const VkSurfaceCapabilitiesKHR* capabilities() const override { return &surface_capabilities; } + rs::iterator enumerate_formats() const override { + return supported_formats.into_iter_ptr().map([](const VkSurfaceFormatKHR* p) { + return static_cast(p); + }); + } + const VkSurfaceFormatKHR* default_format() const override { + return supported_formats.into_iter_ptr().find([](const VkSurfaceFormatKHR* format) { + return format->format == VK_FORMAT_B8G8R8A8_UNORM; + }).value_or(&supported_formats[0]); + } + +private: + VkInstance instance; + VkSurfaceKHR surface; + VkPhysicalDevice physical_device; + VkSurfaceCapabilitiesKHR surface_capabilities; + rs::vector supported_formats; +}; + +class DeviceImpl : public Device { +public: + DeviceImpl(VkDevice handle, rs::vector q) : device(handle), queues(std::move(q)) {} + DeviceImpl(const DeviceImpl&) = delete; + ~DeviceImpl() { + vkDeviceWaitIdle(device); + vkDestroyDevice(device, nullptr); + } + + VkDevice handle() const override { return device; } + void wait_idle() const override { + vkDeviceWaitIdle(device); + } + + rs::iterator enumerate_queues() const override { return queues.into_iter_ptr(); } + rs::vector queue_family_in_use() const override { + return queues.into_iter_ptr().map([](const Queue* q) -> uint32_t { + return q->queue_family_index; + }).collect>().into_iter().collect>(); + } + +private: + VkDevice device; + rs::vector queues; +}; + +class BufferImpl : public Buffer { +public: + BufferImpl(VkDevice device, VkBuffer buffer, VkDeviceMemory mem, size_t size, bool host_visible) + : inner_device(device), buffer(buffer), device_memory(mem), mem_size(size), host_visible(host_visible) {} + BufferImpl(const BufferImpl&) = delete; + ~BufferImpl() { + vkDestroyBuffer(inner_device, buffer, nullptr); + vkFreeMemory(inner_device, device_memory, nullptr); + } + + VkBuffer handle() const override { return buffer; } + bool can_map() const override { return host_visible; } + void* map(uint32_t offset, uint32_t size) override { + void* data = nullptr; + if(mapped || vkMapMemory(inner_device, device_memory, offset, size, 0, &data) != VK_SUCCESS) + return nullptr; + mapped = true; + return data; + } + void unmap() override { + mapped = false; + vkUnmapMemory(inner_device, device_memory); + } + + void update_buffer(const void* src, uint32_t size) { + void* dst = nullptr; + if(vkMapMemory(inner_device, device_memory, 0, size, 0, &dst) != VK_SUCCESS) { + std::cout << "map error!" << std::endl; + return; + } + memcpy(dst, src, size); + vkUnmapMemory(inner_device, device_memory); + } + +private: + VkDevice inner_device; + VkBuffer buffer; + VkDeviceMemory device_memory; + size_t mem_size; + bool mapped = false; + bool host_visible; +}; + +class ImageViewImpl : public ImageView { +public: + ImageViewImpl(VkDevice device, VkImageView handle, std::shared_ptr image) + : device(device), imageview(handle), image(image) {} + ImageViewImpl(const ImageViewImpl&) = delete; + ImageViewImpl(ImageViewImpl&& iv) { + device = iv.device; + imageview = iv.imageview; + iv.imageview = nullptr; + }; + ~ImageViewImpl() { + if(imageview != nullptr) + vkDestroyImageView(device, imageview, nullptr); + } + + VkImageView handle() const override { + return imageview; + } + +private: + VkDevice device; + VkImageView imageview; + std::shared_ptr image; +}; + +class ImageImpl : public Image, public std::enable_shared_from_this { +public: + ImageImpl(VkDevice device, VkImage handle, VkDeviceMemory memory, VkFormat format, bool host_visible) + : inner_device(device), image(handle), device_memory(memory), format(format), host_visible(host_visible) {} + ImageImpl(const ImageImpl&) = delete; + ~ImageImpl() { + vkDestroyImage(inner_device, image, nullptr); + vkFreeMemory(inner_device, device_memory, nullptr); + } + + VkImage handle() const override { return image; } + + std::shared_ptr create_view(VkFormat view_format, VkImageAspectFlags aspect) override { + VkImageViewCreateInfo view_info; + view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view_info.pNext = nullptr; + view_info.flags = 0; + view_info.image = image; + view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + view_info.format = view_format; + view_info.subresourceRange = {aspect, 0u, 1u, 0u, 1u}; + view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + VkImageView image_view; + if (vkCreateImageView(inner_device, &view_info, nullptr, &image_view) != VK_SUCCESS) + return nullptr; + return std::make_shared(inner_device, image_view, shared_from_this()); + } + +private: + VkDevice inner_device; + VkImage image; + VkDeviceMemory device_memory; + bool mapped = false; + bool host_visible; + VkFormat format; +}; + +class SamplerImpl : public Sampler { +public: + SamplerImpl(VkDevice device, VkSampler handle) : inner_device(device), sampler(handle) {} + SamplerImpl(const SamplerImpl&) = delete; + ~SamplerImpl() { + vkDestroySampler(inner_device, sampler, nullptr); + } + + VkSampler handle() const override { return sampler; } + +private: + VkDevice inner_device; + VkSampler sampler; +}; + +class ShaderModuleImpl : public ShaderModule { +public: + ShaderModuleImpl(VkDevice device, VkShaderModule handle) : inner_device(device), shader_module(handle) {} + ShaderModuleImpl(const ShaderModuleImpl&) = delete; + ~ShaderModuleImpl() { + vkDestroyShaderModule(inner_device, shader_module, nullptr); + }; + + VkShaderModule handle() const override { return shader_module; } + +private: + VkDevice inner_device; + VkShaderModule shader_module; +}; + +class SwapchainImpl : public Swapchain { +public: + SwapchainImpl(VkDevice device, VkSwapchainKHR handle, VkFormat fmt) : inner_device(device), swapchain(handle), format(fmt) { + uint32_t swapchain_image_count = 0; + vkGetSwapchainImagesKHR(device, swapchain, &swapchain_image_count, nullptr); + rs::vector swapchain_images(swapchain_image_count); + vkGetSwapchainImagesKHR(device, swapchain, &swapchain_image_count, swapchain_images.data()); + for(VkImage img : swapchain_images) { + VkImageViewCreateInfo imageview_create_info; + imageview_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + imageview_create_info.pNext = nullptr; + imageview_create_info.flags = 0; + imageview_create_info.image = img; + imageview_create_info.format = format; + imageview_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageview_create_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + imageview_create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + imageview_create_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + imageview_create_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + imageview_create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + VkImageView view; + vkCreateImageView(device, &imageview_create_info, nullptr, &view); + swapchain_imageviews.emplace_back(device, view, nullptr); + } + } + SwapchainImpl(const SwapchainImpl&) = delete; + ~SwapchainImpl() { + vkDestroySwapchainKHR(inner_device, swapchain, nullptr); + } + + VkSwapchainKHR handle() const override { return swapchain;} + uint32_t image_count() const override { return swapchain_imageviews.size(); } + + rs::vector get_swapchain_images() override { + return swapchain_imageviews.into_iter_mptr().map([](ImageViewImpl* iv) { + return static_cast(iv); + }).collect>(); + } + +private: + VkDevice inner_device; + VkSwapchainKHR swapchain; + VkFormat format; + rs::vector swapchain_imageviews; +}; + +class RenderPassImpl : public RenderPass { +public: + RenderPassImpl(VkDevice device, VkRenderPass handle) : inner_device(device), render_pass(handle) {} + RenderPassImpl(const RenderPassImpl&) = delete; + ~RenderPassImpl() { + vkDestroyRenderPass(inner_device, render_pass, nullptr); + } + + VkRenderPass handle() const override {return render_pass; } + +private: + VkDevice inner_device; + VkRenderPass render_pass; +}; + +class DescriptorSetImpl : public DescriptorSet { +public: + DescriptorSetImpl(VkDevice device, VkDescriptorSet handle, std::weak_ptr pool) + : inner_device(device), descriptor_set(handle), pool(pool) {} + DescriptorSetImpl(const DescriptorSetImpl&) = delete; + ~DescriptorSetImpl() { + if(auto pool_ptr = pool.lock(); pool_ptr != nullptr) + vkFreeDescriptorSets(inner_device, pool_ptr->handle(), 1, &descriptor_set); + } + VkDescriptorSet handle() const override { return descriptor_set; } + + DescriptorSet& update_write(VkDescriptorType type, uint32_t binding, uint32_t start_index) { + VkWriteDescriptorSet descriptor_write; + descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptor_write.pNext = nullptr; + descriptor_write.dstSet = descriptor_set; + descriptor_write.dstBinding = binding; + descriptor_write.descriptorType = type; + descriptor_write.dstArrayElement = start_index; + descriptor_write.descriptorCount = 0; + descriptor_write.pBufferInfo = nullptr; + descriptor_write.pImageInfo = nullptr; + descriptor_write.pTexelBufferView = nullptr; + write_info.push_back(descriptor_write); + return *this; + } + DescriptorSet& update_write_buffer_info(const Buffer& buffer, uint32_t offset, uint32_t range, uint32_t rep_count) override { + VkDescriptorBufferInfo buffer_info; + buffer_info.buffer = buffer.handle(); + buffer_info.offset = offset; + buffer_info.range = range; + for(uint32_t i = 0; i < rep_count; ++i) + write_buffer_info.push_back(buffer_info); + write_info.back().pBufferInfo = write_buffer_info.data(); + write_info.back().descriptorCount = write_buffer_info.size(); + return *this; + } + + DescriptorSet& update_write_image_info(const ImageView& view, const Sampler& sampler) override { + VkDescriptorImageInfo image_info; + image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + image_info.imageView = view.handle(); + image_info.sampler = sampler.handle(); + write_image_info.push_back(image_info); + write_info.back().pImageInfo = write_image_info.data(); + write_info.back().descriptorCount = write_image_info.size(); + return *this; + } + + virtual DescriptorSet& update_end() override { + if(!write_info.empty()) { + vkUpdateDescriptorSets(inner_device, write_info.size(), write_info.data(), 0, nullptr); + write_buffer_info.clear(); + write_image_info.clear(); + write_info.clear(); + } + return *this; + } +private: + VkDevice inner_device; + VkDescriptorSet descriptor_set; + std::weak_ptr pool; + rs::vector write_buffer_info; + rs::vector write_image_info; + rs::vector write_info; +}; + +class DescriptorSetLayoutImpl : public DescriptorSetLayout { +public: + DescriptorSetLayoutImpl(VkDevice device, VkDescriptorSetLayout handle) : inner_device(device), layout(handle) {} + DescriptorSetLayoutImpl(const DescriptorSetLayoutImpl&) = delete; + ~DescriptorSetLayoutImpl() { + vkDestroyDescriptorSetLayout(inner_device, layout, nullptr); + } + + VkDescriptorSetLayout handle() const override { return layout; } + +private: + VkDevice inner_device; + VkDescriptorSetLayout layout; +}; + +class DescriptorPoolImpl : public DescriptorPool, public std::enable_shared_from_this { +public: + DescriptorPoolImpl(VkDevice device, VkDescriptorPool handle, VkDescriptorPoolCreateFlags flags) + : inner_device(device), pool(handle), flags(flags) {} + DescriptorPoolImpl(const DescriptorPoolImpl&) = delete; + ~DescriptorPoolImpl() { + vkDestroyDescriptorPool(inner_device, pool, nullptr); + } + + VkDescriptorPool handle() const override { return pool; } + + rs::vector> alloc_descriptor_set(const DescriptorSetLayout& layout, uint32_t count) override { + std::vector layouts(count, layout.handle()); + VkDescriptorSetAllocateInfo alloc_info; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.descriptorPool = pool; + alloc_info.descriptorSetCount = count; + alloc_info.pSetLayouts = layouts.data(); + std::vector sets(count); + if(vkAllocateDescriptorSets(inner_device, &alloc_info, sets.data()) != VK_SUCCESS) + return {}; + rs::vector> descriptor_sets; + std::weak_ptr ptr = (flags & VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT) ? shared_from_this() : nullptr; + for(auto& ds : sets) + descriptor_sets.emplace_back(new DescriptorSetImpl(inner_device, ds, ptr)); + return descriptor_sets; + } + + void reset() override { + vkResetDescriptorPool(inner_device, pool, 0); + } + +private: + VkDevice inner_device; + VkDescriptorPool pool; + VkDescriptorPoolCreateFlags flags = 0; +}; + +class PipelineImpl : public Pipeline { +public: + PipelineImpl(VkDevice device, VkPipeline handle, VkPipelineLayout layout) + : inner_device(device), pipeline(handle), pipeline_layout(layout) {} + PipelineImpl(const RenderPassImpl&) = delete; + ~PipelineImpl() { + vkDestroyPipeline(inner_device, pipeline, nullptr); + vkDestroyPipelineLayout(inner_device, pipeline_layout, nullptr); + } + + VkPipeline handle() const override { return pipeline; } + VkPipelineLayout layout() const override { return pipeline_layout; } + +private: + VkDevice inner_device; + VkPipeline pipeline; + VkPipelineLayout pipeline_layout; +}; + +class FramebufferImpl : public Framebuffer { +public: + FramebufferImpl(VkDevice device, VkFramebuffer handle, VkExtent2D sz) : inner_device(device), framebuffer(handle), fsize(sz) {} + FramebufferImpl(const FramebufferImpl&) = delete; + ~FramebufferImpl() { + vkDestroyFramebuffer(inner_device, framebuffer, nullptr); + } + + VkFramebuffer handle() const override { return framebuffer; } + VkExtent2D size() const override { return fsize; } + +private: + VkExtent2D fsize; + VkDevice inner_device; + VkFramebuffer framebuffer; +}; + +class CommandPipelineBarrierBuilderImpl : public CommandPipelineBarrierBuilder { +public: + CommandPipelineBarrierBuilderImpl(CommandBuffer* pcmd, VkPipelineStageFlags src, VkPipelineStageFlags dst) + : cmd_buffer(pcmd), src(src), dst(dst) {} + + CommandPipelineBarrierBuilder& add_image_memory_barrior(const Image& img) override { + VkImageMemoryBarrier barrier; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.pNext = nullptr; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = img.handle(); + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = 0; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + image_barriors.push_back(barrier); + return *this; + } + CommandPipelineBarrierBuilder& image_set_layout(VkImageLayout old_layout, VkImageLayout new_layout) { + image_barriors.back().oldLayout = old_layout; + image_barriors.back().newLayout = new_layout; + return *this; + } + CommandPipelineBarrierBuilder& image_set_aspect_mask(VkImageAspectFlags aspect_mask) { + image_barriors.back().subresourceRange.aspectMask = aspect_mask; + return *this; + } + CommandPipelineBarrierBuilder& image_set_access_mask(VkAccessFlags src, VkAccessFlags dst) { + image_barriors.back().srcAccessMask = src; + image_barriors.back().dstAccessMask = dst; + return *this; + } + CommandBuffer& end_pipeline_barrior() { + auto d = defer([this](){delete this;}); + vkCmdPipelineBarrier( + cmd_buffer->handle(), src, dst, 0, + mem_barriors.size(), (mem_barriors.empty() ? nullptr : mem_barriors.data()), + buffer_barriors.size(), (buffer_barriors.empty() ? nullptr : buffer_barriors.data()), + image_barriors.size(), (image_barriors.empty() ? nullptr : image_barriors.data()) + ); + return *cmd_buffer; + } +private: + rs::vector mem_barriors; + rs::vector buffer_barriors; + rs::vector image_barriors; + CommandBuffer* cmd_buffer; + VkPipelineStageFlags src; + VkPipelineStageFlags dst; +}; + +class CommandBufferCopyBuilderImpl : public CommandBufferCopyBuilder { +public: + CommandBufferCopyBuilderImpl(CommandBuffer* pcmd, const Buffer& src, const Buffer& dst) + : cmd_buffer(pcmd), src(src.handle()), dst(dst.handle()) {} + CommandBufferCopyBuilder& add_region(uint32_t src_offset, uint32_t dst_offset, uint32_t size) override { + VkBufferCopy region; + region.srcOffset = src_offset; + region.dstOffset = dst_offset; + region.size = size; + regions.push_back(region); + return *this; + } + CommandBuffer& end_buffer_copy() override { + auto d = defer([this](){delete this;}); + if(!regions.empty()) + vkCmdCopyBuffer(cmd_buffer->handle(), src, dst, regions.size(), regions.data()); + return *cmd_buffer; + } + +private: + CommandBuffer* cmd_buffer; + VkBuffer src; + VkBuffer dst; + rs::vector regions; +}; + +class CommandBufferImageCopyBuilderImpl : public CommandBufferImageCopyBuilder { +public: + CommandBufferImageCopyBuilderImpl(CommandBuffer* pcmd, const Buffer& buffer, const Image& img) + : cmd_buffer(pcmd), buffer(buffer.handle()), image(img.handle()) {} + CommandBufferImageCopyBuilder& add_region(uint32_t offset, VkOffset3D img_offset, VkExtent3D img_extent) override { + VkBufferImageCopy region; + region.bufferOffset = offset; + region.bufferRowLength = 0; + region.bufferImageHeight = 0; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.mipLevel = 0; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageOffset = img_offset; + region.imageExtent = img_extent; + regions.push_back(region); + return *this; + } + CommandBuffer& end_buffer_image_copy() override { + auto d = defer([this](){delete this;}); + if(!regions.empty()) + vkCmdCopyBufferToImage(cmd_buffer->handle(), buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regions.size(), regions.data()); + return *cmd_buffer; + } + +private: + CommandBuffer* cmd_buffer; + VkBuffer buffer; + VkImage image; + rs::vector regions; +}; + +class CommandBufferImpl : public CommandBuffer { +public: + CommandBufferImpl(VkDevice device, VkCommandBuffer buffer, std::weak_ptr pool) + : inner_device(device), command_buffer(buffer), weak_pool(pool) {} + CommandBufferImpl(const CommandBufferImpl&) = delete; + ~CommandBufferImpl() { + if(auto pool = weak_pool.lock(); pool != nullptr) + vkFreeCommandBuffers(inner_device, pool->handle(), 1, &command_buffer); + } + + VkCommandBuffer handle() const override { return command_buffer; } + CommandBuffer& begin_command(VkCommandBufferUsageFlags flag) override { + VkCommandBufferBeginInfo begin_info; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.pNext = nullptr; + begin_info.flags = flag; + begin_info.pInheritanceInfo = nullptr; + vkBeginCommandBuffer(command_buffer, &begin_info); + return *this; + } + void end_command() override { + vkEndCommandBuffer(command_buffer); + } + CommandBuffer& begin_render_pass(const RenderPass& pass, const Framebuffer& buffer, VkClearValue c) override { + VkClearValue cvalues[2] = {c, {1.0f, 0}}; + VkRenderPassBeginInfo render_pass_info; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + render_pass_info.pNext = nullptr; + render_pass_info.renderPass = pass.handle(); + render_pass_info.renderArea = {{0, 0}, buffer.size()}; + render_pass_info.framebuffer = buffer.handle(); + render_pass_info.clearValueCount = 2; + render_pass_info.pClearValues = cvalues; + vkCmdBeginRenderPass(command_buffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); + return *this; + } + CommandBuffer& end_render_pass() override { + vkCmdEndRenderPass(command_buffer); + return *this; + } + CommandBuffer& bind_pipeline(const Pipeline& pipeline) override { + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.handle()); + return *this; + } + CommandBuffer& bind_pipeline_and_descriptor_sets(const Pipeline& pipeline, const DescriptorSet& sets) override { + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.handle()); + auto descriptor_set = sets.handle(); + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout(), 0, 1, &descriptor_set, 0, nullptr); + return *this; + } + CommandBuffer& draw(uint32_t count) override { + vkCmdDraw(command_buffer, count, 1, 0, 0); + return *this; + } + CommandBuffer& draw_vertices(const Buffer& vertex_buffer, uint32_t count) override { + VkBuffer buffers[] = {vertex_buffer.handle()}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(command_buffer, 0, 1, buffers, offsets); + vkCmdDraw(command_buffer, count, 1, 0, 0); + return *this; + } + CommandBuffer& draw_indexed(const Buffer& vertex_buffer, const Buffer& index_buffer, uint32_t count) override { + VkBuffer buffers[] = {vertex_buffer.handle()}; + VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(command_buffer, 0, 1, buffers, offsets); + vkCmdBindIndexBuffer(command_buffer, index_buffer.handle(), 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(command_buffer, count, 1, 0, 0, 0); + return *this; + } + CommandPipelineBarrierBuilder& begin_pipeline_barrior(VkPipelineStageFlags src, VkPipelineStageFlags dst) override { + return *(new CommandPipelineBarrierBuilderImpl(this, src, dst)); + } + CommandBufferCopyBuilder& begin_copy_buffer(const Buffer& src, const Buffer& dst) override { + return *(new CommandBufferCopyBuilderImpl(this, src, dst)); + } + CommandBufferImageCopyBuilder& begin_copy_buffer_to_image(const Buffer& src, const Image& dst) override { + return *(new CommandBufferImageCopyBuilderImpl(this, src, dst)); + } + +private: + VkDevice inner_device; + VkCommandBuffer command_buffer; + std::weak_ptr weak_pool; +}; + +class CommandPoolImpl : public CommandPool, public std::enable_shared_from_this { +public: + CommandPoolImpl(VkDevice device, VkCommandPool pool) : inner_device(device), command_pool(pool) {} + ~CommandPoolImpl() { + vkDestroyCommandPool(inner_device, command_pool, nullptr); + } + VkCommandPool handle() const override { return command_pool; } + rs::vector> alloc_command_buffer(VkCommandBufferLevel level, uint32_t count) override { + rs::vector> results; + alloc_command_buffer(results, level, count); + return results; + } + + CommandPool& alloc_command_buffer(rs::vector>& vec, VkCommandBufferLevel level, uint32_t count) override { + rs::vector command_buffers(count); + VkCommandBufferAllocateInfo alloc_info; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.commandPool = command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = (uint32_t) command_buffers.size(); + if(vkAllocateCommandBuffers(inner_device, &alloc_info, command_buffers.data()) != VK_SUCCESS) + return *this; + std::weak_ptr pool = shared_from_this(); + for(auto& buf : command_buffers) + vec.emplace_back(new CommandBufferImpl(inner_device, buf, pool)); + return *this; + } + +private: + VkDevice inner_device; + VkCommandPool command_pool; +}; + +class RendererImpl : public Renderer { +public: + RendererImpl(VkDevice device, uint32_t count) : inner_device(device) { + img_count = count; + current_frame = 0; + image_semaphore.resize(img_count); + signal_semaphore.resize(img_count); + submit_fence.resize(img_count); + VkSemaphoreCreateInfo semaphore_info; + semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_info.pNext = nullptr; + semaphore_info.flags = 0; + VkFenceCreateInfo fence_info; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.pNext = nullptr; + fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + + for(uint32_t i = 0; i < img_count; ++i) { + vkCreateSemaphore(device, &semaphore_info, nullptr, &image_semaphore[i]); + vkCreateSemaphore(device, &semaphore_info, nullptr, &signal_semaphore[i]); + vkCreateFence(device, &fence_info, nullptr, &submit_fence[i]); + } + } + RendererImpl(const RendererImpl&) = delete; + ~RendererImpl() { + for(uint32_t i = 0; i < img_count; ++i) { + vkDestroySemaphore(inner_device, image_semaphore[i], nullptr); + vkDestroySemaphore(inner_device, signal_semaphore[i], nullptr); + vkDestroyFence(inner_device, submit_fence[i], nullptr); + } + } + uint32_t prepare_for_next_frame() override { + current_index = current_frame % img_count; + current_frame++; + vkWaitForFences(inner_device, 1, &submit_fence[current_index], VK_TRUE, std::numeric_limits::max()); + vkResetFences(inner_device, 1, &submit_fence[current_index]); + outofdate = false; + return current_index; + } + uint32_t accquire_next_image(const Swapchain& swapchain) override { + uint32_t image_index = 0; + auto result = vkAcquireNextImageKHR(inner_device, swapchain.handle(), std::numeric_limits::max(), + image_semaphore[current_index], nullptr, &image_index); + if(result == VK_ERROR_OUT_OF_DATE_KHR) + outofdate = true; + return image_index; + } + Renderer& submit_draw_command(const Queue& queue, const CommandBuffer& command_buffer) override { + VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + VkSubmitInfo submit_info; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &image_semaphore[current_index]; + submit_info.pWaitDstStageMask = wait_stages; + submit_info.commandBufferCount = 1; + auto cmd = command_buffer.handle(); + submit_info.pCommandBuffers = &cmd; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &signal_semaphore[current_index]; + vkQueueSubmit(queue.handle, 1, &submit_info, submit_fence[current_index]); + return *this; + } + Renderer& present(const Queue& queue, const Swapchain& swapchain, uint32_t index) override { + VkSwapchainKHR swapchains[] = {swapchain.handle()}; + VkPresentInfoKHR present_info; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.pNext = nullptr; + present_info.waitSemaphoreCount = 1; + present_info.pWaitSemaphores = &signal_semaphore[current_index]; + present_info.swapchainCount = 1; + present_info.pSwapchains = swapchains; + present_info.pImageIndices = &index; + present_info.pResults = nullptr; + if(vkQueuePresentKHR(queue.handle, &present_info) == VK_ERROR_OUT_OF_DATE_KHR) + outofdate = true; + return *this; + } + Renderer& wait_idle(const Queue& queue) override { + vkQueueWaitIdle(queue.handle); + return *this; + } + Renderer& submit_onetime_command(const Queue& queue, rs::vector>& command_buffers) override { + rs::vector cmds; + for(auto& ptr : command_buffers) + cmds.push_back(ptr->handle()); + VkSubmitInfo submitInfo = {}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = cmds.size(); + submitInfo.pCommandBuffers = cmds.data(); + vkQueueSubmit(queue.handle, 1, &submitInfo, VK_NULL_HANDLE); + return *this; + } + bool out_of_date() const override { + return outofdate; + } +private: + VkDevice inner_device; + std::vector image_semaphore; + std::vector signal_semaphore; + std::vector submit_fence; + uint32_t img_count = 1; + uint32_t current_frame = 0; + uint32_t current_index = 0; + bool outofdate = false; +}; + +rs::vector list_layer_properties() { + uint32_t layer_count = 0; + vkEnumerateInstanceLayerProperties(&layer_count, nullptr); + rs::vector instance_layer_props(layer_count); + vkEnumerateInstanceLayerProperties(&layer_count, instance_layer_props.data()); + return instance_layer_props; +} + +class InstanceBuilderImpl : public InstanceBuilder { +public: + InstanceBuilder& set_application_name(const char* name) { app_name = name; return *this; } + InstanceBuilder& set_enging_name(const char* name) { engine_name = name; return *this; } + InstanceBuilder& set_application_version(uint32_t ver) { app_version = ver; return *this; } + InstanceBuilder& set_engine_version(uint32_t ver) { engine_version = ver; return *this; } + InstanceBuilder& set_api_version(uint32_t ver) { api_version = ver; return *this; } + InstanceBuilder& set_layers(const rs::vector& layers) { layer_names = layers; return *this; } + InstanceBuilder& set_extensions(const rs::vector& extensions) { extension_names = extensions; return *this; } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkApplicationInfo app_info; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pNext = nullptr; + app_info.pApplicationName = app_name.c_str(); + app_info.applicationVersion = app_version; + app_info.pEngineName = engine_name.c_str(); + app_info.engineVersion = engine_version; + app_info.apiVersion = api_version; + VkInstanceCreateInfo instance_create_info; + instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_create_info.pNext = nullptr; + instance_create_info.flags = 0; + instance_create_info.pApplicationInfo = &app_info; + instance_create_info.enabledLayerCount = layer_names.size(); + instance_create_info.ppEnabledLayerNames = layer_names.empty() ? nullptr : layer_names.data(); + instance_create_info.enabledExtensionCount = extension_names.size(); + instance_create_info.ppEnabledExtensionNames = extension_names.empty() ? nullptr : extension_names.data(); + VkInstance handle; + if(vkCreateInstance(&instance_create_info, nullptr, &handle) != VK_SUCCESS) + return nullptr; + return std::shared_ptr(new InstanceImpl(handle)); + } +private: + std::string app_name; + std::string engine_name; + uint32_t app_version = 0; + uint32_t engine_version = 0; + uint32_t api_version = 0; + rs::vector layer_names; + rs::vector extension_names; +}; + +class DeviceBuilderImpl : public DeviceBuilder { +public: + DeviceBuilderImpl(const PhysicalDevice& handle) : physical_device(&handle) {} + DeviceBuilder& set_extensions(const rs::vector& extensions) { extension_names = extensions; return *this; } + DeviceBuilder& add_queue_family(uint32_t index, float priority) { queue_infos.emplace_back(index, priority); return *this; } + DeviceBuilder& enable_features(const VkPhysicalDeviceFeatures& features) { enabled_features = features; return *this; } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + auto queue_counts = physical_device->enumerate_queue_families().map([](const VkQueueFamilyProperties* q) { + return q->queueCount; + }).collect>(); + rs::vector> priorities(queue_counts.size()); + queue_infos.into_iter().for_each([&priorities, &queue_counts](uint32_t idx, float priority) { + if(idx >= priorities.size()) + return; + if(priorities[idx].size() < queue_counts[idx]) + priorities[idx].push_back(priority); + }); + auto queue_create_infos = priorities.into_iter_ptr().enumerate().filter([](size_t idx, const rs::vector* p) { + return p->size() > 0; + }).map([](size_t idx, const rs::vector* p) { + VkDeviceQueueCreateInfo queue_create_info; + queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info.pNext = nullptr; + queue_create_info.flags = 0; + queue_create_info.queueFamilyIndex = (uint32_t)idx; + queue_create_info.queueCount = p->size(); + queue_create_info.pQueuePriorities = p->data(); + return queue_create_info; + }).collect>(); + VkDeviceCreateInfo device_create_info; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + device_create_info.pNext = nullptr; + device_create_info.flags = 0; + device_create_info.queueCreateInfoCount = (uint32_t)queue_create_infos.size(); + device_create_info.pQueueCreateInfos = queue_create_infos.data(); + device_create_info.enabledLayerCount = 0; + device_create_info.ppEnabledLayerNames = nullptr; + device_create_info.enabledExtensionCount = extension_names.size(); + device_create_info.ppEnabledExtensionNames = extension_names.data(); + device_create_info.pEnabledFeatures = &enabled_features; + VkDevice device; + if(vkCreateDevice(physical_device->handle(), &device_create_info, nullptr, &device) == VK_SUCCESS) { + auto queues = queue_create_infos.into_iter_ptr().flat_map([device](const VkDeviceQueueCreateInfo* pinfo) { + return rs::range(0u, pinfo->queueCount).map([device, pinfo](uint32_t idx) { + VkQueue queue; + vkGetDeviceQueue(device, pinfo->queueFamilyIndex, idx, &queue); + return Queue{queue, pinfo->queueFamilyIndex, idx}; + }); + }).collect>(); + return std::shared_ptr(new DeviceImpl(device, std::move(queues))); + } + return nullptr; + } +private: + const PhysicalDevice* physical_device; + rs::vector extension_names; + rs::vector> queue_infos; + VkPhysicalDeviceFeatures enabled_features; +}; + +class BufferBuilderImpl : public BufferBuilder { +public: + BufferBuilderImpl(const Device& device, const PhysicalDevice& physical_device) : device(device.handle()) { + mem_props = physical_device.mem_properties(); + } + BufferBuilder& alloc_size(size_t size) { this->size = size; return *this; } + BufferBuilder& set_usage(VkBufferUsageFlags usage) { this->usage = usage; return *this; } + BufferBuilder& set_sharing_mode(VkSharingMode mode) { this->mode = mode; return *this; } + BufferBuilder& set_cpu_accessible() { + expected_prop = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + return *this; + } + BufferBuilder& set_device_local() { + expected_prop = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + return *this; + } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkBufferCreateInfo buffer_info; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.pNext = nullptr; + buffer_info.flags = 0; + buffer_info.size = size; + buffer_info.usage = usage; + buffer_info.sharingMode = mode; + VkBuffer buffer; + if(vkCreateBuffer(device, &buffer_info, nullptr, &buffer) != VK_SUCCESS) + return nullptr; + VkMemoryRequirements mem_requirements; + vkGetBufferMemoryRequirements(device, buffer, &mem_requirements); + auto mem_index = rs::range(0u, mem_props->memoryTypeCount).find([&, this](uint32_t idx) { + return ((mem_requirements.memoryTypeBits & (1 << idx)) + && (mem_props->memoryTypes[idx].propertyFlags & expected_prop) == expected_prop); + }).value(); + VkMemoryAllocateInfo alloc_info; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.allocationSize = mem_requirements.size; + alloc_info.memoryTypeIndex = mem_index; + VkDeviceMemory device_memory; + if(vkAllocateMemory(device, &alloc_info, nullptr, &device_memory) != VK_SUCCESS) { + vkDestroyBuffer(device, buffer, nullptr); + return nullptr; + } + vkBindBufferMemory(device, buffer, device_memory, 0); + bool host_visible = (expected_prop & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + return std::shared_ptr(new BufferImpl(device, buffer, device_memory, mem_requirements.size, host_visible)); + } + +private: + VkDevice device; + const VkPhysicalDeviceMemoryProperties* mem_props; + uint32_t size = 0; + VkBufferUsageFlags usage; + VkSharingMode mode = VK_SHARING_MODE_EXCLUSIVE; + VkMemoryPropertyFlags expected_prop; +}; + +class ImageBuilderImpl : public ImageBuilder { +public: + ImageBuilderImpl(const Device& device, const PhysicalDevice& physical_device) : device(device.handle()) { + mem_props = physical_device.mem_properties(); + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.pNext = nullptr; + info.flags = 0; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = VK_FORMAT_B8G8R8A8_UNORM; + info.extent = {0u, 0u, 1u}; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.queueFamilyIndexCount = 0; + info.pQueueFamilyIndices = nullptr; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + ImageBuilder& set_type(VkImageType type) { info.imageType = type; return *this; } + ImageBuilder& set_format(VkFormat format) { info.format = format; return *this; } + ImageBuilder& set_extent(VkExtent3D ext) { info.extent = ext; return *this; } + ImageBuilder& set_mip_level(uint32_t level) { info.mipLevels = level; return *this; } + ImageBuilder& set_array_layers(uint32_t layer) { info.arrayLayers = layer; return *this; } + ImageBuilder& set_samples(VkSampleCountFlagBits samples) { info.samples = samples; return *this; } + ImageBuilder& set_tilling(VkImageTiling tiling) { info.tiling = tiling; return *this; } + ImageBuilder& set_usage(VkImageUsageFlags usage) { info.usage = usage; return *this; } + ImageBuilder& set_sharing_mode(VkSharingMode mode) { info.sharingMode = mode; return *this; } + ImageBuilder& set_init_layout(VkImageLayout layout) { info.initialLayout = layout; return *this; } + ImageBuilder& set_cpu_accessible() { + expected_prop = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + return *this; + } + ImageBuilder& set_device_local() { + expected_prop = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + return *this; + } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkImage image; + if(vkCreateImage(device, &info, nullptr, &image) != VK_SUCCESS) + return nullptr; + VkMemoryRequirements mem_requirements; + vkGetImageMemoryRequirements(device, image, &mem_requirements); + auto mem_index = rs::range(0u, mem_props->memoryTypeCount).find([&, this](uint32_t idx) { + return ((mem_requirements.memoryTypeBits & (1 << idx)) + && (mem_props->memoryTypes[idx].propertyFlags & expected_prop) == expected_prop); + }).value(); + VkMemoryAllocateInfo alloc_info; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.allocationSize = mem_requirements.size; + alloc_info.memoryTypeIndex = mem_index; + VkDeviceMemory device_memory; + if(vkAllocateMemory(device, &alloc_info, nullptr, &device_memory) != VK_SUCCESS) { + vkDestroyImage(device, image, nullptr); + return nullptr; + } + vkBindImageMemory(device, image, device_memory, 0); + bool host_visible = (expected_prop & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + return std::make_shared(device, image, device_memory, info.format, host_visible); + } + +private: + VkDevice device; + const VkPhysicalDeviceMemoryProperties* mem_props; + VkImageCreateInfo info; + VkMemoryPropertyFlags expected_prop; +}; + +class SamplerBuilderImpl : public SamplerBuilder { +public: + SamplerBuilderImpl(const Device& device) : device(device.handle()) { + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_info.pNext = nullptr; + sampler_info.flags = 0; + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler_info.anisotropyEnable = VK_FALSE; + sampler_info.maxAnisotropy = 0; + sampler_info.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + sampler_info.unnormalizedCoordinates = VK_FALSE; + sampler_info.compareEnable = VK_FALSE; + sampler_info.compareOp = VK_COMPARE_OP_ALWAYS; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + sampler_info.mipLodBias = 0.0f; + sampler_info.minLod = 0.0f; + sampler_info.maxLod = 0.0f; + } + SamplerBuilder& set_filter(VkFilter filter) { + sampler_info.magFilter = filter; + sampler_info.minFilter = filter; + return *this; + } + SamplerBuilder& set_address_mode(VkSamplerAddressMode mode) { + sampler_info.addressModeU = mode; + sampler_info.addressModeV = mode; + sampler_info.addressModeW = mode; + return *this; + } + SamplerBuilder& set_anisotrophy(float value) { + sampler_info.anisotropyEnable = VK_TRUE; + sampler_info.maxAnisotropy = value; + return *this; + } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkSampler sampler; + if(vkCreateSampler(device, &sampler_info, nullptr, &sampler) != VK_SUCCESS) + return nullptr; + return std::make_shared(device, sampler); + } + +private: + VkDevice device; + VkSamplerCreateInfo sampler_info; +}; + +class SwapchainBuilderImpl : public SwapchainBuilder { +public: + SwapchainBuilderImpl(const Device& device) : device(device.handle()) { + swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_create_info.pNext = nullptr; + swapchain_create_info.flags = 0; + swapchain_create_info.surface = nullptr; + swapchain_create_info.minImageCount = 2; + swapchain_create_info.imageFormat = VK_FORMAT_B8G8R8A8_UNORM; + swapchain_create_info.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + swapchain_create_info.imageExtent = {128, 128}; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM; + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_create_info.queueFamilyIndexCount = 0; + swapchain_create_info.pQueueFamilyIndices = nullptr; + swapchain_create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchain_create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; + swapchain_create_info.clipped = VK_TRUE; + swapchain_create_info.oldSwapchain = nullptr; + } + SwapchainBuilder& set_surface(const Surface& surface) { swapchain_create_info.surface = surface.handle(); return *this; } + SwapchainBuilder& set_min_image_count(uint32_t count) { swapchain_create_info.minImageCount = count; return *this; } + SwapchainBuilder& set_format(VkFormat format) { swapchain_create_info.imageFormat = format; return *this; } + SwapchainBuilder& set_colorspace(VkColorSpaceKHR colorspace) { swapchain_create_info.imageColorSpace = colorspace; return *this; } + SwapchainBuilder& set_extent(VkExtent2D ext) { swapchain_create_info.imageExtent = ext; return *this; } + SwapchainBuilder& set_array_layer(uint32_t array_layer) { swapchain_create_info.imageArrayLayers = array_layer; return *this; } + SwapchainBuilder& set_image_usage(VkImageUsageFlags usage) { swapchain_create_info.imageUsage = usage; return *this; } + SwapchainBuilder& set_queue_family_indices(const rs::vector& queue_family_indices) { + if(queue_family_indices.size() <= 1) { + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_create_info.queueFamilyIndexCount = 0; + swapchain_create_info.pQueueFamilyIndices = nullptr; + } else { + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + swapchain_create_info.queueFamilyIndexCount = queue_family_indices.size(); + swapchain_create_info.pQueueFamilyIndices = queue_family_indices.data(); + } + return *this; + } + SwapchainBuilder& set_transform(VkSurfaceTransformFlagBitsKHR transform) { swapchain_create_info.preTransform = transform; return *this; } + SwapchainBuilder& set_composite_alpha(VkCompositeAlphaFlagBitsKHR alpha) { swapchain_create_info.compositeAlpha = alpha; return *this; } + SwapchainBuilder& set_present_mode(VkPresentModeKHR mode) { swapchain_create_info.presentMode = mode; return *this; } + SwapchainBuilder& set_clipped(VkBool32 clipped) { swapchain_create_info.clipped = clipped; return *this; } + SwapchainBuilder& set_old_swapchain(Swapchain& old) { swapchain_create_info.oldSwapchain = old.handle(); return *this; } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkSwapchainKHR swapchain; + if(vkCreateSwapchainKHR(device, &swapchain_create_info, nullptr, &swapchain) != VK_SUCCESS) + return nullptr; + return std::shared_ptr(new SwapchainImpl(device, swapchain, swapchain_create_info.imageFormat)); + } +private: + VkDevice device; + VkSwapchainCreateInfoKHR swapchain_create_info; +}; + +class RenderPassBuilderImpl : public RenderPassBuilder { +public: + RenderPassBuilderImpl(const Device& device) : device(device.handle()) {} + + RenderPassBuilder& set_format(VkFormat format) { color_format = format; return *this; } + RenderPassBuilder& set_samples(VkSampleCountFlagBits samples) { this->samples = samples; return *this; } + + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkAttachmentDescription attachments[3]; + // 0 - color attackment + attachments[0].flags = 0; + attachments[0].format = color_format; + attachments[0].samples = samples; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + if(samples == VK_SAMPLE_COUNT_1_BIT) + attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + else + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + // 1- depth attackment + attachments[1].flags = 0; + attachments[1].format = VK_FORMAT_D24_UNORM_S8_UINT; + attachments[1].samples = samples; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + // 2 - color attachment resolve, for msaa + attachments[2].flags = 0; + attachments[2].format = color_format; + attachments[2].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[2].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference attachment_ref[3]; + attachment_ref[0].attachment = 0; + attachment_ref[0].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachment_ref[1].attachment = 1; + attachment_ref[1].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachment_ref[2].attachment = 2; + attachment_ref[2].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &attachment_ref[0]; + subpass.pDepthStencilAttachment = &attachment_ref[1]; + if(samples != VK_SAMPLE_COUNT_1_BIT) + subpass.pResolveAttachments = &attachment_ref[2]; + + VkSubpassDependency dependency; + dependency.dependencyFlags = 0; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + VkRenderPassCreateInfo render_pass_info; + render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_info.pNext = nullptr; + render_pass_info.flags = 0; + render_pass_info.attachmentCount = (samples == VK_SAMPLE_COUNT_1_BIT) ? 2 : 3; + render_pass_info.pAttachments = attachments; + render_pass_info.subpassCount = 1; + render_pass_info.pSubpasses = &subpass; + render_pass_info.dependencyCount = 1; + render_pass_info.pDependencies = &dependency; + VkRenderPass render_pass; + vkCreateRenderPass(device, &render_pass_info, nullptr, &render_pass); + return std::shared_ptr(new RenderPassImpl(device, render_pass)); + } + +private: + VkDevice device; + VkFormat color_format = VK_FORMAT_B8G8R8A8_UNORM; + VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; +}; + +class DescriptorSetLayoutBuilderImpl : public DescriptorSetLayoutBuilder { +public: + DescriptorSetLayoutBuilderImpl(const Device& device) : device(device.handle()) {} + DescriptorSetLayoutBuilder& add_binding(VkDescriptorType type) { + VkDescriptorSetLayoutBinding layout_binding; + layout_binding.binding = bindings.size(); + layout_binding.descriptorType = type; + layout_binding.descriptorCount = 1; + layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + layout_binding.pImmutableSamplers = nullptr; + bindings.push_back(layout_binding); + return *this; + } + DescriptorSetLayoutBuilder& set_count(uint32_t count) { bindings.back().descriptorCount = count; return *this; } + DescriptorSetLayoutBuilder& set_stage(VkShaderStageFlags stage) { bindings.back().stageFlags = stage; return *this; } + DescriptorSetLayoutBuilder& set_samplers() { return *this; } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkDescriptorSetLayoutCreateInfo layout_info; + layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_info.pNext = nullptr; + layout_info.flags = 0; + layout_info.bindingCount = bindings.size(); + layout_info.pBindings = bindings.data(); + VkDescriptorSetLayout descriptor_set_layout; + if(vkCreateDescriptorSetLayout(device, &layout_info, nullptr, &descriptor_set_layout) != VK_SUCCESS) + return nullptr; + return std::shared_ptr(new DescriptorSetLayoutImpl(device, descriptor_set_layout)); + } + +private: + VkDevice device; + rs::vector bindings; +}; + +class DescriptorPoolBuilderImpl : public DescriptorPoolBuilder { +public: + DescriptorPoolBuilderImpl(const Device& device) : device(device.handle()) {} + DescriptorPoolBuilder& add_pool_size(VkDescriptorType type, uint32_t count) { pool_size.push_back({type, count}); return *this; } + DescriptorPoolBuilder& set_max_sets(uint32_t count) { max_sets = count; return *this; } + DescriptorPoolBuilder& set_flags(VkDescriptorPoolCreateFlags flag) { flags = flag; return *this; } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkDescriptorPoolCreateInfo pool_info; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = nullptr; + pool_info.flags = flags; + pool_info.poolSizeCount = pool_size.size(); + pool_info.pPoolSizes = pool_size.data(); + pool_info.maxSets = max_sets;; + VkDescriptorPool descriptor_pool; + if(vkCreateDescriptorPool(device, &pool_info, nullptr, &descriptor_pool) != VK_SUCCESS) + return nullptr; + return std::shared_ptr(new DescriptorPoolImpl(device, descriptor_pool, flags)); + } +private: + VkDevice device; + rs::vector pool_size; + uint32_t max_sets = 0; + VkDescriptorPoolCreateFlags flags = 0; +}; + +class PipelineBuilderImpl : public PipelineBuilder { +public: + PipelineBuilderImpl(const Device& device) : device(device.handle()) {} + PipelineBuilder& set_render_pass(const RenderPass& pass) { render_pass = pass.handle(); return *this; } + PipelineBuilder& set_vertex_shader(const ShaderModule& vert) { vert_shader = vert.handle(); return *this; } + PipelineBuilder& set_fragment_shader(const ShaderModule& frag) { frag_shader = frag.handle(); return *this; } + PipelineBuilder& normal_viewport(float w, float h) { viewport = {0.0f, 0.0f, w, h, 0.0f, 1.0f}; return *this; } + PipelineBuilder& normal_scissor(uint32_t w, uint32_t h) { scissor = {{0, 0}, {w, h}}; return *this; } + PipelineBuilder& draw_topology(VkPrimitiveTopology topology) { this->topology = topology; return *this; } + PipelineBuilder& set_polygon_mode(VkPolygonMode mode) { polygon_mode = mode; return *this; } + PipelineBuilder& set_cull_mode(VkCullModeFlags mode) { cull_mode = mode; return *this; } + PipelineBuilder& set_front_face(VkFrontFace front) { front_face = front; return *this; } + PipelineBuilder& set_line_width(float width) { line_width = width; return *this; } + PipelineBuilder& set_samples(VkSampleCountFlagBits samples) { this->samples = samples; return *this; } + PipelineBuilder& bind_vertex_size(uint32_t sz) { vertex_size = sz; return *this; } + PipelineBuilder& add_binding_attribute(VkFormat format, uint32_t offset) { + input_attributes.push_back({(uint32_t)input_attributes.size(), 0, format, offset}); + return *this; + } + PipelineBuilder& add_descriptor_set_layout(const DescriptorSetLayout& layout) { descriptor_set_layouts.push_back(layout.handle()); return *this; } + + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkPipelineShaderStageCreateInfo shader_stages[2]; + VkPipelineShaderStageCreateInfo& vert_shader_info = shader_stages[0]; + vert_shader_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vert_shader_info.pNext = nullptr; + vert_shader_info.flags = 0; + vert_shader_info.stage = VK_SHADER_STAGE_VERTEX_BIT; + vert_shader_info.module = vert_shader; + vert_shader_info.pName = "main"; + vert_shader_info.pSpecializationInfo = nullptr; + VkPipelineShaderStageCreateInfo& frag_shader_info = shader_stages[1]; + frag_shader_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + frag_shader_info.pNext = nullptr; + frag_shader_info.flags = 0; + frag_shader_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + frag_shader_info.module = frag_shader; + frag_shader_info.pName = "main"; + frag_shader_info.pSpecializationInfo = nullptr; + VkPipelineVertexInputStateCreateInfo vertex_input_info; + vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input_info.pNext = nullptr; + vertex_input_info.flags = 0; + if(input_attributes.empty()) { + vertex_input_info.vertexBindingDescriptionCount = 0; + vertex_input_info.pVertexBindingDescriptions = nullptr; + vertex_input_info.vertexAttributeDescriptionCount = 0; + vertex_input_info.pVertexAttributeDescriptions = nullptr; + } else { + VkVertexInputBindingDescription binding_description; + binding_description.binding = 0; + binding_description.stride = vertex_size; + binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + vertex_input_info.vertexBindingDescriptionCount = 1; + vertex_input_info.pVertexBindingDescriptions = &binding_description; + vertex_input_info.vertexAttributeDescriptionCount = input_attributes.size(); + vertex_input_info.pVertexAttributeDescriptions = input_attributes.data(); + } + VkPipelineInputAssemblyStateCreateInfo input_assemply_info; + input_assemply_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assemply_info.pNext = nullptr; + input_assemply_info.flags = 0; + input_assemply_info.topology = topology; + input_assemply_info.primitiveRestartEnable = VK_FALSE; + VkPipelineViewportStateCreateInfo viewport_state; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state.pNext = nullptr; + viewport_state.flags = 0; + viewport_state.viewportCount = 1; + viewport_state.pViewports = &viewport; + viewport_state.scissorCount = 1; + viewport_state.pScissors = &scissor; + VkPipelineRasterizationStateCreateInfo rasterizer; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.pNext = nullptr; + rasterizer.flags = 0; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = polygon_mode; + rasterizer.cullMode = cull_mode; + rasterizer.frontFace = front_face; + rasterizer.depthBiasEnable = VK_FALSE; + rasterizer.depthBiasConstantFactor = 0.0f; + rasterizer.depthBiasClamp = 0.0f; + rasterizer.depthBiasSlopeFactor = 0.0f; + rasterizer.lineWidth = line_width; + VkPipelineMultisampleStateCreateInfo multisampling; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.pNext = nullptr; + multisampling.flags = 0; + multisampling.rasterizationSamples = samples; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.minSampleShading = 1.0f; + multisampling.pSampleMask = nullptr; + multisampling.alphaToCoverageEnable = VK_FALSE; + multisampling.alphaToOneEnable = VK_FALSE; + VkPipelineColorBlendAttachmentState colorBlendAttachment; + colorBlendAttachment.blendEnable = VK_TRUE; + colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + VkPipelineColorBlendStateCreateInfo colorBlending; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.pNext = nullptr; + colorBlending.flags = 0; + colorBlending.logicOpEnable = VK_FALSE; + colorBlending.logicOp = VK_LOGIC_OP_COPY; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + VkPipelineLayoutCreateInfo pipeline_layout_info; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.pNext = nullptr; + pipeline_layout_info.flags = 0; + if(descriptor_set_layouts.empty()) { + pipeline_layout_info.setLayoutCount = 0; + pipeline_layout_info.pSetLayouts = nullptr; + } else { + pipeline_layout_info.setLayoutCount = descriptor_set_layouts.size(); + pipeline_layout_info.pSetLayouts = descriptor_set_layouts.data(); + } + pipeline_layout_info.pushConstantRangeCount = 0; + VkPipelineLayout pipeline_layout; + if(vkCreatePipelineLayout(device, &pipeline_layout_info, nullptr, &pipeline_layout) != VK_SUCCESS) + return nullptr; + + VkPipelineDepthStencilStateCreateInfo depthStencil = {}; + depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + depthStencil.depthTestEnable = VK_TRUE; + depthStencil.depthWriteEnable = VK_TRUE; + depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; + depthStencil.depthBoundsTestEnable = VK_FALSE; + depthStencil.minDepthBounds = 0.0f; + depthStencil.maxDepthBounds = 1.0f; + depthStencil.stencilTestEnable = VK_FALSE; + depthStencil.front = {}; + depthStencil.back = {}; + + VkGraphicsPipelineCreateInfo pipelineInfo; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.pNext = nullptr; + pipelineInfo.flags = 0; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = shader_stages; + pipelineInfo.pVertexInputState = &vertex_input_info; + pipelineInfo.pInputAssemblyState = &input_assemply_info; + pipelineInfo.pViewportState = &viewport_state; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = &depthStencil; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = nullptr; + pipelineInfo.layout = pipeline_layout; + pipelineInfo.renderPass = render_pass; + pipelineInfo.subpass = 0; + pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; + pipelineInfo.basePipelineIndex = -1; + VkPipeline graphicsPipeline; + if(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) + return nullptr; + return std::shared_ptr(new PipelineImpl(device, graphicsPipeline, pipeline_layout)); + } + +private: + VkDevice device; + VkRenderPass render_pass; + VkShaderModule vert_shader; + VkShaderModule frag_shader; + VkViewport viewport; + VkRect2D scissor; + VkPrimitiveTopology topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + VkPolygonMode polygon_mode = VK_POLYGON_MODE_FILL; + VkCullModeFlags cull_mode = VK_CULL_MODE_NONE; + VkFrontFace front_face = VK_FRONT_FACE_COUNTER_CLOCKWISE; + float line_width = 1.0; + VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; + uint32_t vertex_size = 0; + rs::vector input_attributes; + rs::vector descriptor_set_layouts; +}; + +class FramebufferBuilderImpl : public FramebufferBuilder { +public: + FramebufferBuilderImpl(const Device& dev) : device(dev.handle()) {} + FramebufferBuilder& set_render_pass(const RenderPass& render_pass) { this->render_pass = render_pass.handle(); return *this; } + FramebufferBuilder& set_framebuffer_size(uint32_t w, uint32_t h) { width = w; height = h; return *this; } + FramebufferBuilder& add_image_view(const ImageView& view) { image_views.push_back(view.handle()); return *this; } + FramebufferBuilder& set_flags(VkFramebufferCreateFlags flag) { this->flags = flags; return *this; } + FramebufferBuilder& set_layer(uint32_t layer) { this->layer = layer; return *this; } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkFramebufferCreateInfo create_info; + create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + create_info.pNext = nullptr; + create_info.flags = flags; + create_info.renderPass = render_pass; + create_info.attachmentCount = image_views.size(); + create_info.pAttachments = image_views.data(); + create_info.width = width; + create_info.height = height; + create_info.layers = layer; + VkFramebuffer framebuffer; + if(vkCreateFramebuffer(device, &create_info, nullptr, &framebuffer) != VK_SUCCESS) + return nullptr; + return std::shared_ptr(new FramebufferImpl(device, framebuffer, {width, height})); + } +private: + VkDevice device; + VkRenderPass render_pass; + std::vector image_views; + VkFramebufferCreateFlags flags = 0; + uint32_t layer = 1; + uint32_t width = 0; + uint32_t height = 0; +}; + +class CommandPoolBuilderImpl : public CommandPoolBuilder { +public: + CommandPoolBuilderImpl(const Device& dev) : device(dev.handle()) {} + CommandPoolBuilder& set_queue_family_index(uint32_t idx) { queue_family_index = idx; return *this; } + CommandPoolBuilder& set_pool_flag(VkCommandPoolCreateFlags flags) { pool_flag = flags; return *this; } + std::shared_ptr end() { + auto d = defer([this](){delete this;}); + VkCommandPoolCreateInfo pool_create_info; + pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pool_create_info.pNext = nullptr; + pool_create_info.flags = pool_flag; + pool_create_info.queueFamilyIndex = queue_family_index; + VkCommandPool command_pool; + if(vkCreateCommandPool(device, &pool_create_info, nullptr, &command_pool) != VK_SUCCESS) + return nullptr; + return std::make_shared(device, command_pool); + } + +private: + VkDevice device; + uint32_t queue_family_index = 0; + VkCommandPoolCreateFlags pool_flag = 0; +}; + +// static builders +InstanceBuilder& Instance::begin_build() { + return *(new InstanceBuilderImpl); +} + +std::shared_ptr Surface::from_raw_surface(VkSurfaceKHR surface, const Instance& inst, const PhysicalDevice& physical_device) { + return std::shared_ptr(new SurfaceImpl(surface, inst, physical_device)); +} + +DeviceBuilder& Device::begin_build(const PhysicalDevice& physical_device) { + return *(new DeviceBuilderImpl(physical_device)); +} + +BufferBuilder& Buffer::begin_build(const Device& device, const PhysicalDevice& physical_device) { + return *(new BufferBuilderImpl(device, physical_device)); +} + +ImageBuilder& Image::begin_build(const Device& device, const PhysicalDevice& physical_device) { + return *(new ImageBuilderImpl(device, physical_device)); +} + +SamplerBuilder& Sampler::begin_build(const Device& device) { + return *(new SamplerBuilderImpl(device)); +} + +std::shared_ptr ShaderModule::load_from_spv(const Device& device, const std::string& filename) { + std::ifstream file(filename, std::ios::ate | std::ios::binary); + if(!file.is_open()) + return nullptr; + size_t file_size = (size_t) file.tellg(); + std::vector buffer(file_size); + file.seekg(0); + file.read(buffer.data(), file_size); + VkShaderModuleCreateInfo shader_module_create_info; + shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_module_create_info.pNext = nullptr; + shader_module_create_info.flags = 0; + shader_module_create_info.codeSize = file_size; + shader_module_create_info.pCode = reinterpret_cast(buffer.data()); + VkShaderModule shaderModule; + if(vkCreateShaderModule(device.handle(), &shader_module_create_info, nullptr, &shaderModule) != VK_SUCCESS) + return nullptr; + return std::shared_ptr(new ShaderModuleImpl(device.handle(), shaderModule)); +} + +std::shared_ptr ShaderModule::load_from_src(const Device& device, const std::string& filename, uint32_t kind) { + std::ifstream file(filename, std::ios::ate); + if(!file.is_open()) + return nullptr; + size_t file_size = (size_t) file.tellg(); + std::string text(file_size, ' '); + file.seekg(0); + file.read(text.data(), file_size); + shaderc::Compiler compiler; + shaderc::SpvCompilationResult result = compiler.CompileGlslToSpv(text.c_str(), text.length(), (shaderc_shader_kind)kind, filename.c_str()); + if(result.GetNumErrors() != 0) { + std::cout << result.GetErrorMessage() << std::endl; + return nullptr; + } + if(auto status = result.GetCompilationStatus(); status != shaderc_compilation_status_success) { + std::cout << filename << " compile status error ==> " << status << std::endl; + return nullptr; + } + auto bin_begin = result.begin(); + size_t code_size = (result.end() - result.begin()) * sizeof(uint32_t); + VkShaderModuleCreateInfo shader_module_create_info; + shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shader_module_create_info.pNext = nullptr; + shader_module_create_info.flags = 0; + shader_module_create_info.codeSize = code_size; + shader_module_create_info.pCode = bin_begin; + VkShaderModule shaderModule; + if(vkCreateShaderModule(device.handle(), &shader_module_create_info, nullptr, &shaderModule) != VK_SUCCESS) + return nullptr; + return std::shared_ptr(new ShaderModuleImpl(device.handle(), shaderModule)); +} + +SwapchainBuilder& Swapchain::begin_build(const Device& device) { + return *(new SwapchainBuilderImpl(device)); +} + +RenderPassBuilder& RenderPass::begin_build(const Device& device) { + return *(new RenderPassBuilderImpl(device)); +} + +DescriptorSetLayoutBuilder& DescriptorSetLayout::begin_build(const Device& device) { + return *(new DescriptorSetLayoutBuilderImpl(device)); +} + +DescriptorPoolBuilder& DescriptorPool::begin_build(const Device& device) { + return *(new DescriptorPoolBuilderImpl(device)); +} + +PipelineBuilder& Pipeline::begin_build(const Device& device) { + return *(new PipelineBuilderImpl(device)); +} + +FramebufferBuilder& Framebuffer::begin_build(const Device& device) { + return *(new FramebufferBuilderImpl(device)); +} + +CommandPoolBuilder& CommandPool::begin_build(const Device& device) { + return *(new CommandPoolBuilderImpl(device)); +} + +std::shared_ptr Renderer::create_renderer(const vk::Device& device, uint32_t img_count) { + return std::shared_ptr(new RendererImpl(device.handle(), img_count)); +} + +} diff --git a/vulkan.hpp b/vulkan.hpp new file mode 100644 index 0000000..d5066e4 --- /dev/null +++ b/vulkan.hpp @@ -0,0 +1,373 @@ +#ifndef _VULKAN_HPP_ +#define _VULKAN_HPP_ + +#include + +#include "rusty.hpp" + +namespace vk { + +class InstanceBuilder; +class DeviceBuilder; +class BufferBuilder; +class ImageBuilder; +class SamplerBuilder; +class SwapchainBuilder; +class RenderPassBuilder; +class PipelineBuilder; +class FramebufferBuilder; +class CommandPoolBuilder; +class CommandBufferBuilder; +class DescriptorSetLayoutBuilder; +class DescriptorPoolBuilder; + +struct Queue { + VkQueue handle; + uint32_t queue_family_index; + uint32_t queue_index; +}; + +class PhysicalDevice { +public: + virtual ~PhysicalDevice() = default; + virtual VkPhysicalDevice handle() const = 0; + virtual const VkPhysicalDeviceProperties* properties() const = 0; + virtual const VkPhysicalDeviceMemoryProperties* mem_properties() const = 0; + virtual const VkPhysicalDeviceFeatures* supported_features() const = 0; + virtual rs::iterator enumerate_extensions() const = 0; + virtual rs::iterator enumerate_queue_families() const = 0; +}; + +class Instance { +public: + static InstanceBuilder& begin_build(); + virtual ~Instance() = default; + virtual VkInstance handle() const = 0; + virtual rs::iterator enumerate_physical_device() const = 0; +}; + +class Surface { +public: + static std::shared_ptr from_raw_surface(VkSurfaceKHR surface, const Instance& inst, const PhysicalDevice& physical_device); + virtual ~Surface() = default; + virtual VkSurfaceKHR handle() const = 0; + virtual void refresh() = 0; + virtual const VkSurfaceCapabilitiesKHR* capabilities() const = 0; + virtual rs::iterator enumerate_formats() const = 0; + virtual const VkSurfaceFormatKHR* default_format() const = 0; +}; + +class Device { +public: + static DeviceBuilder& begin_build(const PhysicalDevice& physical_device); + virtual ~Device() = default; + virtual VkDevice handle() const = 0; + virtual void wait_idle() const = 0; + virtual rs::iterator enumerate_queues() const = 0; + virtual rs::vector queue_family_in_use() const = 0; +}; + +class Buffer { +public: + static BufferBuilder& begin_build(const Device& device, const PhysicalDevice& physical_device); + virtual ~Buffer() = default; + virtual VkBuffer handle() const = 0; + virtual bool can_map() const = 0; + virtual void* map(uint32_t offset, uint32_t size) = 0; + virtual void unmap() = 0; + virtual void update_buffer(const void* data, uint32_t size) = 0; +}; + +class ImageView { +public: + virtual ~ImageView() = default; + virtual VkImageView handle() const = 0; +}; + +class Image { +public: + static ImageBuilder& begin_build(const Device& device, const PhysicalDevice& physical_device); + virtual ~Image() = default; + virtual VkImage handle() const = 0; + virtual std::shared_ptr create_view(VkFormat view_format, VkImageAspectFlags aspect) = 0; +}; + +class Sampler { +public: + static SamplerBuilder& begin_build(const Device& device); + virtual ~Sampler() = default; + virtual VkSampler handle() const = 0; +}; + +class ShaderModule { +public: + static std::shared_ptr load_from_spv(const Device& device, const std::string& filename); + static std::shared_ptr load_from_src(const Device& device, const std::string& filename, uint32_t kind); + virtual ~ShaderModule() = default; + virtual VkShaderModule handle() const = 0; + + static const uint32_t shader_type_vertex = 0; + static const uint32_t shader_type_fragment = 1; + static const uint32_t shader_type_compute = 2; + static const uint32_t shader_type_geometry = 3; + static const uint32_t shader_type_tess_control = 4; + static const uint32_t shader_type_tess_evaluation = 5; +}; + +class Swapchain { +public: + static SwapchainBuilder& begin_build(const Device& device); + virtual ~Swapchain() = default; + virtual VkSwapchainKHR handle() const = 0; + virtual uint32_t image_count() const = 0; + virtual rs::vector get_swapchain_images() = 0; +}; + +class RenderPass { +public: + static RenderPassBuilder& begin_build(const Device& device); + virtual ~RenderPass() = default; + virtual VkRenderPass handle() const = 0; +}; + +class DescriptorSet { +public: + virtual ~DescriptorSet() = default; + virtual VkDescriptorSet handle() const = 0; + virtual DescriptorSet& update_write(VkDescriptorType type, uint32_t binding, uint32_t start_index) = 0; + virtual DescriptorSet& update_write_buffer_info(const Buffer& buffer, uint32_t offset, uint32_t range, uint32_t rep_count) = 0; + virtual DescriptorSet& update_write_image_info(const ImageView& view, const Sampler& sampler) = 0; + virtual DescriptorSet& update_end() = 0; +}; + +class DescriptorSetLayout { +public: + static DescriptorSetLayoutBuilder& begin_build(const Device& device); + virtual ~DescriptorSetLayout() = default; + virtual VkDescriptorSetLayout handle() const = 0; +}; + +class DescriptorPool { +public: + static DescriptorPoolBuilder& begin_build(const Device& device); + virtual ~DescriptorPool() = default; + virtual VkDescriptorPool handle() const = 0; + virtual rs::vector> alloc_descriptor_set(const DescriptorSetLayout& layout, uint32_t count) = 0; + virtual void reset() = 0; +}; + +class Pipeline { +public: + static PipelineBuilder& begin_build(const Device& device); + virtual ~Pipeline() = default; + virtual VkPipeline handle() const = 0; + virtual VkPipelineLayout layout() const = 0; +}; + +class Framebuffer { +public: + static FramebufferBuilder& begin_build(const Device& device); + virtual ~Framebuffer() = default; + virtual VkFramebuffer handle() const = 0; + virtual VkExtent2D size() const = 0; +}; + +class CommandBuffer; + +class CommandPipelineBarrierBuilder { +public: + virtual CommandPipelineBarrierBuilder& add_image_memory_barrior(const Image& img) = 0; + virtual CommandPipelineBarrierBuilder& image_set_layout(VkImageLayout old_layout, VkImageLayout new_layout) = 0; + virtual CommandPipelineBarrierBuilder& image_set_aspect_mask(VkImageAspectFlags aspect_mask) = 0; + virtual CommandPipelineBarrierBuilder& image_set_access_mask(VkAccessFlags src, VkAccessFlags dst) = 0; + virtual CommandBuffer& end_pipeline_barrior() = 0; +}; + +class CommandBufferCopyBuilder { +public: + virtual CommandBufferCopyBuilder& add_region(uint32_t src_offset, uint32_t dst_offset, uint32_t size) = 0; + virtual CommandBuffer& end_buffer_copy() = 0; +}; + +class CommandBufferImageCopyBuilder { +public: + virtual CommandBufferImageCopyBuilder& add_region(uint32_t offset, VkOffset3D img_offset, VkExtent3D img_extent) = 0; + virtual CommandBuffer& end_buffer_image_copy() = 0; +}; + +class CommandBuffer { +public: + virtual ~CommandBuffer() = default; + virtual VkCommandBuffer handle() const = 0; + virtual CommandBuffer& begin_command(VkCommandBufferUsageFlags flag) = 0; + virtual void end_command() = 0; + virtual CommandBuffer& begin_render_pass(const RenderPass& pass, const Framebuffer& buffer, VkClearValue c) = 0; + virtual CommandBuffer& end_render_pass() = 0; + virtual CommandBuffer& bind_pipeline(const Pipeline& pipeline) = 0; + virtual CommandBuffer& bind_pipeline_and_descriptor_sets(const Pipeline& pipeline, const DescriptorSet& sets) = 0; + virtual CommandBuffer& draw(uint32_t count) = 0; + virtual CommandBuffer& draw_vertices(const Buffer& vertex_buffer, uint32_t count) = 0; + virtual CommandBuffer& draw_indexed(const Buffer& vertex_buffer, const Buffer& index_buffer, uint32_t count) = 0; + virtual CommandPipelineBarrierBuilder& begin_pipeline_barrior(VkPipelineStageFlags src, VkPipelineStageFlags dst) = 0; + virtual CommandBufferCopyBuilder& begin_copy_buffer(const Buffer& src, const Buffer& dst) = 0; + virtual CommandBufferImageCopyBuilder& begin_copy_buffer_to_image(const Buffer& src, const Image& dst) = 0; +}; + +class CommandPool { +public: + static CommandPoolBuilder& begin_build(const Device& device); + virtual VkCommandPool handle() const = 0; + virtual rs::vector> alloc_command_buffer(VkCommandBufferLevel level, uint32_t count) = 0; + virtual CommandPool& alloc_command_buffer(rs::vector>& vec, VkCommandBufferLevel level, uint32_t count) = 0; +}; + +class Renderer { +public: + static std::shared_ptr create_renderer(const vk::Device& device, uint32_t img_count); + virtual ~Renderer() = default; + virtual uint32_t prepare_for_next_frame() = 0; + virtual uint32_t accquire_next_image(const Swapchain& swapchain) = 0; + virtual Renderer& submit_draw_command(const Queue& queue, const CommandBuffer& command_buffer) = 0; + virtual Renderer& present(const Queue& queue, const Swapchain& swapchain, uint32_t index) = 0; + virtual Renderer& wait_idle(const Queue& queue) = 0; + virtual Renderer& submit_onetime_command(const Queue& queue, rs::vector>& command_buffers) = 0; + virtual bool out_of_date() const = 0; +}; + +rs::vector list_layer_properties(); + +class InstanceBuilder { +public: + virtual InstanceBuilder& set_application_name(const char* name) = 0; + virtual InstanceBuilder& set_enging_name(const char* name) = 0; + virtual InstanceBuilder& set_application_version(uint32_t ver) = 0; + virtual InstanceBuilder& set_engine_version(uint32_t ver) = 0; + virtual InstanceBuilder& set_api_version(uint32_t ver) = 0; + virtual InstanceBuilder& set_layers(const rs::vector& layers) = 0; + virtual InstanceBuilder& set_extensions(const rs::vector& extensions) = 0; + virtual std::shared_ptr end() = 0; +}; + +class DeviceBuilder { +public: + virtual DeviceBuilder& set_extensions(const rs::vector& extensions) = 0; + virtual DeviceBuilder& add_queue_family(uint32_t queue_family_index, float priority) = 0; + virtual DeviceBuilder& enable_features(const VkPhysicalDeviceFeatures& features) = 0; + virtual std::shared_ptr end() = 0; +}; + +class BufferBuilder { +public: + virtual BufferBuilder& alloc_size(size_t size) = 0; + virtual BufferBuilder& set_usage(VkBufferUsageFlags usage) = 0; + virtual BufferBuilder& set_sharing_mode(VkSharingMode mode) = 0; + virtual BufferBuilder& set_cpu_accessible() = 0; + virtual BufferBuilder& set_device_local() = 0; + virtual std::shared_ptr end() = 0; +}; + +class ImageBuilder { +public: + virtual ImageBuilder& set_type(VkImageType type) = 0; + virtual ImageBuilder& set_format(VkFormat format) = 0; + virtual ImageBuilder& set_extent(VkExtent3D ext) = 0; + virtual ImageBuilder& set_mip_level(uint32_t level) = 0; + virtual ImageBuilder& set_array_layers(uint32_t layer) = 0; + virtual ImageBuilder& set_samples(VkSampleCountFlagBits samples) = 0; + virtual ImageBuilder& set_tilling(VkImageTiling tiling) = 0; + virtual ImageBuilder& set_usage(VkImageUsageFlags usage) = 0; + virtual ImageBuilder& set_sharing_mode(VkSharingMode mode) = 0; + virtual ImageBuilder& set_init_layout(VkImageLayout layout) = 0; + virtual ImageBuilder& set_cpu_accessible() = 0; + virtual ImageBuilder& set_device_local() = 0; + virtual std::shared_ptr end() = 0; +}; + +class SamplerBuilder { +public: + virtual SamplerBuilder& set_filter(VkFilter filter) = 0; + virtual SamplerBuilder& set_address_mode(VkSamplerAddressMode mode) = 0; + virtual SamplerBuilder& set_anisotrophy(float value) = 0; + virtual std::shared_ptr end() = 0; +}; + +class SwapchainBuilder { +public: + virtual SwapchainBuilder& set_surface(const Surface& surface) = 0; + virtual SwapchainBuilder& set_min_image_count(uint32_t count) = 0; + virtual SwapchainBuilder& set_format(VkFormat format) = 0; + virtual SwapchainBuilder& set_colorspace(VkColorSpaceKHR colorspace) = 0; + virtual SwapchainBuilder& set_extent(VkExtent2D ext) = 0; + virtual SwapchainBuilder& set_array_layer(uint32_t array_layer) = 0; + virtual SwapchainBuilder& set_image_usage(VkImageUsageFlags usage) = 0; + virtual SwapchainBuilder& set_queue_family_indices(const rs::vector& queue_family_indices) = 0; + virtual SwapchainBuilder& set_transform(VkSurfaceTransformFlagBitsKHR transform) = 0; + virtual SwapchainBuilder& set_composite_alpha(VkCompositeAlphaFlagBitsKHR alpha) = 0; + virtual SwapchainBuilder& set_present_mode(VkPresentModeKHR mode) = 0; + virtual SwapchainBuilder& set_clipped(VkBool32 clipped) = 0; + virtual SwapchainBuilder& set_old_swapchain(Swapchain& old) = 0; + virtual std::shared_ptr end() = 0; +}; + +class RenderPassBuilder { +public: + virtual RenderPassBuilder& set_format(VkFormat format) = 0; + virtual RenderPassBuilder& set_samples(VkSampleCountFlagBits samples) = 0; + virtual std::shared_ptr end() = 0; +}; + +class DescriptorSetLayoutBuilder { +public: + virtual DescriptorSetLayoutBuilder& add_binding(VkDescriptorType type) = 0; + virtual DescriptorSetLayoutBuilder& set_count(uint32_t count) = 0; + virtual DescriptorSetLayoutBuilder& set_stage(VkShaderStageFlags stage) = 0; + virtual DescriptorSetLayoutBuilder& set_samplers() = 0; + virtual std::shared_ptr end() = 0; +}; + +class DescriptorPoolBuilder { +public: + virtual DescriptorPoolBuilder& add_pool_size(VkDescriptorType type, uint32_t count) = 0; + virtual DescriptorPoolBuilder& set_max_sets(uint32_t count) = 0; + virtual DescriptorPoolBuilder& set_flags(VkDescriptorPoolCreateFlags flag) = 0; + virtual std::shared_ptr end() = 0; +}; + +class PipelineBuilder { +public: + virtual PipelineBuilder& set_render_pass(const RenderPass& render_pass) = 0; + virtual PipelineBuilder& set_vertex_shader(const ShaderModule& vert) = 0; + virtual PipelineBuilder& set_fragment_shader(const ShaderModule& frag) = 0; + virtual PipelineBuilder& normal_viewport(float w, float h) = 0; + virtual PipelineBuilder& normal_scissor(uint32_t w, uint32_t h) = 0; + virtual PipelineBuilder& draw_topology(VkPrimitiveTopology topology) = 0; + virtual PipelineBuilder& set_polygon_mode(VkPolygonMode mode) = 0; + virtual PipelineBuilder& set_cull_mode(VkCullModeFlags mode) = 0; + virtual PipelineBuilder& set_front_face(VkFrontFace front) = 0; + virtual PipelineBuilder& set_line_width(float width) = 0; + virtual PipelineBuilder& set_samples(VkSampleCountFlagBits samples) = 0; + virtual PipelineBuilder& bind_vertex_size(uint32_t sz) = 0; + virtual PipelineBuilder& add_binding_attribute(VkFormat format, uint32_t offset) = 0; + virtual PipelineBuilder& add_descriptor_set_layout(const DescriptorSetLayout& layout) = 0; + virtual std::shared_ptr end() = 0; +}; + +class FramebufferBuilder { +public: + virtual FramebufferBuilder& set_render_pass(const RenderPass& render_pass) = 0; + virtual FramebufferBuilder& set_framebuffer_size(uint32_t width, uint32_t height) = 0; + virtual FramebufferBuilder& add_image_view(const ImageView& view) = 0; + virtual FramebufferBuilder& set_layer(uint32_t layer) = 0; + virtual std::shared_ptr end() = 0; +}; + +class CommandPoolBuilder { +public: + virtual CommandPoolBuilder& set_queue_family_index(uint32_t idx) = 0; + virtual CommandPoolBuilder& set_pool_flag(VkCommandPoolCreateFlags flags) = 0; + virtual std::shared_ptr end() = 0; +}; + +} + +#endif // _VULKAN_HPP_