open_toontown_panda3d/panda/src/gobj/preparedGraphicsObjects.cxx

1450 lines
54 KiB
C++

// Filename: preparedGraphicsObjects.cxx
// Created by: drose (19Feb04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "preparedGraphicsObjects.h"
#include "textureContext.h"
#include "vertexBufferContext.h"
#include "indexBufferContext.h"
#include "texture.h"
#include "geom.h"
#include "geomVertexArrayData.h"
#include "geomPrimitive.h"
#include "shader.h"
#include "reMutexHolder.h"
#include "geomContext.h"
#include "shaderContext.h"
#include "config_gobj.h"
#include "throw_event.h"
int PreparedGraphicsObjects::_name_index = 0;
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PreparedGraphicsObjects::
PreparedGraphicsObjects() :
_lock("PreparedGraphicsObjects::_lock"),
_name(init_name()),
_vertex_buffer_cache_size(0),
_index_buffer_cache_size(0),
_texture_residency(_name, "texture"),
_vbuffer_residency(_name, "vbuffer"),
_ibuffer_residency(_name, "ibuffer"),
_graphics_memory_lru("graphics_memory_lru", graphics_memory_limit)
{
// GLGSG will turn this flag on. This is a temporary hack to
// disable this feature for DX8/DX9 for now, until we work out the
// fine points of updating the fvf properly.
_support_released_buffer_cache = false;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PreparedGraphicsObjects::
~PreparedGraphicsObjects() {
// There may be objects that are still prepared when we destruct.
// If this is so, then all of the GSG's that own them have already
// destructed, so we can assume their resources were internally
// cleaned up. Quietly erase these remaining objects.
ReMutexHolder holder(_lock);
release_all_textures();
Textures::iterator tci;
for (tci = _released_textures.begin();
tci != _released_textures.end();
++tci) {
TextureContext *tc = (*tci);
tc->set_owning_chain(NULL);
}
// Is this a leak? Should we delete these TextureContexts?
_released_textures.clear();
release_all_geoms();
_released_geoms.clear();
release_all_shaders();
_released_shaders.clear();
release_all_vertex_buffers();
Buffers::iterator vbci;
for (vbci = _released_vertex_buffers.begin();
vbci != _released_vertex_buffers.end();
++vbci) {
VertexBufferContext *vbc = (VertexBufferContext *)(*vbci);
vbc->set_owning_chain(NULL);
}
_released_vertex_buffers.clear();
release_all_index_buffers();
Buffers::iterator ibci;
for (ibci = _released_index_buffers.begin();
ibci != _released_index_buffers.end();
++ibci) {
IndexBufferContext *ibc = (IndexBufferContext *)(*ibci);
ibc->set_owning_chain(NULL);
}
_released_index_buffers.clear();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::set_graphics_memory_limit
// Access: Public
// Description: Sets an artificial cap on graphics memory that
// will be imposed on this GSG.
//
// This limits the total amount of graphics memory,
// including texture memory and vertex buffer memory,
// that will be consumed by the GSG, regardless of
// whether the hardware claims to provide more graphics
// memory than this. It is useful to put a ceiling on
// graphics memory consumed, since some drivers seem to
// allow the application to consume more memory than the
// hardware can realistically support.
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
set_graphics_memory_limit(size_t limit) {
if (limit != _graphics_memory_lru.get_max_size()) {
_graphics_memory_lru.set_max_size(limit);
// We throw an event here so global objects (particularly the
// TexMemWatcher) can automatically respond to this change.
throw_event("graphics_memory_limit_changed");
}
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::show_graphics_memory_lru
// Access: Public
// Description: Writes to the indicated ostream a report of how the
// various textures and vertex buffers are allocated in
// the LRU.
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
show_graphics_memory_lru(ostream &out) const {
_graphics_memory_lru.write(out, 0);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::show_residency_trackers
// Access: Public
// Description: Writes to the indicated ostream a report of how the
// various textures and vertex buffers are allocated in
// the LRU.
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
show_residency_trackers(ostream &out) const {
out << "Textures:\n";
_texture_residency.write(out, 2);
out << "\nVertex buffers:\n";
_vbuffer_residency.write(out, 2);
out << "\nIndex buffers:\n";
_ibuffer_residency.write(out, 2);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::enqueue_texture
// Access: Public
// Description: Indicates that a texture would like to be put on the
// list to be prepared when the GSG is next ready to
// do this (presumably at the next frame).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
enqueue_texture(Texture *tex) {
ReMutexHolder holder(_lock);
_enqueued_textures.insert(tex);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_texture_queued
// Access: Public
// Description: Returns true if the texture has been queued on this
// GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_texture_queued(const Texture *tex) const {
ReMutexHolder holder(_lock);
EnqueuedTextures::const_iterator qi = _enqueued_textures.find((Texture *)tex);
return (qi != _enqueued_textures.end());
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::dequeue_texture
// Access: Public
// Description: Removes a texture from the queued list of textures to
// be prepared. Normally it is not necessary to call
// this, unless you change your mind about preparing it
// at the last minute, since the texture will
// automatically be dequeued and prepared at the next
// frame.
//
// The return value is true if the texture is
// successfully dequeued, false if it had not been
// queued.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
dequeue_texture(Texture *tex) {
ReMutexHolder holder(_lock);
EnqueuedTextures::iterator qi = _enqueued_textures.find(tex);
if (qi != _enqueued_textures.end()) {
_enqueued_textures.erase(qi);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_texture_prepared
// Access: Public
// Description: Returns true if the texture has been prepared on
// this GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_texture_prepared(const Texture *tex) const {
return tex->is_prepared((PreparedGraphicsObjects *)this);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_texture
// Access: Public
// Description: Indicates that a texture context, created by a
// previous call to prepare_texture(), is no longer
// needed. The driver resources will not be freed until
// some GSG calls update(), indicating it is at a
// stage where it is ready to release textures--this
// prevents conflicts from threading or multiple GSG's
// sharing textures (we have no way of knowing which
// graphics context is currently active, or what state
// it's in, at the time release_texture is called).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
release_texture(TextureContext *tc) {
ReMutexHolder holder(_lock);
tc->_texture->clear_prepared(tc->get_view(), this);
// We have to set the Texture pointer to NULL at this point, since
// the Texture itself might destruct at any time after it has been
// released.
tc->_texture = (Texture *)NULL;
bool removed = (_prepared_textures.erase(tc) != 0);
nassertv(removed);
_released_textures.insert(tc);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_texture
// Access: Public
// Description: Releases a texture if it has already been prepared,
// or removes it from the preparation queue.
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
release_texture(Texture *tex) {
tex->release(this);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_all_textures
// Access: Public
// Description: Releases all textures at once. This will force them
// to be reloaded into texture memory for all GSG's that
// share this object. Returns the number of textures
// released.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
release_all_textures() {
ReMutexHolder holder(_lock);
int num_textures = (int)_prepared_textures.size() + (int)_enqueued_textures.size();
Textures::iterator tci;
for (tci = _prepared_textures.begin();
tci != _prepared_textures.end();
++tci) {
TextureContext *tc = (*tci);
tc->_texture->clear_prepared(tc->get_view(), this);
tc->_texture = (Texture *)NULL;
_released_textures.insert(tc);
}
_prepared_textures.clear();
_enqueued_textures.clear();
return num_textures;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_queued_textures
// Access: Public
// Description: Returns the number of textures that have been
// enqueued to be prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_queued_textures() const {
return _enqueued_textures.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_prepared_textures
// Access: Public
// Description: Returns the number of textures that have already been
// prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_prepared_textures() const {
return _prepared_textures.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::prepare_texture_now
// Access: Public
// Description: Immediately creates a new TextureContext for the
// indicated texture and returns it. This assumes that
// the GraphicsStateGuardian is the currently active
// rendering context and that it is ready to accept new
// textures. If this is not necessarily the case, you
// should use enqueue_texture() instead.
//
// Normally, this function is not called directly. Call
// Texture::prepare_now() instead.
//
// The TextureContext contains all of the pertinent
// information needed by the GSG to keep track of this
// one particular texture, and will exist as long as the
// texture is ready to be rendered.
//
// When either the Texture or the
// PreparedGraphicsObjects object destructs, the
// TextureContext will be deleted.
////////////////////////////////////////////////////////////////////
TextureContext *PreparedGraphicsObjects::
prepare_texture_now(Texture *tex, int view, GraphicsStateGuardianBase *gsg) {
ReMutexHolder holder(_lock);
// Ask the GSG to create a brand new TextureContext. There might
// be several GSG's sharing the same set of textures; if so, it
// doesn't matter which of them creates the context (since they're
// all shared anyway).
TextureContext *tc = gsg->prepare_texture(tex, view);
if (tc != (TextureContext *)NULL) {
bool prepared = _prepared_textures.insert(tc).second;
nassertr(prepared, tc);
}
return tc;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::enqueue_geom
// Access: Public
// Description: Indicates that a geom would like to be put on the
// list to be prepared when the GSG is next ready to
// do this (presumably at the next frame).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
enqueue_geom(Geom *geom) {
ReMutexHolder holder(_lock);
_enqueued_geoms.insert(geom);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_geom_queued
// Access: Public
// Description: Returns true if the geom has been queued on this
// GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_geom_queued(const Geom *geom) const {
ReMutexHolder holder(_lock);
EnqueuedGeoms::const_iterator qi = _enqueued_geoms.find((Geom *)geom);
return (qi != _enqueued_geoms.end());
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::dequeue_geom
// Access: Public
// Description: Removes a geom from the queued list of geoms to
// be prepared. Normally it is not necessary to call
// this, unless you change your mind about preparing it
// at the last minute, since the geom will
// automatically be dequeued and prepared at the next
// frame.
//
// The return value is true if the geom is
// successfully dequeued, false if it had not been
// queued.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
dequeue_geom(Geom *geom) {
ReMutexHolder holder(_lock);
EnqueuedGeoms::iterator qi = _enqueued_geoms.find(geom);
if (qi != _enqueued_geoms.end()) {
_enqueued_geoms.erase(qi);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_geom_prepared
// Access: Public
// Description: Returns true if the vertex buffer has been prepared on
// this GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_geom_prepared(const Geom *geom) const {
return geom->is_prepared((PreparedGraphicsObjects *)this);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_geom
// Access: Public
// Description: Indicates that a geom context, created by a
// previous call to prepare_geom(), is no longer
// needed. The driver resources will not be freed until
// some GSG calls update(), indicating it is at a
// stage where it is ready to release geoms--this
// prevents conflicts from threading or multiple GSG's
// sharing geoms (we have no way of knowing which
// graphics context is currently active, or what state
// it's in, at the time release_geom is called).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
release_geom(GeomContext *gc) {
ReMutexHolder holder(_lock);
gc->_geom->clear_prepared(this);
// We have to set the Geom pointer to NULL at this point, since
// the Geom itself might destruct at any time after it has been
// released.
gc->_geom = (Geom *)NULL;
bool removed = (_prepared_geoms.erase(gc) != 0);
nassertv(removed);
_released_geoms.insert(gc);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_all_geoms
// Access: Public
// Description: Releases all geoms at once. This will force them
// to be reloaded into geom memory for all GSG's that
// share this object. Returns the number of geoms
// released.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
release_all_geoms() {
ReMutexHolder holder(_lock);
int num_geoms = (int)_prepared_geoms.size() + (int)_enqueued_geoms.size();
Geoms::iterator gci;
for (gci = _prepared_geoms.begin();
gci != _prepared_geoms.end();
++gci) {
GeomContext *gc = (*gci);
gc->_geom->clear_prepared(this);
gc->_geom = (Geom *)NULL;
_released_geoms.insert(gc);
}
_prepared_geoms.clear();
_enqueued_geoms.clear();
return num_geoms;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_queued_geoms
// Access: Public
// Description: Returns the number of geoms that have been
// enqueued to be prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_queued_geoms() const {
return _enqueued_geoms.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_prepared_geoms
// Access: Public
// Description: Returns the number of geoms that have already been
// prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_prepared_geoms() const {
return _prepared_geoms.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::prepare_geom_now
// Access: Public
// Description: Immediately creates a new GeomContext for the
// indicated geom and returns it. This assumes that
// the GraphicsStateGuardian is the currently active
// rendering context and that it is ready to accept new
// geoms. If this is not necessarily the case, you
// should use enqueue_geom() instead.
//
// Normally, this function is not called directly. Call
// Geom::prepare_now() instead.
//
// The GeomContext contains all of the pertinent
// information needed by the GSG to keep track of this
// one particular geom, and will exist as long as the
// geom is ready to be rendered.
//
// When either the Geom or the
// PreparedGraphicsObjects object destructs, the
// GeomContext will be deleted.
////////////////////////////////////////////////////////////////////
GeomContext *PreparedGraphicsObjects::
prepare_geom_now(Geom *geom, GraphicsStateGuardianBase *gsg) {
ReMutexHolder holder(_lock);
// Ask the GSG to create a brand new GeomContext. There might
// be several GSG's sharing the same set of geoms; if so, it
// doesn't matter which of them creates the context (since they're
// all shared anyway).
GeomContext *gc = gsg->prepare_geom(geom);
if (gc != (GeomContext *)NULL) {
bool prepared = _prepared_geoms.insert(gc).second;
nassertr(prepared, gc);
}
return gc;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::enqueue_shader
// Access: Public
// Description: Indicates that a shader would like to be put on the
// list to be prepared when the GSG is next ready to
// do this (presumably at the next frame).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
enqueue_shader(Shader *se) {
ReMutexHolder holder(_lock);
_enqueued_shaders.insert(se);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_shader_queued
// Access: Public
// Description: Returns true if the shader has been queued on this
// GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_shader_queued(const Shader *shader) const {
ReMutexHolder holder(_lock);
EnqueuedShaders::const_iterator qi = _enqueued_shaders.find((Shader *)shader);
return (qi != _enqueued_shaders.end());
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::dequeue_shader
// Access: Public
// Description: Removes a shader from the queued list of shaders to
// be prepared. Normally it is not necessary to call
// this, unless you change your mind about preparing it
// at the last minute, since the shader will
// automatically be dequeued and prepared at the next
// frame.
//
// The return value is true if the shader is
// successfully dequeued, false if it had not been
// queued.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
dequeue_shader(Shader *se) {
ReMutexHolder holder(_lock);
EnqueuedShaders::iterator qi = _enqueued_shaders.find(se);
if (qi != _enqueued_shaders.end()) {
_enqueued_shaders.erase(qi);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_shader_prepared
// Access: Public
// Description: Returns true if the shader has been prepared on
// this GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_shader_prepared(const Shader *shader) const {
return shader->is_prepared((PreparedGraphicsObjects *)this);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_shader
// Access: Public
// Description: Indicates that a shader context, created by a
// previous call to prepare_shader(), is no longer
// needed. The driver resources will not be freed until
// some GSG calls update(), indicating it is at a
// stage where it is ready to release shaders--this
// prevents conflicts from threading or multiple GSG's
// sharing shaders (we have no way of knowing which
// graphics context is currently active, or what state
// it's in, at the time release_shader is called).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
release_shader(ShaderContext *sc) {
ReMutexHolder holder(_lock);
sc->_shader->clear_prepared(this);
// We have to set the Shader pointer to NULL at this point, since
// the Shader itself might destruct at any time after it has been
// released.
sc->_shader = (Shader *)NULL;
bool removed = (_prepared_shaders.erase(sc) != 0);
nassertv(removed);
_released_shaders.insert(sc);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_all_shaders
// Access: Public
// Description: Releases all shaders at once. This will force them
// to be reloaded into shader memory for all GSG's that
// share this object. Returns the number of shaders
// released.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
release_all_shaders() {
ReMutexHolder holder(_lock);
int num_shaders = (int)_prepared_shaders.size() + (int)_enqueued_shaders.size();
Shaders::iterator sci;
for (sci = _prepared_shaders.begin();
sci != _prepared_shaders.end();
++sci) {
ShaderContext *sc = (*sci);
sc->_shader->clear_prepared(this);
sc->_shader = (Shader *)NULL;
_released_shaders.insert(sc);
}
_prepared_shaders.clear();
_enqueued_shaders.clear();
return num_shaders;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_queued_shaders
// Access: Public
// Description: Returns the number of shaders that have been
// enqueued to be prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_queued_shaders() const {
return _enqueued_shaders.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_prepared_shaders
// Access: Public
// Description: Returns the number of shaders that have already been
// prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_prepared_shaders() const {
return _prepared_shaders.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::prepare_shader_now
// Access: Public
// Description: Immediately creates a new ShaderContext for the
// indicated shader and returns it. This assumes that
// the GraphicsStateGuardian is the currently active
// rendering context and that it is ready to accept new
// shaders. If this is not necessarily the case, you
// should use enqueue_shader() instead.
//
// Normally, this function is not called directly. Call
// Shader::prepare_now() instead.
//
// The ShaderContext contains all of the pertinent
// information needed by the GSG to keep track of this
// one particular shader, and will exist as long as the
// shader is ready to be rendered.
//
// When either the Shader or the
// PreparedGraphicsObjects object destructs, the
// ShaderContext will be deleted.
////////////////////////////////////////////////////////////////////
ShaderContext *PreparedGraphicsObjects::
prepare_shader_now(Shader *se, GraphicsStateGuardianBase *gsg) {
ReMutexHolder holder(_lock);
// Ask the GSG to create a brand new ShaderContext. There might
// be several GSG's sharing the same set of shaders; if so, it
// doesn't matter which of them creates the context (since they're
// all shared anyway).
ShaderContext *sc = gsg->prepare_shader(se);
if (sc != (ShaderContext *)NULL) {
bool prepared = _prepared_shaders.insert(sc).second;
nassertr(prepared, sc);
}
return sc;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::enqueue_vertex_buffer
// Access: Public
// Description: Indicates that a buffer would like to be put on the
// list to be prepared when the GSG is next ready to
// do this (presumably at the next frame).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
enqueue_vertex_buffer(GeomVertexArrayData *data) {
ReMutexHolder holder(_lock);
_enqueued_vertex_buffers.insert(data);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_vertex_buffer_queued
// Access: Public
// Description: Returns true if the vertex buffer has been queued on
// this GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_vertex_buffer_queued(const GeomVertexArrayData *data) const {
ReMutexHolder holder(_lock);
EnqueuedVertexBuffers::const_iterator qi = _enqueued_vertex_buffers.find((GeomVertexArrayData *)data);
return (qi != _enqueued_vertex_buffers.end());
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::dequeue_vertex_buffer
// Access: Public
// Description: Removes a buffer from the queued list of data
// arrays to be prepared. Normally it is not necessary
// to call this, unless you change your mind about
// preparing it at the last minute, since the data will
// automatically be dequeued and prepared at the next
// frame.
//
// The return value is true if the buffer is
// successfully dequeued, false if it had not been
// queued.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
dequeue_vertex_buffer(GeomVertexArrayData *data) {
ReMutexHolder holder(_lock);
EnqueuedVertexBuffers::iterator qi = _enqueued_vertex_buffers.find(data);
if (qi != _enqueued_vertex_buffers.end()) {
_enqueued_vertex_buffers.erase(qi);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_vertex_buffer_prepared
// Access: Public
// Description: Returns true if the vertex buffer has been prepared on
// this GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_vertex_buffer_prepared(const GeomVertexArrayData *data) const {
return data->is_prepared((PreparedGraphicsObjects *)this);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_vertex_buffer
// Access: Public
// Description: Indicates that a data context, created by a
// previous call to prepare_vertex_buffer(), is no longer
// needed. The driver resources will not be freed until
// some GSG calls update(), indicating it is at a
// stage where it is ready to release datas--this
// prevents conflicts from threading or multiple GSG's
// sharing datas (we have no way of knowing which
// graphics context is currently active, or what state
// it's in, at the time release_vertex_buffer is called).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
release_vertex_buffer(VertexBufferContext *vbc) {
ReMutexHolder holder(_lock);
vbc->_data->clear_prepared(this);
size_t data_size_bytes = vbc->_data->get_data_size_bytes();
GeomEnums::UsageHint usage_hint = vbc->_data->get_usage_hint();
// We have to set the Data pointer to NULL at this point, since
// the Data itself might destruct at any time after it has been
// released.
vbc->_data = (GeomVertexArrayData *)NULL;
bool removed = (_prepared_vertex_buffers.erase(vbc) != 0);
nassertv(removed);
if (_support_released_buffer_cache) {
cache_unprepared_buffer(vbc, data_size_bytes, usage_hint,
_vertex_buffer_cache,
_vertex_buffer_cache_lru, _vertex_buffer_cache_size,
released_vbuffer_cache_size,
_released_vertex_buffers);
} else {
_released_vertex_buffers.insert(vbc);
}
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_all_vertex_buffers
// Access: Public
// Description: Releases all datas at once. This will force them
// to be reloaded into data memory for all GSG's that
// share this object. Returns the number of datas
// released.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
release_all_vertex_buffers() {
ReMutexHolder holder(_lock);
int num_vertex_buffers = (int)_prepared_vertex_buffers.size() + (int)_enqueued_vertex_buffers.size();
Buffers::iterator vbci;
for (vbci = _prepared_vertex_buffers.begin();
vbci != _prepared_vertex_buffers.end();
++vbci) {
VertexBufferContext *vbc = (VertexBufferContext *)(*vbci);
vbc->_data->clear_prepared(this);
vbc->_data = (GeomVertexArrayData *)NULL;
_released_vertex_buffers.insert(vbc);
}
_prepared_vertex_buffers.clear();
_enqueued_vertex_buffers.clear();
// Also clear the cache of recently-unprepared vertex buffers.
BufferCache::iterator bci;
for (bci = _vertex_buffer_cache.begin();
bci != _vertex_buffer_cache.end();
++bci) {
BufferList &buffer_list = (*bci).second;
nassertr(!buffer_list.empty(), num_vertex_buffers);
BufferList::iterator li;
for (li = buffer_list.begin(); li != buffer_list.end(); ++li) {
VertexBufferContext *vbc = (VertexBufferContext *)(*li);
_released_vertex_buffers.insert(vbc);
}
}
_vertex_buffer_cache.clear();
_vertex_buffer_cache_lru.clear();
_vertex_buffer_cache_size = 0;
return num_vertex_buffers;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_queued_vertex_buffers
// Access: Public
// Description: Returns the number of vertex buffers that have been
// enqueued to be prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_queued_vertex_buffers() const {
return _enqueued_vertex_buffers.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_prepared_vertex_buffers
// Access: Public
// Description: Returns the number of vertex buffers that have
// already been prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_prepared_vertex_buffers() const {
return _prepared_vertex_buffers.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::prepare_vertex_buffer_now
// Access: Public
// Description: Immediately creates a new VertexBufferContext for the
// indicated data and returns it. This assumes that
// the GraphicsStateGuardian is the currently active
// rendering context and that it is ready to accept new
// datas. If this is not necessarily the case, you
// should use enqueue_vertex_buffer() instead.
//
// Normally, this function is not called directly. Call
// Data::prepare_now() instead.
//
// The VertexBufferContext contains all of the pertinent
// information needed by the GSG to keep track of this
// one particular data, and will exist as long as the
// data is ready to be rendered.
//
// When either the Data or the
// PreparedGraphicsObjects object destructs, the
// VertexBufferContext will be deleted.
////////////////////////////////////////////////////////////////////
VertexBufferContext *PreparedGraphicsObjects::
prepare_vertex_buffer_now(GeomVertexArrayData *data, GraphicsStateGuardianBase *gsg) {
ReMutexHolder holder(_lock);
// First, see if there might be a cached context of the appropriate
// size.
size_t data_size_bytes = data->get_data_size_bytes();
GeomEnums::UsageHint usage_hint = data->get_usage_hint();
VertexBufferContext *vbc = (VertexBufferContext *)
get_cached_buffer(data_size_bytes, usage_hint,
_vertex_buffer_cache, _vertex_buffer_cache_lru,
_vertex_buffer_cache_size);
if (vbc != (VertexBufferContext *)NULL) {
vbc->_data = data;
} else {
// Ask the GSG to create a brand new VertexBufferContext. There
// might be several GSG's sharing the same set of datas; if so, it
// doesn't matter which of them creates the context (since they're
// all shared anyway).
vbc = gsg->prepare_vertex_buffer(data);
}
if (vbc != (VertexBufferContext *)NULL) {
bool prepared = _prepared_vertex_buffers.insert(vbc).second;
nassertr(prepared, vbc);
}
return vbc;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::enqueue_index_buffer
// Access: Public
// Description: Indicates that a buffer would like to be put on the
// list to be prepared when the GSG is next ready to
// do this (presumably at the next frame).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
enqueue_index_buffer(GeomPrimitive *data) {
ReMutexHolder holder(_lock);
_enqueued_index_buffers.insert(data);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_index_buffer_queued
// Access: Public
// Description: Returns true if the index buffer has been queued on
// this GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_index_buffer_queued(const GeomPrimitive *data) const {
ReMutexHolder holder(_lock);
EnqueuedIndexBuffers::const_iterator qi = _enqueued_index_buffers.find((GeomPrimitive *)data);
return (qi != _enqueued_index_buffers.end());
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::dequeue_index_buffer
// Access: Public
// Description: Removes a buffer from the queued list of data
// arrays to be prepared. Normally it is not necessary
// to call this, unless you change your mind about
// preparing it at the last minute, since the data will
// automatically be dequeued and prepared at the next
// frame.
//
// The return value is true if the buffer is
// successfully dequeued, false if it had not been
// queued.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
dequeue_index_buffer(GeomPrimitive *data) {
ReMutexHolder holder(_lock);
EnqueuedIndexBuffers::iterator qi = _enqueued_index_buffers.find(data);
if (qi != _enqueued_index_buffers.end()) {
_enqueued_index_buffers.erase(qi);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::is_index_buffer_prepared
// Access: Public
// Description: Returns true if the index buffer has been prepared on
// this GSG, false otherwise.
////////////////////////////////////////////////////////////////////
bool PreparedGraphicsObjects::
is_index_buffer_prepared(const GeomPrimitive *data) const {
return data->is_prepared((PreparedGraphicsObjects *)this);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_index_buffer
// Access: Public
// Description: Indicates that a data context, created by a
// previous call to prepare_index_buffer(), is no longer
// needed. The driver resources will not be freed until
// some GSG calls update(), indicating it is at a
// stage where it is ready to release datas--this
// prevents conflicts from threading or multiple GSG's
// sharing datas (we have no way of knowing which
// graphics context is currently active, or what state
// it's in, at the time release_index_buffer is called).
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
release_index_buffer(IndexBufferContext *ibc) {
ReMutexHolder holder(_lock);
ibc->_data->clear_prepared(this);
size_t data_size_bytes = ibc->_data->get_data_size_bytes();
GeomEnums::UsageHint usage_hint = ibc->_data->get_usage_hint();
// We have to set the Data pointer to NULL at this point, since
// the Data itself might destruct at any time after it has been
// released.
ibc->_data = (GeomPrimitive *)NULL;
bool removed = (_prepared_index_buffers.erase(ibc) != 0);
nassertv(removed);
if (_support_released_buffer_cache) {
cache_unprepared_buffer(ibc, data_size_bytes, usage_hint,
_index_buffer_cache,
_index_buffer_cache_lru, _index_buffer_cache_size,
released_ibuffer_cache_size,
_released_index_buffers);
} else {
_released_index_buffers.insert(ibc);
}
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::release_all_index_buffers
// Access: Public
// Description: Releases all datas at once. This will force them
// to be reloaded into data memory for all GSG's that
// share this object. Returns the number of datas
// released.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
release_all_index_buffers() {
ReMutexHolder holder(_lock);
int num_index_buffers = (int)_prepared_index_buffers.size() + (int)_enqueued_index_buffers.size();
Buffers::iterator ibci;
for (ibci = _prepared_index_buffers.begin();
ibci != _prepared_index_buffers.end();
++ibci) {
IndexBufferContext *ibc = (IndexBufferContext *)(*ibci);
ibc->_data->clear_prepared(this);
ibc->_data = (GeomPrimitive *)NULL;
_released_index_buffers.insert(ibc);
}
_prepared_index_buffers.clear();
_enqueued_index_buffers.clear();
// Also clear the cache of recently-unprepared index buffers.
BufferCache::iterator bci;
for (bci = _index_buffer_cache.begin();
bci != _index_buffer_cache.end();
++bci) {
BufferList &buffer_list = (*bci).second;
nassertr(!buffer_list.empty(), num_index_buffers);
BufferList::iterator li;
for (li = buffer_list.begin(); li != buffer_list.end(); ++li) {
IndexBufferContext *vbc = (IndexBufferContext *)(*li);
_released_index_buffers.insert(vbc);
}
}
_index_buffer_cache.clear();
_index_buffer_cache_lru.clear();
_index_buffer_cache_size = 0;
return num_index_buffers;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_queued_index_buffers
// Access: Public
// Description: Returns the number of index buffers that have been
// enqueued to be prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_queued_index_buffers() const {
return _enqueued_index_buffers.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_num_prepared_index_buffers
// Access: Public
// Description: Returns the number of index buffers that have
// already been prepared on this GSG.
////////////////////////////////////////////////////////////////////
int PreparedGraphicsObjects::
get_num_prepared_index_buffers() const {
return _prepared_index_buffers.size();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::prepare_index_buffer_now
// Access: Public
// Description: Immediately creates a new IndexBufferContext for the
// indicated data and returns it. This assumes that
// the GraphicsStateGuardian is the currently active
// rendering context and that it is ready to accept new
// datas. If this is not necessarily the case, you
// should use enqueue_index_buffer() instead.
//
// Normally, this function is not called directly. Call
// Data::prepare_now() instead.
//
// The IndexBufferContext contains all of the pertinent
// information needed by the GSG to keep track of this
// one particular data, and will exist as long as the
// data is ready to be rendered.
//
// When either the Data or the
// PreparedGraphicsObjects object destructs, the
// IndexBufferContext will be deleted.
////////////////////////////////////////////////////////////////////
IndexBufferContext *PreparedGraphicsObjects::
prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg) {
ReMutexHolder holder(_lock);
// First, see if there might be a cached context of the appropriate
// size.
size_t data_size_bytes = data->get_data_size_bytes();
GeomEnums::UsageHint usage_hint = data->get_usage_hint();
IndexBufferContext *ibc = (IndexBufferContext *)
get_cached_buffer(data_size_bytes, usage_hint,
_index_buffer_cache, _index_buffer_cache_lru,
_index_buffer_cache_size);
if (ibc != (IndexBufferContext *)NULL) {
ibc->_data = data;
} else {
// Ask the GSG to create a brand new IndexBufferContext. There
// might be several GSG's sharing the same set of datas; if so, it
// doesn't matter which of them creates the context (since they're
// all shared anyway).
ibc = gsg->prepare_index_buffer(data);
}
if (ibc != (IndexBufferContext *)NULL) {
bool prepared = _prepared_index_buffers.insert(ibc).second;
nassertr(prepared, ibc);
}
return ibc;
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::begin_frame
// Access: Public
// Description: This is called by the GraphicsStateGuardian to
// indicate that it is about to begin processing of the
// frame.
//
// Any texture contexts that were previously passed to
// release_texture() are actually passed to the GSG to
// be freed at this point; textures that were previously
// passed to prepare_texture are actually loaded.
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
ReMutexHolder holder(_lock, current_thread);
// First, release all the textures, geoms, and buffers awaiting
// release.
if (!_released_textures.empty()) {
Textures::iterator tci;
for (tci = _released_textures.begin();
tci != _released_textures.end();
++tci) {
TextureContext *tc = (*tci);
gsg->release_texture(tc);
}
}
_released_textures.clear();
Geoms::iterator gci;
for (gci = _released_geoms.begin();
gci != _released_geoms.end();
++gci) {
GeomContext *gc = (*gci);
gsg->release_geom(gc);
}
_released_geoms.clear();
Shaders::iterator sci;
for (sci = _released_shaders.begin();
sci != _released_shaders.end();
++sci) {
ShaderContext *sc = (*sci);
gsg->release_shader(sc);
}
_released_shaders.clear();
Buffers::iterator vbci;
for (vbci = _released_vertex_buffers.begin();
vbci != _released_vertex_buffers.end();
++vbci) {
VertexBufferContext *vbc = (VertexBufferContext *)(*vbci);
gsg->release_vertex_buffer(vbc);
}
_released_vertex_buffers.clear();
Buffers::iterator ibci;
for (ibci = _released_index_buffers.begin();
ibci != _released_index_buffers.end();
++ibci) {
IndexBufferContext *ibc = (IndexBufferContext *)(*ibci);
gsg->release_index_buffer(ibc);
}
_released_index_buffers.clear();
// Reset the residency trackers.
_texture_residency.begin_frame(current_thread);
_vbuffer_residency.begin_frame(current_thread);
_ibuffer_residency.begin_frame(current_thread);
// Now prepare all the textures, geoms, and buffers awaiting
// preparation.
EnqueuedTextures::iterator qti;
for (qti = _enqueued_textures.begin();
qti != _enqueued_textures.end();
++qti) {
Texture *tex = (*qti);
for (int view = 0; view < tex->get_num_views(); ++view) {
TextureContext *tc = tex->prepare_now(view, this, gsg);
if (tc != (TextureContext *)NULL) {
gsg->update_texture(tc, true);
}
}
}
_enqueued_textures.clear();
EnqueuedGeoms::iterator qgi;
for (qgi = _enqueued_geoms.begin();
qgi != _enqueued_geoms.end();
++qgi) {
Geom *geom = (*qgi);
geom->prepare_now(this, gsg);
}
_enqueued_geoms.clear();
EnqueuedShaders::iterator qsi;
for (qsi = _enqueued_shaders.begin();
qsi != _enqueued_shaders.end();
++qsi) {
Shader *shader = (*qsi);
shader->prepare_now(this, gsg);
}
_enqueued_shaders.clear();
EnqueuedVertexBuffers::iterator qvbi;
for (qvbi = _enqueued_vertex_buffers.begin();
qvbi != _enqueued_vertex_buffers.end();
++qvbi) {
GeomVertexArrayData *data = (*qvbi);
data->prepare_now(this, gsg);
}
_enqueued_vertex_buffers.clear();
EnqueuedIndexBuffers::iterator qibi;
for (qibi = _enqueued_index_buffers.begin();
qibi != _enqueued_index_buffers.end();
++qibi) {
GeomPrimitive *data = (*qibi);
data->prepare_now(this, gsg);
}
_enqueued_index_buffers.clear();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::end_frame
// Access: Public
// Description: This is called by the GraphicsStateGuardian to
// indicate that it has finished processing of the
// frame.
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
end_frame(Thread *current_thread) {
ReMutexHolder holder(_lock, current_thread);
_texture_residency.end_frame(current_thread);
_vbuffer_residency.end_frame(current_thread);
_ibuffer_residency.end_frame(current_thread);
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::init_name
// Access: Private, Static
// Description: Returns a new, unique name for a newly-constructed
// object.
////////////////////////////////////////////////////////////////////
string PreparedGraphicsObjects::
init_name() {
++_name_index;
ostringstream strm;
strm << "context" << _name_index;
return strm.str();
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::cache_unprepared_buffer
// Access: Private
// Description: Called when a vertex or index buffer is no longer
// officially "prepared". However, we still have the
// context on the graphics card, and we might be able to
// reuse that context if we're about to re-prepare a
// different buffer, especially one exactly the same
// size. So instead of immediately enqueuing the vertex
// buffer for release, we cache it.
////////////////////////////////////////////////////////////////////
void PreparedGraphicsObjects::
cache_unprepared_buffer(BufferContext *buffer, size_t data_size_bytes,
GeomEnums::UsageHint usage_hint,
PreparedGraphicsObjects::BufferCache &buffer_cache,
PreparedGraphicsObjects::BufferCacheLRU &buffer_cache_lru,
size_t &buffer_cache_size,
int released_buffer_cache_size,
PreparedGraphicsObjects::Buffers &released_buffers) {
BufferCacheKey key;
key._data_size_bytes = data_size_bytes;
key._usage_hint = usage_hint;
buffer_cache[key].push_back(buffer);
buffer_cache_size += data_size_bytes;
// Move the key to the head of the LRU.
BufferCacheLRU::iterator li =
find(buffer_cache_lru.begin(), buffer_cache_lru.end(), key);
if (li != buffer_cache_lru.end()) {
buffer_cache_lru.erase(li);
}
buffer_cache_lru.insert(buffer_cache_lru.begin(), key);
// Now release not-recently-used buffers until we fit within the
// constrained size.
while ((int)buffer_cache_size > released_buffer_cache_size) {
nassertv(!buffer_cache_lru.empty());
const BufferCacheKey &release_key = *buffer_cache_lru.rbegin();
BufferList &buffer_list = buffer_cache[release_key];
while (!buffer_list.empty() &&
(int)buffer_cache_size > released_buffer_cache_size) {
BufferContext *released_buffer = buffer_list.back();
buffer_list.pop_back();
released_buffers.insert(released_buffer);
buffer_cache_size -= release_key._data_size_bytes;
}
if (buffer_list.empty()) {
buffer_cache.erase(release_key);
buffer_cache_lru.pop_back();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PreparedGraphicsObjects::get_cached_buffer
// Access: Private
// Description: Returns a previously-cached buffer from the cache, or
// NULL if there is no such buffer.
////////////////////////////////////////////////////////////////////
BufferContext *PreparedGraphicsObjects::
get_cached_buffer(size_t data_size_bytes, GeomEnums::UsageHint usage_hint,
PreparedGraphicsObjects::BufferCache &buffer_cache,
PreparedGraphicsObjects::BufferCacheLRU &buffer_cache_lru,
size_t &buffer_cache_size) {
BufferCacheKey key;
key._data_size_bytes = data_size_bytes;
key._usage_hint = usage_hint;
BufferCache::iterator bci = buffer_cache.find(key);
if (bci == buffer_cache.end()) {
return NULL;
}
BufferList &buffer_list = (*bci).second;
nassertr(!buffer_list.empty(), NULL);
BufferContext *buffer = buffer_list.back();
buffer_list.pop_back();
if (buffer_list.empty()) {
buffer_cache.erase(bci);
BufferCacheLRU::iterator li =
find(buffer_cache_lru.begin(), buffer_cache_lru.end(), key);
if (li != buffer_cache_lru.end()) {
buffer_cache_lru.erase(li);
}
}
buffer_cache_size -= data_size_bytes;
return buffer;
}