// AUTOGENERATED COPYRIGHT HEADER START // Copyright (C) 2017-2023 Michael Fabian 'Xaymar' Dirks // Copyright (C) 2022 lainon // AUTOGENERATED COPYRIGHT HEADER END #include "gs-vertexbuffer.hpp" #include "obs/gs/gs-helper.hpp" #include "warning-disable.hpp" #include #include "warning-enable.hpp" void streamfx::obs::gs::vertexbuffer::initialize(uint32_t capacity, uint8_t layers) { finalize(); if (capacity > MAXIMUM_VERTICES) { throw std::out_of_range("capacity"); } if (layers > MAXIMUM_UVW_LAYERS) { throw std::out_of_range("layers"); } // Allocate necessary memory for storing information. _positions = decltype(_positions)(static_cast(streamfx::util::memory::malloc_aligned(16, sizeof(decltype(_positions)::element_type) * _capacity)), [](decltype(_positions)::element_type* v) { streamfx::util::memory::free_aligned(v); }); _normals = decltype(_normals)(static_cast(streamfx::util::memory::malloc_aligned(16, sizeof(vec3) * _capacity)), [](vec3* v) { streamfx::util::memory::free_aligned(v); }); _tangents = decltype(_tangents)(static_cast(streamfx::util::memory::malloc_aligned(16, sizeof(vec3) * _capacity)), [](vec3* v) { streamfx::util::memory::free_aligned(v); }); _colors = decltype(_colors)(static_cast(streamfx::util::memory::malloc_aligned(16, sizeof(decltype(_colors)::element_type) * _capacity)), [](decltype(_colors)::element_type* v) { streamfx::util::memory::free_aligned(v); }); if (_layers) { for (auto i = 0; i < _layers; i++) { using tn = std::remove_all_extents::type; _uvs[i] = tn(static_cast(streamfx::util::memory::malloc_aligned(16, sizeof(tn::element_type) * _capacity)), [](tn::element_type* v) { streamfx::util::memory::free_aligned(v); }); } } // Allocate memory for data. _data = std::make_unique(); _data->num = _capacity; _data->num_tex = _layers; _data->points = _positions.get(); _data->normals = _normals.get(); _data->tangents = _tangents.get(); _data->colors = _colors.get(); if (_layers == 0) { _data->tvarray = nullptr; } else { _uv_layers = decltype(_uv_layers)(static_cast(streamfx::util::memory::malloc_aligned(16, sizeof(decltype(_uv_layers)::element_type) * _capacity)), [](decltype(_uv_layers)::element_type* v) { streamfx::util::memory::free_aligned(v); }); _data->tvarray = _uv_layers.get(); for (auto i = 0; i < _layers; i++) { // This insanity is sponsored by C. It works, get over it. _uv_layers.get()[i].array = _uvs[i].get(); _uv_layers.get()[i].width = 4; } } // Allocate actual GPU vertex buffer. { auto gctx = streamfx::obs::gs::context(); _buffer = decltype(_buffer)(gs_vertexbuffer_create(_data.get(), GS_DYNAMIC | GS_DUP_BUFFER), [](gs_vertbuffer_t* v) { auto gctx = streamfx::obs::gs::context(); gs_vertexbuffer_destroy(v); }); _obs_data = gs_vertexbuffer_get_data(_buffer.get()); } if (!_buffer) { throw std::runtime_error("Failed to create vertex buffer."); } } void streamfx::obs::gs::vertexbuffer::finalize() { // Free data _positions.reset(); _normals.reset(); _tangents.reset(); _colors.reset(); _uv_layers.reset(); for (std::size_t n = 0; n < _layers; n++) { _uvs[n].reset(); } _buffer.reset(); _data.reset(); } streamfx::obs::gs::vertexbuffer::~vertexbuffer() { finalize(); } streamfx::obs::gs::vertexbuffer::vertexbuffer(uint32_t size, uint8_t layers) : _capacity(size), _size(size), _layers(layers), _buffer(nullptr), _data(nullptr), _positions(nullptr), _normals(nullptr), _tangents(nullptr), _colors(nullptr), _uv_layers(nullptr), _uvs(), _obs_data(nullptr) { initialize(_size, _layers); } streamfx::obs::gs::vertexbuffer::vertexbuffer(gs_vertbuffer_t* vb) : _capacity(0), _size(0), _layers(0), _buffer(nullptr), _data(nullptr), _positions(nullptr), _normals(nullptr), _tangents(nullptr), _colors(nullptr), _uv_layers(nullptr), _uvs(), _obs_data(nullptr) { auto gctx = streamfx::obs::gs::context(); gs_vb_data* vbd = gs_vertexbuffer_get_data(vb); if (!vbd) throw std::runtime_error("vertex buffer with no data"); initialize(static_cast(vbd->num), static_cast(vbd->num_tex)); if (_positions && vbd->points) memcpy(_positions.get(), vbd->points, vbd->num * sizeof(vec3)); if (_normals && vbd->normals) memcpy(_normals.get(), vbd->normals, vbd->num * sizeof(vec3)); if (_tangents && vbd->tangents) memcpy(_tangents.get(), vbd->tangents, vbd->num * sizeof(vec3)); if (_colors && vbd->colors) memcpy(_colors.get(), vbd->colors, vbd->num * sizeof(uint32_t)); if (vbd->tvarray != nullptr) { for (std::size_t n = 0; n < vbd->num_tex; n++) { if (vbd->tvarray[n].array != nullptr && vbd->tvarray[n].width <= 4 && vbd->tvarray[n].width > 0) { if (vbd->tvarray[n].width == 4) { memcpy(_uvs[n].get(), vbd->tvarray[n].array, vbd->num * sizeof(vec4)); } else if (vbd->tvarray[n].width < 4) { for (std::size_t idx = 0; idx < _capacity; idx++) { float* mem = reinterpret_cast(vbd->tvarray[n].array) + (idx * vbd->tvarray[n].width); memset(&_uvs[n].get()[idx], 0, sizeof(vec4)); memcpy(&_uvs[n].get()[idx], mem, vbd->tvarray[n].width); } } } } } } streamfx::obs::gs::vertexbuffer::vertexbuffer(vertexbuffer const& other) : vertexbuffer(other._capacity, other._layers) { // Copy Constructor memcpy(_positions.get(), other._positions.get(), _capacity * sizeof(vec3)); memcpy(_normals.get(), other._normals.get(), _capacity * sizeof(vec3)); memcpy(_tangents.get(), other._tangents.get(), _capacity * sizeof(vec3)); memcpy(_colors.get(), other._colors.get(), _capacity * sizeof(vec3)); for (std::size_t n = 0; n < other._layers; n++) { memcpy(_uvs[n].get(), other._uvs[n].get(), _capacity * sizeof(vec4)); } } void streamfx::obs::gs::vertexbuffer::operator=(vertexbuffer const& other) { // Copy operator initialize(other._capacity, other._layers); _size = other._size; // Copy actual data over. memcpy(_positions.get(), other._positions.get(), other._capacity * sizeof(vec3)); memcpy(_normals.get(), other._normals.get(), other._capacity * sizeof(vec3)); memcpy(_tangents.get(), other._tangents.get(), other._capacity * sizeof(vec3)); memcpy(_colors.get(), other._colors.get(), other._capacity * sizeof(uint32_t)); memcpy(_uv_layers.get(), other._uv_layers.get(), sizeof(gs_tvertarray)); for (std::size_t n = 0; n < other._layers; n++) { memcpy(_uvs[n].get(), other._uvs[n].get(), _capacity * sizeof(vec4)); } } streamfx::obs::gs::vertexbuffer::vertexbuffer(vertexbuffer const&& other) noexcept { // Move Constructor _capacity = other._capacity; _size = other._size; _layers = other._layers; _buffer = other._buffer; _data = other._data; _positions = other._positions; _normals = other._normals; _tangents = other._tangents; _colors = other._colors; _uv_layers = other._uv_layers; for (std::size_t n = 0; n < MAXIMUM_UVW_LAYERS; n++) { _uvs[n] = other._uvs[n]; } _obs_data = other._obs_data; } void streamfx::obs::gs::vertexbuffer::operator=(vertexbuffer const&& other) noexcept { // Move Assignment finalize(); _capacity = other._capacity; _size = other._size; _layers = other._layers; _buffer = other._buffer; _data = other._data; _positions = other._positions; _normals = other._normals; _tangents = other._tangents; _colors = other._colors; _uv_layers = other._uv_layers; for (std::size_t n = 0; n < MAXIMUM_UVW_LAYERS; n++) { _uvs[n] = other._uvs[n]; } _obs_data = other._obs_data; } void streamfx::obs::gs::vertexbuffer::resize(uint32_t size) { if (size > _capacity) { throw std::out_of_range("size larger than capacity"); } _size = size; } uint32_t streamfx::obs::gs::vertexbuffer::size() { return _size; } uint32_t streamfx::obs::gs::vertexbuffer::capacity() { return _capacity; } bool streamfx::obs::gs::vertexbuffer::empty() { return _size == 0; } const streamfx::obs::gs::vertex streamfx::obs::gs::vertexbuffer::at(uint32_t idx) { if (idx >= _size) { throw std::out_of_range("idx out of range"); } streamfx::obs::gs::vertex vtx(&_positions.get()[idx], &_normals.get()[idx], &_tangents.get()[idx], &_colors.get()[idx], nullptr); for (std::size_t n = 0; n < _layers; n++) { vtx.uv[n] = &_uvs[n].get()[idx]; } return vtx; } const streamfx::obs::gs::vertex streamfx::obs::gs::vertexbuffer::operator[](uint32_t const pos) { return at(pos); } void streamfx::obs::gs::vertexbuffer::set_uv_layers(uint8_t layers) { _layers = layers; } uint8_t streamfx::obs::gs::vertexbuffer::get_uv_layers() { return _layers; } vec3* streamfx::obs::gs::vertexbuffer::get_positions() { return _positions.get(); } vec3* streamfx::obs::gs::vertexbuffer::get_normals() { return _normals.get(); } vec3* streamfx::obs::gs::vertexbuffer::get_tangents() { return _tangents.get(); } uint32_t* streamfx::obs::gs::vertexbuffer::get_colors() { return _colors.get(); } vec4* streamfx::obs::gs::vertexbuffer::get_uv_layer(uint8_t idx) { if (idx >= _layers) { throw std::out_of_range("idx out of range"); } return _uvs[idx].get(); } gs_vertbuffer_t* streamfx::obs::gs::vertexbuffer::update(bool refreshGPU) { if (refreshGPU) { auto gctx = streamfx::obs::gs::context(); gs_vertexbuffer_flush_direct(_buffer.get(), _data.get()); _obs_data = gs_vertexbuffer_get_data(_buffer.get()); } return _buffer.get(); } gs_vertbuffer_t* streamfx::obs::gs::vertexbuffer::update() { return update(true); } streamfx::obs::gs::vertexbuffer::pool::pool() {} streamfx::obs::gs::vertexbuffer::pool::~pool() {} void streamfx::obs::gs::vertexbuffer::pool::release(_underlying* value) { cleanup(); std::unique_lock lock{_lock}; auto key = _key{value->capacity(), value->get_uv_layers()}; auto rvalue = _value{_tracked{value}, std::chrono::high_resolution_clock::now()}; if (auto kv = _pool.find(key); kv != _pool.end()) { kv->second.push_front(rvalue); } else { _pool.emplace(key, std::list{rvalue}); } } streamfx::obs::gs::vertexbuffer::pool::_tracked streamfx::obs::gs::vertexbuffer::pool::acquire(uint32_t capacity, uint8_t layers) { cleanup(); std::unique_lock lock{_lock}; _underlying* ptr = nullptr; if (auto kv = _pool.find(_key{capacity, layers}); kv != _pool.end()) { // Found an existing list and item. auto value = kv->second.front(); kv->second.pop_front(); ptr = new streamfx::obs::gs::vertexbuffer(std::move(*value.first.get())); // Move Construction should do the trick } else { ptr = new streamfx::obs::gs::vertexbuffer(capacity, layers); } return _tracked(ptr, [](_underlying* v) { streamfx::obs::gs::vertexbuffer::pool::instance()->release(v); }); } void streamfx::obs::gs::vertexbuffer::pool::cleanup() { auto time = std::chrono::high_resolution_clock::now(); std::unique_lock lock{_lock}; for (auto kv : _pool) { std::erase_if(kv.second, [&time](const auto& value) { return ((time - value.second) > std::chrono::seconds(1)); }); } std::erase_if(_pool, [](const auto& value) { return value.second.empty(); }); } std::shared_ptr streamfx::obs::gs::vertexbuffer::pool::instance() { static std::weak_ptr winst; static std::mutex mtx; std::unique_lock lock(mtx); auto instance = winst.lock(); if (!instance) { instance = std::shared_ptr(new streamfx::obs::gs::vertexbuffer::pool()); winst = instance; } return instance; } static std::shared_ptr loader_instance; static auto loader = streamfx::component( "core::gs::vertexbuffer", []() { // Initializer loader_instance = streamfx::obs::gs::vertexbuffer::pool::instance(); }, []() { // Finalizer loader_instance.reset(); }, {});