1450 lines
54 KiB
C++
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;
|
|
}
|