obs-StreamFX/source/gs-vertexbuffer.cpp
Michael Fabian 'Xaymar' Dirks c7cfbff7dd gs-vertexbuffer: Workaround for a foreign deallocation bug in libobs
The graphics subsystem in OBS is freeing memory it didn't allocate, resulting in stack/heap corruption and other kinds of messy situations. The official "fix" for this is to use bmalloc, but then you lose any kind of ability to re-use the same buffer for multiple vertex buffers or update on the fly without adjusting code that is possibly outside of your control (such as in libraries).

This works around the issues by "patching" the gs_vertexbuffer object to no longer hold a reference to the gs_vb_data object. Currently only D3D11 is supported for this kind of hack and it might break in a future obs-studio release.

PR fixing this Issue: https://github.com/jp9000/obs-studio/pull/993
2017-11-03 00:13:07 +01:00

137 lines
4.3 KiB
C++

/*
* 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.h"
#include <stdexcept>
extern "C" {
#pragma warning( push )
#pragma warning( disable: 4201 )
#include <libobs/obs.h>
#pragma warning( pop )
}
const uint32_t defaultMaximumVertices = 65535u;
GS::VertexBuffer::VertexBuffer(uint32_t maximumVertices) {
m_maximumVertices = maximumVertices;
m_uvwLayers = MAXIMUM_UVW_LAYERS;
// Reserve Space
m_vertexbufferdata.num = m_maximumVertices;
m_data.positions.resize(m_maximumVertices);
m_vertexbufferdata.points = m_data.positions.data();
m_data.normals.resize(m_maximumVertices);
m_vertexbufferdata.normals = m_data.normals.data();
m_data.tangents.resize(m_maximumVertices);
m_vertexbufferdata.tangents = m_data.tangents.data();
m_data.colors.resize(m_maximumVertices);
m_vertexbufferdata.colors = m_data.colors.data();
m_vertexbufferdata.num_tex = m_uvwLayers;
m_data.uvws.resize(m_uvwLayers);
m_data.uvwdata.resize(m_uvwLayers);
for (uint32_t n = 0; n < m_uvwLayers; n++) {
m_data.uvws[n].resize(m_maximumVertices);
m_data.uvwdata[n].width = 4;
m_data.uvwdata[n].array = m_data.uvws[n].data();
}
m_vertexbufferdata.tvarray = m_data.uvwdata.data();
// Allocate GPU
obs_enter_graphics();
m_vertexbuffer = gs_vertexbuffer_create(&m_vertexbufferdata, GS_DYNAMIC);
obs_leave_graphics();
if (!m_vertexbuffer) {
throw std::runtime_error("Failed to create vertex buffer.");
}
}
GS::VertexBuffer::VertexBuffer(gs_vertbuffer_t* vb) {
m_vertexbuffer = vb;
}
GS::VertexBuffer::VertexBuffer() : VertexBuffer(defaultMaximumVertices) {}
GS::VertexBuffer::VertexBuffer(std::vector<Vertex>& other) : VertexBuffer((uint32_t)other.capacity()) {
std::copy(other.begin(), other.end(), this->end());
}
GS::VertexBuffer::VertexBuffer(VertexBuffer& other) : VertexBuffer(other.m_maximumVertices) {
std::copy(other.begin(), other.end(), this->end());
}
GS::VertexBuffer::~VertexBuffer() {
if (m_vertexbuffer) {
std::memset(&m_vertexbufferdata, 0, sizeof(m_vertexbufferdata));
// Workaround API stupidity in obs-studio.
// See PR: https://github.com/jp9000/obs-studio/pull/993
if (gs_get_device_type() == GS_DEVICE_DIRECT3D_11) {
#ifdef _DEBUG
*(reinterpret_cast<intptr_t*>(m_vertexbuffer) + 14) = 0;
#else
*(reinterpret_cast<intptr_t*>(m_vertexbuffer) + 13) = 0;
#endif
} else {
//*(reinterpret_cast<intptr_t*>(m_vertexbuffer)+14) = 0;
}
obs_enter_graphics();
gs_vertexbuffer_destroy(m_vertexbuffer);
obs_leave_graphics();
}
m_vertexbuffer = nullptr;
}
void GS::VertexBuffer::set_uv_layers(uint32_t layers) {
m_uvwLayers = layers;
}
uint32_t GS::VertexBuffer::uv_layers() {
return m_uvwLayers;
}
gs_vertbuffer_t* GS::VertexBuffer::get(bool refreshGPU) {
if (refreshGPU) {
if (size() > m_maximumVertices)
throw std::runtime_error("Too many vertices in Vertex Buffer.");
for (size_t vertexIdx = 0; vertexIdx < size(); vertexIdx++) {
Vertex& v = this->at(vertexIdx);
vec3_copy(&m_data.positions[vertexIdx], &(v.position));
vec3_copy(&m_data.normals[vertexIdx], &(v.normal));
vec3_copy(&m_data.tangents[vertexIdx], &(v.tangent));
for (size_t texcoordIdx = 0; texcoordIdx < m_uvwLayers; texcoordIdx++) {
vec4_copy(&m_data.uvws[texcoordIdx][vertexIdx], &(v.uv[texcoordIdx]));
}
m_data.colors[vertexIdx] = v.color;
}
m_vertexbufferdata.num = size();
m_vertexbufferdata.num_tex = m_uvwLayers;
obs_enter_graphics();
gs_vertexbuffer_flush(m_vertexbuffer);
obs_leave_graphics();
}
return m_vertexbuffer;
}
gs_vertbuffer_t* GS::VertexBuffer::get() {
return get(true);
}