work-in-progress: asynchronous texture loads

This commit is contained in:
David Rose 2008-08-13 01:13:09 +00:00
parent b83c917981
commit d7e6603118
32 changed files with 1136 additions and 315 deletions

View File

@ -32,7 +32,7 @@ void DrawCullHandler::
record_object(CullableObject *object, const CullTraverser *traverser) {
// Munge vertices as needed for the GSG's requirements, and the
// object's current state.
bool force = !allow_incomplete_render;
bool force = !_gsg->get_incomplete_render();
Thread *current_thread = traverser->get_current_thread();
if (object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser, force)) {

View File

@ -205,6 +205,15 @@ ConfigVariableBool alpha_scale_via_texture
"application specifically enables it. See also "
"color-scale-via-lighting."));
ConfigVariableBool allow_incomplete_render
("allow-incomplete-render", false,
PRC_DESC("When this is true, the frame may be rendered even if some of the "
"geometry in the scene has been paged out. The nonresident "
"geometry will be rendered as soon as it can be paged back in, "
"which may be several frames in the future. When this is false, "
"geometry is always paged in when needed, holding up the frame "
"render if necessary."));
ConfigVariableInt win_size
("win-size", "640 480",
PRC_DESC("This is the default size at which to open a new window. This "

View File

@ -58,6 +58,7 @@ extern EXPCL_PANDA_DISPLAY ConfigVariableString red_blue_stereo_colors;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool auto_generate_mipmaps;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool color_scale_via_lighting;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool alpha_scale_via_texture;
extern EXPCL_PANDA_DISPLAY ConfigVariableBool allow_incomplete_render;
extern EXPCL_PANDA_DISPLAY ConfigVariableInt win_size;
extern EXPCL_PANDA_DISPLAY ConfigVariableInt win_origin;

View File

@ -74,6 +74,30 @@ get_portal_cull() const {
return _portal_enabled;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::set_default_loader
// Access: Public
// Description: Sets the Loader object that will be assigned to every
// GSG created with this GraphicsEngine. See
// GraphicsStateGuardian::set_loader().
////////////////////////////////////////////////////////////////////
INLINE void GraphicsEngine::
set_default_loader(Loader *loader) {
_default_loader = loader;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::get_default_loader
// Access: Public, Virtual
// Description: Returns the Loader object that will be assigned to
// every GSG created with this GraphicsEngine. See
// GraphicsStateGuardian::set_loader().
////////////////////////////////////////////////////////////////////
INLINE Loader *GraphicsEngine::
get_default_loader() const {
return _default_loader;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::close_gsg
// Access: Published

View File

@ -1760,6 +1760,9 @@ do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe,
gsg->_threading_model = threading_model;
gsg->_pipe = pipe;
gsg->_engine = this;
if (!_default_loader.is_null()) {
gsg->set_loader(_default_loader);
}
auto_adjust_capabilities(gsg);

View File

@ -30,6 +30,7 @@
#include "pset.h"
#include "ordered_vector.h"
#include "indirectLess.h"
#include "loader.h"
class Pipeline;
class DisplayRegion;
@ -66,6 +67,9 @@ PUBLISHED:
INLINE void set_portal_cull(bool value);
INLINE bool get_portal_cull() const;
INLINE void set_default_loader(Loader *loader);
INLINE Loader *get_default_loader() const;
GraphicsOutput *make_output(GraphicsPipe *pipe,
const string &name, int sort,
const FrameBufferProperties &fb_prop,
@ -322,6 +326,7 @@ private:
GraphicsThreadingModel _threading_model;
bool _auto_flip;
bool _portal_enabled; //toggle to portal culling on/off
PT(Loader) _default_loader;
enum FlipState {
FS_draw, // Still drawing.

View File

@ -105,6 +105,79 @@ is_valid() const {
return _is_valid;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::needs_reset
// Access: Public
// Description: Returns true if the gsg is marked as needing a
// reset.
////////////////////////////////////////////////////////////////////
INLINE bool GraphicsStateGuardian::
needs_reset() const {
return _needs_reset;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_incomplete_render
// Access: Public
// Description: Sets the incomplete_render flag. When this is
// true, the frame will be rendered even if some of the
// geometry or textures in the scene are not available
// (e.g. they have been temporarily paged out). When
// this is false, the frame will be held up while this
// data is reloaded.
//
// Setting this true allows for a smoother frame rate,
// but occasionally parts of the frame will be invisible
// or missing (they will generally come in within a
// second or two). Setting this false guarantees that
// every frame will be complete, but may cause more
// chugs as things are loaded up at runtime.
//
// You may want to set this false during loading
// screens, to guarantee that all of your assets are
// available by the time you take the loading screen
// down.
////////////////////////////////////////////////////////////////////
INLINE void GraphicsStateGuardian::
set_incomplete_render(bool incomplete_render) {
_incomplete_render = incomplete_render;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_incomplete_render
// Access: Public, Virtual
// Description: Returns the incomplete_render flag. See
// set_incomplete_render().
////////////////////////////////////////////////////////////////////
INLINE bool GraphicsStateGuardian::
get_incomplete_render() const {
return _incomplete_render;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_loader
// Access: Public
// Description: Sets the Loader object that will be used by this GSG
// to load textures when necessary, if
// get_incomplete_render() is true.
////////////////////////////////////////////////////////////////////
INLINE void GraphicsStateGuardian::
set_loader(Loader *loader) {
_loader = loader;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_loader
// Access: Public, Virtual
// Description: Returns the Loader object that will be used by this
// GSG to load textures when necessary, if
// get_incomplete_render() is true.
////////////////////////////////////////////////////////////////////
INLINE Loader *GraphicsStateGuardian::
get_loader() const {
return _loader;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_pipe
// Access: Published
@ -716,17 +789,6 @@ mark_new() {
_needs_reset = true;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::needs_reset
// Access: Public
// Description: Returns true if the gsg is marked as needing a
// reset.
////////////////////////////////////////////////////////////////////
INLINE bool GraphicsStateGuardian::
needs_reset() const {
return _needs_reset;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_external_transform
// Access: Public

View File

@ -40,6 +40,7 @@
#include "directionalLight.h"
#include "pointLight.h"
#include "spotlight.h"
#include "textureReloadRequest.h"
#include <algorithm>
#include <limits.h>
@ -131,6 +132,7 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
_active = true;
_prepared_objects = new PreparedGraphicsObjects;
_stereo_buffer_mask = ~0;
_incomplete_render = allow_incomplete_render;
_is_hardware = false;
_prefers_triangle_strips = false;
@ -317,106 +319,6 @@ get_internal_coordinate_system() const {
return _internal_coordinate_system;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::reset
// Access: Public, Virtual
// Description: Resets all internal state as if the gsg were newly
// created.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
reset() {
_needs_reset = false;
_is_valid = false;
_state_rs = NULL;
_target_rs = NULL;
_state.clear_to_zero();
_target.clear_to_defaults();
_internal_transform = _cs_transform;
_scene_null = new SceneSetup;
_scene_setup = _scene_null;
_color_write_mask = ColorWriteAttrib::C_all;
_has_scene_graph_color = false;
_scene_graph_color.set(1.0f, 1.0f, 1.0f, 1.0f);
_transform_stale = true;
_color_blend_involves_color_scale = false;
_texture_involves_color_scale = false;
_vertex_colors_enabled = true;
_lighting_enabled = false;
_num_lights_enabled = 0;
_num_clip_planes_enabled = 0;
_color_scale_enabled = false;
_current_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f);
_has_texture_alpha_scale = false;
_has_material_force_color = false;
_material_force_color.set(1.0f, 1.0f, 1.0f, 1.0f);
_light_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f);
_tex_gen_modifies_mat = false;
_last_max_stage_index = 0;
_is_valid = true;
if (_stencil_render_states) {
delete _stencil_render_states;
_stencil_render_states = 0;
}
_stencil_render_states = new StencilRenderStates (this);
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_state_and_transform
// Access: Public
// Description: Simultaneously resets the render state and the
// transform state.
//
// This transform specified is the "internal" net
// transform, already converted into the GSG's internal
// coordinate space by composing it to
// get_cs_transform(). (Previously, this used to be the
// "external" net transform, with the assumption that
// that GSG would convert it internally, but that is no
// longer the case.)
//
// Special case: if (state==NULL), then the target
// state is already stored in _target.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
set_state_and_transform(const RenderState *state,
const TransformState *trans) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::clear
// Access: Public
// Description: Clears the framebuffer within the current
// DisplayRegion, according to the flags indicated by
// the given DrawableRegion object.
//
// This does not set the DisplayRegion first. You
// should call prepare_display_region() to specify the
// region you wish the clear operation to apply to.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
clear(DrawableRegion *clearable) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_render_buffer
// Access: Public
// Description: Returns a RenderBuffer object suitable for operating
// on the requested set of buffers. buffer_type is the
// union of all the desired RenderBuffer::Type values.
////////////////////////////////////////////////////////////////////
RenderBuffer GraphicsStateGuardian::
get_render_buffer(int buffer_type, const FrameBufferProperties &prop) {
return RenderBuffer(this, buffer_type & prop.get_buffer_mask() & _stereo_buffer_mask);
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_prepared_objects
// Access: Public, Virtual
@ -429,6 +331,37 @@ get_prepared_objects() {
return _prepared_objects;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_gamma
// Access: Published, Virtual
// Description: Set gamma. Returns true on success.
////////////////////////////////////////////////////////////////////
bool GraphicsStateGuardian::
set_gamma(float gamma) {
_gamma = gamma;
return false;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_gamma
// Access: Published
// Description: Get the current gamma setting.
////////////////////////////////////////////////////////////////////
float GraphicsStateGuardian::
get_gamma(float gamma) {
return _gamma;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::restore_gamma
// Access: Published, Virtual
// Description: Restore original gamma setting.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
restore_gamma() {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_scene
// Access: Public
@ -1152,6 +1085,26 @@ prepare_lens() {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::calc_projection_mat
// Access: Public, Virtual
// Description: Given a lens, this function calculates the appropriate
// projection matrix for this gsg. The result depends
// on the peculiarities of the rendering API.
////////////////////////////////////////////////////////////////////
CPT(TransformState) GraphicsStateGuardian::
calc_projection_mat(const Lens *lens) {
if (lens == (Lens *)NULL) {
return NULL;
}
if (!lens->is_linear()) {
return NULL;
}
return TransformState::make_identity();
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::begin_frame
// Access: Public, Virtual
@ -1462,6 +1415,106 @@ end_draw_primitives() {
_data_reader = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::reset
// Access: Public, Virtual
// Description: Resets all internal state as if the gsg were newly
// created.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
reset() {
_needs_reset = false;
_is_valid = false;
_state_rs = NULL;
_target_rs = NULL;
_state.clear_to_zero();
_target.clear_to_defaults();
_internal_transform = _cs_transform;
_scene_null = new SceneSetup;
_scene_setup = _scene_null;
_color_write_mask = ColorWriteAttrib::C_all;
_has_scene_graph_color = false;
_scene_graph_color.set(1.0f, 1.0f, 1.0f, 1.0f);
_transform_stale = true;
_color_blend_involves_color_scale = false;
_texture_involves_color_scale = false;
_vertex_colors_enabled = true;
_lighting_enabled = false;
_num_lights_enabled = 0;
_num_clip_planes_enabled = 0;
_color_scale_enabled = false;
_current_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f);
_has_texture_alpha_scale = false;
_has_material_force_color = false;
_material_force_color.set(1.0f, 1.0f, 1.0f, 1.0f);
_light_color_scale.set(1.0f, 1.0f, 1.0f, 1.0f);
_tex_gen_modifies_mat = false;
_last_max_stage_index = 0;
_is_valid = true;
if (_stencil_render_states) {
delete _stencil_render_states;
_stencil_render_states = 0;
}
_stencil_render_states = new StencilRenderStates (this);
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_state_and_transform
// Access: Public
// Description: Simultaneously resets the render state and the
// transform state.
//
// This transform specified is the "internal" net
// transform, already converted into the GSG's internal
// coordinate space by composing it to
// get_cs_transform(). (Previously, this used to be the
// "external" net transform, with the assumption that
// that GSG would convert it internally, but that is no
// longer the case.)
//
// Special case: if (state==NULL), then the target
// state is already stored in _target.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
set_state_and_transform(const RenderState *state,
const TransformState *trans) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::clear
// Access: Public
// Description: Clears the framebuffer within the current
// DisplayRegion, according to the flags indicated by
// the given DrawableRegion object.
//
// This does not set the DisplayRegion first. You
// should call prepare_display_region() to specify the
// region you wish the clear operation to apply to.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
clear(DrawableRegion *clearable) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_render_buffer
// Access: Public
// Description: Returns a RenderBuffer object suitable for operating
// on the requested set of buffers. buffer_type is the
// union of all the desired RenderBuffer::Type values.
////////////////////////////////////////////////////////////////////
RenderBuffer GraphicsStateGuardian::
get_render_buffer(int buffer_type, const FrameBufferProperties &prop) {
return RenderBuffer(this, buffer_type & prop.get_buffer_mask() & _stereo_buffer_mask);
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_cs_transform
// Access: Public, Virtual
@ -1798,6 +1851,58 @@ init_frame_pstats() {
}
#endif // DO_PSTATS
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::create_gamma_table
// Access: Public, Static
// Description: Create a gamma table.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
create_gamma_table (float gamma, unsigned short *red_table, unsigned short *green_table, unsigned short *blue_table) {
int i;
if (gamma <= 0.0) {
// avoid divide by zero and negative exponents
gamma = 1.0;
}
for (i = 0; i < 256; i++) {
double g;
double x;
float gamma_correction;
x = ((double) i / 255.0);
gamma_correction = 1.0 / gamma;
x = pow (x, (double) gamma_correction);
if (x > 1.00) {
x = 1.0;
}
g = x * 65535.0;
red_table [i] = (int)g;
green_table [i] = (int)g;
blue_table [i] = (int)g;
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::traverse_prepared_textures
// Access: Public
// Description: Calls the indicated function on all
// currently-prepared textures, or until the callback
// function returns false.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg) {
PreparedGraphicsObjects::Textures::const_iterator ti;
for (ti = _prepared_objects->_prepared_textures.begin();
ti != _prepared_objects->_prepared_textures.end();
++ti) {
bool bResult=(*pertex_callbackfn)(*ti,callback_arg);
if(!bResult)
return;
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::enable_lighting
// Access: Protected, Virtual
@ -2095,104 +2200,20 @@ get_untextured_state() {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::traverse_prepared_textures
// Access: Public
// Description: Calls the indicated function on all
// currently-prepared textures, or until the callback
// function returns false.
// Function: GraphicsStateGuardian::async_reload_texture
// Access: Protected
// Description: Should be called when a texture is encountered that
// needs to have its RAM image reloaded, and
// get_incomplete_render() is true. This will fire off
// a thread on the current Loader object that will
// request the texture to load its image. The image
// will be available at some point in the future (no
// event will be generated).
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg) {
PreparedGraphicsObjects::Textures::const_iterator ti;
for (ti = _prepared_objects->_prepared_textures.begin();
ti != _prepared_objects->_prepared_textures.end();
++ti) {
bool bResult=(*pertex_callbackfn)(*ti,callback_arg);
if(!bResult)
return;
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::calc_projection_mat
// Access: Public, Virtual
// Description: Given a lens, this function calculates the appropriate
// projection matrix for this gsg. The result depends
// on the peculiarities of the rendering API.
////////////////////////////////////////////////////////////////////
CPT(TransformState) GraphicsStateGuardian::
calc_projection_mat(const Lens *lens) {
if (lens == (Lens *)NULL) {
return NULL;
}
if (!lens->is_linear()) {
return NULL;
}
return TransformState::make_identity();
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_gamma
// Access: Published, Virtual
// Description: Set gamma. Returns true on success.
////////////////////////////////////////////////////////////////////
bool GraphicsStateGuardian::
set_gamma(float gamma) {
_gamma = gamma;
return false;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::create_gamma_table
// Access: Published
// Description: Get the current gamma setting.
////////////////////////////////////////////////////////////////////
float GraphicsStateGuardian::
get_gamma(float gamma) {
return _gamma;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::restore_gamma
// Access: Published, Virtual
// Description: Restore original gamma setting.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
restore_gamma() {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::create_gamma_table
// Access: Public, Static
// Description: Create a gamma table.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
create_gamma_table (float gamma, unsigned short *red_table, unsigned short *green_table, unsigned short *blue_table) {
int i;
if (gamma <= 0.0) {
// avoid divide by zero and negative exponents
gamma = 1.0;
}
for (i = 0; i < 256; i++) {
double g;
double x;
float gamma_correction;
x = ((double) i / 255.0);
gamma_correction = 1.0 / gamma;
x = pow (x, (double) gamma_correction);
if (x > 1.00) {
x = 1.0;
}
g = x * 65535.0;
red_table [i] = (int)g;
green_table [i] = (int)g;
blue_table [i] = (int)g;
}
async_reload_texture(TextureContext *tc) {
nassertv(_loader != (Loader *)NULL);
PT(AsyncTask) request = new TextureReloadRequest(tc);
_loader->load_async(request);
}

View File

@ -44,6 +44,7 @@
#include "texture.h"
#include "occlusionQueryContext.h"
#include "stencilRenderStates.h"
#include "loader.h"
class DrawableRegion;
class GraphicsEngine;
@ -90,6 +91,12 @@ PUBLISHED:
INLINE bool is_valid() const;
INLINE bool needs_reset() const;
INLINE void set_incomplete_render(bool incomplete_render);
virtual INLINE bool get_incomplete_render() const;
INLINE void set_loader(Loader *loader);
INLINE Loader *get_loader() const;
INLINE GraphicsPipe *get_pipe() const;
INLINE GraphicsEngine *get_engine() const;
INLINE const GraphicsThreadingModel &get_threading_model() const;
@ -268,6 +275,8 @@ public:
static void init_frame_pstats();
#endif
void traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg);
protected:
virtual void enable_lighting(bool enable);
virtual void set_ambient_light(const Colorf &color);
@ -293,6 +302,8 @@ protected:
static CPT(RenderState) get_unclipped_state();
static CPT(RenderState) get_untextured_state();
void async_reload_texture(TextureContext *tc);
protected:
PT(SceneSetup) _scene_null;
PT(SceneSetup) _scene_setup;
@ -353,6 +364,8 @@ protected:
bool _is_valid;
bool _closing_gsg;
bool _active;
bool _incomplete_render;
PT(Loader) _loader;
PT(PreparedGraphicsObjects) _prepared_objects;
@ -482,9 +495,6 @@ private:
GraphicsEngine *_engine;
GraphicsThreadingModel _threading_model;
public:
void traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg);
public:
static TypeHandle get_class_type() {
return _type_handle;

View File

@ -2377,7 +2377,7 @@ bool vertex_buffer_page_in_function (LruPage *lru_page)
Not sure if this is the correct thing to do. Can we return false
from the page_in function? Will we get called again next frame if
we do?
if (allow_incomplete_render) {
if (_incomplete_render) {
// Check if the data is resident before continuing.
const unsigned char *data_pointer = reader->get_read_pointer(false);
if (data_pointer == NULL) {
@ -2431,7 +2431,7 @@ bool index_buffer_page_in_function (LruPage *lru_page)
Not sure if this is the correct thing to do. Can we return false
from the page_in function? Will we get called again next frame if
we do?
if (allow_incomplete_render) {
if (_incomplete_render) {
// Check if the data is resident before continuing.
const unsigned char *data_pointer = reader.get_read_pointer(false);
if (data_pointer == NULL) {

View File

@ -290,7 +290,6 @@ CLP(GraphicsStateGuardian)::
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
reset() {
cerr << "begin reset\n";
free_pointers();
GraphicsStateGuardian::reset();
@ -1332,7 +1331,6 @@ reset() {
// Now that the GSG has been initialized, make it available for
// optimizations.
add_gsg(this);
cerr << "end reset()\n";
}
@ -6393,7 +6391,10 @@ update_standard_texture_bindings() {
}
GLP(Enable)(target);
apply_texture(tc);
if (!apply_texture(tc)) {
GLP(Disable)(target);
break;
}
if (stage->involves_color_scale() && _color_scale_enabled) {
Colorf color = stage->get_color();
@ -6939,14 +6940,14 @@ specify_texture(Texture *tex) {
// texture, and makes it the current texture available
// for rendering.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
bool CLP(GraphicsStateGuardian)::
apply_texture(TextureContext *tc) {
CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
gtc->set_active(true);
GLenum target = get_texture_target(gtc->get_texture()->get_texture_type());
if (target == GL_NONE) {
return;
return false;
}
GLP(BindTexture)(target, gtc->_index);
@ -6954,8 +6955,12 @@ apply_texture(TextureContext *tc) {
// If the texture image was modified, reload the texture. This
// means we also re-specify the properties for good measure.
specify_texture(gtc->get_texture());
upload_texture(gtc);
gtc->mark_loaded();
bool okflag = upload_texture(gtc);
if (!okflag) {
GLCAT.error()
<< "Could not load " << *gtc->get_texture() << "\n";
return false;
}
} else if (gtc->was_properties_modified()) {
// If only the properties have been modified, we don't necessarily
@ -6965,6 +6970,7 @@ apply_texture(TextureContext *tc) {
}
report_my_gl_errors();
return true;
}
////////////////////////////////////////////////////////////////////
@ -6980,6 +6986,21 @@ bool CLP(GraphicsStateGuardian)::
upload_texture(CLP(TextureContext) *gtc) {
Texture *tex = gtc->get_texture();
if (_incomplete_render &&
!tex->has_ram_image() && tex->might_have_ram_image() &&
tex->has_simple_ram_image() &&
!_loader.is_null()) {
// If we don't have the texture data right now, go get it, but in
// the meantime load a temporary simple image in its place.
async_reload_texture(gtc);
if (!tex->has_ram_image()) {
if (gtc->was_simple_image_modified()) {
return upload_simple_texture(gtc);
}
return true;
}
}
CPTA_uchar image = tex->get_ram_image();
Texture::CompressionMode image_compression;
@ -7139,6 +7160,7 @@ upload_texture(CLP(TextureContext) *gtc) {
#endif
tex->texture_uploaded();
gtc->mark_loaded();
report_my_gl_errors();
return true;
@ -7462,6 +7484,68 @@ upload_texture_image(CLP(TextureContext) *gtc,
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::upload_simple_texture
// Access: Protected
// Description: This is used as a standin for upload_texture
// when the texture in question is unavailable (e.g. it
// hasn't yet been loaded from disk). Until the texture
// image itself becomes available, we will render the
// texture's "simple" image--a sharply reduced version
// of the same texture.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
upload_simple_texture(CLP(TextureContext) *gtc) {
report_my_gl_errors();
PStatTimer timer(_load_texture_pcollector);
Texture *tex = gtc->get_texture();
nassertr(tex != (Texture *)NULL, false);
int internal_format = GL_RGBA;
int external_format = GL_BGRA;
const unsigned char *image_ptr = tex->get_simple_ram_image();
if (image_ptr == (const unsigned char *)NULL) {
return false;
}
size_t image_size = tex->get_simple_ram_image_size();
PTA_uchar bgr_image;
if (!_supports_bgr) {
// If the GL doesn't claim to support BGR, we may have to reverse
// the component ordering of the image.
external_format = GL_RGBA;
image_ptr = fix_component_ordering(bgr_image, image_ptr, image_size,
external_format, tex);
}
int width = tex->get_simple_x_size();
int height = tex->get_simple_y_size();
int component_type = GL_UNSIGNED_BYTE;
if (GLCAT.is_debug()) {
GLCAT.debug()
<< "loading simple image for " << tex->get_name() << "\n";
}
// Turn off mipmaps for the simple texture.
if (tex->uses_mipmaps()) {
if (is_at_least_version(1, 2)) {
GLP(TexParameteri)(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
}
}
GLP(TexImage2D)(GL_TEXTURE_2D, 0, internal_format,
width, height, 0,
external_format, component_type, image_ptr);
gtc->mark_simple_loaded();
report_my_gl_errors();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::get_texture_memory_size
// Access: Protected

View File

@ -323,7 +323,7 @@ protected:
void do_auto_rescale_normal();
void specify_texture(Texture *tex);
void apply_texture(TextureContext *tc);
bool apply_texture(TextureContext *tc);
bool upload_texture(CLP(TextureContext) *gtc);
bool upload_texture_image(CLP(TextureContext) *gtc,
bool uses_mipmaps, int mipmap_bias,
@ -331,7 +331,8 @@ protected:
GLint internal_format, GLint external_format,
GLenum component_type,
bool one_page_only, int z,
Texture::CompressionMode image_compression);
Texture::CompressionMode image_compression);
bool upload_simple_texture(CLP(TextureContext) *gtc);
size_t get_texture_memory_size(Texture *tex);
void check_nonresident_texture(BufferContextChain &chain);

View File

@ -108,6 +108,15 @@ ConfigVariableBool preload_textures
"wasted memory from Textures that are created but never used "
"to render."));
ConfigVariableBool preload_simple_textures
("preload-simple-textures", false,
PRC_DESC("When this is true, every texture image will have a simple "
"image generated for it at load time. (Normally, textures "
"get a simple image at egg2bam time.) This slows the initial "
"loading time of textures, but allows you to take advantage "
"of gsg::set_incomplete_render() to load textures on-the-fly "
"in a sub-thread."));
ConfigVariableBool compressed_textures
("compressed-textures", false,
PRC_DESC("Set this to true to compress textures as they are loaded into "
@ -253,6 +262,31 @@ ConfigVariableBool textures_auto_power_2
"you then open a second window that doesn't support the same "
"capabilities, it will have no choice but to print an error message."));
ConfigVariableBool textures_header_only
("textures-header-only", false,
PRC_DESC("If this is true, texture images will not actually be loaded from "
"disk, but the image header information will be consulted to verify "
"number of channels and so forth. The texture images themselves "
"will be generated in a default blue color."));
ConfigVariableInt simple_image_size
("simple-image-size", "16 16",
PRC_DESC("This is an x y pair that specifies the maximum size of an "
"automatically-generated "
"texture simple image. The simple image can displayed before "
"the texture has been loaded from disk."));
ConfigVariableDouble simple_image_threshold
("simple-image-threshold", 0.1,
PRC_DESC("This is a value that indicates how closely a texture's "
"generated simple "
"image should approximate the original image. The smaller the "
"number, the closer the match; small numbers will result in "
"simple images close to the maximum size specified by "
"simple-image-size. Larger numbers will result in smaller "
"simple images. Generally the value should be considerably "
"less than 1."));
ConfigVariableEnum<ShaderUtilization> shader_utilization
("shader-utilization", SUT_none,
PRC_DESC("At times, panda may generate shaders. This variable controls what "
@ -270,14 +304,6 @@ ConfigVariableBool shader_auto_utilization
"you then open a second window that doesn't support the same "
"capabilities, it will have no choice but to print an error message."));
extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_header_only;
ConfigVariableBool textures_header_only
("textures-header-only", false,
PRC_DESC("If this is true, texture images will not actually be loaded from "
"disk, but the image header information will be consulted to verify "
"number of channels and so forth. The texture images themselves "
"will be generated in a default blue color."));
ConfigVariableInt geom_cache_size
("geom-cache-size", 5000,
PRC_DESC("Specifies the maximum number of entries in the cache "

View File

@ -52,6 +52,7 @@ extern EXPCL_PANDA_GOBJ ConfigVariableList exclude_texture_scale;
extern EXPCL_PANDA_GOBJ ConfigVariableBool keep_texture_ram;
extern EXPCL_PANDA_GOBJ ConfigVariableBool preload_textures;
extern EXPCL_PANDA_GOBJ ConfigVariableBool preload_simple_textures;
extern EXPCL_PANDA_GOBJ ConfigVariableBool compressed_textures;
extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_buffers;
extern EXPCL_PANDA_GOBJ ConfigVariableBool vertex_arrays;
@ -69,6 +70,8 @@ extern EXPCL_PANDA_GOBJ ConfigVariableEnum<AutoTextureScale> textures_power_2;
extern EXPCL_PANDA_GOBJ ConfigVariableEnum<AutoTextureScale> textures_square;
extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_auto_power_2;
extern EXPCL_PANDA_GOBJ ConfigVariableBool textures_header_only;
extern EXPCL_PANDA_GOBJ ConfigVariableInt simple_image_size;
extern EXPCL_PANDA_GOBJ ConfigVariableDouble simple_image_threshold;
extern EXPCL_PANDA_GOBJ ConfigVariableEnum<ShaderUtilization> shader_utilization;
extern EXPCL_PANDA_GOBJ ConfigVariableBool shader_auto_utilization;

View File

@ -194,6 +194,17 @@ release_texture(TextureContext *tc) {
_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

View File

@ -71,6 +71,7 @@ PUBLISHED:
bool dequeue_texture(Texture *tex);
bool is_texture_prepared(const Texture *tex) const;
void release_texture(TextureContext *tc);
void release_texture(Texture *tex);
int release_all_textures();
int get_num_queued_textures() const;
int get_num_prepared_textures() const;

View File

@ -129,9 +129,10 @@ setup_cube_map(int size, ComponentType component_type,
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
read(const Filename &fullpath) {
ReMutexHolder holder(_lock);
clear();
return do_read(fullpath, Filename(), 0, 0, 0, 0, false, false,
!preload_textures, NULL);
!preload_textures && !preload_simple_textures, NULL);
}
////////////////////////////////////////////////////////////////////
@ -148,9 +149,11 @@ read(const Filename &fullpath) {
INLINE bool Texture::
read(const Filename &fullpath, const Filename &alpha_fullpath,
int primary_file_num_channels, int alpha_file_channel) {
ReMutexHolder holder(_lock);
clear();
return do_read(fullpath, alpha_fullpath, primary_file_num_channels,
alpha_file_channel, 0, 0, false, false, !preload_textures,
alpha_file_channel, 0, 0, false, false,
!preload_textures && !preload_simple_textures,
NULL);
}
@ -167,10 +170,11 @@ read(const Filename &fullpath, const Filename &alpha_fullpath,
INLINE bool Texture::
read(const Filename &fullpath, int z, int n,
bool read_pages, bool read_mipmaps) {
ReMutexHolder holder(_lock);
++_properties_modified;
++_image_modified;
return do_read(fullpath, Filename(), 0, 0, z, n, read_pages, read_mipmaps,
!preload_textures, NULL);
!preload_textures && !preload_simple_textures, NULL);
}
////////////////////////////////////////////////////////////////////
@ -247,11 +251,12 @@ read(const Filename &fullpath, const Filename &alpha_fullpath,
int primary_file_num_channels, int alpha_file_channel,
int z, int n, bool read_pages, bool read_mipmaps,
BamCacheRecord *record) {
ReMutexHolder holder(_lock);
++_properties_modified;
++_image_modified;
return do_read(fullpath, alpha_fullpath, primary_file_num_channels,
alpha_file_channel, z, n, read_pages, read_mipmaps,
!preload_textures, record);
!preload_textures && !preload_simple_textures, record);
}
////////////////////////////////////////////////////////////////////
@ -261,6 +266,7 @@ read(const Filename &fullpath, const Filename &alpha_fullpath,
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
write(const Filename &fullpath) {
ReMutexHolder holder(_lock);
return do_write(fullpath, 0, 0, false, false);
}
@ -321,6 +327,7 @@ write(const Filename &fullpath) {
INLINE bool Texture::
write(const Filename &fullpath, int z, int n,
bool write_pages, bool write_mipmaps) {
ReMutexHolder holder(_lock);
return do_write(fullpath, z, n, write_pages, write_mipmaps);
}
@ -331,6 +338,7 @@ write(const Filename &fullpath, int z, int n,
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
load(const PNMImage &pnmimage) {
ReMutexHolder holder(_lock);
clear();
return do_load_one(pnmimage, get_name(), 0, 0);
}
@ -343,6 +351,7 @@ load(const PNMImage &pnmimage) {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
load(const PNMImage &pnmimage, int z, int n) {
ReMutexHolder holder(_lock);
++_properties_modified;
++_image_modified;
return do_load_one(pnmimage, get_name(), z, n);
@ -356,6 +365,7 @@ load(const PNMImage &pnmimage, int z, int n) {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
store(PNMImage &pnmimage) const {
ReMutexHolder holder(_lock);
return do_store_one(pnmimage, 0, 0);
}
@ -367,6 +377,7 @@ store(PNMImage &pnmimage) const {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
store(PNMImage &pnmimage, int z, int n) const {
ReMutexHolder holder(_lock);
return do_store_one(pnmimage, z, n);
}
@ -378,6 +389,7 @@ store(PNMImage &pnmimage, int z, int n) const {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
has_filename() const {
ReMutexHolder holder(_lock);
return !_filename.empty();
}
@ -390,6 +402,7 @@ has_filename() const {
////////////////////////////////////////////////////////////////////
INLINE const Filename &Texture::
get_filename() const {
ReMutexHolder holder(_lock);
return _filename;
}
@ -401,6 +414,7 @@ get_filename() const {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
has_alpha_filename() const {
ReMutexHolder holder(_lock);
return !_alpha_filename.empty();
}
@ -414,6 +428,7 @@ has_alpha_filename() const {
////////////////////////////////////////////////////////////////////
INLINE const Filename &Texture::
get_alpha_filename() const {
ReMutexHolder holder(_lock);
return _alpha_filename;
}
@ -425,6 +440,7 @@ get_alpha_filename() const {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
has_fullpath() const {
ReMutexHolder holder(_lock);
return !_fullpath.empty();
}
@ -437,6 +453,7 @@ has_fullpath() const {
////////////////////////////////////////////////////////////////////
INLINE const Filename &Texture::
get_fullpath() const {
ReMutexHolder holder(_lock);
return _fullpath;
}
@ -448,6 +465,7 @@ get_fullpath() const {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
has_alpha_fullpath() const {
ReMutexHolder holder(_lock);
return !_alpha_fullpath.empty();
}
@ -461,6 +479,7 @@ has_alpha_fullpath() const {
////////////////////////////////////////////////////////////////////
INLINE const Filename &Texture::
get_alpha_fullpath() const {
ReMutexHolder holder(_lock);
return _alpha_fullpath;
}
@ -546,6 +565,7 @@ get_pad_z_size() const {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_pad_size(int x, int y, int z) {
ReMutexHolder holder(_lock);
if (x > _x_size) x = _x_size;
if (y > _y_size) y = _y_size;
if (z > _z_size) z = _z_size;
@ -770,6 +790,7 @@ get_quality_level() const {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
might_have_ram_image() const {
ReMutexHolder holder(_lock);
return (has_ram_image() || has_filename());
}
@ -781,6 +802,7 @@ might_have_ram_image() const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_image_size() const {
ReMutexHolder holder(_lock);
if (_ram_images.empty()) {
return 0;
}
@ -801,6 +823,7 @@ get_ram_image_size() const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_page_size() const {
ReMutexHolder holder(_lock);
if (_ram_image_compression == CM_off || _ram_images.empty()) {
return get_expected_ram_page_size();
} else {
@ -817,6 +840,7 @@ get_ram_page_size() const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_expected_ram_image_size() const {
ReMutexHolder holder(_lock);
return get_expected_ram_page_size() * (size_t)_z_size;
}
@ -830,6 +854,7 @@ get_expected_ram_image_size() const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_expected_ram_page_size() const {
ReMutexHolder holder(_lock);
return (size_t)(_x_size * _y_size * _num_components * _component_width);
}
@ -860,6 +885,7 @@ get_ram_image_compression() const {
////////////////////////////////////////////////////////////////////
INLINE PTA_uchar Texture::
modify_ram_image() {
ReMutexHolder holder(_lock);
do_modify_ram_image();
++_image_modified;
return _ram_images[0]._image;
@ -876,6 +902,7 @@ modify_ram_image() {
////////////////////////////////////////////////////////////////////
INLINE PTA_uchar Texture::
make_ram_image() {
ReMutexHolder holder(_lock);
++_image_modified;
do_make_ram_image();
return _ram_images[0]._image;
@ -911,6 +938,7 @@ set_keep_ram_image(bool keep_ram_image) {
////////////////////////////////////////////////////////////////////
INLINE int Texture::
get_num_ram_mipmap_images() const {
ReMutexHolder holder(_lock);
return _ram_images.size();
}
@ -926,6 +954,7 @@ get_num_ram_mipmap_images() const {
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
has_ram_mipmap_image(int n) const {
ReMutexHolder holder(_lock);
return (n >= 0 && n < (int)_ram_images.size() && !_ram_images[n]._image.empty());
}
@ -938,6 +967,7 @@ has_ram_mipmap_image(int n) const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_mipmap_image_size(int n) const {
ReMutexHolder holder(_lock);
if (n >= 0 && n < (int)_ram_images.size()) {
return _ram_images[n]._image.size();
}
@ -959,6 +989,7 @@ get_ram_mipmap_image_size(int n) const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_mipmap_page_size(int n) const {
ReMutexHolder holder(_lock);
if (_ram_image_compression != CM_off) {
if (n >= 0 && n < (int)_ram_images.size()) {
return _ram_images[n]._page_size;
@ -978,6 +1009,7 @@ get_ram_mipmap_page_size(int n) const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_expected_ram_mipmap_image_size(int n) const {
ReMutexHolder holder(_lock);
return get_expected_ram_mipmap_page_size(n) * (size_t)get_expected_mipmap_z_size(n);
}
@ -991,6 +1023,7 @@ get_expected_ram_mipmap_image_size(int n) const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_expected_ram_mipmap_page_size(int n) const {
ReMutexHolder holder(_lock);
return (size_t)(get_expected_mipmap_x_size(n) * get_expected_mipmap_y_size(n) * _num_components * _component_width);
}
@ -1006,11 +1039,79 @@ get_expected_ram_mipmap_page_size(int n) const {
////////////////////////////////////////////////////////////////////
INLINE PTA_uchar Texture::
modify_ram_mipmap_image(int n) {
ReMutexHolder holder(_lock);
do_modify_ram_mipmap_image(n);
++_image_modified;
return _ram_images[n]._image;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_simple_x_size
// Access: Published
// Description: Returns the width of the "simple" image in texels.
////////////////////////////////////////////////////////////////////
INLINE int Texture::
get_simple_x_size() const {
return _simple_x_size;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_simple_y_size
// Access: Published
// Description: Returns the height of the "simple" image in texels.
////////////////////////////////////////////////////////////////////
INLINE int Texture::
get_simple_y_size() const {
return _simple_y_size;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::has_simple_ram_image
// Access: Published, Virtual
// Description: Returns true if the Texture has a "simple" image
// available in main RAM.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
has_simple_ram_image() const {
ReMutexHolder holder(_lock);
return !_simple_ram_image._image.empty();
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_simple_ram_image_size
// Access: Published
// Description: Returns the number of bytes used by the "simple"
// image, or 0 if there is no simple image.
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_simple_ram_image_size() const {
ReMutexHolder holder(_lock);
return _simple_ram_image._image.size();
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_simple_ram_image
// Access: Published
// Description: Returns the image data associated with the "simple"
// texture image. This is provided for some textures as
// an option to display while the main texture image is
// being loaded from disk.
//
// Unlike get_ram_image(), this function will always
// return immediately. Either the simple image is
// available, or it is not.
//
// The "simple" image is always 4 components, 1 byte
// each, regardless of the parameters of the full
// texture. The simple image is only supported for
// ordinary 2-d textures.
////////////////////////////////////////////////////////////////////
INLINE CPTA_uchar Texture::
get_simple_ram_image() const {
ReMutexHolder holder(_lock);
return _simple_ram_image._image;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_properties_modified
// Access: Published
@ -1035,6 +1136,18 @@ get_image_modified() const {
return _image_modified;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_simple_image_modified
// Access: Published
// Description: Returns a sequence number which is guaranteed to
// change at least every time the texture's "simple"
// image data is modified.
////////////////////////////////////////////////////////////////////
INLINE UpdateSeq Texture::
get_simple_image_modified() const {
return _simple_image_modified;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_filename
// Access: Published
@ -1050,6 +1163,7 @@ get_image_modified() const {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_filename(const Filename &filename) {
ReMutexHolder holder(_lock);
_filename = filename;
}
@ -1061,6 +1175,7 @@ set_filename(const Filename &filename) {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
clear_filename() {
ReMutexHolder holder(_lock);
_filename = Filename();
}
@ -1082,6 +1197,7 @@ clear_filename() {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_alpha_filename(const Filename &alpha_filename) {
ReMutexHolder holder(_lock);
_alpha_filename = alpha_filename;
}
@ -1093,6 +1209,7 @@ set_alpha_filename(const Filename &alpha_filename) {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
clear_alpha_filename() {
ReMutexHolder holder(_lock);
_alpha_filename = Filename();
}
@ -1106,6 +1223,7 @@ clear_alpha_filename() {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_fullpath(const Filename &fullpath) {
ReMutexHolder holder(_lock);
_fullpath = fullpath;
}
@ -1117,6 +1235,7 @@ set_fullpath(const Filename &fullpath) {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
clear_fullpath() {
ReMutexHolder holder(_lock);
_fullpath = Filename();
}
@ -1131,6 +1250,7 @@ clear_fullpath() {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_alpha_fullpath(const Filename &alpha_fullpath) {
ReMutexHolder holder(_lock);
_alpha_fullpath = alpha_fullpath;
}
@ -1142,6 +1262,7 @@ set_alpha_fullpath(const Filename &alpha_fullpath) {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
clear_alpha_fullpath() {
ReMutexHolder holder(_lock);
_alpha_fullpath = Filename();
}
@ -1154,6 +1275,7 @@ clear_alpha_fullpath() {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_x_size(int x_size) {
ReMutexHolder holder(_lock);
if (_x_size != x_size) {
_x_size = x_size;
++_image_modified;
@ -1171,6 +1293,7 @@ set_x_size(int x_size) {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_y_size(int y_size) {
ReMutexHolder holder(_lock);
if (_y_size != y_size) {
nassertv(_texture_type != Texture::TT_1d_texture || y_size == 1);
_y_size = y_size;
@ -1189,6 +1312,7 @@ set_y_size(int y_size) {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_z_size(int z_size) {
ReMutexHolder holder(_lock);
if (_z_size != z_size) {
nassertv(_texture_type == Texture::TT_3d_texture ||
(_texture_type == Texture::TT_cube_map && z_size == 6) ||

View File

@ -96,6 +96,10 @@ Texture(const string &name) :
_has_read_pages = false;
_has_read_mipmaps = false;
_num_mipmap_levels_read = 0;
_simple_x_size = 0;
_simple_y_size = 0;
_simple_ram_image._page_size = 0;
}
////////////////////////////////////////////////////////////////////
@ -105,44 +109,12 @@ Texture(const string &name) :
// an existing Texture.
////////////////////////////////////////////////////////////////////
Texture::
Texture(const Texture &copy) :
Namable(copy),
_filename(copy._filename),
_alpha_filename(copy._alpha_filename),
_fullpath(copy._fullpath),
_alpha_fullpath(copy._alpha_fullpath),
_primary_file_num_channels(copy._primary_file_num_channels),
_alpha_file_channel(copy._alpha_file_channel),
_x_size(copy._x_size),
_y_size(copy._y_size),
_z_size(copy._z_size),
_num_components(copy._num_components),
_component_width(copy._component_width),
_texture_type(copy._texture_type),
_format(copy._format),
_component_type(copy._component_type),
_loaded_from_image(copy._loaded_from_image),
_loaded_from_txo(copy._loaded_from_txo),
_has_read_pages(copy._has_read_pages),
_has_read_mipmaps(copy._has_read_mipmaps),
_num_mipmap_levels_read(copy._num_mipmap_levels_read),
_wrap_u(copy._wrap_u),
_wrap_v(copy._wrap_v),
_wrap_w(copy._wrap_w),
_minfilter(copy._minfilter),
_magfilter(copy._magfilter),
_anisotropic_degree(copy._anisotropic_degree),
_keep_ram_image(copy._keep_ram_image),
_border_color(copy._border_color),
_compression(copy._compression),
_match_framebuffer_format(copy._match_framebuffer_format),
_quality_level(copy._quality_level),
_pad_x_size(copy._pad_x_size),
_pad_y_size(copy._pad_y_size),
_pad_z_size(copy._pad_z_size),
_ram_image_compression(copy._ram_image_compression),
_ram_images(copy._ram_images)
{
Texture(const Texture &copy) {
_has_read_pages = false;
_has_read_mipmaps = false;
_num_mipmap_levels_read = 0;
operator = (copy);
}
////////////////////////////////////////////////////////////////////
@ -154,6 +126,10 @@ Texture(const Texture &copy) :
void Texture::
operator = (const Texture &copy) {
Namable::operator = (copy);
ReMutexHolder holder(_lock);
ReMutexHolder holder2(copy._lock);
_filename = copy._filename;
_alpha_filename = copy._alpha_filename;
if (!copy._fullpath.empty()) {
@ -190,8 +166,13 @@ operator = (const Texture &copy) {
_quality_level = copy._quality_level;
_ram_image_compression = copy._ram_image_compression;
_ram_images = copy._ram_images;
_simple_x_size = copy._simple_x_size;
_simple_y_size = copy._simple_y_size;
_simple_ram_image = copy._simple_ram_image;
++_properties_modified;
++_image_modified;
++_simple_image_modified;
}
////////////////////////////////////////////////////////////////////
@ -245,6 +226,7 @@ void Texture::
setup_texture(Texture::TextureType texture_type, int x_size, int y_size,
int z_size, Texture::ComponentType component_type,
Texture::Format format) {
ReMutexHolder holder(_lock);
if (texture_type == TT_cube_map) {
// Cube maps must always consist of six square images.
nassertv(x_size == y_size && z_size == 6);
@ -256,6 +238,9 @@ setup_texture(Texture::TextureType texture_type, int x_size, int y_size,
_wrap_v = WM_clamp;
_wrap_w = WM_clamp;
}
if (texture_type != TT_2d_texture) {
clear_simple_ram_image();
}
_texture_type = texture_type;
_x_size = x_size;
@ -284,6 +269,7 @@ setup_texture(Texture::TextureType texture_type, int x_size, int y_size,
////////////////////////////////////////////////////////////////////
void Texture::
generate_normalization_cube_map(int size) {
ReMutexHolder holder(_lock);
setup_cube_map(size, T_unsigned_byte, F_rgb);
PTA_uchar image = make_ram_image();
_keep_ram_image = true;
@ -385,6 +371,7 @@ generate_normalization_cube_map(int size) {
////////////////////////////////////////////////////////////////////
void Texture::
generate_alpha_scale_map() {
ReMutexHolder holder(_lock);
setup_1d_texture(256, T_unsigned_byte, F_alpha);
set_wrap_u(WM_clamp);
set_minfilter(FT_nearest);
@ -416,6 +403,7 @@ generate_alpha_scale_map() {
////////////////////////////////////////////////////////////////////
size_t Texture::
estimate_texture_memory() const {
ReMutexHolder holder(_lock);
size_t pixels = get_x_size() * get_y_size();
size_t bpp = 4;
@ -487,6 +475,7 @@ estimate_texture_memory() const {
////////////////////////////////////////////////////////////////////
void Texture::
set_aux_data(const string &key, TypedReferenceCount *aux_data) {
ReMutexHolder holder(_lock);
_aux_data[key] = aux_data;
}
@ -498,6 +487,7 @@ set_aux_data(const string &key, TypedReferenceCount *aux_data) {
////////////////////////////////////////////////////////////////////
void Texture::
clear_aux_data(const string &key) {
ReMutexHolder holder(_lock);
_aux_data.erase(key);
}
@ -510,6 +500,7 @@ clear_aux_data(const string &key) {
////////////////////////////////////////////////////////////////////
TypedReferenceCount *Texture::
get_aux_data(const string &key) const {
ReMutexHolder holder(_lock);
AuxData::const_iterator di;
di = _aux_data.find(key);
if (di != _aux_data.end()) {
@ -529,6 +520,7 @@ get_aux_data(const string &key) const {
////////////////////////////////////////////////////////////////////
bool Texture::
read_txo(istream &in, const string &filename) {
ReMutexHolder holder(_lock);
DatagramInputFile din;
if (!din.open(in)) {
@ -605,6 +597,7 @@ read_txo(istream &in, const string &filename) {
////////////////////////////////////////////////////////////////////
bool Texture::
write_txo(ostream &out, const string &filename) const {
ReMutexHolder holder(_lock);
DatagramOutputFile dout;
if (!dout.open(out)) {
@ -651,6 +644,7 @@ write_txo(ostream &out, const string &filename) const {
////////////////////////////////////////////////////////////////////
bool Texture::
reload() {
ReMutexHolder holder(_lock);
if (_loaded_from_image && has_filename()) {
reload_ram_image();
++_image_modified;
@ -671,6 +665,7 @@ reload() {
////////////////////////////////////////////////////////////////////
Texture *Texture::
load_related(const InternalName *suffix) const {
ReMutexHolder holder(_lock);
RelatedTextures::const_iterator ti;
ti = _related_textures.find(suffix);
if (ti != _related_textures.end()) {
@ -719,6 +714,7 @@ load_related(const InternalName *suffix) const {
////////////////////////////////////////////////////////////////////
void Texture::
set_wrap_u(Texture::WrapMode wrap) {
ReMutexHolder holder(_lock);
if (_wrap_u != wrap) {
++_properties_modified;
_wrap_u = wrap;
@ -732,6 +728,7 @@ set_wrap_u(Texture::WrapMode wrap) {
////////////////////////////////////////////////////////////////////
void Texture::
set_wrap_v(Texture::WrapMode wrap) {
ReMutexHolder holder(_lock);
if (_wrap_v != wrap) {
++_properties_modified;
_wrap_v = wrap;
@ -745,6 +742,7 @@ set_wrap_v(Texture::WrapMode wrap) {
////////////////////////////////////////////////////////////////////
void Texture::
set_wrap_w(Texture::WrapMode wrap) {
ReMutexHolder holder(_lock);
if (_wrap_w != wrap) {
++_properties_modified;
_wrap_w = wrap;
@ -758,6 +756,7 @@ set_wrap_w(Texture::WrapMode wrap) {
////////////////////////////////////////////////////////////////////
void Texture::
set_minfilter(Texture::FilterType filter) {
ReMutexHolder holder(_lock);
if (_minfilter != filter) {
++_properties_modified;
_minfilter = filter;
@ -771,6 +770,7 @@ set_minfilter(Texture::FilterType filter) {
////////////////////////////////////////////////////////////////////
void Texture::
set_magfilter(Texture::FilterType filter) {
ReMutexHolder holder(_lock);
if (_magfilter != filter) {
++_properties_modified;
_magfilter = filter;
@ -788,6 +788,7 @@ set_magfilter(Texture::FilterType filter) {
////////////////////////////////////////////////////////////////////
void Texture::
set_anisotropic_degree(int anisotropic_degree) {
ReMutexHolder holder(_lock);
if (_anisotropic_degree != anisotropic_degree) {
++_properties_modified;
_anisotropic_degree = anisotropic_degree;
@ -804,6 +805,7 @@ set_anisotropic_degree(int anisotropic_degree) {
////////////////////////////////////////////////////////////////////
void Texture::
set_border_color(const Colorf &color) {
ReMutexHolder holder(_lock);
if (_border_color != color) {
++_properties_modified;
_border_color = color;
@ -831,6 +833,7 @@ set_border_color(const Colorf &color) {
////////////////////////////////////////////////////////////////////
void Texture::
set_compression(Texture::CompressionMode compression) {
ReMutexHolder holder(_lock);
if (_compression != compression) {
++_properties_modified;
_compression = compression;
@ -870,6 +873,7 @@ set_render_to_texture(bool render_to_texture) {
////////////////////////////////////////////////////////////////////
void Texture::
set_quality_level(Texture::QualityLevel quality_level) {
ReMutexHolder holder(_lock);
if (_quality_level != quality_level) {
++_properties_modified;
_quality_level = quality_level;
@ -888,6 +892,7 @@ set_quality_level(Texture::QualityLevel quality_level) {
////////////////////////////////////////////////////////////////////
int Texture::
get_expected_num_mipmap_levels() const {
ReMutexHolder holder(_lock);
int size = max(_x_size, max(_y_size, _z_size));
int count = 1;
while (size > 1) {
@ -905,6 +910,7 @@ get_expected_num_mipmap_levels() const {
////////////////////////////////////////////////////////////////////
int Texture::
get_expected_mipmap_x_size(int n) const {
ReMutexHolder holder(_lock);
int size = max(_x_size, 1);
while (n > 0 && size > 1) {
size >>= 1;
@ -921,6 +927,7 @@ get_expected_mipmap_x_size(int n) const {
////////////////////////////////////////////////////////////////////
int Texture::
get_expected_mipmap_y_size(int n) const {
ReMutexHolder holder(_lock);
int size = max(_y_size, 1);
while (n > 0 && size > 1) {
size >>= 1;
@ -937,6 +944,7 @@ get_expected_mipmap_y_size(int n) const {
////////////////////////////////////////////////////////////////////
int Texture::
get_expected_mipmap_z_size(int n) const {
ReMutexHolder holder(_lock);
// 3-D textures have a different number of pages per each mipmap
// level. Other kinds of textures--especially, cube map
// textures--always have the same.
@ -983,6 +991,7 @@ get_expected_mipmap_z_size(int n) const {
////////////////////////////////////////////////////////////////////
bool Texture::
has_ram_image() const {
ReMutexHolder holder(_lock);
return !_ram_images.empty() && !_ram_images[0]._image.empty();
}
@ -1016,6 +1025,7 @@ has_ram_image() const {
////////////////////////////////////////////////////////////////////
CPTA_uchar Texture::
get_ram_image() {
ReMutexHolder holder(_lock);
if (_loaded_from_image && !has_ram_image() && has_filename()) {
reload_ram_image();
}
@ -1040,6 +1050,7 @@ get_ram_image() {
void Texture::
set_ram_image(PTA_uchar image, Texture::CompressionMode compression,
size_t page_size) {
ReMutexHolder holder(_lock);
nassertv(compression != CM_default);
nassertv(compression != CM_off || image.size() == get_expected_ram_image_size());
if (_ram_images.empty()) {
@ -1067,6 +1078,7 @@ set_ram_image(PTA_uchar image, Texture::CompressionMode compression,
////////////////////////////////////////////////////////////////////
void Texture::
clear_ram_image() {
ReMutexHolder holder(_lock);
_ram_image_compression = CM_off;
_ram_images.clear();
}
@ -1093,6 +1105,7 @@ get_keep_ram_image() const {
////////////////////////////////////////////////////////////////////
bool Texture::
has_all_ram_mipmap_images() const {
ReMutexHolder holder(_lock);
if (_ram_images.empty() || _ram_images[0]._image.empty()) {
// If we don't even have a base image, the answer is no.
return false;
@ -1128,6 +1141,7 @@ has_all_ram_mipmap_images() const {
////////////////////////////////////////////////////////////////////
CPTA_uchar Texture::
get_ram_mipmap_image(int n) {
ReMutexHolder holder(_lock);
if (n < (int)_ram_images.size()) {
return _ram_images[n]._image;
}
@ -1145,6 +1159,7 @@ get_ram_mipmap_image(int n) {
////////////////////////////////////////////////////////////////////
PTA_uchar Texture::
make_ram_mipmap_image(int n) {
ReMutexHolder holder(_lock);
nassertr(_ram_image_compression == CM_off, PTA_uchar(get_class_type()));
while (n >= (int)_ram_images.size()) {
@ -1171,6 +1186,7 @@ make_ram_mipmap_image(int n) {
////////////////////////////////////////////////////////////////////
void Texture::
set_ram_mipmap_image(int n, PTA_uchar image, size_t page_size) {
ReMutexHolder holder(_lock);
nassertv(_ram_image_compression != CM_off || image.size() == get_expected_ram_mipmap_image_size(n));
while (n >= (int)_ram_images.size()) {
@ -1197,6 +1213,7 @@ set_ram_mipmap_image(int n, PTA_uchar image, size_t page_size) {
////////////////////////////////////////////////////////////////////
void Texture::
clear_ram_mipmap_image(int n) {
ReMutexHolder holder(_lock);
if (n >= (int)_ram_images.size()) {
return;
}
@ -1212,6 +1229,7 @@ clear_ram_mipmap_image(int n) {
////////////////////////////////////////////////////////////////////
void Texture::
clear_ram_mipmap_images() {
ReMutexHolder holder(_lock);
if (!_ram_images.empty()) {
_ram_images.erase(_ram_images.begin() + 1, _ram_images.end());
}
@ -1232,6 +1250,7 @@ clear_ram_mipmap_images() {
////////////////////////////////////////////////////////////////////
void Texture::
generate_ram_mipmap_images() {
ReMutexHolder holder(_lock);
nassertv(has_ram_image());
nassertv(get_ram_image_compression() == CM_off);
nassertv(get_component_type() != T_float);
@ -1274,6 +1293,142 @@ generate_ram_mipmap_images() {
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_simple_ram_image
// Access: Published
// Description: Replaces the internal "simple" texture image. This
// can be used as an option to display while the main
// texture image is being loaded from disk. It is
// normally a very small image, 16x16 or smaller (and
// maybe even 1x1), that is designed to give just enough
// sense of color to serve as a placeholder until the
// full texture is available.
//
// The "simple" image is always 4 components, 1 byte
// each, regardless of the parameters of the full
// texture. The simple image is only supported for
// ordinary 2-d textures.
//
// Also see generate_simple_ram_image().
////////////////////////////////////////////////////////////////////
void Texture::
set_simple_ram_image(PTA_uchar image, int x_size, int y_size) {
ReMutexHolder holder(_lock);
nassertv(get_texture_type() == TT_2d_texture);
size_t expected_page_size = (size_t)(x_size * y_size * 4);
nassertv(image.size() == expected_page_size);
_simple_x_size = x_size;
_simple_y_size = y_size;
_simple_ram_image._image = image;
_simple_ram_image._page_size = image.size();
_simple_image_date_generated = 0;
++_simple_image_modified;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::generate_simple_ram_image
// Access: Published
// Description: Computes the "simple" ram image by loading the main
// RAM image, if it is not already available, and
// reducing it to 16x16 or smaller. This may be an
// expensive operation.
////////////////////////////////////////////////////////////////////
void Texture::
generate_simple_ram_image() {
ReMutexHolder holder(_lock);
if (get_texture_type() != TT_2d_texture) {
clear_simple_ram_image();
return;
}
PNMImage pnmimage;
if (!store(pnmimage)) {
clear_simple_ram_image();
return;
}
// Start at the suggested size from the config file.
int x_size = simple_image_size.get_word(0);
int y_size = simple_image_size.get_word(1);
// Limit it to no larger than the source image, and also make it a
// power of two.
x_size = down_to_power_2(min(x_size, get_x_size()));
y_size = down_to_power_2(min(y_size, get_y_size()));
// Generate a reduced image of that size.
PNMImage scaled(x_size, y_size, pnmimage.get_num_channels());
scaled.quick_filter_from(pnmimage);
// Make sure the reduced image has 4 components, by convention.
if (!scaled.has_alpha()) {
scaled.add_alpha();
scaled.alpha_fill(1.0);
}
scaled.set_num_channels(4);
// Now see if we can go even smaller.
bool did_anything;
do {
did_anything = false;
// Try to reduce X.
if (x_size > 1) {
int new_x_size = (x_size >> 1);
PNMImage smaller(new_x_size, y_size, 4);
smaller.quick_filter_from(scaled);
PNMImage bigger(x_size, y_size, 4);
bigger.quick_filter_from(smaller);
if (compare_images(scaled, bigger)) {
scaled.take_from(smaller);
x_size = new_x_size;
did_anything = true;
}
}
// Try to reduce Y.
if (y_size > 1) {
int new_y_size = (y_size >> 1);
PNMImage smaller(x_size, new_y_size, 4);
smaller.quick_filter_from(scaled);
PNMImage bigger(x_size, y_size, 4);
bigger.quick_filter_from(smaller);
if (compare_images(scaled, bigger)) {
scaled.take_from(smaller);
y_size = new_y_size;
did_anything = true;
}
}
} while (did_anything);
size_t expected_page_size = (size_t)(x_size * y_size * 4);
PTA_uchar image = PTA_uchar::empty_array(expected_page_size, get_class_type());
convert_from_pnmimage(image, expected_page_size, 0, scaled, 4, 1);
set_simple_ram_image(image, x_size, y_size);
_simple_image_date_generated = (PN_int32)time(NULL);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::clear_simple_ram_image
// Access: Published
// Description: Discards the current "simple" image.
////////////////////////////////////////////////////////////////////
void Texture::
clear_simple_ram_image() {
ReMutexHolder holder(_lock);
_simple_x_size = 0;
_simple_y_size = 0;
_simple_ram_image._image.clear();
_simple_ram_image._page_size = 0;
_simple_image_date_generated = 0;
++_simple_image_modified;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::prepare
// Access: Published
@ -1300,6 +1455,7 @@ prepare(PreparedGraphicsObjects *prepared_objects) {
////////////////////////////////////////////////////////////////////
bool Texture::
is_prepared(PreparedGraphicsObjects *prepared_objects) const {
ReMutexHolder holder(_lock);
Contexts::const_iterator ci;
ci = _contexts.find(prepared_objects);
if (ci != _contexts.end()) {
@ -1317,6 +1473,7 @@ is_prepared(PreparedGraphicsObjects *prepared_objects) const {
////////////////////////////////////////////////////////////////////
bool Texture::
release(PreparedGraphicsObjects *prepared_objects) {
ReMutexHolder holder(_lock);
Contexts::iterator ci;
ci = _contexts.find(prepared_objects);
if (ci != _contexts.end()) {
@ -1342,6 +1499,7 @@ release(PreparedGraphicsObjects *prepared_objects) {
////////////////////////////////////////////////////////////////////
int Texture::
release_all() {
ReMutexHolder holder(_lock);
// We have to traverse a copy of the _contexts list, because the
// PreparedGraphicsObjects object will call clear_prepared() in response
// to each release_texture(), and we don't want to be modifying the
@ -1373,6 +1531,7 @@ release_all() {
////////////////////////////////////////////////////////////////////
void Texture::
write(ostream &out, int indent_level) const {
ReMutexHolder holder(_lock);
indent(out, indent_level)
<< get_type() << " " << get_name();
if (!get_filename().empty()) {
@ -1549,6 +1708,13 @@ write(ostream &out, int indent_level) const {
indent(out, indent_level + 2)
<< "no ram image\n";
}
if (has_simple_ram_image()) {
indent(out, indent_level + 2)
<< "simple image: " << get_simple_x_size() << " x "
<< get_simple_y_size() << ", "
<< get_simple_ram_image_size() << " bytes\n";
}
}
////////////////////////////////////////////////////////////////////
@ -1559,6 +1725,7 @@ write(ostream &out, int indent_level) const {
////////////////////////////////////////////////////////////////////
void Texture::
set_format(Texture::Format format) {
ReMutexHolder holder(_lock);
_format = format;
switch (_format) {
@ -1606,6 +1773,7 @@ set_format(Texture::Format format) {
////////////////////////////////////////////////////////////////////
void Texture::
set_component_type(Texture::ComponentType component_type) {
ReMutexHolder holder(_lock);
_component_type = component_type;
switch (component_type) {
@ -1662,6 +1830,7 @@ is_mipmap(FilterType filter_type) {
TextureContext *Texture::
prepare_now(PreparedGraphicsObjects *prepared_objects,
GraphicsStateGuardianBase *gsg) {
ReMutexHolder holder(_lock);
Contexts::const_iterator ci;
ci = _contexts.find(prepared_objects);
if (ci != _contexts.end()) {
@ -1689,6 +1858,7 @@ prepare_now(PreparedGraphicsObjects *prepared_objects,
////////////////////////////////////////////////////////////////////
void Texture::
texture_uploaded() {
ReMutexHolder holder(_lock);
if (!keep_texture_ram && !_keep_ram_image) {
// Once we have prepared the texture, we can generally safely
// remove the pixels from main RAM. The GSG is now responsible
@ -2074,6 +2244,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
<< "Texture::read() - couldn't read: " << fullpath << endl;
return false;
}
Thread::consider_yield();
}
PNMImage alpha_image;
@ -2119,6 +2290,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
<< "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
return false;
}
Thread::consider_yield();
}
}
@ -2156,6 +2328,7 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
alpha_image.get_num_channels(),
alpha_image.get_maxval(), alpha_image.get_type());
scaled.quick_filter_from(alpha_image);
Thread::consider_yield();
alpha_image = scaled;
}
}
@ -2375,17 +2548,19 @@ do_load_one(const PNMImage &pnmimage, const string &name, int z, int n) {
PNMImage scaled(x_size, y_size, pnmimage.get_num_channels(),
pnmimage.get_maxval(), pnmimage.get_type());
scaled.quick_filter_from(pnmimage);
Thread::consider_yield();
convert_from_pnmimage(_ram_images[n]._image,
get_expected_ram_mipmap_page_size(n), z,
scaled);
scaled, _num_components, _component_width);
} else {
// Now copy the pixel data from the PNMImage into our internal
// _image component.
convert_from_pnmimage(_ram_images[n]._image,
get_expected_ram_mipmap_page_size(n), z,
pnmimage);
pnmimage, _num_components, _component_width);
}
Thread::consider_yield();
return true;
}
@ -2408,6 +2583,7 @@ do_store_one(PNMImage &pnmimage, int z, int n) const {
return convert_to_pnmimage(pnmimage,
get_expected_mipmap_x_size(n),
get_expected_mipmap_y_size(n),
_num_components, _component_width,
_ram_images[n]._image,
get_ram_mipmap_page_size(n), z);
}
@ -2718,26 +2894,27 @@ reconsider_image_properties(int x_size, int y_size, int num_components,
////////////////////////////////////////////////////////////////////
// Function: Texture::convert_from_pnmimage
// Access: Private
// Access: Private, Static
// Description: Internal method to convert pixel data from the
// indicated PNMImage into the given ram_image.
////////////////////////////////////////////////////////////////////
void Texture::
convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
const PNMImage &pnmimage) {
const PNMImage &pnmimage,
int num_components, int component_width) {
int x_size = pnmimage.get_x_size();
int y_size = pnmimage.get_y_size();
xelval maxval = pnmimage.get_maxval();
bool is_grayscale = (_num_components == 1 || _num_components == 2);
bool has_alpha = (_num_components == 2 || _num_components == 4);
bool is_grayscale = (num_components == 1 || num_components == 2);
bool has_alpha = (num_components == 2 || num_components == 4);
bool img_has_alpha = pnmimage.has_alpha();
int idx = page_size * z;
nassertv(idx + page_size <= image.size());
unsigned char *p = &image[idx];
if (maxval == 255) {
if (maxval == 255 && component_width == 1) {
// Most common case: one byte per pixel, and the source image
// shows a maxval of 255. No scaling is necessary.
for (int j = y_size-1; j >= 0; j--) {
@ -2759,7 +2936,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
}
}
} else if (maxval == 65535) {
} else if (maxval == 65535 && component_width == 2) {
// Another possible case: two bytes per pixel, and the source
// image shows a maxval of 65535. Again, no scaling is necessary.
for (int j = y_size-1; j >= 0; j--) {
@ -2781,7 +2958,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
}
}
} else if (maxval <= 255) {
} else if (component_width == 1) {
// A less common case: one byte per pixel, but the maxval is
// something other than 255. In this case, we should scale the
// pixel values up to the appropriate amount.
@ -2806,7 +2983,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
}
}
} else {
} else { // component_width == 2
// Another uncommon case: two bytes per pixel, and the maxval is
// something other than 65535. Again, we must scale the pixel
// values.
@ -2837,14 +3014,15 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
////////////////////////////////////////////////////////////////////
// Function: Texture::convert_to_pnmimage
// Access: Private
// Access: Private, Static
// Description: Internal method to convert pixel data to the
// indicated PNMImage from the given ram_image.
////////////////////////////////////////////////////////////////////
bool Texture::
convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
CPTA_uchar image, size_t page_size, int z) const {
pnmimage.clear(x_size, y_size, _num_components);
int num_components, int component_width,
CPTA_uchar image, size_t page_size, int z) {
pnmimage.clear(x_size, y_size, num_components);
bool has_alpha = pnmimage.has_alpha();
bool is_grayscale = pnmimage.is_grayscale();
@ -2852,7 +3030,7 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
nassertr(idx + page_size <= image.size(), false);
const unsigned char *p = &image[idx];
if (_component_type == T_unsigned_byte) {
if (component_width == 1) {
for (int j = y_size-1; j >= 0; j--) {
for (int i = 0; i < x_size; i++) {
if (is_grayscale) {
@ -2868,7 +3046,7 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
}
}
} else if (_component_type == T_unsigned_short) {
} else if (component_width == 2) {
for (int j = y_size-1; j >= 0; j--) {
for (int i = 0; i < x_size; i++) {
if (is_grayscale) {
@ -2885,9 +3063,6 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
}
} else {
gobj_cat.error()
<< "Couldn't write image for " << get_name()
<< "; inappropriate data type " << (int)_component_type << ".\n";
return false;
}
@ -3043,6 +3218,34 @@ consider_downgrade(PNMImage &pnmimage, int num_channels) {
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::compare_images
// Access: Private, Static
// Description: Called by generate_simple_ram_image(), this compares
// the two PNMImages pixel-by-pixel. If they're similar
// enough (within a given threshold), returns true.
////////////////////////////////////////////////////////////////////
bool Texture::
compare_images(const PNMImage &a, const PNMImage &b) {
nassertr(a.get_maxval() == 255 && b.get_maxval() == 255, false);
nassertr(a.get_num_channels() == 4 && b.get_num_channels() == 4, false);
nassertr(a.get_x_size() == b.get_x_size() &&
a.get_y_size() == b.get_y_size(), false);
int delta = 0;
for (int yi = 0; yi < a.get_y_size(); ++yi) {
for (int xi = 0; xi < a.get_x_size(); ++xi) {
delta += abs(a.get_red_val(xi, yi) - b.get_red_val(xi, yi));
delta += abs(a.get_green_val(xi, yi) - b.get_green_val(xi, yi));
delta += abs(a.get_blue_val(xi, yi) - b.get_blue_val(xi, yi));
delta += abs(a.get_alpha_val(xi, yi) - b.get_alpha_val(xi, yi));
}
}
double average_delta = (double)delta / ((double)a.get_x_size() * (double)b.get_y_size() * (double)a.get_maxval());
return (average_delta <= simple_image_threshold);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read_txo_file
// Access: Private

View File

@ -26,6 +26,8 @@
#include "pmap.h"
#include "config_gobj.h"
#include "pStatCollector.h"
#include "reMutex.h"
#include "reMutexHolder.h"
class PNMImage;
class TextureContext;
@ -313,8 +315,18 @@ PUBLISHED:
void clear_ram_mipmap_images();
void generate_ram_mipmap_images();
INLINE int get_simple_x_size() const;
INLINE int get_simple_y_size() const;
INLINE bool has_simple_ram_image() const;
INLINE size_t get_simple_ram_image_size() const;
INLINE CPTA_uchar get_simple_ram_image() const;
void set_simple_ram_image(PTA_uchar image, int x_size, int y_size);
void generate_simple_ram_image();
void clear_simple_ram_image();
INLINE UpdateSeq get_properties_modified() const;
INLINE UpdateSeq get_image_modified() const;
INLINE UpdateSeq get_simple_image_modified() const;
void prepare(PreparedGraphicsObjects *prepared_objects);
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const;
@ -428,15 +440,20 @@ protected:
};
private:
void convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
const PNMImage &pnmimage);
bool convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
CPTA_uchar image, size_t page_size, int z) const;
static void convert_from_pnmimage(PTA_uchar &image, size_t page_size,
int z, const PNMImage &pnmimage,
int num_components, int component_width);
static bool convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
int num_components, int component_width,
CPTA_uchar image, size_t page_size,
int z);
void clear_prepared(PreparedGraphicsObjects *prepared_objects);
void consider_rescale(PNMImage &pnmimage, const string &name);
void consider_downgrade(PNMImage &pnmimage, int num_channels);
static bool compare_images(const PNMImage &a, const PNMImage &b);
INLINE static void store_unscaled_byte(unsigned char *&p, int value);
INLINE static void store_unscaled_short(unsigned char *&p, int value);
INLINE static void store_scaled_byte(unsigned char *&p, int value, double scale);
@ -480,6 +497,9 @@ private:
size_t page_size);
protected:
// Protects all of the members of this class.
ReMutex _lock;
Filename _filename;
Filename _alpha_filename;
Filename _fullpath;
@ -550,9 +570,17 @@ protected:
// additional mipmap levels.
typedef pvector<RamImage> RamImages;
RamImages _ram_images;
// This is the simple image, which may be loaded before the texture
// is loaded from disk. It exists only for 2-d textures.
RamImage _simple_ram_image;
int _simple_x_size;
int _simple_y_size;
PN_int32 _simple_image_date_generated;
UpdateSeq _properties_modified;
UpdateSeq _image_modified;
UpdateSeq _simple_image_modified;
private:
// The auxiliary data is not recorded to a bam file.

View File

@ -72,12 +72,24 @@ was_image_modified() const {
return _image_modified != _texture->get_image_modified();
}
////////////////////////////////////////////////////////////////////
// Function: TextureContext::was_simple_image_modified
// Access: Public
// Description: Returns true if the texture's "simple" image has been
// modified since the last time mark_simple_loaded() was
// called.
////////////////////////////////////////////////////////////////////
INLINE bool TextureContext::
was_simple_image_modified() const {
return _simple_image_modified != _texture->get_simple_image_modified();
}
////////////////////////////////////////////////////////////////////
// Function: TextureContext::mark_loaded
// Access: Public
// Description: Should be called after the TextureContext has been
// loaded into graphics memory, this updates the
// internal flags for changed_size() and modified().
// Description: Should be called after the texture has been loaded
// into graphics memory, this updates the internal flags
// for changed_size() and modified().
////////////////////////////////////////////////////////////////////
INLINE void TextureContext::
mark_loaded() {
@ -89,3 +101,19 @@ mark_loaded() {
// Assume the texture is now resident.
set_resident(true);
}
////////////////////////////////////////////////////////////////////
// Function: TextureContext::mark_simple_loaded
// Access: Public
// Description: Should be called after the texture's "simple" image
// has been loaded into graphics memory.
////////////////////////////////////////////////////////////////////
INLINE void TextureContext::
mark_simple_loaded() {
_properties_modified = _texture->get_properties_modified();
_simple_image_modified = _texture->get_simple_image_modified();
update_modified(max(_properties_modified, _simple_image_modified));
// The texture's not exactly resident now, but some part of it is.
set_resident(true);
}

View File

@ -44,9 +44,11 @@ PUBLISHED:
INLINE bool was_modified() const;
INLINE bool was_properties_modified() const;
INLINE bool was_image_modified() const;
INLINE bool was_simple_image_modified() const;
public:
INLINE void mark_loaded();
INLINE void mark_simple_loaded();
private:
// This cannot be a PT(Texture), because the texture and the GSG
@ -55,6 +57,7 @@ private:
Texture *_texture;
UpdateSeq _properties_modified;
UpdateSeq _image_modified;
UpdateSeq _simple_image_modified;
public:
static TypeHandle get_class_type() {

View File

@ -278,6 +278,9 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
gobj_cat.info()
<< "Texture " << filename << " found in disk cache.\n";
tex = DCAST(Texture, record->extract_data());
if (preload_simple_textures && !tex->has_simple_ram_image()) {
tex->generate_simple_ram_image();
}
if (!preload_textures) {
// But drop the RAM until we need it.
tex->clear_ram_image();
@ -300,6 +303,11 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
report_texture_unreadable(filename);
return NULL;
}
if (preload_simple_textures) {
tex->generate_simple_ram_image();
}
store_record = (record != (BamCacheRecord *)NULL);
}
@ -331,11 +339,11 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
nassertr(tex->has_ram_image(), tex);
record->set_data(tex, false);
cache->store(record);
}
if (!preload_textures) {
// And now drop the RAM until we need it.
tex->clear_ram_image();
}
if (!preload_textures) {
// And now drop the RAM until we need it.
tex->clear_ram_image();
}
nassertr(!tex->get_fullpath().empty(), tex);
@ -424,6 +432,11 @@ ns_load_texture(const Filename &orig_filename,
report_texture_unreadable(filename);
return NULL;
}
if (preload_simple_textures) {
tex->generate_simple_ram_image();
}
store_record = (record != (BamCacheRecord *)NULL);
}
@ -457,6 +470,11 @@ ns_load_texture(const Filename &orig_filename,
cache->store(record);
}
if (!preload_textures) {
// And now drop the RAM until we need it.
tex->clear_ram_image();
}
nassertr(!tex->get_fullpath().empty(), tex);
// Finally, apply any post-loading texture filters.

View File

@ -108,6 +108,8 @@ class Lens;
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_GSGBASE GraphicsStateGuardianBase : public TypedWritableReferenceCount {
PUBLISHED:
virtual bool get_incomplete_render() const=0;
virtual bool prefers_triangle_strips() const=0;
virtual int get_max_vertices_per_array() const=0;
virtual int get_max_vertices_per_primitive() const=0;

View File

@ -119,6 +119,7 @@
textureAttrib.I textureAttrib.h \
texGenAttrib.I texGenAttrib.h \
textureCollection.I textureCollection.h \
textureReloadRequest.I textureReloadRequest.h \
textureStageCollection.I textureStageCollection.h \
transformState.I transformState.h \
transparencyAttrib.I transparencyAttrib.h \
@ -232,6 +233,7 @@
textureAttrib.cxx \
texGenAttrib.cxx \
textureCollection.cxx \
textureReloadRequest.cxx \
textureStageCollection.cxx \
transformState.cxx \
transparencyAttrib.cxx \
@ -343,6 +345,7 @@
textureAttrib.I textureAttrib.h \
texGenAttrib.I texGenAttrib.h \
textureCollection.I textureCollection.h \
textureReloadRequest.I textureReloadRequest.h \
textureStageCollection.I textureStageCollection.h \
transformState.I transformState.h \
transparencyAttrib.I transparencyAttrib.h \

View File

@ -94,6 +94,7 @@
#include "texMatrixAttrib.h"
#include "texProjectorEffect.h"
#include "textureAttrib.h"
#include "textureReloadRequest.h"
#include "texGenAttrib.h"
#include "transformState.h"
#include "transparencyAttrib.h"
@ -338,15 +339,6 @@ ConfigVariableString default_model_extension
"Panda's loader; new code should probably give the correct name "
"for each model file they intend to load."));
ConfigVariableBool allow_incomplete_render
("allow-incomplete-render", false,
PRC_DESC("When this is true, the frame may be rendered even if some of the "
"geometry in the scene has been paged out. The nonresident "
"geometry will be rendered as soon as it can be paged back in, "
"which may be several frames in the future. When this is false, "
"geometry is always paged in when needed, holding up the frame "
"render if necessary."));
ConfigVariableEnum<LODNodeType> default_lod_type
("default-lod-type", LNT_pop,
PRC_DESC("Set this to either 'pop' or 'fade' to determine the type of "
@ -448,6 +440,7 @@ init_libpgraph() {
TexMatrixAttrib::init_type();
TexProjectorEffect::init_type();
TextureAttrib::init_type();
TextureReloadRequest::init_type();
TexGenAttrib::init_type();
TransformState::init_type();
TransparencyAttrib::init_type();

View File

@ -68,7 +68,6 @@ extern ConfigVariableBool m_dual_flash;
extern ConfigVariableList load_file_type;
extern ConfigVariableString default_model_extension;
extern EXPCL_PANDA_PGRAPH ConfigVariableBool allow_incomplete_render;
extern ConfigVariableEnum<LODNodeType> default_lod_type;

View File

@ -107,7 +107,7 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
static const Colorf flash_multisample_color(0.78f, 0.05f, 0.81f, 1.0f);
static const Colorf flash_dual_color(0.92f, 0.01f, 0.01f, 1.0f);
bool force = !allow_incomplete_render;
bool force = !_gsg->get_incomplete_render();
Thread *current_thread = traverser->get_current_thread();
// Check to see if there's a special transparency setting.
@ -264,7 +264,7 @@ finish_cull(SceneSetup *scene_setup, Thread *current_thread) {
////////////////////////////////////////////////////////////////////
void CullResult::
draw(Thread *current_thread) {
bool force = !allow_incomplete_render;
bool force = !_gsg->get_incomplete_render();
// Ask the bin manager for the correct order to draw all the bins.
CullBinManager *bin_manager = CullBinManager::get_global_ptr();

View File

@ -25,6 +25,7 @@
#include "textureAttrib.cxx"
#include "texGenAttrib.cxx"
#include "textureCollection.cxx"
#include "textureReloadRequest.cxx"
#include "textureStageCollection.cxx"
#include "transformState.cxx"
#include "transparencyAttrib.cxx"

View File

@ -0,0 +1,51 @@
// Filename: textureReloadRequest.I
// Created by: drose (12Aug08)
//
////////////////////////////////////////////////////////////////////
//
// 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."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: TextureReloadRequest::Constructor
// Access: Published
// Description: Create a new TextureReloadRequest, and add it to the loader
// via load_async(), to begin an asynchronous load.
////////////////////////////////////////////////////////////////////
INLINE TextureReloadRequest::
TextureReloadRequest(TextureContext *tc) :
_texture_context(tc),
_is_ready(false)
{
nassertv(_texture_context != (TextureContext *)NULL);
_texture = _texture_context->get_texture();
}
////////////////////////////////////////////////////////////////////
// Function: TextureReloadRequest::get_texture_context
// Access: Published
// Description: Returns the TextureContext object associated with
// this asynchronous TextureReloadRequest.
////////////////////////////////////////////////////////////////////
INLINE TextureContext *TextureReloadRequest::
get_texture_context() const {
return _texture_context;
}
////////////////////////////////////////////////////////////////////
// Function: TextureReloadRequest::is_ready
// Access: Published
// Description: Returns true if this request has completed, false if
// it is still pending.
////////////////////////////////////////////////////////////////////
INLINE bool TextureReloadRequest::
is_ready() const {
return _is_ready;
}

View File

@ -0,0 +1,35 @@
// Filename: textureReloadRequest.cxx
// Created by: drose (12Aug08)
//
////////////////////////////////////////////////////////////////////
//
// 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 "textureReloadRequest.h"
#include "loader.h"
TypeHandle TextureReloadRequest::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: TextureReloadRequest::do_task
// Access: Protected, Virtual
// Description: Performs the task: that is, loads the one model.
////////////////////////////////////////////////////////////////////
bool TextureReloadRequest::
do_task() {
// Don't reload the texture if it doesn't need it.
if (_texture_context->was_image_modified()) {
_texture->get_ram_image();
}
_is_ready = true;
// Don't continue the task; we're done.
return false;
}

View File

@ -0,0 +1,71 @@
// Filename: textureReloadRequest.h
// Created by: drose (12Aug08)
//
////////////////////////////////////////////////////////////////////
//
// 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."
//
////////////////////////////////////////////////////////////////////
#ifndef TEXTURERELOADREQUEST
#define TEXTURERELOADREQUEST
#include "pandabase.h"
#include "asyncTask.h"
#include "texture.h"
#include "textureContext.h"
#include "pointerTo.h"
////////////////////////////////////////////////////////////////////
// Class : TextureReloadRequest
// Description : This loader request will call
// Texture::get_ram_image() in a sub-thread, to force
// the texture's image to be re-read from disk. It is
// used by GraphicsStateGuardian::async_reload_texture(),
// when get_incomplete_render() is true.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA_PGRAPH TextureReloadRequest : public AsyncTask {
public:
ALLOC_DELETED_CHAIN(TextureReloadRequest);
PUBLISHED:
INLINE TextureReloadRequest(TextureContext *tc);
INLINE TextureContext *get_texture_context() const;
INLINE bool is_ready() const;
protected:
virtual bool do_task();
private:
TextureContext *_texture_context;
PT(Texture) _texture;
bool _is_ready;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
AsyncTask::init_type();
register_type(_type_handle, "TextureReloadRequest",
AsyncTask::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "textureReloadRequest.I"
#endif

View File

@ -415,6 +415,7 @@ read_data(xel *array, xelval *) {
}
x++;
}
Thread::consider_yield();
}
/* Step 7: Finish decompression */