377 lines
12 KiB
C++
377 lines
12 KiB
C++
// AUTOGENERATED COPYRIGHT HEADER START
|
|
// Copyright (C) 2017-2023 Michael Fabian 'Xaymar' Dirks <info@xaymar.com>
|
|
// Copyright (C) 2022 lainon <GermanAizek@yandex.ru>
|
|
// AUTOGENERATED COPYRIGHT HEADER END
|
|
|
|
#include "gs-vertexbuffer.hpp"
|
|
#include "obs/gs/gs-helper.hpp"
|
|
|
|
#include "warning-disable.hpp"
|
|
#include <stdexcept>
|
|
#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<decltype(_positions)::element_type*>(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<decltype(_normals)::element_type*>(streamfx::util::memory::malloc_aligned(16, sizeof(vec3) * _capacity)), [](vec3* v) { streamfx::util::memory::free_aligned(v); });
|
|
_tangents = decltype(_tangents)(static_cast<decltype(_tangents)::element_type*>(streamfx::util::memory::malloc_aligned(16, sizeof(vec3) * _capacity)), [](vec3* v) { streamfx::util::memory::free_aligned(v); });
|
|
_colors = decltype(_colors)(static_cast<decltype(_colors)::element_type*>(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<decltype(_uvs)>::type;
|
|
_uvs[i] = tn(static_cast<tn::element_type*>(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<decltype(_data)::element_type>();
|
|
_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<decltype(_uv_layers)::element_type*>(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<uint32_t>(vbd->num), static_cast<uint8_t>(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<float*>(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<decltype(_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<decltype(_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<decltype(_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> streamfx::obs::gs::vertexbuffer::pool::instance()
|
|
{
|
|
static std::weak_ptr<streamfx::obs::gs::vertexbuffer::pool> winst;
|
|
static std::mutex mtx;
|
|
|
|
std::unique_lock<decltype(mtx)> lock(mtx);
|
|
auto instance = winst.lock();
|
|
if (!instance) {
|
|
instance = std::shared_ptr<streamfx::obs::gs::vertexbuffer::pool>(new streamfx::obs::gs::vertexbuffer::pool());
|
|
winst = instance;
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
static std::shared_ptr<streamfx::obs::gs::vertexbuffer::pool> loader_instance;
|
|
|
|
static auto loader = streamfx::component(
|
|
"core::gs::vertexbuffer",
|
|
[]() { // Initializer
|
|
loader_instance = streamfx::obs::gs::vertexbuffer::pool::instance();
|
|
},
|
|
[]() { // Finalizer
|
|
loader_instance.reset();
|
|
},
|
|
{});
|