1
0
Fork 0

Render API

tmp
May B. 2020-10-04 15:32:28 +02:00
parent a3d9a157f3
commit d95b9eefee
75 changed files with 1414 additions and 1371 deletions

View File

@ -27,9 +27,7 @@ void Client::run(server_handle* const localHandle) {
pipeline->LightInvDir = glm::normalize(glm::vec3(-.5f, 2, -2));
render::Renderer::Get()->loadUI(window);
auto world = world::client::Load(options.connection, localHandle, options.world);
world->setContouring(contouring::load(options.contouring.idx, options.contouring.data));
auto world = world::client::Load(options.connection, localHandle, options.world, options.contouring);
state.contouring = world->getContouring();
//TODO: loop
@ -115,10 +113,8 @@ void Client::run(server_handle* const localHandle) {
if(actions && render::UI::Actions::Control) {
player.setOptions(options.control);
}
if(actions && render::UI::Actions::ChangeContouring) {
state.contouring = NULL;
world->setContouring(contouring::load(options.contouring.idx, options.contouring.data));
state.contouring = world->getContouring();
if(actions && render::UI::Actions::FillMode) {
//TODO: pipeline->setFillMode(options.renderer.wireframe);
}
}
{ // Rendering
@ -134,10 +130,10 @@ void Client::run(server_handle* const localHandle) {
const auto offset = state.position.raw_as_long();
{ // Chunks
const auto pass = pipeline->beginWorldPass();
const auto draw = [&](glm::mat4 model, buffer::Abstract *const buffer, const contouring::Abstract::area_info &area, const voxel_pos &pos) {
const auto draw = [&](glm::mat4 model, render::LodModel *const buffer, const contouring::Abstract::area_info &area, const voxel_pos &pos) {
pipeline->setCurvature(glm::vec4(pos, std::get<1>(area)), std::get<2>(area));
reports.models_count++;
reports.tris_count += buffer->draw(pass(model));
reports.tris_count += pass(buffer, model);
};
if (options.culling > 0) {
std::vector<glm::vec3> occlusion;
@ -188,5 +184,5 @@ void Client::run(server_handle* const localHandle) {
render::Renderer::Unload();
window.destroy();
contouring::save(options.contouring.idx, state.contouring, options.contouring.data);
options.contouring = state.contouring->getOptions();
}

View File

@ -6,7 +6,7 @@
#include <iomanip>
#include "world/Universe.hpp"
#include "render/Renderer.hpp"
#include "contouring/index.hpp"
#include "contouring/Abstract.hpp"
#include "control/Camera.hpp"
namespace config::client {
@ -50,10 +50,7 @@ public:
renderer.voxel.curv_depth = config["render"]["curvature_depth"].value_or(renderer.voxel.curv_depth);
culling = config["render"]["culling"].value_or(culling);
contouring.idx = contouring::idxByName(config["contouring"]["mode"].value_or(std::string("")));
for(const auto& name: contouring::names) {
contouring.data.emplace(name, config["contouring"]["options"][name].value_or(std::string("")));
}
contouring = config["contouring"].value_or(std::string(""));
camera.far = config["camera"]["far"].value_or(camera.far);
camera.near = config["camera"]["near"].value_or(camera.near);
@ -120,14 +117,7 @@ public:
{"curvature_depth", renderer.voxel.curv_depth},
{"culling", culling}
}));
config.insert_or_assign("contouring", toml::table({
{"mode", contouring::names[contouring.idx]},
{"options", toml::table()}
}));
for(const auto& [key, val]: contouring.data) {
if(!val.empty())
config["contouring"]["options"].as_table()->insert_or_assign(key, val);
}
config.insert_or_assign("contouring", contouring);
config.insert_or_assign("camera", toml::table({
{"far", camera.far},
{"near", camera.near},
@ -199,10 +189,7 @@ public:
render::renderOptions renderer;
int culling = 0;
struct {
int idx;
std::map<std::string, std::string> data;
} contouring;
std::string contouring;
Controllable::options control;
Camera::options camera;

View File

@ -1,11 +1,11 @@
#pragma once
#include "../render/gl/buffer/Abstract.hpp"
#include "../../core/world/forward.h"
#include "../../core/geometry/Frustum.hpp"
#include "../../core/geometry/Ray.hpp"
#include "../../core/geometry/Faces.hpp"
namespace render { class LodModel; }
/// Mesh creation (from world to render)
namespace contouring {
/// Generating mesh from world data
@ -35,7 +35,7 @@ namespace contouring {
virtual size_t getQueueSize() = 0;
using area_info = std::tuple<area_pos, voxel_pos::value_type, float>;
using draw_call = const std::function<void(glm::mat4, buffer::Abstract *const, const area_info&, const voxel_pos&)> &;
using draw_call = const std::function<void(glm::mat4, render::LodModel *const, const area_info&, const voxel_pos&)> &;
/// Get buffers in frustum with model matrices
/// @note buffers invalidated after update

View File

@ -1,22 +0,0 @@
#pragma once
#include "Abstract.hpp"
namespace contouring {
/// Useless contouring
class Dummy: public Abstract {
public:
Dummy(): Abstract() { }
virtual ~Dummy() { }
void update(const voxel_pos &, const world::client::area_map &) override {}
void onUpdate(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &, geometry::Faces) override {}
void onNotify(const area_<chunk_pos> &, const chunk_pos &, const world::ChunkContainer &) override {}
void onGui() override { }
std::string getOptions() const override { return ""; }
std::pair<float, float> getFarRange() const override { return std::make_pair(0, 0); }
size_t getQueueSize() override { return 0; }
void getModels(draw_call, const std::optional<geometry::Frustum> &, const glm::llvec3 &, int) override {}
void getModels(draw_call, const glm::ifvec3 &, float, const std::vector<glm::vec3> &, const glm::llvec3 &, int) override {}
};
}

View File

@ -41,14 +41,15 @@ namespace contouring {
#if TRACY_ENABLE
tracy::SetThreadName("Contouring");
#endif
std::vector<buffer::VertexData> tmp;
std::vector<render::VertexData> tmp;
while (running) {
std::pair<area_<chunk_pos>, surrounding::corners> ctx;
loadQueue.wait();
if (loadQueue.pop(ctx)) {
ZoneScopedN("ProcessContouring");
buffer::LodShortIndexed::LodData data;
render::LodModel::LodData data;
render(ctx.second, data, tmp);
//TODO: direct upload with vulkan
loadedQueue.emplace(ctx.first, data);
}
}
@ -121,12 +122,12 @@ namespace contouring {
void FlatDualMC::update(const voxel_pos& pos, const world::client::area_map& areas) {
ZoneScopedN("Ct");
std::pair<area_<chunk_pos>, buffer::LodShortIndexed::LodData> out;
std::pair<area_<chunk_pos>, render::LodModel::LodData> out;
TracyPlot("CtLoad", static_cast<int64_t>(loadQueue.size()));
//MAYBE: clear out of range loadQueue.trim(keepDistance * keepDistance)
TracyPlot("CtLoaded", static_cast<int64_t>(loadedQueue.size()));
for(auto handle = loadedQueue.extractor(); handle.first(out);) {
const auto buffer = out.second.first.empty() ? NULL : new buffer::LodShortIndexed(GL_TRIANGLES, out.second);
const auto buffer = out.second.first.empty() ? NULL : render::LodModel::Create(out.second).release();
auto &bfs = buffers[out.first.first].second; //NOTE: buffer.first uninitialized (will be set in clear())
if (const auto it = bfs.find(out.first.second); it != bfs.end()) {
if(it->second != NULL)
@ -163,7 +164,7 @@ namespace contouring {
it = bfs.erase(it);
} else {
if(it->second != NULL) {
static_cast<buffer::LodShortIndexed*>(it->second)->setLevel(std::clamp<size_t>((1+lod_quality-distRatio)*levelMax*(1+lod_strength), 0, levelMax));
it->second->setLevel(std::clamp<size_t>((1+lod_quality-distRatio)*levelMax*(1+lod_strength), 0, levelMax));
buffer_count++;
}
++it;
@ -212,7 +213,7 @@ namespace contouring {
}
}
void FlatDualMC::render(const surrounding::corners &surrounding, buffer::LodShortIndexed::LodData &out, std::vector<buffer::VertexData> &tmp) const {
void FlatDualMC::render(const surrounding::corners &surrounding, render::LodModel::LodData &out, std::vector<render::VertexData> &tmp) const {
const int SIZE = CHUNK_LENGTH + 3;
std::array<dualmc::DualMC<float>::Point, SIZE * SIZE * SIZE> grid;
{
@ -255,7 +256,7 @@ namespace contouring {
tmp.reserve(dmc_vertices.size());
constexpr auto HALF_MANTISSA = 10;
std::transform(dmc_vertices.begin(), dmc_vertices.end(), std::back_inserter(tmp), [](const dualmc::Vertex &v) {
return buffer::VertexData(glm::vec3(meshopt_quantizeFloat(v.x, HALF_MANTISSA),meshopt_quantizeFloat(v.y, HALF_MANTISSA),
return render::VertexData(glm::vec3(meshopt_quantizeFloat(v.x, HALF_MANTISSA),meshopt_quantizeFloat(v.y, HALF_MANTISSA),
meshopt_quantizeFloat(v.z, HALF_MANTISSA)), v.w, glm::vec3(0));
});
@ -285,8 +286,8 @@ namespace contouring {
}
out.second = simplify_lod(data.indices, tmp, loadedLevels);
std::transform(tmp.begin(), tmp.end(), std::back_inserter(data.vertices), [](const buffer::VertexData &v) {
return buffer::PackedVertexData(meshopt_quantizeHalf(v.Position.x), meshopt_quantizeHalf(v.Position.y),
std::transform(tmp.begin(), tmp.end(), std::back_inserter(data.vertices), [](const render::VertexData &v) {
return render::PackedVertexData(meshopt_quantizeHalf(v.Position.x), meshopt_quantizeHalf(v.Position.y),
meshopt_quantizeHalf(v.Position.z), v.Material,
meshopt_quantizeHalf(v.Normal.x), meshopt_quantizeHalf(v.Normal.y),
meshopt_quantizeHalf(v.Normal.z));

View File

@ -5,14 +5,14 @@
#include "../../core/data/safe_queue.hpp"
#include "../../core/data/safe_priority_queue.hpp"
#include "../render/gl/buffer/LodShortIndexed.hpp"
#include "../render/api/Models.hpp"
#include "../../core/data/math.hpp"
#include <thread>
using namespace data;
namespace contouring {
/// Dual Marching Cube 1:1 contouring
class FlatDualMC: public Abstract {
class FlatDualMC final: public Abstract {
public:
FlatDualMC(const std::string&);
virtual ~FlatDualMC();
@ -39,10 +39,11 @@ namespace contouring {
void getModels(draw_call draw, const glm::ifvec3 &from, float far, const std::vector<glm::vec3> &occlusion, const glm::llvec3 &offset, int density) override;
protected:
robin_hood::unordered_map<area_id, robin_hood::pair<area_info, robin_hood::unordered_map<chunk_pos, buffer::Abstract *>>> buffers;
//FIXME: use unique_ptr
robin_hood::unordered_map<area_id, robin_hood::pair<area_info, robin_hood::unordered_map<chunk_pos, render::LodModel*>>> buffers;
safe_priority_queue_map<area_<chunk_pos>, surrounding::corners, int, area_hash> loadQueue;
safe_queue<std::pair<area_<chunk_pos>, buffer::LodShortIndexed::LodData>> loadedQueue;
safe_queue<std::pair<area_<chunk_pos>, render::LodModel::LodData>> loadedQueue;
bool running = true;
std::vector<std::thread> workers;
@ -61,6 +62,6 @@ namespace contouring {
std::vector<std::pair<float, float>> loadedLevels;
void render(const surrounding::corners &surrounding, buffer::LodShortIndexed::LodData& out, std::vector<buffer::VertexData>& tmp) const;
void render(const surrounding::corners &surrounding, render::LodModel::LodData& out, std::vector<render::VertexData>& tmp) const;
};
}

View File

@ -1,58 +0,0 @@
#pragma once
#include <vector>
#include <glm/gtc/matrix_transform.hpp>
#include <meshoptimizer.h>
#include "../data/geometry/Faces.hpp"
#include "../render/buffer/VertexData.hpp"
using namespace geometry;
namespace contouring::box {
static const auto BOX_FACES = 6;
static const std::vector<glm::vec3> g_quad_vertices = {
glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.5f, -0.5f, 0.5f), glm::vec3(0.5f, -0.5f, -0.5f),
glm::vec3(0.5f, -0.5f, -0.5f), glm::vec3(0.5f, 0.5f, -0.5f), glm::vec3(0.5f, 0.5f, 0.5f)};
static const glm::vec3 g_cube_normals[BOX_FACES] = {
glm::vec3(1, 0, 0), glm::vec3(-1, 0, 0), glm::vec3(0, 1, 0), glm::vec3(0, -1, 0), glm::vec3(0, 0, 1), glm::vec3(0, 0, -1)};
static const glm::mat4 g_cube_rotate[BOX_FACES] = {
glm::mat4(1),
glm::rotate<float>(glm::mat4(1), glm::pi<float>(), glm::vec3(0, 1, 0)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 0, 1)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 0, -1)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, -1, 0)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 1, 0)),
};
static void addQuad(std::vector<buffer::PackedVertexData> & out, glm::vec3 position, ushort texture, Face face, glm::vec3 size) {
for (auto vertex : g_quad_vertices) {
const auto p = glm::vec3(g_cube_rotate[static_cast<int>(face)] * glm::vec4(vertex, 1)) * size + position;
out.emplace_back(meshopt_quantizeHalf(p.x), meshopt_quantizeHalf(p.y),
meshopt_quantizeHalf(p.z), texture,
meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].x), meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].y),
meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].z));
}
}
static void addCube(std::vector<buffer::PackedVertexData>& out, glm::vec3 position, uint texture, Faces faces = Faces::All, glm::vec3 size = glm::vec3(1)) {
if (faces && Faces::Right)
addQuad(out, position, texture, Face::Right, size);
if(faces && Faces::Left)
addQuad(out, position, texture, Face::Left, size);
if (faces && Faces::Up)
addQuad(out, position, texture, Face::Up, size);
if (faces && Faces::Down)
addQuad(out, position, texture, Face::Down, size);
if (faces && Faces::Forward)
addQuad(out, position, texture, Face::Forward, size);
if (faces && Faces::Backward)
addQuad(out, position, texture, Face::Backward, size);
}
}

View File

@ -1,26 +0,0 @@
#include "index.hpp"
#include "Dummy.hpp"
#include "FlatDualMC.hpp"
namespace contouring {
int idxByName(const std::string& name) {
for (size_t i = 0; i < names.size(); i++) {
if(name == names[i])
return (int)i;
}
return 0;
}
std::shared_ptr<Abstract> load(int idx, const std::map<std::string, std::string>& data) {
switch (idx) {
case 2:
return std::make_shared<Dummy>();
default:
return std::make_shared<FlatDualMC>(data.at(names[0]));
}
}
void save(int idx, std::shared_ptr<Abstract>& ct, std::map<std::string, std::string>& data) {
data.insert_or_assign(names[idx], ct->getOptions());
}
}

View File

@ -1,12 +0,0 @@
#pragma once
#include "Abstract.hpp"
#include <map>
namespace contouring {
static const std::array<std::string, 2> names = {"FlatDualMC", "Dummy"};
int idxByName(const std::string &name);
std::shared_ptr<Abstract> load(int idx, const std::map<std::string, std::string> &data);
void save(int idx, std::shared_ptr<Abstract> &ct, std::map<std::string, std::string> &data);
}

View File

@ -1,23 +1,23 @@
#pragma once
#include <meshoptimizer.h>
inline void optimize_fetch(buffer::ShortIndexed::Data& out) {
inline void optimize_fetch(render::Model::Data& out) {
ZoneScopedN("Optimize");
std::vector<unsigned int> remap(out.indices.size());
size_t vertex_count = meshopt_optimizeVertexFetch(out.vertices.data(), out.indices.data(), out.indices.size(), out.vertices.data(), out.vertices.size(), sizeof(buffer::PackedVertexData));
out.vertices.resize(vertex_count, buffer::PackedVertexData(0, 0, 0, 0, 0, 0, 0));
size_t vertex_count = meshopt_optimizeVertexFetch(out.vertices.data(), out.indices.data(), out.indices.size(), out.vertices.data(), out.vertices.size(), sizeof(render::PackedVertexData));
out.vertices.resize(vertex_count, render::PackedVertexData(0, 0, 0, 0, 0, 0, 0));
}
//MAYBE: when networking meshopt_encodeVertexBuffer
template<typename I>
inline void simplify_buffer(std::vector<I> &out, const std::vector<I> &indices, const std::vector<buffer::VertexData>& vertices, float threshold = .2f, float target_error = 1e-2f) {
inline void simplify_buffer(std::vector<I> &out, const std::vector<I> &indices, const std::vector<render::VertexData>& vertices, float threshold = .2f, float target_error = 1e-2f) {
out.resize(indices.size());
out.resize(meshopt_simplify(out.data(), indices.data(), indices.size(), &vertices.front().Position.x, vertices.size(), sizeof(buffer::VertexData), indices.size() * threshold, target_error));
out.resize(meshopt_simplify(out.data(), indices.data(), indices.size(), &vertices.front().Position.x, vertices.size(), sizeof(render::VertexData), indices.size() * threshold, target_error));
}
#include <iostream>
template <typename I>
inline std::vector<size_t> simplify_lod(std::vector<I> &indices, const std::vector<buffer::VertexData> &vertices, const std::vector<std::pair<float, float>> &levels)
inline std::vector<size_t> simplify_lod(std::vector<I> &indices, const std::vector<render::VertexData> &vertices, const std::vector<std::pair<float, float>> &levels)
{
ZoneScopedN("LOD");
typename std::vector<I> full(indices);

View File

@ -1,7 +1,6 @@
#pragma once
#include "../../core/flags.hpp"
#include "buffer/Abstract.hpp"
#include <cassert>
#include <memory>
#include <string>
@ -11,6 +10,8 @@
class Window;
class Camera;
namespace render {
class Model;
class LodModel;
/// Pass options
struct passOptions {
@ -63,9 +64,9 @@ public:
/// Start new frame and setup
virtual void beginFrame() = 0;
/// Get started world program
virtual std::function<buffer::params(glm::mat4)> beginWorldPass() = 0;
virtual std::function<size_t(render::LodModel *const, glm::mat4)> beginWorldPass() = 0;
/// Get started entity program
virtual std::function<buffer::params(const std::vector<glm::mat4> &)> beginEntityPass() = 0;
virtual std::function<size_t(render::Model *const, const std::vector<glm::mat4> &)> beginEntityPass() = 0;
/// Draw cube indicator
virtual size_t drawIndicatorCube(glm::mat4 model) = 0;
/// Apply postprocessing

View File

@ -102,8 +102,9 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons
if (ImGui::ColorEdit3("Fog color", &options.renderer.clear_color[0])) {
actions |= Actions::ClearColor;
}
if (ImGui::Checkbox("Wireframe", &options.renderer.wireframe))
glPolygonMode(GL_FRONT_AND_BACK, options.renderer.wireframe ? GL_LINE : GL_FILL);
if (ImGui::Checkbox("Wireframe", &options.renderer.wireframe)) {
actions |= Actions::FillMode;
}
ImGui::Text("Textures '%s'", options.renderer.textures.c_str()); // MAYBE: select
if (ImGui::SliderFloat("LOD", &options.renderer.mipMapLOD, -1, 1) |
ImGui::SliderInt("Anisotropy", &options.renderer.anisotropy, 0, 8)) {
@ -130,23 +131,6 @@ UI::Actions UI::draw(config::client::options &options, state::state &state, cons
if (options.debugMenu.contouring) {
ImGui::Begin("Debug: Contouring", &options.debugMenu.contouring, ImGuiWindowFlags_AlwaysAutoResize);
if (ImGui::BeginCombo("Contouring", contouring::names[options.contouring.idx].c_str())) {
for (size_t i = 0; i < contouring::names.size(); i++) {
const bool is_selected = (options.contouring.idx == (int)i);
if (ImGui::Selectable(contouring::names[i].c_str(), is_selected)) {
actions |= Actions::ChangeContouring;
contouring::save(options.contouring.idx, state.contouring, options.contouring.data);
options.contouring.idx = i;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
if(ImGui::Button("Reload")) {
actions |= Actions::ChangeContouring;
contouring::save(options.contouring.idx, state.contouring, options.contouring.data);
}
ImGui::SliderInt("Culling", &options.culling, -1, 10, options.culling > 0 ? "Occlusion %d" : (options.culling < 0 ? "None" : "Frustum"));
state.contouring->onGui();
ImGui::End();

View File

@ -31,7 +31,7 @@ public:
World = 1 << 5,
Camera = 1 << 6,
Control = 1 << 7,
ChangeContouring = 1 << 8,
FillMode = 1 << 8,
};
friend inline void operator|=(Actions& a, Actions b) {
a = static_cast<Actions>(static_cast<int>(a) | static_cast<int>(b));

View File

@ -0,0 +1,7 @@
#include "Buffers.hpp"
using namespace render;
std::unique_ptr<WritableBuffer> (*WritableBuffer::createFunc)(size_t, Usage, const data_view) = nullptr;
std::unique_ptr<FastBuffer> (*FastBuffer::createFunc)(size_t, Usage, const data_view) = nullptr;
std::unique_ptr<ShortIndexedVertexBuffer> (*ShortIndexedVertexBuffer::createFunc)(const data_view, const data_view) = nullptr;

View File

@ -0,0 +1,75 @@
#pragma once
#include "common.hpp"
namespace render {
/// Abstract raw data buffer
class Buffer {
public:
virtual ~Buffer() { }
// NOTE: matches VkBufferUsageFlags
enum class Usage {
TRANSFER_SRC = 1,
TRANSFER_DST = 2,
UNIFORM = 16,
STORAGE = 32,
INDEX = 64,
VERTEX = 128
};
friend inline Usage operator|(Usage a, Usage b) {
return static_cast<Usage>(static_cast<int>(a) | static_cast<int>(b));
}
struct requirement {
requirement(size_t size, Usage usage, const data_view view = data_view()):
size(size), usage(usage), view(view) { }
size_t size;
Usage usage;
data_view view;
};
};
/// Writable raw data buffer
class WritableBuffer: public Buffer {
public:
static _FORCE_INLINE_ std::unique_ptr<WritableBuffer> Create(size_t size, Usage usage = Usage::TRANSFER_SRC, const data_view write = data_view()) {
assert(createFunc != nullptr && "Uninitialized renderer");
return createFunc(size, usage, write);
}
virtual void write(const data_view, size_t offset = 0) = 0;
virtual void read(data_ref, size_t offset = 0) = 0;
protected:
static std::unique_ptr<WritableBuffer> (*createFunc)(size_t, Usage, const data_view);
};
/// Fast raw data buffer
class FastBuffer: public Buffer {
public:
// NOTE: implementation may implicitly add Usage::TRANSFER_DST
static _FORCE_INLINE_ std::unique_ptr<FastBuffer> Create(size_t size, Usage usage, const data_view write = data_view()) {
assert(createFunc != nullptr && "Uninitialized renderer");
return createFunc(size, usage, write);
}
protected:
static std::unique_ptr<FastBuffer> (*createFunc)(size_t, Usage, const data_view);
};
/// Grouped vertex and u16 index buffers
/// NOTE: vertex structure is undefined
class ShortIndexedVertexBuffer: public Buffer {
public:
static _FORCE_INLINE_ std::unique_ptr<ShortIndexedVertexBuffer> Create(const data_view vertices, const data_view indices) {
assert(createFunc != nullptr && "Uninitialized renderer");
return createFunc(vertices, indices);
}
protected:
static std::unique_ptr<ShortIndexedVertexBuffer> (*createFunc)(const data_view, const data_view);
};
}

View File

@ -0,0 +1,71 @@
#include "Images.hpp"
using namespace render;
std::unique_ptr<Texture> (*Texture::loadFunc)(const std::string&, const sampling&) = nullptr;
#include <stdio.h>
#include <string.h>
#include <string>
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
std::optional<Image::properties> Image::Read(const std::string& imagepath, std::vector<unsigned char>& data) {
unsigned char header[124];
properties info;
FILE *fp;
/* try to open the file */
fp = fopen(imagepath.c_str(), "rb");
if (fp == NULL){
printf("%s could not be opened.\n", imagepath.c_str()); getchar();
return {};
}
/* verify the type of file */
char filecode[4];
fread(filecode, 1, 4, fp);
if (strncmp(filecode, "DDS ", 4) != 0) {
fclose(fp);
return {};
}
/* get the surface desc */
fread(&header, 124, 1, fp);
info.size.height = *(unsigned int*)&(header[8 ]);
info.size.width = *(unsigned int*)&(header[12]);
unsigned int linearSize = *(unsigned int*)&(header[16]);
info.mipMap = *(unsigned int*)&(header[24]);
unsigned int fourCC = *(unsigned int*)&(header[80]);
/* how big is it going to be including all mipmaps? */
unsigned int bufsize = info.mipMap > 1 ? linearSize * 2 : linearSize;
data.resize(bufsize);
fread(data.data(), 1, bufsize, fp);
/* close the file pointer */
fclose(fp);
switch(fourCC)
{
case FOURCC_DXT1:
info.format = Format::BC1;
break;
case FOURCC_DXT3:
info.format = Format::BC2;
break;
case FOURCC_DXT5:
info.format = Format::BC3;
break;
//MAYBE: VK_FORMAT_BC6H_SFLOAT_BLOCK
default:
return {};
}
return info;
}

View File

@ -0,0 +1,105 @@
#pragma once
#include "common.hpp"
#include <optional>
#include <vector>
namespace render {
/// Abstract raw image
class Image {
public:
virtual ~Image() { }
struct frame {
uint32_t height;
uint32_t width;
};
// NOTE: matches VkFormat
enum class Format {
/// DXT1 RGBA SRGB
BC1 = 134,
/// DXT3 RGBA SRGB
BC2 = 136,
/// DXT1 RGBA SRGB
BC3 = 138,
// MAYBE: BMP RGBA SRGB ??
// MAYBE: For HDR BC6H_SFLOAT = 144,
};
// NOTE: matches VkImageLayout
enum class Layout {
UNDEFINED = 0,
GENERAL = 1,
COLOR_ATTACHMENT = 2,
DEPTH_STENCIL_ATTACHMENT = 3,
DEPTH_STENCIL_READ_ONLY = 4,
SHADER_READ_ONLY = 5,
TRANSFER_SRC = 6,
TRANSFER_DST = 7,
PREINITIALIZED = 8,
DEPTH_READ_ONLY_STENCIL_ATTACHMENT = 1000117000,
DEPTH_ATTACHMENT_STENCIL_READ_ONLY = 1000117001,
DEPTH_ATTACHMENT = 1000241000,
DEPTH_READ_ONLY = 1000241001,
STENCIL_ATTACHMENT = 1000241002,
STENCIL_READ_ONLY = 1000241003
};
// NOTE: matches VkImageUsageFlags
enum class Usage {
TRANSFER_SRC = 0x00000001,
TRANSFER_DST = 0x00000002,
SAMPLED = 0x00000004,
STORAGE = 0x00000008,
COLOR_ATTACHMENT = 0x00000010,
DEPTH_STENCIL_ATTACHMENT = 0x00000020,
TRANSIENT_ATTACHMENT = 0x00000040,
INPUT_ATTACHMENT = 0x00000080,
};
friend inline Usage operator|(Usage a, Usage b) {
return static_cast<Usage>(static_cast<int>(a) | static_cast<int>(b));
}
struct properties {
frame size;
uint32_t mipMap;
Format format;
};
struct requirement {
properties props;
Layout layout;
Usage usage;
bool linear = false;
};
static std::optional<properties> Read(const std::string&, std::vector<unsigned char>& data);
};
/// Const image (single texture2D) with sampler
class Texture: public Image {
public:
// NOTE: matches VkSamplerAddressMode
enum class Wrap {
REPEAT = 0,
MIRRORED_REPEAT = 1,
CLAMP_TO_EDGE = 2,
CLAMP_TO_BORDER = 3,
};
struct sampling {
bool magLinear = true;
bool minLinear = true;
Wrap wrap = Wrap::MIRRORED_REPEAT;
int anisotropy = 0;
//TODO: mipmap
};
/// Only supports dds files
/// DXT1(BC1) DXT3(BC2) DXT5(BC3)
static _FORCE_INLINE_ std::unique_ptr<Texture> LoadFromFile(const std::string &path, const sampling &props) {
assert(loadFunc != nullptr && "Uninitialized renderer");
return loadFunc(path, props);
}
protected:
static std::unique_ptr<Texture> (*loadFunc)(const std::string&, const sampling&);
};
}

View File

@ -0,0 +1,63 @@
#include "Models.hpp"
#include <fstream>
#include <map>
using namespace render;
std::unique_ptr<Model> (*Model::createFunc)(const Data&) = nullptr;
std::unique_ptr<LodModel> (*LodModel::createFunc)(const LodData&) = nullptr;
void indexVBO(const std::vector<PackedVertexData> &in_vertices, std::vector<glm::u16> &out_indices, std::vector<PackedVertexData> &out_vertices);
Model::Data::Data(const std::vector<PackedVertexData> &vs, const std::vector<glm::u16> &indices): indices(indices), vertices(vs) { }
Model::Data::Data(std::ifstream& in) {
{
size_t i_size;
in.read(reinterpret_cast<char *>(&i_size), sizeof(i_size));
indices.resize(i_size);
in.read(reinterpret_cast<char *>(indices.data()), sizeof(glm::u16) * i_size);
}
size_t v_size;
in.read(reinterpret_cast<char *>(&v_size), sizeof(v_size));
vertices.resize(v_size, PackedVertexData(0, 0, 0, 0, 0, 0, 0, 0));
in.read(reinterpret_cast<char *>(vertices.data()), sizeof(PackedVertexData) * v_size);
}
void Model::Data::serialize(std::ofstream& out) {
{
size_t i_size = indices.size();
out.write(reinterpret_cast<char *>(&i_size), sizeof(i_size));
out.write(reinterpret_cast<char *>(indices.data()), sizeof(glm::u16) * i_size);
}
size_t v_size = vertices.size();
out.write(reinterpret_cast<char *>(&v_size), sizeof(v_size));
out.write(reinterpret_cast<char *>(vertices.data()), sizeof(PackedVertexData) * v_size);
}
void Model::Data::index(const std::vector<PackedVertexData>& vs) {
indexVBO(vs, indices, vertices);
}
void indexVBO(const std::vector<PackedVertexData> &in_vertices, std::vector<glm::u16> &out_indices,
std::vector<PackedVertexData> &out_vertices)
{
std::map<PackedVertexData, glm::u16> VertexToOutIndex;
auto getSimilarVertexIndex_fast = [&] (const PackedVertexData &packed, glm::u16 &out) {
if (auto it = VertexToOutIndex.find(packed); it == VertexToOutIndex.end()) {
return false;
}else{
out = it->second;
return true;
}
};
out_indices.reserve(in_vertices.size());
for (const auto& vertex: in_vertices) {
if (glm::u16 index; getSimilarVertexIndex_fast(vertex, index)) {
out_indices.push_back(index);
} else {
out_vertices.push_back(vertex);
out_indices.push_back(out_vertices.size() - 1);
VertexToOutIndex[vertex] = out_indices.back();
}
}
}

View File

@ -0,0 +1,109 @@
/**
* Application specific vertex objects.
*/
#pragma once
#include "common.hpp"
#include <glm/glm.hpp>
#include <string.h>
#include <vector>
#include <iosfwd>
namespace render {
/// Vertex properties
struct VertexData {
VertexData(const glm::vec3 &position, glm::u16 material, const glm::vec3 &normal):
Position(position), Material(material), Normal(normal) { }
glm::vec3 Position;
glm::u16 Material;
glm::vec3 Normal;
};
/// Quantized vertex properties
struct PackedVertexData {
PackedVertexData(glm::u16 x, glm::u16 y, glm::u16 z, glm::u16 mat, glm::u16 nx, glm::u16 ny, glm::u16 nz, glm::u16 nw = 0) {
PosMat[0] = x;
PosMat[1] = y;
PosMat[2] = z;
PosMat[3] = mat;
Nrm[0] = nx;
Nrm[1] = ny;
Nrm[2] = nz;
Nrm[3] = nw;
}
glm::u16 PosMat[4];
glm::u16 Nrm[4]; //NOTE: Triplanar does not handle 10_10_10_2_REV
bool operator<(const PackedVertexData that) const {
return memcmp((void *)this, (void *)&that, sizeof(PackedVertexData)) > 0;
};
};
/// Color lines model
class Indicator {
public:
virtual ~Indicator() { }
};
/// VertexData model with index
class Model {
public:
virtual ~Model() { }
/// Preindexed buffer data
struct Data {
std::vector<glm::u16> indices;
std::vector<PackedVertexData> vertices;
Data() { }
Data(const std::vector<PackedVertexData> &vertices, const std::vector<glm::u16> &indices);
Data(const std::vector<PackedVertexData> &vertices) { index(vertices); }
Data(std::ifstream &in);
void index(const std::vector<PackedVertexData> &vertices);
bool empty() const {
return indices.empty();
}
void clear() {
indices.clear();
vertices.clear();
}
void serialize(std::ofstream &out);
};
static _FORCE_INLINE_ std::unique_ptr<Model> Create(const Data& data) {
assert(createFunc != nullptr && "Uninitialized renderer");
return createFunc(data);
}
protected:
static std::unique_ptr<Model> (*createFunc)(const Data&);
};
/// VertexData model with Level Of Detail indices
class LodModel {
public:
virtual ~LodModel() { }
using LodData = std::pair<Model::Data, std::vector<size_t>>;
static _FORCE_INLINE_ std::unique_ptr<LodModel> Create(const LodData& data) {
assert(createFunc != nullptr && "Uninitialized renderer");
return createFunc(data);
}
void inline setLevel(size_t l) { level = l; }
protected:
size_t level = 0;
size_t indexSize;
std::vector<size_t> offsets;
constexpr inline size_t getOffset(size_t level) const {
return level <= 0 ? 0 : (level-1 < offsets.size() ? offsets[level-1] : indexSize);
}
static std::unique_ptr<LodModel> (*createFunc)(const LodData&);
};
}

View File

@ -0,0 +1,36 @@
#pragma once
#include "../../../core/flags.hpp"
#include <memory>
#include <cassert>
namespace render {
struct data_view {
data_view() { }
data_view(const void *ptr, size_t size): ptr(ptr), size(size) { }
template <typename Container>
data_view(const Container &c): data_view(c.data(), c.size() * sizeof(typename Container::value_type)) {}
constexpr bool isUsable() const { return ptr != nullptr && size > 0; }
explicit operator bool() const { return isUsable(); }
const void *ptr = nullptr;
size_t size = 0;
};
struct data_ref {
data_ref() { }
data_ref(void *ptr, size_t size): ptr(ptr), size(size) { }
template<typename Container>
data_ref(Container &c):
data_ref(c.data(), c.size() * sizeof(typename Container::value_type)) {}
constexpr bool isUsable() const { return ptr != nullptr && size > 0; }
explicit operator bool() const { return isUsable(); }
void *ptr = nullptr;
size_t size = 0;
};
}

View File

@ -1,14 +0,0 @@
#pragma once
#include <GL/gl3w.h>
#include <sys/types.h>
namespace buffer {
/// Draw options
struct params {
/// Bind only vertices positions
bool vertexOnly;
/// Draw instanced model
size_t instances = 1;
};
}

View File

@ -15,7 +15,7 @@ constexpr auto GL_MAJOR = 4;
constexpr auto GL_MINOR = 6;
Renderer::Renderer(const renderOptions& options):
IndicatorCubeBuffer(GL_LINES, 24, {
IndicatorCubeBuffer({
glm::vec3(0, 0, 0), glm::vec3(0, 0, 1),
glm::vec3(0, 0, 1), glm::vec3(0, 1, 1),
glm::vec3(0, 1, 1), glm::vec3(0, 1, 0),
@ -101,6 +101,7 @@ bool Renderer::Load(Window& window, const renderOptions& opt) {
TracyGpuContext;
sInstance = new Renderer(opt);
LodModel::MakeDefault();
return true;
}
@ -114,25 +115,28 @@ void Renderer::beginFrame() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
std::function<buffer::params(glm::mat4)> Renderer::beginWorldPass() {
std::function<size_t(render::LodModel *const, glm::mat4)> Renderer::beginWorldPass() {
WorldPass->useIt();
WorldPass->start(this);
return [&](glm::mat4 model) {
return WorldPass->setup(this, model);
return [&](render::LodModel *const buf, glm::mat4 model) {
WorldPass->setup(this, model);
return dynamic_cast<LodModel *const>(buf)->draw();
};
}
std::function<buffer::params(const std::vector<glm::mat4> &)> Renderer::beginEntityPass() {
std::function<size_t(render::Model *const, const std::vector<glm::mat4> &)> Renderer::beginEntityPass() {
EntityPass->useIt();
EntityPass->start(this);
return [&](const std::vector<glm::mat4>& models) {
return EntityPass->setup(this, models);
return [&](render::Model *const buf, const std::vector<glm::mat4> &models) {
EntityPass->setup(this, models);
return dynamic_cast<Model *const>(buf)->drawInstanced(models.size());
};
}
size_t Renderer::drawIndicatorCube(glm::mat4 model) {
IndicatorPass->useIt();
return IndicatorCubeBuffer.draw(IndicatorPass->setup(this, model));
IndicatorPass->setup(this, model);
return IndicatorCubeBuffer.draw();
}
void Renderer::endPass() {

View File

@ -6,7 +6,7 @@
#include "pass/EntityProgram.hpp"
#include "pass/SkyProgram.hpp"
#include "pass/ColorProgram.hpp"
#include "buffer/Colored.hpp"
#include "api/Models.hpp"
namespace render::gl {
@ -41,8 +41,8 @@ public:
}
void beginFrame() override;
std::function<buffer::params(glm::mat4)> beginWorldPass() override;
std::function<buffer::params(const std::vector<glm::mat4>&)> beginEntityPass() override;
std::function<size_t(render::LodModel *const, glm::mat4)> beginWorldPass() override;
std::function<size_t(render::Model *const, const std::vector<glm::mat4>&)> beginEntityPass() override;
size_t drawIndicatorCube(glm::mat4 model) override;
void endPass() override;
void swapBuffer(Window&) override;
@ -74,7 +74,7 @@ private:
std::unique_ptr<pass::EntityProgram> EntityPass;
std::unique_ptr<pass::SkyProgram> SkyPass;
std::unique_ptr<pass::ColorProgram> IndicatorPass;
buffer::Colored IndicatorCubeBuffer;
Indicator IndicatorCubeBuffer;
glm::mat4 ProjectionMatrix;
glm::mat4 ViewMatrix;

View File

@ -0,0 +1,152 @@
#include "Models.hpp"
using namespace render::gl;
Shape::Shape(const std::vector<glm::vec3>& pos) {
size = pos.size();
glGenBuffers(1, &bufferId);
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glBufferData(GL_ARRAY_BUFFER, pos.size() * sizeof(glm::vec3), pos.data(), GL_STATIC_DRAW);
}
Shape::~Shape() {
glDeleteBuffers(1, &bufferId);
}
size_t Shape::draw() {
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, bufferId);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
glDrawArrays(GL_TRIANGLES, 0, size);
glDisableVertexAttribArray(0);
return size;
}
Indicator::Indicator(const std::vector<glm::vec3>& pos, const std::vector<glm::vec4>& col) {
assert(pos.size() == col.size());
size = pos.size();
glGenBuffers(1, &vertexBufferId);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
glBufferData(GL_ARRAY_BUFFER, pos.size() * sizeof(glm::vec3), pos.data(), GL_STATIC_DRAW);
glGenBuffers(1, &colorBufferId);
glBindBuffer(GL_ARRAY_BUFFER, colorBufferId);
glBufferData(GL_ARRAY_BUFFER, col.size() * sizeof(glm::vec4), col.data(), GL_STATIC_DRAW);
}
Indicator::~Indicator() {
glDeleteBuffers(1, &colorBufferId);
glDeleteBuffers(1, &vertexBufferId);
}
void Indicator::enableAttribs() {
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void *)0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, colorBufferId);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void *)0);
}
void Indicator::disableAttribs() {
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
size_t Indicator::draw() {
enableAttribs();
glDrawArrays(GL_LINES, 0, size);
disableAttribs();
return size;
}
size_t Indicator::drawInstanced(size_t count) {
enableAttribs();
glDrawArraysInstanced(GL_LINES, 0, size, count);
disableAttribs();
return size * count;
}
void enableAttribs() {
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_HALF_FLOAT, GL_FALSE, sizeof(render::PackedVertexData),
reinterpret_cast<void *>(offsetof(render::PackedVertexData, PosMat[0])));
glEnableVertexAttribArray(1);
glVertexAttribIPointer(1, 1, GL_UNSIGNED_SHORT, sizeof(render::PackedVertexData),
reinterpret_cast<void *>(offsetof(render::PackedVertexData, PosMat[3])));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_HALF_FLOAT, GL_FALSE, sizeof(render::PackedVertexData),
reinterpret_cast<void *>(offsetof(render::PackedVertexData, Nrm)));
}
void disableAttribs() {
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
Model::Model(const Data& data) {
indexSize = data.indices.size();
glGenBuffers(1, &vertexBufferId);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
glBufferData(GL_ARRAY_BUFFER, data.vertices.size() * sizeof(render::PackedVertexData), data.vertices.data(), GL_STATIC_DRAW);
glGenBuffers(1, &indexBufferId);
glBindBuffer(GL_ARRAY_BUFFER, indexBufferId);
glBufferData(GL_ARRAY_BUFFER, data.indices.size() * sizeof(glm::u16), data.indices.data(), GL_STATIC_DRAW);
}
Model::~Model() {
glDeleteBuffers(1, &vertexBufferId);
glDeleteBuffers(1, &indexBufferId);
}
size_t Model::draw() {
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
enableAttribs();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
glDrawElements(GL_TRIANGLES, indexSize, GL_UNSIGNED_SHORT, (void *)0);
disableAttribs();
return indexSize;
}
size_t Model::drawInstanced(size_t count) {
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
enableAttribs();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
glDrawElementsInstanced(GL_TRIANGLES, indexSize, GL_UNSIGNED_SHORT, (void *)0, count);
disableAttribs();
return indexSize * count;
}
LodModel::LodModel(const LodData& data) {
indexSize = data.first.indices.size();
offsets = data.second;
glGenBuffers(1, &vertexBufferId);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
glBufferData(GL_ARRAY_BUFFER, data.first.vertices.size() * sizeof(render::PackedVertexData), data.first.vertices.data(), GL_STATIC_DRAW);
glGenBuffers(1, &indexBufferId);
glBindBuffer(GL_ARRAY_BUFFER, indexBufferId);
glBufferData(GL_ARRAY_BUFFER, data.first.indices.size() * sizeof(glm::u16), data.first.indices.data(), GL_STATIC_DRAW);
}
LodModel::~LodModel() {
glDeleteBuffers(1, &vertexBufferId);
glDeleteBuffers(1, &indexBufferId);
}
size_t LodModel::draw() {
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
enableAttribs();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
const auto start = getOffset(level);
const auto end = getOffset(level+1);
const auto count = end - start;
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (void *)(start*sizeof(glm::u16)));
disableAttribs();
return count;
}
size_t LodModel::drawInstanced(size_t i) {
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId);
enableAttribs();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferId);
const auto start = getOffset(level);
const auto end = getOffset(level+1);
const auto count = end - start;
glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (void *)(start*sizeof(glm::u16)), i);
disableAttribs();
return i * count;
}
std::unique_ptr<render::LodModel> createLodModel(const LodModel::LodData& data) {
return std::make_unique<LodModel>(data);
}
void LodModel::MakeDefault() {
createFunc = createLodModel;
}

View File

@ -0,0 +1,68 @@
#pragma once
#include "../../api/Models.hpp"
#include <GL/gl3w.h>
#include <vector>
#include <glm/glm.hpp>
namespace render::gl {
/// Positions only buffer
class Shape final {
public:
Shape(const std::vector<glm::vec3>&);
~Shape();
size_t draw();
private:
size_t size;
GLuint bufferId;
};
class Indicator final: public render::Indicator {
public:
Indicator(const std::vector<glm::vec3>&, const std::vector<glm::vec4>&);
~Indicator();
size_t draw();
size_t drawInstanced(size_t count);
private:
size_t size;
GLuint vertexBufferId;
GLuint colorBufferId;
void enableAttribs();
void disableAttribs();
};
class Model final: public render::Model {
public:
Model(const Data&);
~Model();
size_t draw();
size_t drawInstanced(size_t count);
private:
size_t indexSize;
GLuint vertexBufferId;
GLuint indexBufferId;
};
class LodModel final: public render::LodModel {
public:
LodModel(const LodData&);
~LodModel();
static void MakeDefault();
size_t draw();
size_t drawInstanced(size_t count);
private:
GLuint vertexBufferId;
GLuint indexBufferId;
};
}

View File

@ -1,38 +0,0 @@
#include "Abstract.hpp"
using namespace buffer;
Abstract::Abstract(GLenum shape): Shape(shape) {
glGenBuffers(1, &VertexBufferID);
}
Abstract::~Abstract() {
glDeleteBuffers(1, &VertexBufferID);
}
void Abstract::enableVertexAttrib() {
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
glVertexAttribPointer(
0, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void *)0 // array buffer offset
);
}
void Abstract::enableAllAttribs() {
enableVertexAttrib();
}
void Abstract::disableVertexAttrib() {
glDisableVertexAttribArray(0);
}
void Abstract::disableAllAttribs() {
disableVertexAttrib();
}
void Abstract::setVertices(const unsigned long size, const void *data) {
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}

View File

@ -1,27 +0,0 @@
#pragma once
#include "../../buffer/Abstract.hpp"
namespace buffer {
/// Abstract OpenGL Buffer
class Abstract {
public:
Abstract(GLenum shape);
virtual ~Abstract();
/// Bind and draw buffer
virtual uint draw(params params) = 0;
protected:
GLenum Shape;
GLuint VertexBufferID;
void enableVertexAttrib();
void disableVertexAttrib();
void enableAllAttribs();
void disableAllAttribs();
void setVertices(const unsigned long size, const void *data);
};
}

View File

@ -1,58 +0,0 @@
#include "Colored.hpp"
using namespace buffer;
Colored::Colored(GLenum shape, const unsigned long count, const std::vector<glm::vec3> &vertices, const std::vector<glm::vec4> &colors): Abstract(shape) {
glGenBuffers(1, &ColorBufferID);
setVertices(vertices.size() * sizeof(glm::vec3), &vertices[0]);
setColors(colors.size() * sizeof(glm::vec4), &colors[0]);
ElementCount = count;
}
Colored::~Colored() {
glDeleteBuffers(1, &ColorBufferID);
}
uint Colored::draw(params params) {
if (params.vertexOnly) {
enableVertexAttrib();
} else {
enableAllAttribs();
}
if (params.instances == 1) {
glDrawArrays(Shape, 0, ElementCount);
} else {
glDrawArraysInstanced(Shape, 0, ElementCount, params.instances);
}
if (params.vertexOnly) {
disableVertexAttrib();
} else {
disableAllAttribs();
}
return ElementCount * params.instances;
}
void Colored::enableAllAttribs() {
enableVertexAttrib();
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, ColorBufferID);
glVertexAttribPointer(
1, // attribute
4, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void *)0 // array buffer offset
);
}
void Colored::disableAllAttribs() {
glDisableVertexAttribArray(1);
disableVertexAttrib();
}
void Colored::setColors(const unsigned long size, const void *data) {
glBindBuffer(GL_ARRAY_BUFFER, ColorBufferID);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}

View File

@ -1,28 +0,0 @@
#pragma once
#include <GL/gl3w.h>
#include <vector>
#include <glm/glm.hpp>
#include "Abstract.hpp"
namespace buffer {
/// OpenGL VertexBuffer with Colors
class Colored: public Abstract {
public:
Colored(GLenum shape, const unsigned long count, const std::vector<glm::vec3> &vertices, const std::vector<glm::vec4> &colors);
virtual ~Colored();
void enableAllAttribs();
void disableAllAttribs();
uint draw(params params) override;
private:
unsigned long ElementCount;
GLuint ColorBufferID;
void setColors(const unsigned long size, const void *data);
};
}

View File

@ -1,33 +0,0 @@
#include "LodShortIndexed.hpp"
using namespace buffer;
LodShortIndexed::LodShortIndexed(GLenum shape, const ShortIndexed::Data &data, const std::vector<size_t> &offsets): ShortIndexed(shape, data), offsets(offsets) { }
LodShortIndexed::LodShortIndexed(GLenum shape, const LodShortIndexed::LodData &data): ShortIndexed(shape, data.first), offsets(data.second) { }
LodShortIndexed::~LodShortIndexed() { }
#include <iostream>
uint LodShortIndexed::draw(buffer::params params) {
if(params.vertexOnly) {
enableVertexAttrib();
} else {
enableAllAttribs();
}
enableIndex();
const auto start = getOffset(level);
const auto end = getOffset(level+1);
const auto count = end - start;
if (params.instances == 1) {
glDrawElements(Shape, count, GL_UNSIGNED_SHORT, (void *)(start*sizeof(GLushort)));
} else {
glDrawElementsInstanced(Shape, count, GL_UNSIGNED_SHORT, (void *)(start*sizeof(GLushort)), params.instances);
}
if(params.vertexOnly) {
disableVertexAttrib();
} else {
disableAllAttribs();
}
return count * params.instances;
}

View File

@ -1,27 +0,0 @@
#pragma once
#include "ShortIndexed.hpp"
namespace buffer {
/// ShortIndexed with merge indices for LOD
class LodShortIndexed: public ShortIndexed {
public:
using LodData = std::pair<Data, std::vector<size_t>>;
LodShortIndexed(GLenum shape, const ShortIndexed::Data &data, const std::vector<size_t> &lod_offsets);
LodShortIndexed(GLenum shape, const LodShortIndexed::LodData &data);
virtual ~LodShortIndexed();
uint draw(params params) override;
void inline setLevel(size_t l) { level = l; }
private:
size_t level = 0;
std::vector<size_t> offsets;
constexpr inline size_t getOffset(size_t level) const {
return level <= 0 ? 0 : (level-1 < offsets.size() ? offsets[level-1] : IndexSize);
}
};
}

View File

@ -1,131 +0,0 @@
#include "ShortIndexed.hpp"
#include "vboindexer.hpp"
#include "../../../../core/utils/logger.hpp"
using namespace buffer;
ShortIndexed::Data::Data(const std::vector<PackedVertexData> &vs, const std::vector<GLushort> &indices): indices(indices), vertices(vs) { }
ShortIndexed::Data::Data(std::ifstream& in) {
{
size_t i_size;
in.read(reinterpret_cast<char *>(&i_size), sizeof(i_size));
indices.resize(i_size);
in.read(reinterpret_cast<char *>(indices.data()), sizeof(GLushort) * i_size);
}
size_t v_size;
in.read(reinterpret_cast<char *>(&v_size), sizeof(v_size));
vertices.resize(v_size, PackedVertexData(0, 0, 0, 0, 0, 0, 0, 0));
in.read(reinterpret_cast<char *>(vertices.data()), sizeof(buffer::PackedVertexData) * v_size);
}
void ShortIndexed::Data::serialize(std::ofstream& out) {
{
size_t i_size = indices.size();
out.write(reinterpret_cast<char *>(&i_size), sizeof(i_size));
out.write(reinterpret_cast<char *>(indices.data()), sizeof(GLushort) * i_size);
}
size_t v_size = vertices.size();
out.write(reinterpret_cast<char *>(&v_size), sizeof(v_size));
out.write(reinterpret_cast<char *>(vertices.data()), sizeof(buffer::PackedVertexData) * v_size);
}
void ShortIndexed::Data::index(const std::vector<PackedVertexData>& vs) {
indexVBO(vs, indices, vertices);
}
ShortIndexed::ShortIndexed(GLenum shape, const std::vector<PackedVertexData> &vertices): Abstract(shape) {
setData(ShortIndexed::Data(vertices));
}
ShortIndexed::ShortIndexed(GLenum shape, const std::vector<PackedVertexData> &vertices, const std::vector<GLushort>& indices): Abstract(shape) {
setData(ShortIndexed::Data(vertices, indices));
}
ShortIndexed::ShortIndexed(GLenum shape, const ShortIndexed::Data &data): Abstract(shape) {
setData(data);
}
ShortIndexed::~ShortIndexed() {
glDeleteBuffers(1, &IndexBufferID);
}
void ShortIndexed::enableVertexAttrib() {
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
glVertexAttribPointer(
0, // attribute
3, // size
GL_HALF_FLOAT, // type
GL_FALSE, // normalized?
sizeof(PackedVertexData), // stride
reinterpret_cast<void *>(offsetof(PackedVertexData, PosMat[0])) // array buffer offset
);
}
void ShortIndexed::enableAllAttribs() {
enableVertexAttrib();
glEnableVertexAttribArray(1);
glVertexAttribIPointer(
1, // attribute
1, // size
GL_UNSIGNED_SHORT, // type
sizeof(PackedVertexData), // stride
reinterpret_cast<void *>(offsetof(PackedVertexData, PosMat[3])) // array buffer offset
);
glEnableVertexAttribArray(2);
glVertexAttribPointer(
2, // attribute
3, // size
GL_HALF_FLOAT, // type
GL_FALSE, // normalized?
sizeof(PackedVertexData), // stride
reinterpret_cast<void *>(offsetof(PackedVertexData, Nrm)) // array buffer offset
);
}
void ShortIndexed::disableAllAttribs() {
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(1);
disableVertexAttrib();
}
void ShortIndexed::enableIndex() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
}
uint ShortIndexed::draw(buffer::params params) {
if(IndexSize == 0)
return 0;
if(params.vertexOnly) {
enableVertexAttrib();
} else {
enableAllAttribs();
}
enableIndex();
if (params.instances == 1) {
glDrawElements(Shape, IndexSize, GL_UNSIGNED_SHORT, (void *)0);
} else {
glDrawElementsInstanced(Shape, IndexSize, GL_UNSIGNED_SHORT, (void *)0, params.instances);
}
if(params.vertexOnly) {
disableVertexAttrib();
} else {
disableAllAttribs();
}
return IndexSize * params.instances;
}
void ShortIndexed::setData(const ShortIndexed::Data& data) {
glGenBuffers(1, &IndexBufferID);
IndexSize = data.indices.size();
if(IndexSize != data.indices.size()) {
LOG_E("ShortBuffer overflow: " << data.indices.size());
}
setIndicies(IndexSize * sizeof(GLushort), data.indices.data());
setVertices(data.vertices.size() * sizeof(PackedVertexData), data.vertices.data());
}
void ShortIndexed::setIndicies(const unsigned long size, const void *data) {
glBindBuffer(GL_ARRAY_BUFFER, IndexBufferID);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}

View File

@ -1,57 +0,0 @@
#pragma once
#include <GL/gl3w.h>
#include <glm/glm.hpp>
#include <vector>
#include <fstream>
#include "Abstract.hpp"
#include "VertexData.hpp"
namespace buffer {
/// OpenGL VertexBuffer with IndexBuffer
class ShortIndexed: public Abstract {
public:
/// Preindexed buffer data
struct Data {
std::vector<GLushort> indices;
std::vector<PackedVertexData> vertices;
Data() { }
Data(const std::vector<PackedVertexData> &vertices, const std::vector<GLushort> &indices);
Data(const std::vector<PackedVertexData> &vertices) { index(vertices); }
Data(std::ifstream &in);
void index(const std::vector<PackedVertexData> &vertices);
bool empty() const {
return indices.empty();
}
void clear() {
indices.clear();
vertices.clear();
}
void serialize(std::ofstream &out);
};
ShortIndexed(GLenum shape, const typename std::vector<PackedVertexData> &vertices);
ShortIndexed(GLenum shape, const typename std::vector<PackedVertexData> &vertices, const typename std::vector<GLushort> &indices);
ShortIndexed(GLenum shape, const typename ShortIndexed::Data &data);
virtual ~ShortIndexed();
uint draw(params params) override;
protected:
void enableVertexAttrib();
void enableAllAttribs();
void disableAllAttribs();
void enableIndex();
GLushort IndexSize = 0;
private:
GLuint IndexBufferID;
void setData(const ShortIndexed::Data &data);
void setIndicies(const unsigned long size, const void *data);
};
}

View File

@ -1,15 +0,0 @@
#include "Vertex.hpp"
using namespace buffer;
Vertex::Vertex(GLenum shape, const unsigned long count, const unsigned long size, const void *data): Abstract(shape) {
setVertices(size, data);
ElementCount = count;
}
uint Vertex::draw(params) {
enableAllAttribs();
glDrawArrays(Shape, 0, ElementCount);
disableAllAttribs();
return ElementCount;
}

View File

@ -1,18 +0,0 @@
#pragma once
#include <GL/gl3w.h>
#include "Abstract.hpp"
/// OpenGL VertexBuffer
namespace buffer {
class Vertex: public Abstract {
public:
Vertex(GLenum shape, const unsigned long count, const unsigned long size, const void *data);
uint draw(params) override;
private:
unsigned long ElementCount;
};
}

View File

@ -1,35 +0,0 @@
#pragma once
#include <glm/glm.hpp>
#include <string.h> // for memcmp
#include <GL/gl3w.h>
namespace buffer {
/// Vertex properties
struct VertexData {
VertexData(const glm::vec3 &position, GLushort material, const glm::vec3 &normal):
Position(position), Material(material), Normal(normal) { }
glm::vec3 Position;
GLushort Material;
glm::vec3 Normal;
};
/// Quantized vertex properties
struct PackedVertexData {
PackedVertexData(GLushort x, GLushort y, GLushort z, GLushort mat, GLushort nx, GLushort ny, GLushort nz, GLushort nw = 0) {
PosMat[0] = x;
PosMat[1] = y;
PosMat[2] = z;
PosMat[3] = mat;
Nrm[0] = nx;
Nrm[1] = ny;
Nrm[2] = nz;
Nrm[3] = nw;
}
GLushort PosMat[4];
GLushort Nrm[4]; //NOTE: Triplanar does not handle 10_10_10_2_REV
bool operator<(const PackedVertexData that) const {
return memcmp((void *)this, (void *)&that, sizeof(PackedVertexData)) > 0;
};
};
}

View File

@ -1,59 +0,0 @@
#include <vector>
#include <map>
#include <glm/glm.hpp>
#include "vboindexer.hpp"
bool getSimilarVertexIndex_fast(
const buffer::PackedVertexData &packed,
std::map<buffer::PackedVertexData, GLushort> &VertexToOutIndex,
GLushort &result)
{
std::map<buffer::PackedVertexData, GLushort>::iterator it = VertexToOutIndex.find(packed);
if ( it == VertexToOutIndex.end() ){
return false;
}else{
result = it->second;
return true;
}
}
bool getSimilarVertexIndex_fast(
const buffer::PackedVertexData &packed,
std::map<buffer::PackedVertexData, unsigned int> &VertexToOutIndex,
unsigned int &result)
{
std::map<buffer::PackedVertexData, unsigned int>::iterator it = VertexToOutIndex.find(packed);
if ( it == VertexToOutIndex.end() ){
return false;
}else{
result = it->second;
return true;
}
}
void indexVBO(
const std::vector<buffer::PackedVertexData> &in_vertices,
std::vector<GLushort> &out_indices,
std::vector<buffer::PackedVertexData> &out_vertices)
{
std::map<buffer::PackedVertexData, GLushort> VertexToOutIndex;
out_indices.reserve(in_vertices.size());
// For each input vertex
for (size_t i=0; i<in_vertices.size(); i++ ){
// Try to find a similar vertex in out_XXXX
GLushort index;
bool found = getSimilarVertexIndex_fast(in_vertices[i], VertexToOutIndex, index);
if ( found ){ // A similar vertex is already in the VBO, use it instead !
out_indices.push_back( index );
}else{ // If not, it needs to be added in the output data.
out_vertices.push_back(in_vertices[i]);
GLushort newindex = (GLushort)out_vertices.size() - 1;
out_indices.push_back( newindex );
VertexToOutIndex[ in_vertices[i] ] = newindex;
}
}
}

View File

@ -1,15 +0,0 @@
#ifndef VBOINDEXER_HPP
#define VBOINDEXER_HPP
#include <vector>
#include <glm/glm.hpp>
#include "VertexData.hpp"
#include <GL/gl3w.h>
void indexVBO(
const std::vector<buffer::PackedVertexData> &in_vertices,
std::vector<GLushort> &out_indices,
std::vector<buffer::PackedVertexData> &out_vertices);
#endif

View File

@ -20,10 +20,9 @@ ColorProgram::~ColorProgram() { }
std::string ColorProgram::getName() const {
return "Color";
}
buffer::params ColorProgram::setup(render::gl::Renderer *renderer, glm::mat4 modelMatrix) {
void ColorProgram::setup(render::gl::Renderer *renderer, glm::mat4 modelMatrix) {
const auto mvp = renderer->getProjectionMatrix() * renderer->getViewMatrix() * modelMatrix;
setMVP(&mvp[0][0]);
return buffer::params{.vertexOnly = false};
}
void ColorProgram::setMVP(const GLfloat *matrix) {

View File

@ -9,7 +9,7 @@ namespace pass {
ColorProgram();
~ColorProgram();
buffer::params setup(render::gl::Renderer *, glm::mat4 modelMatrix);
void setup(render::gl::Renderer *, glm::mat4 modelMatrix);
protected:
std::string getName() const override;

View File

@ -17,11 +17,9 @@ EntityProgram::EntityProgram(const EntityProgram::options &opts) : VoxelProgram(
}
EntityProgram::~EntityProgram() { }
buffer::params EntityProgram::setup(render::gl::Renderer *renderer, const std::vector<glm::mat4>& modelsMatrices) {
void EntityProgram::setup(render::gl::Renderer *renderer, const std::vector<glm::mat4>& modelsMatrices) {
setModels(&modelsMatrices[0][0][0], modelsMatrices.size());
auto params = VoxelProgram::setup(renderer);
params.instances = modelsMatrices.size();
return params;
VoxelProgram::setup(renderer);
}
void EntityProgram::setModels(const GLfloat *matrices, size_t count) {

View File

@ -11,7 +11,7 @@ namespace pass {
static constexpr auto LOCATION = 6;
buffer::params setup(render::gl::Renderer *, const std::vector<glm::mat4> &modelsMatrices);
void setup(render::gl::Renderer *, const std::vector<glm::mat4> &modelsMatrices);
void disable();
protected:

View File

@ -6,7 +6,6 @@
#include <glm/glm.hpp>
#include "Shader.hpp"
#include "../buffer/Abstract.hpp"
namespace render {
struct passOptions;

View File

@ -4,50 +4,50 @@
using namespace pass;
const GLfloat g_cubemap_vertices[] = {
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
const std::vector<glm::vec3> g_cubemap_vertices = {
{-1.0f, 1.0f, -1.0f},
{-1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f},
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
{-1.0f, -1.0f, 1.0f},
{-1.0f, -1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, 1.0f},
{-1.0f, -1.0f, 1.0f},
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
{-1.0f, -1.0f, 1.0f},
{-1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f},
{-1.0f, -1.0f, 1.0f},
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
{-1.0f, 1.0f, -1.0f},
{ 1.0f, 1.0f, -1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{-1.0f, 1.0f, 1.0f},
{-1.0f, 1.0f, -1.0f},
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f
{-1.0f, -1.0f, -1.0f},
{-1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{-1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f}
};
SkyProgram::SkyProgram(): Program(), CubeBuffer(GL_TRIANGLES, 36, sizeof(g_cubemap_vertices), g_cubemap_vertices) {
SkyProgram::SkyProgram(): Program(), CubeBuffer(g_cubemap_vertices) {
std::vector<std::string> flags;
std::vector<Shader*> shaders;
@ -72,13 +72,10 @@ void SkyProgram::start(render::gl::Renderer *renderer) {
bindTexture(renderer->getSkyTexture());
}
buffer::params SkyProgram::setup() {
return buffer::params{.vertexOnly = true};
}
void SkyProgram::draw(render::gl::Renderer *renderer) {
useIt();
start(renderer);
CubeBuffer.draw(setup());
CubeBuffer.draw();
}
void SkyProgram::setView(const GLfloat *matrix) {

View File

@ -1,7 +1,7 @@
#pragma once
#include "Program.hpp"
#include "../buffer/Vertex.hpp"
#include "../api/Models.hpp"
namespace pass {
/// Skybox pass
@ -11,7 +11,6 @@ namespace pass {
~SkyProgram();
void start(render::gl::Renderer *);
buffer::params setup();
/// Direct draw using internal buffer
void draw(render::gl::Renderer *);
@ -28,6 +27,6 @@ namespace pass {
GLuint ProjectionMatrixID;
GLuint TextureID;
buffer::Vertex CubeBuffer;
render::gl::Shape CubeBuffer;
};
}

View File

@ -62,10 +62,9 @@ void VoxelProgram::start(render::gl::Renderer *renderer) {
setView(&renderer->getViewMatrix()[0][0]);
setProj(&renderer->getProjectionMatrix()[0][0]);
}
buffer::params VoxelProgram::setup(render::gl::Renderer *renderer) {
void VoxelProgram::setup(render::gl::Renderer *renderer) {
setSphereProj(&renderer->getSphereProj()[0]);
setCurvature(renderer->getCurvature());
return buffer::params{.vertexOnly = false};
}
void VoxelProgram::setProj(const GLfloat *matrix) {

View File

@ -14,7 +14,7 @@ namespace pass {
void start(render::gl::Renderer *);
protected:
buffer::params setup(render::gl::Renderer *);
void setup(render::gl::Renderer *);
std::string getName() const override;
void setView(const GLfloat *matrix);

View File

@ -11,9 +11,9 @@ WorldProgram::WorldProgram(const WorldProgram::options& opts): VoxelProgram(opts
WorldProgram::~WorldProgram() { }
buffer::params WorldProgram::setup(render::gl::Renderer *renderer, glm::mat4 modelMatrix) {
void WorldProgram::setup(render::gl::Renderer *renderer, glm::mat4 modelMatrix) {
setModel(&modelMatrix[0][0]);
return VoxelProgram::setup(renderer);
VoxelProgram::setup(renderer);
}
void WorldProgram::setModel(const GLfloat *matrix) {

View File

@ -9,7 +9,7 @@ namespace pass {
WorldProgram(const options &opts);
~WorldProgram();
buffer::params setup(render::gl::Renderer *, glm::mat4 modelMatrix);
void setup(render::gl::Renderer *, glm::mat4 modelMatrix);
protected:
void setModel(const GLfloat *matrix);

View File

@ -7,12 +7,15 @@
using namespace render::vk;
constexpr auto HOST_EASILY_WRITABLE = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
constexpr VkDeviceSize MIN_ALLOC_SIZE = 1 << 28;
const auto NO_DELETER = Allocator::MemoryDeleter(nullptr);
Allocator::memory_ptr Allocator::GetNull() { return Allocator::memory_ptr(nullptr, NO_DELETER); }
Allocator* Allocator::sInstance = nullptr;
Allocator::Allocator(VkDevice device, const PhysicalDeviceInfo &info): physicalDevice(info.device), device(device) {
constexpr VkDeviceSize MIN_ALLOC_SIZE = 1 << 28;
const auto NO_DELETER = memory::Deleter(nullptr);
memory::ptr memory::GetNull() { return memory::ptr(nullptr, NO_DELETER); }
Allocator::Allocator(VkDevice device, const PhysicalDeviceInfo &info): physicalDevice(info.device),
capabilities({info.features.samplerAnisotropy == VK_TRUE}), device(device)
{
if(info.hasMemoryBudget()) {
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
properties2.pNext = &budget;
@ -83,166 +86,6 @@ void Allocator::setTracyZone(const char* name) {
(void)name;
}
Allocator::memory_ptr Allocator::createBuffer(const buffer_requirement& requirement, VkMemoryPropertyFlags properties, buffer_info& out) {
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = requirement.size;
bufferInfo.usage = requirement.usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(device, &bufferInfo, ALLOC, &out.buffer) != VK_SUCCESS) {
LOG_E("Failed to create buffer");
return GetNull();
}
out.offset = 0;
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device, out.buffer, &memRequirements);
auto memory = allocate(memRequirements, properties);
if (!memory || vkBindBufferMemory(device, out.buffer, memory->ref, memory->offset) != VK_SUCCESS) {
LOG_E("Failed to allocate buffer memory");
return GetNull();
}
if (requirement.size != 0 && requirement.data != nullptr) {
if (memory->ptr != nullptr) {
memory->write(requirement.data, requirement.data_size, requirement.data_offset);
} else {
Allocator::buffer_info stagingBuffer;
if(auto stagingMemory = createBuffer({requirement.size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT}, HOST_EASILY_WRITABLE, stagingBuffer)) {
stagingMemory->write(requirement.data, requirement.data_size, requirement.data_offset);
copyBuffer(stagingBuffer, out, requirement.size);
vkDestroyBuffer(device, stagingBuffer.buffer, ALLOC); //TODO: move to buffer destructor
} else {
FATAL("Cannot allocate staging memory");
return GetNull();
}
}
}
return memory;
}
Allocator::memory_ptr Allocator::createBuffers(const std::vector<buffer_requirement>& requirements, VkMemoryPropertyFlags properties, std::vector<buffer_info>& out) {
assert(!requirements.empty());
out.resize(requirements.size()+1);
// Create buffers
VkMemoryRequirements memRequirements = {0, 0, UINT32_MAX};
std::vector<VkDeviceSize> sizes;
sizes.resize(requirements.size());
for (size_t i = 0; i < requirements.size(); i++) {
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = requirements[i].size;
bufferInfo.usage = requirements[i].usage;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(device, &bufferInfo, ALLOC, &out[i].buffer) != VK_SUCCESS) {
LOG_E("Failed to create buffer");
return GetNull();
}
VkMemoryRequirements individualMemRequirements;
vkGetBufferMemoryRequirements(device, out[i].buffer, &individualMemRequirements);
memRequirements.alignment = std::max(memRequirements.alignment, individualMemRequirements.alignment);
memRequirements.memoryTypeBits &= individualMemRequirements.memoryTypeBits;
sizes[i] = individualMemRequirements.size;
}
// Align blocks
auto aligned = [&](VkDeviceSize offset) {
if (offset % memRequirements.alignment == 0)
return offset;
return offset + memRequirements.alignment - (offset % memRequirements.alignment);
};
out[0].offset = 0;
for (size_t i = 1; i < out.size(); i++) {
out[i].offset = aligned(out[i-1].offset + sizes[i-1]);
}
memRequirements.size = out.back().offset;
out.pop_back();
// Bind memory
auto memory = allocate(memRequirements, properties);
if (!memory) {
LOG_E("Failed to allocate buffers");
return GetNull();
}
for (size_t i = 0; i < requirements.size(); i++) {
if (vkBindBufferMemory(device, out[i].buffer, memory->ref, memory->offset + out[i].offset) != VK_SUCCESS) {
LOG_E("Failed to bind buffer");
return GetNull();
}
}
VkDeviceSize stagingSize = 0;
for (auto& requirement: requirements)
if (requirement.data != nullptr)
stagingSize = std::max(stagingSize, requirement.size);
// Copy datas
if (stagingSize != 0) {
if (memory->ptr != nullptr) {
for (size_t i = 0; i < requirements.size(); i++) {
if (requirements[i].data != nullptr && requirements[i].size != 0) {
assert(requirements[i].data_size + requirements[i].data_offset <= requirements[i].size);
memory->write(requirements[i].data, requirements[i].data_size, out[i].offset + requirements[i].data_offset);
}
}
} else {
Allocator::buffer_info stagingBuffer;
if(auto stagingMemory = createBuffer({stagingSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT}, HOST_EASILY_WRITABLE, stagingBuffer)) {
for (size_t i = 0; i < requirements.size(); i++) {
if (requirements[i].data != nullptr && requirements[i].size != 0) {
assert(requirements[i].data_size + requirements[i].data_offset <= requirements[i].size);
stagingMemory->write(requirements[i].data, requirements[i].data_size, requirements[i].data_offset);
copyBuffer(stagingBuffer, out[i], requirements[i].size);
}
}
vkDestroyBuffer(device, stagingBuffer.buffer, ALLOC); //TODO: move to buffer destructor
} else {
FATAL("Cannot allocate staging memory");
return GetNull();
}
}
}
return memory;
}
Allocator::memory_ptr Allocator::createImage(const VkImageCreateInfo& info, VkMemoryPropertyFlags properties, image_info& out,
const void *data, VkDeviceSize data_size, VkDeviceSize data_offset, VkImageLayout finalLayout) {
if (vkCreateImage(device, &info, ALLOC, &out.image) != VK_SUCCESS) {
LOG_E("Failed to create image");
return GetNull();
}
out.offset = 0;
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(device, out.image, &memRequirements);
auto memory = allocate(memRequirements, properties, true);
if (!memory || vkBindImageMemory(device, out.image, memory->ref, memory->offset) != VK_SUCCESS) {
LOG_E("Failed to allocate image memory");
return GetNull();
}
if (data_size != 0 && data != nullptr) {
Allocator::buffer_info stagingBuffer;
if(auto stagingMemory = createBuffer({data_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT}, HOST_EASILY_WRITABLE, stagingBuffer)) {
stagingMemory->write(data, data_size, data_offset);
transitionImageLayout(out.image, info.format, info.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyBufferToImage(stagingBuffer, out, info.extent.width, info.extent.height);
transitionImageLayout(out.image, info.format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, finalLayout);
vkDestroyBuffer(device, stagingBuffer.buffer, ALLOC); //TODO: move to buffer destructor
} else {
FATAL("Cannot allocate staging memory");
return GetNull();
}
}
return memory;
}
void Allocator::updateProperties() {
if (hasBudget()) {
vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &properties2);
@ -256,7 +99,7 @@ void Allocator::updateProperties() {
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &properties);
}
}
Allocator::memory_ptr Allocator::allocate(VkMemoryRequirements requirements, VkMemoryPropertyFlags properties, bool optimalTiling) {
memory::ptr Allocator::allocate(VkMemoryRequirements requirements, VkMemoryPropertyFlags properties, bool optimalTiling) {
// Search in existing allocations
for (auto& alloc: allocations) {
if ((requirements.memoryTypeBits & (1 << alloc->memoryType)) &&
@ -272,7 +115,7 @@ Allocator::memory_ptr Allocator::allocate(VkMemoryRequirements requirements, VkM
auto it = alloc->areas.cbegin();
auto done = [&] {
alloc->areas.insert(it, {requirements.size, start});
return memory_ptr(new memory_area{alloc->memory, requirements.size, start, alloc->ptr != nullptr ? alloc->ptr + start : nullptr}, alloc->deleter);
return memory::ptr(new memory::area{alloc->memory, requirements.size, start, alloc->ptr != nullptr ? static_cast<uint8_t*>(alloc->ptr) + start : nullptr}, alloc->deleter);
};
while (it != alloc->areas.cend()) {
if (it->offset - start > requirements.size) {
@ -300,24 +143,24 @@ Allocator::memory_ptr Allocator::allocate(VkMemoryRequirements requirements, VkM
} else {
LOG_E("No suitable memory heap within memory budget");
LOG_D(requirements.memoryTypeBits << ' ' << properties << ' ' << requirements.size);
return GetNull();
return memory::GetNull();
}
VkDeviceMemory memory;
if (vkAllocateMemory(device, &allocInfo, ALLOC, &memory) != VK_SUCCESS) {
LOG_E("Failed to allocate memory!");
return GetNull();
return memory::GetNull();
}
void *ptr = nullptr;
if ((getProperties().memoryTypes[allocInfo.memoryTypeIndex].propertyFlags & HOST_EASILY_WRITABLE) == HOST_EASILY_WRITABLE) {
if ((getProperties().memoryTypes[allocInfo.memoryTypeIndex].propertyFlags & memory::HOST_EASILY_WRITABLE) == memory::HOST_EASILY_WRITABLE) {
vkMapMemory(device, memory, 0, VK_WHOLE_SIZE, 0, &ptr);
}
} //TODO: allow flushable memory
auto allocation = allocations.emplace_back(new Allocation(device, memory, allocInfo.allocationSize, allocInfo.memoryTypeIndex, optimalTiling, ptr)).get();
allocation->areas.push_back({requirements.size, 0});
return memory_ptr(new memory_area{memory, requirements.size, 0, ptr}, allocation->deleter);
return memory::ptr(new memory::area{memory, requirements.size, 0, ptr}, allocation->deleter);
}
void beginCmd(VkCommandBuffer buffer) {
@ -340,16 +183,14 @@ void submitCmd(VkCommandBuffer buffer, VkQueue queue) {
vkQueueWaitIdle(queue); //MAYBE: use fences
vkResetCommandBuffer(buffer, 0);
}
void Allocator::copyBuffer(buffer_info src, buffer_info dst, VkDeviceSize size) {
//FIXME: assert no out of range
void Allocator::copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size) {
beginCmd(transferBuffer);
VkBufferCopy copyRegion{};
copyRegion.srcOffset = 0;
copyRegion.dstOffset = 0;
copyRegion.size = size;
vkCmdCopyBuffer(transferBuffer, src.buffer, dst.buffer, 1, &copyRegion);
vkCmdCopyBuffer(transferBuffer, src, dst, 1, &copyRegion);
submitCmd(transferBuffer, transferQueue);
}
@ -394,11 +235,11 @@ void Allocator::transitionImageLayout(VkImage image, VkFormat format, VkImageLay
submitCmd(graphicsBuffer, graphicsQueue);
}
void Allocator::copyBufferToImage(buffer_info src, image_info dest, uint32_t width, uint32_t height) {
void Allocator::copyBufferToImage(VkBuffer src, VkImage dest, uint32_t width, uint32_t height) {
beginCmd(transferBuffer);
VkBufferImageCopy region{};
region.bufferOffset = src.offset;
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
@ -410,7 +251,7 @@ void Allocator::copyBufferToImage(buffer_info src, image_info dest, uint32_t wid
region.imageOffset = {0, 0, 0};
region.imageExtent = {width, height, 1};
vkCmdCopyBufferToImage(transferBuffer, src.buffer, dest.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
vkCmdCopyBufferToImage(transferBuffer, src, dest, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
submitCmd(transferBuffer, transferQueue);
}
@ -451,12 +292,16 @@ std::optional<uint32_t> Allocator::findMemory(uint32_t typeFilter, VkMemoryPrope
return {};
}
void Allocator::memory_area::write(const void* data, size_t data_size, size_t write_offset) {
void memory::area::write(const void* data, size_t data_size, size_t write_offset) {
assert(ptr != nullptr && size >= write_offset + data_size);
memcpy(ptr + write_offset, data, data_size);
memcpy(static_cast<uint8_t*>(ptr) + write_offset, data, data_size);
}
void memory::area::read(void* data, size_t data_size, size_t read_offset) {
assert(ptr != nullptr && size >= read_offset + data_size);
memcpy(data, static_cast<uint8_t*>(ptr) + read_offset, data_size);
}
void Allocator::MemoryDeleter::operator()(memory_area* area) {
void memory::Deleter::operator()(memory::area* area) {
assert(area != nullptr && "Deleting null area");
if(owner != nullptr) {
for (auto it = owner->areas.begin(); it != owner->areas.end(); ++it) {
@ -472,9 +317,9 @@ void Allocator::MemoryDeleter::operator()(memory_area* area) {
LOG_E("Allocation area not found");
delete area;
}
Allocator::Allocation::Allocation(VkDevice device, VkDeviceMemory memory, VkDeviceSize size, uint32_t memoryType, bool optimalTiling, void *ptr):
Allocation::Allocation(VkDevice device, VkDeviceMemory memory, VkDeviceSize size, uint32_t memoryType, bool optimalTiling, void *ptr):
device(device), memory(memory), size(size), memoryType(memoryType), optimalTiling(optimalTiling), ptr(ptr), deleter(this) { }
Allocator::Allocation::~Allocation() {
Allocation::~Allocation() {
if(!areas.empty())
LOG_E("Freeing " << areas.size() << " floating buffers");

View File

@ -1,9 +1,11 @@
#pragma once
#include "forward.hpp"
#include "../../../core/flags.hpp"
#include "api/Memory.hpp"
#include <cassert>
#include <vector>
#include <optional>
#include <memory>
namespace tracy {
class VkCtx;
@ -11,65 +13,47 @@ namespace tracy {
typedef tracy::VkCtx* TracyVkCtx;
namespace render::vk {
class Allocator {
private:
struct Allocation;
struct Allocation {
Allocation(VkDevice, VkDeviceMemory, VkDeviceSize, uint32_t, bool optimalTiling, void *ptr);
~Allocation();
const VkDevice device;
const VkDeviceMemory memory;
const VkDeviceSize size;
const uint32_t memoryType;
const bool optimalTiling;
void *const ptr = nullptr;
const memory::Deleter deleter;
struct area { VkDeviceSize size; VkDeviceSize offset; };
std::vector<area> areas;
};
class Allocator final {
public:
Allocator(VkDevice, const PhysicalDeviceInfo&);
~Allocator();
struct memory_area {
VkDeviceMemory ref;
VkDeviceSize size;
VkDeviceSize offset;
void *ptr = nullptr;
memory::ptr allocate(VkMemoryRequirements, VkMemoryPropertyFlags, bool optimalTiling = false);
bool deallocate(const memory::area&);
void write(const void*, VkDeviceSize size, VkDeviceSize offset = 0);
void read(void*, VkDeviceSize size, VkDeviceSize offset = 0);
};
class MemoryDeleter {
public:
MemoryDeleter(Allocation *owner): owner(owner) { }
void operator()(memory_area *);
private:
Allocation *owner;
};
using memory_ptr = std::unique_ptr<memory_area, MemoryDeleter>;
memory_ptr allocate(VkMemoryRequirements, VkMemoryPropertyFlags, bool optimalTiling = false);
bool deallocate(const memory_area&);
struct buffer_info {
VkBuffer buffer = nullptr;
VkDeviceSize offset = 0;
};
struct buffer_requirement {
VkDeviceSize size;
VkBufferUsageFlags usage;
const void *data = nullptr;
VkDeviceSize data_size = 0;
VkDeviceSize data_offset = 0;
};
memory_ptr createBuffer(const buffer_requirement&, VkMemoryPropertyFlags, buffer_info&);
memory_ptr createBuffers(const std::vector<buffer_requirement> &, VkMemoryPropertyFlags, std::vector<buffer_info> &);
struct image_info {
VkImage image = nullptr;
VkDeviceSize offset = 0;
};
memory_ptr createImage(const VkImageCreateInfo&, VkMemoryPropertyFlags, image_info&,
const void *data = nullptr, VkDeviceSize data_size = 0, VkDeviceSize data_offset = 0,
VkImageLayout finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
void copyBuffer(buffer_info srcBuffer, buffer_info dstBuffer, VkDeviceSize size);
void copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size);
void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout);
void copyBufferToImage(buffer_info buffer, image_info image, uint32_t width, uint32_t height);
void copyBufferToImage(VkBuffer src, VkImage dst, uint32_t width, uint32_t height);
void setTracyZone(const char* name);
static memory_ptr GetNull();
struct Capabilities {
bool anisotropy;
};
constexpr VkDevice getDevice() const { return device; }
constexpr Capabilities getCapabilities() const { return capabilities; }
static void MakeDefault(Allocator *ptr) { sInstance = ptr; }
static _FORCE_INLINE_ Allocator *GetDefault() {
assert(sInstance != nullptr && "No default allocator");
return sInstance;
}
private:
std::optional<uint32_t> findMemory(uint32_t, VkMemoryPropertyFlags, VkDeviceSize size = 0);
@ -77,23 +61,8 @@ private:
constexpr const VkPhysicalDeviceMemoryProperties &getProperties() const { return hasBudget() ? properties2.memoryProperties : properties; }
void updateProperties();
struct Allocation {
Allocation(VkDevice, VkDeviceMemory, VkDeviceSize, uint32_t, bool optimalTiling, void *ptr);
~Allocation();
const VkDevice device;
const VkDeviceMemory memory;
const VkDeviceSize size;
const uint32_t memoryType;
const bool optimalTiling;
void *const ptr = nullptr;
const MemoryDeleter deleter;
struct area { VkDeviceSize size; VkDeviceSize offset; };
std::vector<area> areas;
};
VkPhysicalDevice const physicalDevice;
Capabilities const capabilities;
VkDevice const device;
VkPhysicalDeviceMemoryProperties properties{};
VkPhysicalDeviceMemoryProperties2 properties2{};
@ -108,5 +77,7 @@ private:
VkCommandBuffer graphicsBuffer;
std::vector<std::unique_ptr<Allocation>> allocations;
static Allocator *sInstance;
};
}

View File

@ -2,20 +2,15 @@
#include "PhysicalDeviceInfo.hpp"
#include "Pipeline.hpp"
#include "../Renderer.hpp"
#include "Renderer.hpp"
#include "buffer/VertexData.hpp"
#include "texture.hpp"
using namespace render::vk;
#define CONTENT_DIR "content/"
#define TEXTURES_DIR CONTENT_DIR "textures/"
CommandCenter::CommandCenter(VkDevice device, Allocator& alloc, const std::vector<VkImageView> &views,
const Pipeline& pipe, const PhysicalDeviceInfo &info, const renderOptions &opt): device(device),
indexedBufferMemory(Allocator::GetNull()), sampleImageMemory(Allocator::GetNull()),
uniformBuffersMemory(Allocator::GetNull())
{
CommandCenter::CommandCenter(VkDevice device, const PhysicalDeviceInfo &info, const renderOptions &opt): device(device) {
{ // Graphics command pool
vkGetDeviceQueue(device, info.queueIndices.graphicsFamily.value(), 0, &graphicsQueue);
VkCommandPoolCreateInfo poolInfo{};
@ -28,114 +23,26 @@ CommandCenter::CommandCenter(VkDevice device, Allocator& alloc, const std::vecto
}
}
{ // Vertex buffers (const)
size_t vertexSize = sizeof(buffer::vk::vertices[0]) * buffer::vk::vertices.size();
size_t indexSize = sizeof(buffer::vk::indices[0]) * buffer::vk::indices.size();
if(std::vector<Allocator::buffer_info> out; indexedBufferMemory = alloc.createBuffers({
{indexSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, buffer::vk::indices.data(), indexSize, 0},
{vertexSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, buffer::vk::vertices.data(), vertexSize, 0}
}, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, out)) {
indexBuffer = out[0];
vertexBuffer = out[1];
} else {
modelBuffer = ShortIndexedVertexBuffer::Create(buffer::vk::vertices, buffer::vk::indices);
if (!modelBuffer) {
FATAL("Cannot create vertex buffer");
}
}
{ // Texture sampler (const)
std::vector<unsigned char> data;
dds::header_info header;
if (dds::readDDS(TEXTURES_DIR "1024-realistic/terrain/Mapl.dds", data, header)) {
FATAL("Cannot read texture");
}
VkImageCreateInfo imageInfo{};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = header.width;
imageInfo.extent.height = header.height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = header.mipMapCount; //TODO:
imageInfo.arrayLayers = 1;
imageInfo.format = header.format;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.flags = 0;
sampleImageMemory = alloc.createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, sampleImage, data.data(), data.size());
if(!sampleImageMemory) {
FATAL("Cannot create texture image");
}
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = sampleImage.image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = header.format;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &viewInfo, nullptr, &sampleImageView) != VK_SUCCESS) {
FATAL("Failed to create texture image view!");
}
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
if (info.features.samplerAnisotropy && opt.anisotropy > 0) {
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = 1 << (opt.anisotropy-1);
} else {
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.f;
}
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f; //TODO:
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
if (vkCreateSampler(device, &samplerInfo, nullptr, &sampleSampler) != VK_SUCCESS) {
sampleTexture = Texture::LoadFromFile(TEXTURES_DIR "1024-realistic/terrain/Mapl.dds", {true, true, Texture::Wrap::MIRRORED_REPEAT, opt.anisotropy});
if (!sampleTexture) {
FATAL("Failed to create texture sampler!");
}
}
allocate(alloc, views, pipe, info.swapDetails.capabilities.currentExtent, opt);
}
CommandCenter::~CommandCenter() {
if(!freed)
free();
vkDestroyBuffer(device, indexBuffer.buffer, ALLOC);
vkDestroyBuffer(device, vertexBuffer.buffer, ALLOC);
indexedBufferMemory.reset();
vkDestroySampler(device, sampleSampler, ALLOC);
vkDestroyImageView(device, sampleImageView, ALLOC);
vkDestroyImage(device, sampleImage.image, ALLOC);
sampleImageMemory.reset();
vkDestroyCommandPool(device, graphicsPool, ALLOC);
}
#include <glm/gtc/matrix_transform.hpp>
void CommandCenter::allocate(Allocator& alloc, const std::vector<VkImageView>& views, const Pipeline& pipe, VkExtent2D extent,
const renderOptions& opt)
{
void CommandCenter::allocate(const std::vector<VkImageView>& views, const Pipeline& pipe, VkExtent2D extent, const renderOptions& opt) {
assert(freed);
framebuffers.resize(views.size());
@ -160,10 +67,10 @@ void CommandCenter::allocate(Allocator& alloc, const std::vector<VkImageView>& v
{ // Uniform buffers
VkDeviceSize bufferSize = sizeof(buffer::vk::UniformBufferObject);
std::vector<Allocator::buffer_requirement> requirements;
requirements.resize(framebuffers.size(), Allocator::buffer_requirement{bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT});
uniformBuffersMemory = alloc.createBuffers(requirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, uniformBuffers);
if (!uniformBuffersMemory) {
std::vector<Buffer::requirement> requirements;
requirements.resize(framebuffers.size(), Buffer::requirement(bufferSize, Buffer::Usage::UNIFORM));
uniformBuffers.allocate(requirements, true);
if (!uniformBuffers) {
FATAL("Failed to allocate UBO");
}
}
@ -200,15 +107,10 @@ void CommandCenter::allocate(Allocator& alloc, const std::vector<VkImageView>& v
for (size_t i = 0; i < descriptorSets.size(); i++) {
VkDescriptorBufferInfo bufferInfo{};
bufferInfo.buffer = uniformBuffers[i].buffer;
bufferInfo.buffer = uniformBuffers.at(i);
bufferInfo.offset = 0;
bufferInfo.range = sizeof(buffer::vk::UniformBufferObject);
VkDescriptorImageInfo imageInfo{};
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = sampleImageView;
imageInfo.sampler = sampleSampler;
std::array<VkWriteDescriptorSet, 2> descriptorWrites{};
descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptorWrites[0].dstSet = descriptorSets[i];
@ -224,7 +126,7 @@ void CommandCenter::allocate(Allocator& alloc, const std::vector<VkImageView>& v
descriptorWrites[1].dstArrayElement = 0;
descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrites[1].descriptorCount = 1;
descriptorWrites[1].pImageInfo = &imageInfo;
descriptorWrites[1].pImageInfo = &sampleTexture->getDescriptor();
vkUpdateDescriptorSets(device, descriptorWrites.size(), descriptorWrites.data(), 0, nullptr);
}
@ -266,10 +168,10 @@ void CommandCenter::allocate(Allocator& alloc, const std::vector<VkImageView>& v
vkCmdBeginRenderPass(graphicsBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(graphicsBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.getPipeline());
VkBuffer vertexBuffers[] = {vertexBuffer.buffer};
VkBuffer vertexBuffers[] = {modelBuffer->getVertex()};
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(graphicsBuffers[i], 0, 1, vertexBuffers, offsets);
vkCmdBindIndexBuffer(graphicsBuffers[i], indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT16);
vkCmdBindIndexBuffer(graphicsBuffers[i], modelBuffer->getIndex(), 0, VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(graphicsBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.getLayout(), 0, 1, &descriptorSets[i], 0, nullptr);
vkCmdDrawIndexed(graphicsBuffers[i], static_cast<uint32_t>(buffer::vk::indices.size()), 1, 0, 0, 0);
vkCmdEndRenderPass(graphicsBuffers[i]);
@ -290,12 +192,6 @@ void CommandCenter::free() {
vkDestroyDescriptorPool(device, descriptorPool, nullptr);
for (size_t i = 0; i < uniformBuffers.size(); i++) {
vkDestroyBuffer(device, uniformBuffers[i].buffer, ALLOC);
}
uniformBuffersMemory.reset();
for (size_t i = 0; i < framebuffers.size(); i++) {
vkDestroyFramebuffer(device, framebuffers[i], nullptr);
}
@ -315,8 +211,7 @@ void CommandCenter::updateUBO(uint32_t idx) {
ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
ubo.proj = proj;
memcpy(uniformBuffersMemory->ptr + uniformBuffers[idx].offset, &ubo, sizeof(ubo));
uniformBuffers.write(idx, data_view(&ubo, sizeof(ubo)));
}
void CommandCenter::submitGraphics(uint32_t idx, VkSemaphore waitSemaphore, VkSemaphore signalSemaphore, VkFence submittedFence) {

View File

@ -2,22 +2,23 @@
#include "forward.hpp"
#include <vector>
#include "Allocator.hpp"
#include <glm/mat4x4.hpp>
#include "api/Buffers.hpp"
#include "api/Images.hpp"
namespace render::vk {
class SwapChain;
class Pipeline;
class CommandCenter {
class CommandCenter final {
public:
CommandCenter(VkDevice, Allocator&, const std::vector<VkImageView>&, const Pipeline&, const PhysicalDeviceInfo&, const renderOptions&);
CommandCenter(VkDevice, const PhysicalDeviceInfo&, const renderOptions&);
~CommandCenter();
void updateUBO(uint32_t idx);
void submitGraphics(uint32_t, VkSemaphore, VkSemaphore, VkFence);
void allocate(Allocator&, const std::vector<VkImageView> &, const Pipeline&, VkExtent2D, const renderOptions&);
void allocate(const std::vector<VkImageView>&, const Pipeline&, VkExtent2D, const renderOptions&);
void free();
private:
@ -32,18 +33,12 @@ private:
VkDescriptorPool descriptorPool;
std::vector<VkDescriptorSet> descriptorSets;
Allocator::buffer_info vertexBuffer;
Allocator::buffer_info indexBuffer;
Allocator::memory_ptr indexedBufferMemory;
std::unique_ptr<ShortIndexedVertexBuffer> modelBuffer;
Allocator::image_info sampleImage;
VkImageView sampleImageView;
VkSampler sampleSampler;
Allocator::memory_ptr sampleImageMemory;
std::unique_ptr<Texture> sampleTexture;
glm::mat4 proj;
std::vector<Allocator::buffer_info> uniformBuffers;
Allocator::memory_ptr uniformBuffersMemory;
BufferGroup uniformBuffers;
bool freed = true;
};

View File

@ -4,7 +4,7 @@
namespace render::vk {
class Pipeline {
class Pipeline final {
public:
Pipeline(VkDevice, const PhysicalDeviceInfo&, const renderOptions&);
~Pipeline();

View File

@ -46,10 +46,12 @@ Renderer::Renderer(VkInstance instance, VkDevice device, const PhysicalDeviceInf
set_current_extent(physicalInfo->swapDetails.capabilities, physicalInfo->window);
allocator = std::make_unique<Allocator>(device, *physicalInfo.get());
swapChain = std::make_unique<SwapChain>(device, *physicalInfo.get());
pipeline = std::make_unique<Pipeline>(device, *physicalInfo.get(), options);
commandCenter = std::make_unique<CommandCenter>(device, *allocator.get(), swapChain->getImageViews(), *pipeline.get(), *physicalInfo.get(), options);
allocator = std::make_unique<Allocator>(device, getInfos());
Allocator::MakeDefault(allocator.get());
swapChain = std::make_unique<SwapChain>(device, getInfos());
pipeline = std::make_unique<Pipeline>(device, getInfos(), options);
commandCenter = std::make_unique<CommandCenter>(device, getInfos(), options);
commandCenter->allocate(swapChain->getImageViews(), *pipeline.get(), getInfos().swapDetails.capabilities.currentExtent, options);
{
imageAvailableSemaphores.resize(opt.inFlightFrames);
@ -101,9 +103,9 @@ void Renderer::recreateSwapChain() {
physicalInfo->swapDetails = SwapChainSupportDetails::Query(physicalInfo->device, physicalInfo->surface);
set_current_extent(physicalInfo->swapDetails.capabilities, physicalInfo->window);
swapChain = std::make_unique<SwapChain>(device, *physicalInfo.get());
pipeline = std::make_unique<Pipeline>(device, *physicalInfo.get(), options);
commandCenter->allocate(*allocator.get(), swapChain->getImageViews(), *pipeline.get(), physicalInfo->swapDetails.capabilities.currentExtent, options);
swapChain = std::make_unique<SwapChain>(device, getInfos());
pipeline = std::make_unique<Pipeline>(device, getInfos(), options);
commandCenter->allocate(swapChain->getImageViews(), *pipeline.get(), getInfos().swapDetails.capabilities.currentExtent, options);
}
void Renderer::destroySwapChain() {
commandCenter->free();
@ -124,11 +126,8 @@ void set_current_extent(VkSurfaceCapabilitiesKHR &capabilities, GLFWwindow* ptr)
std::max(capabilities.minImageExtent.width, std::min<uint32_t>(capabilities.maxImageExtent.width, windowSize.first)),
std::max(capabilities.minImageExtent.height, std::min<uint32_t>(capabilities.maxImageExtent.height, windowSize.second))};
};
VKAPI_ATTR VkBool32 VKAPI_CALL debugValidationCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
VKAPI_ATTR VkBool32 VKAPI_CALL debugValidationCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void*)
{
switch (messageSeverity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
@ -414,7 +413,7 @@ void Renderer::beginFrame() {
}
}
std::function<buffer::params(glm::mat4)> Renderer::beginWorldPass() {
std::function<size_t(render::LodModel *const, glm::mat4)> Renderer::beginWorldPass() {
assert(currentImage < swapChain->getImageViews().size());
commandCenter->updateUBO(currentImage);
@ -422,16 +421,16 @@ std::function<buffer::params(glm::mat4)> Renderer::beginWorldPass() {
renderFinishedSemaphores[currentFrame], inFlightFences[currentFrame]);
/*WorldPass->useIt();
WorldPass->start(this);*/
return [&](glm::mat4) {
return buffer::params{}; //WorldPass->setup(this, model);
return [&](render::LodModel *const, glm::mat4) {
return 0; //WorldPass->setup(this, model);
};
}
std::function<buffer::params(const std::vector<glm::mat4> &)> Renderer::beginEntityPass() {
std::function<size_t(render::Model *const, const std::vector<glm::mat4> &)> Renderer::beginEntityPass() {
/*EntityPass->useIt();
EntityPass->start(this);*/
return [&](const std::vector<glm::mat4>&) {
return buffer::params{}; //EntityPass->setup(this, models);
return [&](render::Model *const, const std::vector<glm::mat4>&) {
return 0; //EntityPass->setup(this, models);
};
}

View File

@ -20,8 +20,8 @@ public:
static _FORCE_INLINE_ Renderer *Get() { return static_cast<Renderer*>(render::Renderer::Get()); }
void beginFrame() override;
std::function<buffer::params(glm::mat4)> beginWorldPass() override;
std::function<buffer::params(const std::vector<glm::mat4> &)> beginEntityPass() override;
std::function<size_t(render::LodModel *const, glm::mat4)> beginWorldPass() override;
std::function<size_t(render::Model *const, const std::vector<glm::mat4> &)> beginEntityPass() override;
size_t drawIndicatorCube(glm::mat4 model) override;
void endPass() override;
void swapBuffer(Window&) override;
@ -34,11 +34,15 @@ public:
void reloadShaders(const passOptions &) override;
void reloadTextures(const std::string &, float mipMapLOD, float anisotropy) override;
Allocator* getAllocator() const { return allocator.get(); }
void setResized() { framebufferResized = true; }
private:
Renderer(VkInstance, VkDevice, const PhysicalDeviceInfo &, const renderOptions &);
const PhysicalDeviceInfo &getInfos() const { return *physicalInfo.get(); }
renderOptions options;
VkInstance instance;

View File

@ -6,7 +6,7 @@
namespace render::vk {
class SwapChain {
class SwapChain final {
public:
SwapChain(VkDevice, const PhysicalDeviceInfo &);
~SwapChain();

View File

@ -0,0 +1,184 @@
#include "Buffers.hpp"
#include "../Allocator.hpp"
#include "../../../../core/utils/logger.hpp"
using namespace render::vk;
Buffer::~Buffer() {
vkDestroyBuffer(Allocator::GetDefault()->getDevice(), ref, ALLOC);
//NOTE: memory_ptr self destroy
}
memory::ptr createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, const render::data_view view, Buffer::info &out) {
auto alloc = Allocator::GetDefault();
auto device = alloc->getDevice();
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
bufferInfo.size = size;
bufferInfo.usage = usage;
if (view)
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
if (vkCreateBuffer(device, &bufferInfo, ALLOC, &out.ref) != VK_SUCCESS) {
LOG_E("Failed to create buffer");
return memory::GetNull();
}
out.offset = 0;
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device, out.ref, &memRequirements);
auto memory = alloc->allocate(memRequirements, properties);
if (!memory || vkBindBufferMemory(device, out.ref, memory->ref, memory->offset) != VK_SUCCESS) {
LOG_E("Failed to allocate buffer memory");
return memory::GetNull();
}
if (view) {
if (memory->ptr != nullptr) {
memory->write(view.ptr, view.size);
} else if(auto staging = WritableBuffer::Create(size)) {
staging->write(view, 0);
alloc->copyBuffer(staging->getRef(), out.ref, size);
} else {
FATAL("Cannot allocate staging memory");
return memory::GetNull();
}
}
return memory;
}
memory::ptr createBuffers(const std::vector<Buffer::requirement>& requirements, VkMemoryPropertyFlags properties, std::vector<Buffer::info>& out) {
assert(!requirements.empty());
out.resize(requirements.size()+1);
auto alloc = Allocator::GetDefault();
auto device = alloc->getDevice();
// Create buffers
VkMemoryRequirements memRequirements = {0, 0, UINT32_MAX};
std::vector<VkDeviceSize> sizes;
sizes.resize(requirements.size());
for (size_t i = 0; i < requirements.size(); i++) {
VkBufferCreateInfo bufferInfo{};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
bufferInfo.size = requirements[i].size;
bufferInfo.usage = static_cast<VkBufferUsageFlags>(requirements[i].usage);
if (requirements[i].view)
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
if (vkCreateBuffer(device, &bufferInfo, ALLOC, &out[i].ref) != VK_SUCCESS) {
LOG_E("Failed to create buffer");
return memory::GetNull();
}
VkMemoryRequirements individualMemRequirements;
vkGetBufferMemoryRequirements(device, out[i].ref, &individualMemRequirements);
memRequirements.alignment = std::max(memRequirements.alignment, individualMemRequirements.alignment);
memRequirements.memoryTypeBits &= individualMemRequirements.memoryTypeBits;
sizes[i] = individualMemRequirements.size;
}
// Align blocks
auto aligned = [&](VkDeviceSize offset) {
if (offset % memRequirements.alignment == 0)
return offset;
return offset + memRequirements.alignment - (offset % memRequirements.alignment);
};
out[0].offset = 0;
for (size_t i = 1; i < out.size(); i++) {
out[i].offset = aligned(out[i-1].offset + sizes[i-1]);
}
memRequirements.size = out.back().offset;
out.pop_back();
// Bind memory
auto memory = alloc->allocate(memRequirements, properties);
if (!memory) {
LOG_E("Failed to allocate buffers");
return memory::GetNull();
}
for (size_t i = 0; i < requirements.size(); i++) {
if (vkBindBufferMemory(device, out[i].ref, memory->ref, memory->offset + out[i].offset) != VK_SUCCESS) {
LOG_E("Failed to bind buffer");
return memory::GetNull();
}
}
VkDeviceSize stagingSize = 0;
for (auto& requirement: requirements)
if (requirement.view)
stagingSize = std::max(stagingSize, requirement.size);
// Copy views
// MAYBE: allow single copy
if (stagingSize != 0) {
if (memory->ptr != nullptr) {
for (size_t i = 0; i < requirements.size(); i++) {
if (requirements[i].view) {
assert(requirements[i].view.size <= requirements[i].size);
memory->write(requirements[i].view.ptr, requirements[i].view.size, out[i].offset);
}
}
} else if(auto staging = WritableBuffer::Create(stagingSize)) {
for (size_t i = 0; i < requirements.size(); i++) {
if (requirements[i].view) {
assert(requirements[i].view.size <= requirements[i].size);
staging->write(requirements[i].view, 0);
alloc->copyBuffer(staging->getRef(), out[i].ref, requirements[i].size);
}
}
} else {
FATAL("Cannot allocate staging memory");
return memory::GetNull();
}
}
return memory;
}
void Buffer::MakeDefault() { }
std::unique_ptr<WritableBuffer> WritableBuffer::Create(size_t size, Usage usage, const data_view write) {
vk::Buffer::info tmp;
auto mem = createBuffer(size, static_cast<int>(usage), memory::HOST_EASILY_WRITABLE, write, tmp);
return std::unique_ptr<WritableBuffer>(new WritableBuffer(tmp.ref, std::move(mem), tmp.offset));
}
void WritableBuffer::write(const data_view view, size_t offset) {
memory->write(view.ptr, view.size, memOffset + offset);
}
void WritableBuffer::read(data_ref ref, size_t offset) {
memory->read(ref.ptr, ref.size, memOffset + offset);
}
ShortIndexedVertexBuffer::~ShortIndexedVertexBuffer() {
vkDestroyBuffer(Allocator::GetDefault()->getDevice(), vertex, ALLOC);
vkDestroyBuffer(Allocator::GetDefault()->getDevice(), index, ALLOC);
//NOTE: memory_ptr self destroy
}
std::unique_ptr<ShortIndexedVertexBuffer> ShortIndexedVertexBuffer::Create(const data_view vertices, const data_view indices) {
std::vector<vk::Buffer::info> tmp;
auto mem = createBuffers({{vertices.size, Usage::VERTEX, vertices}, {indices.size, Usage::INDEX, indices}}, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, tmp);
return std::unique_ptr<ShortIndexedVertexBuffer>(new ShortIndexedVertexBuffer(tmp.at(0).ref, tmp.at(1).ref, std::move(mem)));
}
BufferGroup::~BufferGroup() {
free();
}
void BufferGroup::allocate(const std::vector<Buffer::requirement> & reqs, bool writable) {
free();
memory = createBuffers(reqs, writable ? memory::HOST_EASILY_WRITABLE : (VkMemoryPropertyFlags)VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, refs);
}
void BufferGroup::free() {
for (const auto& buffer: refs) {
vkDestroyBuffer(Allocator::GetDefault()->getDevice(), buffer.ref, ALLOC);
}
refs.clear();
memory = memory::GetNull();
}
void BufferGroup::write(size_t i, const data_view view) {
memory->write(view.ptr, view.size, refs.at(i).offset);
}

View File

@ -0,0 +1,91 @@
#pragma once
#include "../../api/Buffers.hpp"
#include "Memory.hpp"
#include <vector>
namespace render::vk {
/// Self-container Vulkan buffer
class Buffer: public render::Buffer {
public:
~Buffer();
VkBuffer getRef() const { return ref; }
static void MakeDefault();
struct info {
VkBuffer ref = nullptr;
VkDeviceSize offset = 0;
};
protected:
Buffer(VkBuffer ref, memory::ptr mem):
ref(ref), memory(std::move(mem)) { }
VkBuffer ref;
memory::ptr memory;
};
/// Writable (aka Host visible) vulkan buffer
class WritableBuffer: public render::WritableBuffer, public Buffer {
public:
static std::unique_ptr<WritableBuffer> Create(size_t size, Usage usage = Usage::TRANSFER_SRC, const data_view write = data_view());
void write(const data_view, size_t offset) override;
void read(data_ref, size_t offset) override;
explicit operator bool() const { return ref && memory->ptr; }
protected:
WritableBuffer(VkBuffer ref, memory::ptr mem, VkDeviceSize memOffset = 0):
vk::Buffer(ref, std::move(mem)), memOffset(memOffset) { }
const VkDeviceSize memOffset;
};
/// Fast (aka Device local local) vulkan buffer
class FastBuffer: public render::FastBuffer, public Buffer {
public:
protected:
FastBuffer(VkBuffer ref, memory::ptr mem):
vk::Buffer(ref, std::move(mem)) { }
};
class ShortIndexedVertexBuffer: public render::ShortIndexedVertexBuffer {
public:
static std::unique_ptr<ShortIndexedVertexBuffer> Create(const data_view vertices, const data_view indices);
~ShortIndexedVertexBuffer();
VkBuffer getVertex() const { return vertex; }
VkBuffer getIndex() const { return index; }
explicit operator bool() const { return vertex && index; }
protected:
ShortIndexedVertexBuffer(VkBuffer vertex, VkBuffer index, memory::ptr mem):
vertex(vertex), index(index), memory(std::move(mem)) { }
VkBuffer vertex;
VkBuffer index;
memory::ptr memory;
};
class BufferGroup {
public:
BufferGroup(): memory(memory::GetNull()) { }
~BufferGroup();
void allocate(const std::vector<Buffer::requirement> &, bool writable);
VkBuffer at(size_t i) const { return refs.at(i).ref; }
/// Requires last allocate to be writable
void write(size_t, const data_view);
explicit operator bool() const { return memory && !refs.empty(); }
private:
void free();
std::vector<Buffer::info> refs;
memory::ptr memory;
};
}

View File

@ -0,0 +1,139 @@
#include "Images.hpp"
#include "Buffers.hpp"
#include "../Allocator.hpp"
#include "../../../../core/utils/logger.hpp"
using namespace render::vk;
Image::~Image() {
vkDestroyImage(Allocator::GetDefault()->getDevice(), ref, ALLOC);
}
Texture::~Texture() {
vkDestroySampler(Allocator::GetDefault()->getDevice(), sampler, ALLOC);
vkDestroyImageView(Allocator::GetDefault()->getDevice(), view, ALLOC);
}
memory::ptr createImage(const Image::requirement& req, VkMemoryPropertyFlags properties, const render::data_view view, Image::info& out) {
auto alloc = Allocator::GetDefault();
auto device = alloc->getDevice();
VkImageCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
info.imageType = VK_IMAGE_TYPE_2D;
info.extent.width = req.props.size.width;
info.extent.height = req.props.size.height;
info.extent.depth = 1;
info.mipLevels = req.props.mipMap; //TODO:
info.arrayLayers = 1;
info.format = static_cast<VkFormat>(req.props.format);
info.tiling = req.linear ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL;
info.initialLayout = static_cast<VkImageLayout>(req.layout);
info.usage = static_cast<VkImageUsageFlags>(req.usage);
info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.flags = 0;
if (view) {
info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (vkCreateImage(device, &info, ALLOC, &out.ref) != VK_SUCCESS) {
LOG_E("Failed to create image");
return memory::GetNull();
}
out.offset = 0;
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(device, out.ref, &memRequirements);
auto memory = alloc->allocate(memRequirements, properties, true);
if (!memory || vkBindImageMemory(device, out.ref, memory->ref, memory->offset) != VK_SUCCESS) {
LOG_E("Failed to allocate image memory");
return memory::GetNull();
}
if (view) {
if(auto staging = WritableBuffer::Create(view.size)) {
staging->write(view, 0);
alloc->transitionImageLayout(out.ref, info.format, info.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
alloc->copyBufferToImage(staging->getRef(), out.ref, info.extent.width, info.extent.height);
alloc->transitionImageLayout(out.ref, info.format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<VkImageLayout>(req.layout));
} else {
FATAL("Cannot allocate staging memory");
return memory::GetNull();
}
}
return memory;
}
//TODO: createImages
std::unique_ptr<Texture> Texture::LoadFromFile(const std::string& path, const sampling& props) {
auto device = Allocator::GetDefault()->getDevice();
std::vector<unsigned char> data;
auto header = [&] {
if (auto header = render::Image::Read(path, data)) {
return header.value();
}
FATAL("Cannot read texture");
}();
vk::Image::info img;
auto mem = createImage({header.size, header.mipMap, header.format, Layout::SHADER_READ_ONLY, Usage::SAMPLED, false},
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, data, img);
if(!mem) {
FATAL("Cannot create texture image");
}
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = img.ref;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = static_cast<VkFormat>(header.format);
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VkImageView view;
if (vkCreateImageView(device, &viewInfo, ALLOC, &view) != VK_SUCCESS) {
FATAL("Failed to create texture image view!");
}
VkSamplerCreateInfo samplerInfo{};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = props.magLinear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
samplerInfo.minFilter = props.minLinear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
const auto wrap = static_cast<VkSamplerAddressMode>(props.wrap);
samplerInfo.addressModeU = wrap;
samplerInfo.addressModeV = wrap;
samplerInfo.addressModeW = wrap;
if (Allocator::GetDefault()->getCapabilities().anisotropy && props.anisotropy > 0) {
samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = 1 << (props.anisotropy-1);
} else {
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.f;
}
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f; //TODO:
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
VkSampler sampler;
if (vkCreateSampler(device, &samplerInfo, ALLOC, &sampler) != VK_SUCCESS) {
FATAL("Failed to create texture sampler!");
}
return std::unique_ptr<Texture>(new Texture(sampler, view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, img.ref, std::move(mem)));
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "../../api/Images.hpp"
#include "Memory.hpp"
namespace render::vk {
class Image: public render::Image {
public:
virtual ~Image();
static void MakeDefault();
struct info {
VkImage ref = nullptr;
VkDeviceSize offset = 0;
};
protected:
Image(VkImage ref, memory::ptr mem):
ref(ref), memory(std::move(mem)) { }
VkImage ref;
memory::ptr memory;
};
class Texture: public render::Texture, Image {
public:
~Texture();
const VkDescriptorImageInfo &getDescriptor() const { return descriptor; }
static std::unique_ptr<Texture> LoadFromFile(const std::string&, const sampling&);
protected:
Texture(VkSampler sampler, VkImageView view, VkImageLayout layout, VkImage ref, memory::ptr memory):
vk::Image(ref, std::move(memory)), sampler(sampler), view(view), descriptor({sampler, view, layout}) { }
VkSampler sampler;
VkImageView view;
const VkDescriptorImageInfo descriptor;
};
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "../forward.hpp"
#include <memory>
namespace render::vk { struct Allocation; }
namespace render::vk::memory {
constexpr VkMemoryPropertyFlags HOST_EASILY_WRITABLE = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
struct area {
VkDeviceMemory ref;
VkDeviceSize size;
VkDeviceSize offset;
void *ptr = nullptr;
void write(const void*, VkDeviceSize size, VkDeviceSize offset = 0);
void read(void*, VkDeviceSize size, VkDeviceSize offset = 0);
};
class Deleter {
public:
Deleter(Allocation *owner): owner(owner) { }
void operator()(area *);
private:
Allocation *owner;
};
using ptr = std::unique_ptr<area, Deleter>;
ptr GetNull();
}

View File

@ -1,27 +0,0 @@
#pragma once
#include <volk.h>
namespace buffer::vk {
class MemoryArea {
private:
VkDeviceMemory ref;
size_t size;
std::vector<>
};
class Buffer {
private:
std::weak_ptr<>
VkBuffer ref;
size_t offset;
size_t size;
// Only is mappable
void* data = nullptr;
};
}

View File

@ -1,75 +0,0 @@
#include "texture.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cassert>
#include <cmath>
#include <iostream>
#include <array>
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII
VkResult render::vk::dds::readDDS(const std::string& imagepath, std::vector<unsigned char>& data, header_info& info) {
unsigned char header[124];
FILE *fp;
/* try to open the file */
fp = fopen(imagepath.c_str(), "rb");
if (fp == NULL){
printf("%s could not be opened.\n", imagepath.c_str()); getchar();
return VK_ERROR_INVALID_DEVICE_ADDRESS_EXT;
}
/* verify the type of file */
char filecode[4];
fread(filecode, 1, 4, fp);
if (strncmp(filecode, "DDS ", 4) != 0) {
fclose(fp);
return VK_ERROR_INVALID_DEVICE_ADDRESS_EXT;
}
/* get the surface desc */
fread(&header, 124, 1, fp);
info.height = *(unsigned int*)&(header[8 ]);
info.width = *(unsigned int*)&(header[12]);
unsigned int linearSize = *(unsigned int*)&(header[16]);
info.mipMapCount = *(unsigned int*)&(header[24]);
unsigned int fourCC = *(unsigned int*)&(header[80]);
/* how big is it going to be including all mipmaps? */
unsigned int bufsize = info.mipMapCount > 1 ? linearSize * 2 : linearSize;
data.resize(bufsize);
fread(data.data(), 1, bufsize, fp);
/* close the file pointer */
fclose(fp);
switch(fourCC)
{
case FOURCC_DXT1:
info.format = VK_FORMAT_BC1_RGBA_SRGB_BLOCK;
break;
case FOURCC_DXT3:
info.format = VK_FORMAT_BC2_SRGB_BLOCK;
break;
case FOURCC_DXT5:
info.format = VK_FORMAT_BC3_SRGB_BLOCK;
break;
//MAYBE: VK_FORMAT_BC6H_SFLOAT_BLOCK
default:
return VK_ERROR_FORMAT_NOT_SUPPORTED;
}
#if FALSE
glTextureParameteri(textureID, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTextureParameteri(textureID, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST);
TODO: glGenerateTextureMipmap(textureID);
#endif
return VK_SUCCESS;
}

View File

@ -1,16 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <array>
#include <volk.h>
namespace render::vk::dds {
struct header_info {
unsigned int height;
unsigned int width;
unsigned int mipMapCount;
VkFormat format;
};
VkResult readDDS(const std::string& imagepath, std::vector<unsigned char>& data, header_info& info);
}

View File

@ -10,7 +10,7 @@ struct state {
camera_pos position = camera_pos(voxel_pos(0), 1);
std::optional<world::Universe::ray_target> look_at = {};
std::shared_ptr<contouring::Abstract> contouring;
contouring::Abstract* contouring;
std::array<char, 256> console_buffer;
};

View File

@ -11,7 +11,7 @@
using namespace world::client;
DistantUniverse::DistantUniverse(const connection& ct, const options& opt): Universe(), peer(ct),
DistantUniverse::DistantUniverse(const connection& ct, const options& opt, const std::string& contouring): Universe(contouring), peer(ct),
loadDistance(opt.loadDistance), keepDistance(opt.keepDistance), serverDistance(0) { }
DistantUniverse::~DistantUniverse() { }

View File

@ -10,7 +10,7 @@ namespace world::client {
/// Whole universe container in network client
class DistantUniverse final: public Universe {
public:
DistantUniverse(const connection&, const options &);
DistantUniverse(const connection&, const options &, const std::string&);
~DistantUniverse();
void update(voxel_pos, float) override;

View File

@ -8,7 +8,7 @@
using namespace world::client;
LocalUniverse::LocalUniverse(server_handle *const handle): Universe(), handle(handle) {
LocalUniverse::LocalUniverse(server_handle *const handle, const std::string& contour): Universe(contour), handle(handle) {
assert(handle != nullptr);
for (auto i = 0; i < 500 && !handle->running; i++) {
LOG_D("Waiting local server startup");
@ -22,11 +22,6 @@ LocalUniverse::~LocalUniverse() {
handle->running = false;
}
void LocalUniverse::setContouring(const std::shared_ptr<contouring::Abstract>& ct) {
Universe::setContouring(ct);
last_chunk = chunk_pos(INT_MAX);
}
void LocalUniverse::update(voxel_pos pos, float) {
const auto cur_chunk = glm::divide(pos);
const auto chunkChange = cur_chunk != last_chunk;

View File

@ -7,14 +7,12 @@ namespace world::client {
/// Whole universe container in client with in-memory server
class LocalUniverse final: public Universe {
public:
LocalUniverse(server_handle *const handle);
LocalUniverse(server_handle *const handle, const std::string& contouring);
~LocalUniverse();
void update(voxel_pos pos, float deltaTime) override;
void emit(const action::packet &) override;
void setContouring(const std::shared_ptr<contouring::Abstract> &ct) override;
ray_result raycast(const geometry::Ray &ray) const override;
protected:

View File

@ -1,16 +1,12 @@
#include "Universe.hpp"
#include "../contouring/Dummy.hpp"
#include "../contouring/FlatDualMC.hpp"
using namespace world::client;
Universe::Universe(): world::Universe(), contouring(std::make_shared<contouring::Dummy>()) { }
Universe::Universe(const std::string& ct): world::Universe(), contouring(std::make_unique<contouring::FlatDualMC>(ct)) { }
Universe::~Universe() { }
void Universe::setContouring(const std::shared_ptr<contouring::Abstract>& ct) {
contouring = ct;
}
/*
void ServerUniverse::getEntitiesModels(const std::function<void(const std::vector<glm::mat4>&, buffer::Abstract *const)> &draw, const std::optional<geometry::Frustum> &frustum, const glm::llvec3 &offset, int density) {
std::vector<glm::mat4> mats;

View File

@ -12,7 +12,7 @@ namespace world::client {
/// Whole universe container in abstract client
class Universe: public world::Universe {
public:
Universe();
Universe(const std::string& contouring);
virtual ~Universe();
struct connection: net::connection {
@ -25,11 +25,9 @@ namespace world::client {
/// Send action to ServerUniverse
virtual void emit(const action::packet &) = 0;
/// Change contouring worker
virtual void setContouring(const std::shared_ptr<contouring::Abstract> &ct);
/// Get current contouring worker
std::shared_ptr<contouring::Abstract> getContouring() const {
return contouring;
contouring::Abstract* getContouring() const {
return contouring.get();
}
//TODO: move to ClientUniverse
@ -37,6 +35,6 @@ namespace world::client {
protected:
/// Contouring worker
std::shared_ptr<contouring::Abstract> contouring;
std::unique_ptr<contouring::Abstract> contouring;
};
}

View File

@ -6,13 +6,13 @@
#include "../../core/utils/logger.hpp"
namespace world::client {
std::unique_ptr<Universe> Load(const std::optional<Universe::connection>& ct, server_handle *const localHandle, const Universe::options &distOpts) {
std::unique_ptr<Universe> Load(const std::optional<Universe::connection>& ct, server_handle *const localHandle, const Universe::options &distOpts, const std::string& contouring) {
if(ct.has_value()) {
return std::make_unique<DistantUniverse>(ct.value(), distOpts);
return std::make_unique<DistantUniverse>(ct.value(), distOpts, contouring);
#ifndef STANDALONE
} else if(localHandle != nullptr) {
LOG_D("Using local universe");
return std::make_unique<LocalUniverse>(localHandle);
return std::make_unique<LocalUniverse>(localHandle, contouring);
#endif
} else {
FATAL("Must enable server.allow_local or define client.connection");

View File

@ -4,5 +4,5 @@
#include "../../core/server_handle.hpp"
namespace world::client {
std::unique_ptr<Universe> Load(const std::optional<Universe::connection>& ct, server_handle *const localHandle, const Universe::options& distOpts);
std::unique_ptr<Universe> Load(const std::optional<Universe::connection>& ct, server_handle *const localHandle, const Universe::options& distOpts, const std::string& contouring);
}

View File

@ -14,7 +14,7 @@ public:
void run();
server_handle *const getHandle() { return localHandle; }
server_handle *getHandle() { return localHandle; }
private:
config::server::options& options;