mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-12 10:49:59 +01:00
Graphics Card Memory check before allocating FBO
Improved warning when allocating FrameBuffer. Avoid allocating FrameBuffer when buffering delay in Clone Source if we risk to consume all RAM in graphics card.
This commit is contained in:
@@ -35,7 +35,7 @@ const char* CloneSource::cloning_provenance_label[2] = { "Original texture", "Po
|
|||||||
|
|
||||||
|
|
||||||
CloneSource::CloneSource(Source *origin, uint64_t id) : Source(id), origin_(origin), cloningsurface_(nullptr),
|
CloneSource::CloneSource(Source *origin, uint64_t id) : Source(id), origin_(origin), cloningsurface_(nullptr),
|
||||||
to_delete_(nullptr), delay_(0.0), paused_(false), provenance_(CLONE_TEXTURE)
|
garbage_image_(nullptr), delay_(0.0), paused_(false), provenance_(CLONE_TEXTURE)
|
||||||
{
|
{
|
||||||
// initial name copies the origin name: diplucates are namanged in session
|
// initial name copies the origin name: diplucates are namanged in session
|
||||||
name_ = origin->name();
|
name_ = origin->name();
|
||||||
@@ -152,18 +152,19 @@ void CloneSource::update(float dt)
|
|||||||
if (!paused_ && cloningsurface_ != nullptr) {
|
if (!paused_ && cloningsurface_ != nullptr) {
|
||||||
|
|
||||||
// if temporary FBO was pending to be deleted, delete it now
|
// if temporary FBO was pending to be deleted, delete it now
|
||||||
if (to_delete_ != nullptr) {
|
if (garbage_image_ != nullptr) {
|
||||||
delete to_delete_;
|
delete garbage_image_;
|
||||||
to_delete_ = nullptr;
|
garbage_image_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// What time is it?
|
// What time is it?
|
||||||
double now = g_timer_elapsed (timer_, NULL);
|
double now = g_timer_elapsed (timer_, NULL);
|
||||||
|
|
||||||
// is the total buffer of images longer than delay ?
|
// is the total buffer of images longer than delay ?
|
||||||
if ( !images_.empty() && now - elapsed_.front() > delay_ ) {
|
if ( !images_.empty() && now - elapsed_.front() > delay_ )
|
||||||
|
{
|
||||||
// remember FBO to be reused if needed (see below) or deleted later
|
// remember FBO to be reused if needed (see below) or deleted later
|
||||||
to_delete_ = images_.front();
|
garbage_image_ = images_.front();
|
||||||
// remove element from queue (front)
|
// remove element from queue (front)
|
||||||
images_.pop();
|
images_.pop();
|
||||||
elapsed_.pop();
|
elapsed_.pop();
|
||||||
@@ -171,16 +172,24 @@ void CloneSource::update(float dt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add image to queue to accumulate buffer images until delay reached
|
// add image to queue to accumulate buffer images until delay reached
|
||||||
if ( images_.empty() || now - elapsed_.front() < delay_ + (dt * 0.001) ) {
|
if ( images_.empty() || now - elapsed_.front() < delay_ + (dt * 0.001) )
|
||||||
// create a FBO if none can be reused (from above)
|
{
|
||||||
if (to_delete_ == nullptr)
|
// create a FBO if none can be reused (from above) and test for RAM in GPU
|
||||||
to_delete_ = new FrameBuffer( origin_->frame()->resolution(), origin_->frame()->use_alpha() );
|
if (garbage_image_ == nullptr && ( images_.empty() || FrameBuffer::shouldHaveEnoughMemory(origin_->frame()->resolution(), origin_->frame()->use_alpha()) ) )
|
||||||
|
garbage_image_ = new FrameBuffer( origin_->frame()->resolution(), origin_->frame()->use_alpha() );
|
||||||
|
// no image available
|
||||||
|
if (garbage_image_ != nullptr) {
|
||||||
// add element to queue (back)
|
// add element to queue (back)
|
||||||
images_.push( to_delete_ );
|
images_.push( garbage_image_ );
|
||||||
elapsed_.push( now );
|
elapsed_.push( now );
|
||||||
timestamps_.push( origin_->playtime() );
|
timestamps_.push( origin_->playtime() );
|
||||||
// to_delete_ FBO is now used, should not be deleted
|
// garbage_image_ FBO is now used, it should not be deleted
|
||||||
to_delete_ = nullptr;
|
garbage_image_ = nullptr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delay_ = now - elapsed_.front() - (dt * 0.001);
|
||||||
|
Log::Warning("Cannot satisfy delay for Clone %s: not enough RAM in graphics card.", name_.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLONE_RENDER : blit rendered framebuffer in the newest image (back)
|
// CLONE_RENDER : blit rendered framebuffer in the newest image (back)
|
||||||
@@ -232,16 +241,17 @@ bool CloneSource::playable () const
|
|||||||
void CloneSource::replay()
|
void CloneSource::replay()
|
||||||
{
|
{
|
||||||
// clear to_delete_ FBO if pending
|
// clear to_delete_ FBO if pending
|
||||||
if (to_delete_ != nullptr) {
|
if (garbage_image_ != nullptr) {
|
||||||
delete to_delete_;
|
g_printerr(" REPLAY delete garbage %d \n", garbage_image_->opengl_id());
|
||||||
to_delete_ = nullptr;
|
delete garbage_image_;
|
||||||
|
garbage_image_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all images except the one in the back (newest)
|
// remove all images except the one in the back (newest)
|
||||||
while (images_.size() > 1) {
|
while (images_.size() > 1) {
|
||||||
// do not delete immediately the (oldest) front image (the FBO is currently displayed)
|
// do not delete immediately the (oldest) front image (the FBO is currently displayed)
|
||||||
if (to_delete_ == nullptr)
|
if (garbage_image_ == nullptr)
|
||||||
to_delete_ = images_.front();
|
garbage_image_ = images_.front();
|
||||||
// delete other FBO (unused)
|
// delete other FBO (unused)
|
||||||
else if (images_.front() != nullptr)
|
else if (images_.front() != nullptr)
|
||||||
delete images_.front();
|
delete images_.front();
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ protected:
|
|||||||
// cloning & queue of past frames
|
// cloning & queue of past frames
|
||||||
std::queue<FrameBuffer *> images_;
|
std::queue<FrameBuffer *> images_;
|
||||||
Surface *cloningsurface_;
|
Surface *cloningsurface_;
|
||||||
FrameBuffer *to_delete_;
|
FrameBuffer *garbage_image_;
|
||||||
|
|
||||||
// time management
|
// time management
|
||||||
GTimer *timer_;
|
GTimer *timer_;
|
||||||
|
|||||||
101
FrameBuffer.cpp
101
FrameBuffer.cpp
@@ -32,7 +32,7 @@
|
|||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
//#define FRAMEBUFFER_DEBUG
|
#define FRAMEBUFFER_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX 0x9048
|
#define GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX 0x9048
|
||||||
@@ -140,7 +140,7 @@ void FrameBuffer::init()
|
|||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
|
||||||
|
|
||||||
#ifdef FRAMEBUFFER_DEBUG
|
#ifdef FRAMEBUFFER_DEBUG
|
||||||
g_printerr("New FBO %d Multi Sampling ", framebufferid_);
|
g_printerr("New FBO %d Multi Sampling\n", framebufferid_);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -149,7 +149,7 @@ void FrameBuffer::init()
|
|||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
|
||||||
|
|
||||||
#ifdef FRAMEBUFFER_DEBUG
|
#ifdef FRAMEBUFFER_DEBUG
|
||||||
g_printerr("New FBO %d Single Sampling ", framebufferid_);
|
g_printerr("New FBO %d Single Sampling\n", framebufferid_);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -171,8 +171,8 @@ FrameBuffer::~FrameBuffer()
|
|||||||
glDeleteTextures(1, &intermediate_textureid_);
|
glDeleteTextures(1, &intermediate_textureid_);
|
||||||
|
|
||||||
#ifdef FRAMEBUFFER_DEBUG
|
#ifdef FRAMEBUFFER_DEBUG
|
||||||
GLint framebufferMemoryInKB = ( width() * height() * 5 ) / 1024;
|
GLint framebufferMemoryInKB = ( width() * height() * (use_alpha_?4:3) ) / 1024;
|
||||||
g_printerr("Framebuffer deleted %d x %d, %d kB freed\n", width(), height(), framebufferMemoryInKB);
|
g_printerr("Framebuffer deleted %d x %d, ~%d kB freed\n", width(), height(), framebufferMemoryInKB);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,21 +346,62 @@ void FrameBuffer::checkFramebufferStatus()
|
|||||||
Log::Warning(" GL_FRAMEBUFFER_UNDEFINED is returned if target is the default framebuffer, but the default framebuffer does not exist.");
|
Log::Warning(" GL_FRAMEBUFFER_UNDEFINED is returned if target is the default framebuffer, but the default framebuffer does not exist.");
|
||||||
break;
|
break;
|
||||||
case GL_FRAMEBUFFER_COMPLETE:
|
case GL_FRAMEBUFFER_COMPLETE:
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// approximation of RAM needed for this FBO
|
||||||
|
GLint framebufferMemoryInKB = ( width() * height() * (use_alpha_?4:3) * (use_multi_sampling_?2:1) ) / 1024;
|
||||||
|
|
||||||
|
// test available memory if created buffer is big (more than 8MB)
|
||||||
|
if ( framebufferMemoryInKB > 8000 ) {
|
||||||
|
|
||||||
|
// Obtain RAM usage in GPU (if possible)
|
||||||
|
glm::ivec2 RAM = getGPUMemoryInformation();
|
||||||
|
|
||||||
|
// bad case: not enough RAM, we should warn the user
|
||||||
|
if ( RAM.x < framebufferMemoryInKB * 3 ) {
|
||||||
|
Log::Warning("Critical allocation of frame buffer: only %d kB RAM remaining in graphics card.", RAM.x );
|
||||||
|
if (RAM.y < INT_MAX)
|
||||||
|
Log::Warning("Only %.1f %% of %d kB available.", 100.f*float(RAM.x)/float(RAM.y), RAM.y);
|
||||||
|
}
|
||||||
|
#ifdef FRAMEBUFFER_DEBUG
|
||||||
|
else {
|
||||||
|
// normal case: enough RAM (or unknown) for this FBO
|
||||||
|
g_printerr("Framebuffer allocated %d x %d, ~%d kB", width(), height(), framebufferMemoryInKB);
|
||||||
|
if (RAM.x < INT_MAX)
|
||||||
|
g_printerr(" (%d kB remaining)", RAM.x);
|
||||||
|
g_printerr("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// RAM usage in GPU
|
||||||
|
// returns { CurAvailMemoryInKB, TotalMemoryInKB }
|
||||||
|
// MAX values means the info in not available
|
||||||
|
glm::ivec2 FrameBuffer::getGPUMemoryInformation()
|
||||||
|
{
|
||||||
|
glm::ivec2 ret(INT_MAX, INT_MAX);
|
||||||
|
|
||||||
|
// Detect method to get info
|
||||||
static int meminfomode = -1;
|
static int meminfomode = -1;
|
||||||
// first time check which way to get memory info
|
|
||||||
if (meminfomode<0) {
|
if (meminfomode<0) {
|
||||||
|
// initialized
|
||||||
meminfomode = 0;
|
meminfomode = 0;
|
||||||
GLint numExtensions = 0;
|
GLint numExtensions = 0;
|
||||||
glGetIntegerv( GL_NUM_EXTENSIONS, &numExtensions );
|
glGetIntegerv( GL_NUM_EXTENSIONS, &numExtensions );
|
||||||
for (int i = 0; i < numExtensions; ++i){
|
for (int i = 0; i < numExtensions; ++i){
|
||||||
|
|
||||||
const GLubyte *ccc = glGetStringi(GL_EXTENSIONS, i);
|
const GLubyte *ccc = glGetStringi(GL_EXTENSIONS, i);
|
||||||
|
// NVIDIA extension available
|
||||||
if ( strcmp( (const char*)ccc, "GL_NVX_gpu_memory_info") == 0 ){
|
if ( strcmp( (const char*)ccc, "GL_NVX_gpu_memory_info") == 0 ){
|
||||||
meminfomode = 1;
|
meminfomode = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// ATI extension available
|
||||||
else if ( strcmp( (const char*)ccc, "GL_ATI_meminfo") == 0 ){
|
else if ( strcmp( (const char*)ccc, "GL_ATI_meminfo") == 0 ){
|
||||||
meminfomode = 2;
|
meminfomode = 2;
|
||||||
break;
|
break;
|
||||||
@@ -369,46 +410,32 @@ void FrameBuffer::checkFramebufferStatus()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLint framebufferMemoryInKB = ( width() * height() * 5 ) / 1024;
|
|
||||||
|
|
||||||
// NVIDIA
|
// NVIDIA
|
||||||
if (meminfomode == 1) {
|
if (meminfomode == 1) {
|
||||||
static GLint nTotalMemoryInKB = -1;
|
static GLint nTotalMemoryInKB = -1;
|
||||||
if (nTotalMemoryInKB<0)
|
if (nTotalMemoryInKB<0)
|
||||||
glGetIntegerv( GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX, &nTotalMemoryInKB );
|
glGetIntegerv( GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX, &nTotalMemoryInKB );
|
||||||
|
ret.y = nTotalMemoryInKB;
|
||||||
|
|
||||||
GLint nCurAvailMemoryInKB = 0;
|
glGetIntegerv( GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX, &ret.x );
|
||||||
glGetIntegerv( GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX, &nCurAvailMemoryInKB );
|
|
||||||
|
|
||||||
if ( nCurAvailMemoryInKB < framebufferMemoryInKB )
|
|
||||||
Log::Warning("Cannot allocate frame buffer: only %d kB GPU RAM remaining on the %d kB available (%d %%).",
|
|
||||||
nCurAvailMemoryInKB, nTotalMemoryInKB, int(float(nCurAvailMemoryInKB) / float(nTotalMemoryInKB) * 100.f ) );
|
|
||||||
#ifdef FRAMEBUFFER_DEBUG
|
|
||||||
else
|
|
||||||
g_printerr("Framebuffer created %d x %d, %d kB allocated (%d kB remaining on %d kB)\n", width(), height(), framebufferMemoryInKB, nCurAvailMemoryInKB, nTotalMemoryInKB);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
// ATI
|
// ATI
|
||||||
else if (meminfomode == 2) {
|
else if (meminfomode == 2) {
|
||||||
|
glGetIntegerv( GL_TEXTURE_FREE_MEMORY_ATI, &ret.x );
|
||||||
GLint nCurAvailMemoryInKB = 0;
|
|
||||||
glGetIntegerv( GL_TEXTURE_FREE_MEMORY_ATI, &nCurAvailMemoryInKB );
|
|
||||||
|
|
||||||
if ( nCurAvailMemoryInKB < framebufferMemoryInKB )
|
|
||||||
Log::Warning("Cannot allocate frame buffer: only %d kB GPU RAM remaining.", nCurAvailMemoryInKB );
|
|
||||||
#ifdef FRAMEBUFFER_DEBUG
|
|
||||||
else
|
|
||||||
g_printerr("Framebuffer created %d x %d, %d kB allocated (%d kB remaining)\n", width(), height(), framebufferMemoryInKB, nCurAvailMemoryInKB);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#ifdef FRAMEBUFFER_DEBUG
|
|
||||||
else
|
|
||||||
g_printerr("Framebuffer created %d x %d, %d kB allocated\n", width(), height(), framebufferMemoryInKB);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
return ret;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
bool FrameBuffer::shouldHaveEnoughMemory(glm::vec3 resolution, bool useAlpha, bool multiSampling)
|
||||||
|
{
|
||||||
|
glm::ivec2 RAM = getGPUMemoryInformation();
|
||||||
|
|
||||||
|
// approximation of RAM needed for such FBO
|
||||||
|
GLint framebufferMemoryInKB = ( resolution.x * resolution.x * (useAlpha?4:3) * (multiSampling?2:1) ) / 1024;
|
||||||
|
|
||||||
|
return ( RAM.x > framebufferMemoryInKB * 3 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,8 +44,9 @@ public:
|
|||||||
static float resolution_height[5];
|
static float resolution_height[5];
|
||||||
static glm::vec3 getResolutionFromParameters(int ar, int h);
|
static glm::vec3 getResolutionFromParameters(int ar, int h);
|
||||||
static glm::ivec2 getParametersFromResolution(glm::vec3 res);
|
static glm::ivec2 getParametersFromResolution(glm::vec3 res);
|
||||||
// unbind any framebuffer object
|
// memory management
|
||||||
static void release();
|
static glm::ivec2 getGPUMemoryInformation();
|
||||||
|
static bool shouldHaveEnoughMemory(glm::vec3 resolution, bool useAlpha = false, bool multiSampling = false);
|
||||||
|
|
||||||
FrameBuffer(glm::vec3 resolution, bool useAlpha = false, bool multiSampling = false);
|
FrameBuffer(glm::vec3 resolution, bool useAlpha = false, bool multiSampling = false);
|
||||||
FrameBuffer(uint width, uint height, bool useAlpha = false, bool multiSampling = false);
|
FrameBuffer(uint width, uint height, bool useAlpha = false, bool multiSampling = false);
|
||||||
@@ -56,6 +57,8 @@ public:
|
|||||||
void begin(bool clear = true);
|
void begin(bool clear = true);
|
||||||
// pop attrib and unbind to end draw
|
// pop attrib and unbind to end draw
|
||||||
void end();
|
void end();
|
||||||
|
// unbind (any) framebuffer object
|
||||||
|
static void release();
|
||||||
// blit copy to another, returns true on success
|
// blit copy to another, returns true on success
|
||||||
bool blit(FrameBuffer *destination);
|
bool blit(FrameBuffer *destination);
|
||||||
// bind the FrameBuffer in READ and perform glReadPixels
|
// bind the FrameBuffer in READ and perform glReadPixels
|
||||||
@@ -101,7 +104,6 @@ private:
|
|||||||
uint textureid_, intermediate_textureid_;
|
uint textureid_, intermediate_textureid_;
|
||||||
uint framebufferid_, intermediate_framebufferid_;
|
uint framebufferid_, intermediate_framebufferid_;
|
||||||
bool use_alpha_, use_multi_sampling_;
|
bool use_alpha_, use_multi_sampling_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user