diff --git a/panda/src/cull/drawCullHandler.cxx b/panda/src/cull/drawCullHandler.cxx index f59555b6a6..d55240141c 100644 --- a/panda/src/cull/drawCullHandler.cxx +++ b/panda/src/cull/drawCullHandler.cxx @@ -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)) { diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 252a20583c..58f454aed9 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -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 " diff --git a/panda/src/display/config_display.h b/panda/src/display/config_display.h index fe59c07b15..e1cca29cf7 100644 --- a/panda/src/display/config_display.h +++ b/panda/src/display/config_display.h @@ -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; diff --git a/panda/src/display/graphicsEngine.I b/panda/src/display/graphicsEngine.I index 6cce7d6a6d..090d998243 100644 --- a/panda/src/display/graphicsEngine.I +++ b/panda/src/display/graphicsEngine.I @@ -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 diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index a4f0fd0274..8818cf6ab2 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -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); diff --git a/panda/src/display/graphicsEngine.h b/panda/src/display/graphicsEngine.h index 982e4c2157..98141a8e40 100644 --- a/panda/src/display/graphicsEngine.h +++ b/panda/src/display/graphicsEngine.h @@ -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. diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index d2cac168b5..89d784a387 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -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 diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 20a6334eeb..5715fe82ee 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -40,6 +40,7 @@ #include "directionalLight.h" #include "pointLight.h" #include "spotlight.h" +#include "textureReloadRequest.h" #include #include @@ -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); } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index de4465d5a0..9f6e7cb2fd 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -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; diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 8ec7cd020b..0c2b393a4e 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -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) { diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index a2a4163dc0..d09181267e 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -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 diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 1c24b15db9..2fe7f83c59 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -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); diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 22deff6b44..455c47666b 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -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 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 " diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h index 5f06623e1c..b7bc652dfd 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -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 textures_power_2; extern EXPCL_PANDA_GOBJ ConfigVariableEnum 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 shader_utilization; extern EXPCL_PANDA_GOBJ ConfigVariableBool shader_auto_utilization; diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index 1df8480da8..aa28fc44da 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -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 diff --git a/panda/src/gobj/preparedGraphicsObjects.h b/panda/src/gobj/preparedGraphicsObjects.h index a1616a1602..41993a365a 100644 --- a/panda/src/gobj/preparedGraphicsObjects.h +++ b/panda/src/gobj/preparedGraphicsObjects.h @@ -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; diff --git a/panda/src/gobj/texture.I b/panda/src/gobj/texture.I index 5a4b99d108..b63fa132f1 100644 --- a/panda/src/gobj/texture.I +++ b/panda/src/gobj/texture.I @@ -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) || diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 4ea1685e2d..27a895717c 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -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 ©) : - 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 ©) { + _has_read_pages = false; + _has_read_mipmaps = false; + _num_mipmap_levels_read = 0; + + operator = (copy); } //////////////////////////////////////////////////////////////////// @@ -154,6 +126,10 @@ Texture(const Texture ©) : void Texture:: operator = (const Texture ©) { 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 ©) { _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 diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 7f2d5eeda0..22547acac8 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -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 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. diff --git a/panda/src/gobj/textureContext.I b/panda/src/gobj/textureContext.I index a3d0acfc6a..b6c273391a 100644 --- a/panda/src/gobj/textureContext.I +++ b/panda/src/gobj/textureContext.I @@ -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); +} diff --git a/panda/src/gobj/textureContext.h b/panda/src/gobj/textureContext.h index 311304824f..2c26d4449d 100644 --- a/panda/src/gobj/textureContext.h +++ b/panda/src/gobj/textureContext.h @@ -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() { diff --git a/panda/src/gobj/texturePool.cxx b/panda/src/gobj/texturePool.cxx index a6477e66a9..788eeb8a2a 100644 --- a/panda/src/gobj/texturePool.cxx +++ b/panda/src/gobj/texturePool.cxx @@ -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. diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index 605b9768bd..83fc451b42 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -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; diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index cf4f5a6a3d..25b831537a 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -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 \ diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index 20c495b089..d27f14e2a9 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -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 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(); diff --git a/panda/src/pgraph/config_pgraph.h b/panda/src/pgraph/config_pgraph.h index 23103c5483..2b0b16003a 100644 --- a/panda/src/pgraph/config_pgraph.h +++ b/panda/src/pgraph/config_pgraph.h @@ -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 default_lod_type; diff --git a/panda/src/pgraph/cullResult.cxx b/panda/src/pgraph/cullResult.cxx index 39e5de0b1e..811ab8f75d 100644 --- a/panda/src/pgraph/cullResult.cxx +++ b/panda/src/pgraph/cullResult.cxx @@ -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(); diff --git a/panda/src/pgraph/pgraph_composite4.cxx b/panda/src/pgraph/pgraph_composite4.cxx index 683667bc54..6a62ffeaaa 100644 --- a/panda/src/pgraph/pgraph_composite4.cxx +++ b/panda/src/pgraph/pgraph_composite4.cxx @@ -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" diff --git a/panda/src/pgraph/textureReloadRequest.I b/panda/src/pgraph/textureReloadRequest.I new file mode 100644 index 0000000000..60449fde88 --- /dev/null +++ b/panda/src/pgraph/textureReloadRequest.I @@ -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; +} diff --git a/panda/src/pgraph/textureReloadRequest.cxx b/panda/src/pgraph/textureReloadRequest.cxx new file mode 100644 index 0000000000..d0845d3cc7 --- /dev/null +++ b/panda/src/pgraph/textureReloadRequest.cxx @@ -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; +} diff --git a/panda/src/pgraph/textureReloadRequest.h b/panda/src/pgraph/textureReloadRequest.h new file mode 100644 index 0000000000..8a0d65ef00 --- /dev/null +++ b/panda/src/pgraph/textureReloadRequest.h @@ -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 diff --git a/panda/src/pnmimagetypes/pnmFileTypeJPGReader.cxx b/panda/src/pnmimagetypes/pnmFileTypeJPGReader.cxx index 525679667a..56ef623a15 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeJPGReader.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypeJPGReader.cxx @@ -415,6 +415,7 @@ read_data(xel *array, xelval *) { } x++; } + Thread::consider_yield(); } /* Step 7: Finish decompression */