/* * Modern effects for a modern Streamer * Copyright (C) 2017 Michael Fabian Dirks * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "gs-vertexbuffer.hpp" #include #include "util-memory.hpp" // OBS #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4201) #endif #include #ifdef _MSC_VER #pragma warning(pop) #endif gs::vertex_buffer::~vertex_buffer() { if (m_positions) { util::free_aligned(m_positions); m_positions = nullptr; } if (m_normals) { util::free_aligned(m_normals); m_normals = nullptr; } if (m_tangents) { util::free_aligned(m_tangents); m_tangents = nullptr; } if (m_colors) { util::free_aligned(m_colors); m_colors = nullptr; } for (size_t n = 0; n < m_layers; n++) { if (m_uvs[n]) { util::free_aligned(m_uvs[n]); m_uvs[n] = nullptr; } } if (m_layerdata) { util::free_aligned(m_layerdata); m_layerdata = nullptr; } if (m_vertexbufferdata) { memset(m_vertexbufferdata, 0, sizeof(gs_vb_data)); if (!m_vertexbuffer) { gs_vbdata_destroy(m_vertexbufferdata); m_vertexbufferdata = nullptr; } } if (m_vertexbuffer) { obs_enter_graphics(); gs_vertexbuffer_destroy(m_vertexbuffer); obs_leave_graphics(); m_vertexbuffer = nullptr; } } gs::vertex_buffer::vertex_buffer() : vertex_buffer(MAXIMUM_VERTICES, MAXIMUM_UVW_LAYERS) {} gs::vertex_buffer::vertex_buffer(uint32_t vertices) : vertex_buffer(vertices, MAXIMUM_UVW_LAYERS) {} gs::vertex_buffer::vertex_buffer(uint32_t vertices, uint8_t uvlayers) : m_size(vertices), m_capacity(vertices), m_layers(uvlayers), m_positions(nullptr), m_normals(nullptr), m_tangents(nullptr), m_colors(nullptr), m_vertexbufferdata(nullptr), m_vertexbuffer(nullptr), m_layerdata(nullptr) { if (vertices > MAXIMUM_VERTICES) { throw std::out_of_range("vertices out of range"); } if (uvlayers > MAXIMUM_UVW_LAYERS) { throw std::out_of_range("uvlayers out of range"); } // Allocate memory for data. m_vertexbufferdata = gs_vbdata_create(); m_vertexbufferdata->num = m_capacity; m_vertexbufferdata->points = m_positions = (vec3*)util::malloc_aligned(16, sizeof(vec3) * m_capacity); m_vertexbufferdata->normals = m_normals = (vec3*)util::malloc_aligned(16, sizeof(vec3) * m_capacity); m_vertexbufferdata->tangents = m_tangents = (vec3*)util::malloc_aligned(16, sizeof(vec3) * m_capacity); m_vertexbufferdata->colors = m_colors = (uint32_t*)util::malloc_aligned(16, sizeof(uint32_t) * m_capacity); // cppcheck-suppress memsetClassFloat memset(m_positions, 0, sizeof(vec3) * m_capacity); // cppcheck-suppress memsetClassFloat memset(m_normals, 0, sizeof(vec3) * m_capacity); // cppcheck-suppress memsetClassFloat memset(m_tangents, 0, sizeof(vec3) * m_capacity); memset(m_colors, 0, sizeof(uint32_t) * m_capacity); m_vertexbufferdata->num_tex = m_layers; if (m_layers > 0) { m_vertexbufferdata->tvarray = m_layerdata = (gs_tvertarray*)util::malloc_aligned(16, sizeof(gs_tvertarray) * m_layers); for (size_t n = 0; n < m_layers; n++) { m_layerdata[n].array = m_uvs[n] = (vec4*)util::malloc_aligned(16, sizeof(vec4) * m_capacity); m_layerdata[n].width = 4; memset(m_uvs[n], 0, sizeof(vec4) * m_capacity); } } else { m_vertexbufferdata->tvarray = nullptr; } // Allocate GPU obs_enter_graphics(); m_vertexbuffer = gs_vertexbuffer_create(m_vertexbufferdata, GS_DYNAMIC); memset(m_vertexbufferdata, 0, sizeof(gs_vb_data)); m_vertexbufferdata->num = m_capacity; m_vertexbufferdata->num_tex = m_layers; obs_leave_graphics(); if (!m_vertexbuffer) { throw std::runtime_error("Failed to create vertex buffer."); } } // cppcheck-suppress uninitMemberVar gs::vertex_buffer::vertex_buffer(gs_vertbuffer_t* vb) { obs_enter_graphics(); gs_vb_data* vbd = gs_vertexbuffer_get_data(vb); if (!vbd) throw std::runtime_error("vertex buffer with no data"); vertex_buffer((uint32_t)vbd->num); this->set_uv_layers((uint32_t)vbd->num_tex); if (vbd->points != nullptr) memcpy(m_positions, vbd->points, vbd->num * sizeof(vec3)); if (vbd->normals != nullptr) memcpy(m_normals, vbd->normals, vbd->num * sizeof(vec3)); if (vbd->tangents != nullptr) memcpy(m_tangents, vbd->tangents, vbd->num * sizeof(vec3)); if (vbd->colors != nullptr) memcpy(m_colors, vbd->colors, vbd->num * sizeof(uint32_t)); if (vbd->tvarray != nullptr) { for (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(m_uvs[n], vbd->tvarray[n].array, vbd->num * sizeof(vec4)); } else { for (size_t idx = 0; idx < m_capacity; idx++) { float* mem = reinterpret_cast(vbd->tvarray[n].array) + (idx * vbd->tvarray[n].width); // cppcheck-suppress memsetClassFloat memset(&m_uvs[n][idx], 0, sizeof(vec4)); memcpy(&m_uvs[n][idx], mem, vbd->tvarray[n].width); } } } } } obs_leave_graphics(); } // cppcheck-suppress uninitMemberVar gs::vertex_buffer::vertex_buffer(vertex_buffer const& other) : vertex_buffer(other.m_capacity) { // Copy Constructor memcpy(m_positions, other.m_positions, m_capacity * sizeof(vec3)); memcpy(m_normals, other.m_normals, m_capacity * sizeof(vec3)); memcpy(m_tangents, other.m_tangents, m_capacity * sizeof(vec3)); memcpy(m_colors, other.m_colors, m_capacity * sizeof(vec3)); for (size_t n = 0; n < MAXIMUM_UVW_LAYERS; n++) { memcpy(m_uvs[n], other.m_uvs[n], m_capacity * sizeof(vec3)); } } gs::vertex_buffer::vertex_buffer(vertex_buffer const&& other) { // Move Constructor m_capacity = other.m_capacity; m_size = other.m_size; m_layers = other.m_layers; m_positions = other.m_positions; m_normals = other.m_normals; m_tangents = other.m_tangents; for (size_t n = 0; n < MAXIMUM_UVW_LAYERS; n++) { m_uvs[n] = other.m_uvs[n]; } m_vertexbufferdata = other.m_vertexbufferdata; m_vertexbuffer = other.m_vertexbuffer; m_layerdata = other.m_layerdata; } void gs::vertex_buffer::operator=(vertex_buffer const&& other) { // Move Assignment /// First self-destruct (semi-destruct itself). if (m_positions) { util::free_aligned(m_positions); m_positions = nullptr; } if (m_normals) { util::free_aligned(m_normals); m_normals = nullptr; } if (m_tangents) { util::free_aligned(m_tangents); m_tangents = nullptr; } if (m_colors) { util::free_aligned(m_colors); m_colors = nullptr; } for (size_t n = 0; n < MAXIMUM_UVW_LAYERS; n++) { if (m_uvs[n]) { util::free_aligned(m_uvs[n]); m_uvs[n] = nullptr; } } if (m_layerdata) { util::free_aligned(m_layerdata); m_layerdata = nullptr; } if (m_vertexbufferdata) { memset(m_vertexbufferdata, 0, sizeof(gs_vb_data)); if (!m_vertexbuffer) { gs_vbdata_destroy(m_vertexbufferdata); m_vertexbufferdata = nullptr; } } if (m_vertexbuffer) { obs_enter_graphics(); gs_vertexbuffer_destroy(m_vertexbuffer); obs_leave_graphics(); m_vertexbuffer = nullptr; } /// Then assign new values. m_capacity = other.m_capacity; m_size = other.m_size; m_layers = other.m_layers; m_positions = other.m_positions; m_normals = other.m_normals; m_tangents = other.m_tangents; for (size_t n = 0; n < MAXIMUM_UVW_LAYERS; n++) { m_uvs[n] = other.m_uvs[n]; } m_vertexbufferdata = other.m_vertexbufferdata; m_vertexbuffer = other.m_vertexbuffer; m_layerdata = other.m_layerdata; } void gs::vertex_buffer::resize(uint32_t new_size) { if (new_size > m_capacity) { throw std::out_of_range("new_size out of range"); } m_size = new_size; } uint32_t gs::vertex_buffer::size() { return m_size; } bool gs::vertex_buffer::empty() { return m_size == 0; } const gs::vertex gs::vertex_buffer::at(uint32_t idx) { if ((idx < 0) || (idx >= m_size)) { throw std::out_of_range("idx out of range"); } gs::vertex vtx(&m_positions[idx], &m_normals[idx], &m_tangents[idx], &m_colors[idx], nullptr); for (size_t n = 0; n < m_layers; n++) { vtx.uv[n] = &m_uvs[n][idx]; } return vtx; } const gs::vertex gs::vertex_buffer::operator[](uint32_t const pos) { return at(pos); } void gs::vertex_buffer::set_uv_layers(uint32_t layers) { m_layers = layers; } uint32_t gs::vertex_buffer::get_uv_layers() { return m_layers; } vec3* gs::vertex_buffer::get_positions() { return m_positions; } vec3* gs::vertex_buffer::get_normals() { return m_normals; } vec3* gs::vertex_buffer::get_tangents() { return m_tangents; } uint32_t* gs::vertex_buffer::get_colors() { return m_colors; } vec4* gs::vertex_buffer::get_uv_layer(size_t idx) { if ((idx < 0) || (idx >= m_layers)) { throw std::out_of_range("idx out of range"); } return m_uvs[idx]; } gs_vertbuffer_t* gs::vertex_buffer::update(bool refreshGPU) { if (!refreshGPU) return m_vertexbuffer; if (m_size > m_capacity) throw std::out_of_range("size is larger than capacity"); // Update VertexBuffer data. obs_enter_graphics(); m_vertexbufferdata = gs_vertexbuffer_get_data(m_vertexbuffer); memset(m_vertexbufferdata, 0, sizeof(gs_vb_data)); m_vertexbufferdata->num = m_capacity; m_vertexbufferdata->points = m_positions; m_vertexbufferdata->normals = m_normals; m_vertexbufferdata->tangents = m_tangents; m_vertexbufferdata->colors = m_colors; m_vertexbufferdata->num_tex = m_layers; m_vertexbufferdata->tvarray = m_layerdata; for (size_t n = 0; n < m_layers; n++) { m_layerdata[n].array = m_uvs[n]; m_layerdata[n].width = 4; } // Update GPU gs_vertexbuffer_flush(m_vertexbuffer); obs_leave_graphics(); // WORKAROUND: OBS Studio 20.x and below incorrectly deletes data that it doesn't own. memset(m_vertexbufferdata, 0, sizeof(gs_vb_data)); m_vertexbufferdata->num = m_capacity; m_vertexbufferdata->num_tex = m_layers; for (uint32_t n = 0; n < m_layers; n++) { m_layerdata[n].width = 4; } return m_vertexbuffer; } gs_vertbuffer_t* gs::vertex_buffer::update() { return update(true); }