Browse Source

gobj: Move TextureReloadRequest to new Texture::async_ensure_ram_image()

The task is implemented with just a simple lambda, much more compact than a whole TextureReloadRequest task.  The latter is now deprecated.

Since Loader can't be used within Texture, there's now a new task chain, configurable with texture-reload-num-threads and texture-reload-thread-priority.  This does mean that it no longer happens on the same thread as model loads, but I think that's fine, and perhaps texture reloads should be higher priority than model loads anyway since a long texture reload delay with allow-incomplete-render directly and negatively affects user experience during gameplay.

The new name also better communicates that it just calls get_ram_image(), it doesn't force a reload, but we could add an async_reload() for that if we want.
rdb 4 years ago
parent
commit
fa0ea312ea

+ 2 - 25
panda/src/display/graphicsStateGuardian.cxx

@@ -3581,31 +3581,8 @@ async_reload_texture(TextureContext *tc) {
     priority = _current_display_region->get_texture_reload_priority();
   }
 
-  string task_name = string("reload:") + tc->get_texture()->get_name();
-  PT(AsyncTaskManager) task_mgr = _loader->get_task_manager();
-
-  // See if we are already loading this task.
-  AsyncTaskCollection orig_tasks = task_mgr->find_tasks(task_name);
-  size_t num_tasks = orig_tasks.get_num_tasks();
-  for (size_t ti = 0; ti < num_tasks; ++ti) {
-    AsyncTask *task = orig_tasks.get_task(ti);
-    if (task->is_exact_type(TextureReloadRequest::get_class_type()) &&
-        ((TextureReloadRequest *)task)->get_texture() == tc->get_texture()) {
-      // This texture is already queued to be reloaded.  Don't queue it again,
-      // just make sure the priority is updated, and return.
-      task->set_priority(std::max(task->get_priority(), priority));
-      return (AsyncFuture *)task;
-    }
-  }
-
-  // This texture has not yet been queued to be reloaded.  Queue it up now.
-  PT(AsyncTask) request =
-    new TextureReloadRequest(task_name,
-                             _prepared_objects, tc->get_texture(),
-                             _supports_compressed_texture);
-  request->set_priority(priority);
-  _loader->load_async(request);
-  return (AsyncFuture *)request.p();
+  Texture *tex = tc->get_texture();
+  return tex->async_ensure_ram_image(_supports_compressed_texture, priority);
 }
 
 /**

+ 17 - 0
panda/src/gobj/config_gobj.cxx

@@ -366,6 +366,23 @@ ConfigVariableDouble simple_image_threshold
           "simple images.  Generally the value should be considerably "
           "less than 1."));
 
+ConfigVariableInt texture_reload_num_threads
+ ("texture-reload-num-threads", 1,
+  PRC_DESC("The number of threads that will be started by the Texture class "
+           "to reload textures asynchronously.  These threads will only be "
+           "started if the asynchronous interface is used, and if threading "
+           "support is compiled into Panda.  The default is one thread, "
+           "which allows textures to be loaded one at a time in a single "
+           "asychronous thread.  You can set this higher, particularly if "
+           "you have many CPU's available, to allow loading multiple models "
+           "simultaneously."));
+
+ConfigVariableEnum<ThreadPriority> texture_reload_thread_priority
+ ("texture-reload-thread-priority", TP_normal,
+  PRC_DESC("The default thread priority to assign to the threads created for "
+           "asynchronous texture loading.  The default is 'normal'; you may "
+           "also specify 'low', 'high', or 'urgent'."));
+
 ConfigVariableInt geom_cache_size
 ("geom-cache-size", 5000,
  PRC_DESC("Specifies the maximum number of entries in the cache "

+ 3 - 0
panda/src/gobj/config_gobj.h

@@ -24,6 +24,7 @@
 #include "configVariableString.h"
 #include "configVariableList.h"
 #include "autoTextureScale.h"
+#include "threadPriority.h"
 
 NotifyCategoryDecl(gobj, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ);
 NotifyCategoryDecl(shader, EXPCL_PANDA_GOBJ, EXPTP_PANDA_GOBJ);
@@ -62,6 +63,8 @@ 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 ConfigVariableInt texture_reload_num_threads;
+extern EXPCL_PANDA_GOBJ ConfigVariableEnum<ThreadPriority> texture_reload_thread_priority;
 
 extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_size;
 extern EXPCL_PANDA_GOBJ ConfigVariableInt geom_cache_min_frames;

+ 70 - 0
panda/src/gobj/texture.cxx

@@ -43,6 +43,7 @@
 #include "streamReader.h"
 #include "texturePeeker.h"
 #include "convert_srgb.h"
+#include "asyncTaskManager.h"
 
 #ifdef HAVE_SQUISH
 #include <squish.h>
@@ -1019,6 +1020,69 @@ load_related(const InternalName *suffix) const {
   return res;
 }
 
+/**
+ * Schedules a background task that reloads the the Texture from its disk file
+ * if there is not currently a RAM image (or uncompressed RAM image, if
+ * allow_compression is false).
+ *
+ * A higher priority value indicates that this texture should be reloaded sooner
+ * than textures with a lower priority value.  If the reload hasn't taken place
+ * yet, you can call this again to update the priority value.
+ *
+ * If someone else reloads the texture using an explicit call to reload() while
+ * an async reload request is pending, the async reload request is cancelled.
+ */
+PT(AsyncFuture) Texture::
+async_ensure_ram_image(bool allow_compression, int priority) {
+  CDLockedReader cdata(_cycler);
+  if (allow_compression ? do_has_ram_image(cdata) : do_has_uncompressed_ram_image(cdata)) {
+    // We already have a RAM image.
+    PT(AsyncFuture) fut = new AsyncFuture;
+    fut->set_result(nullptr);
+    return fut;
+  }
+  if (!do_can_reload(cdata)) {
+    // We don't have a filename to load from.  This is an error.
+    return nullptr;
+  }
+
+  AsyncTask *task = cdata->_reload_task;
+  if (task != nullptr) {
+    // This texture is already queued to be reloaded.  Don't queue it again,
+    // just make sure the priority is updated, and return.
+    task->set_priority(std::max(task->get_priority(), priority));
+    return (AsyncFuture *)task;
+  }
+
+  CDWriter cdataw(_cycler, cdata, true);
+
+  string task_name = string("reload:") + get_name();
+  AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr();
+  static PT(AsyncTaskChain) chain = task_mgr->make_task_chain("texture_reload");
+  chain->set_num_threads(texture_reload_num_threads);
+  chain->set_thread_priority(texture_reload_thread_priority);
+
+  double delay = async_load_delay;
+
+  // This texture has not yet been queued to be reloaded.  Queue it up now.
+  task = new FunctionAsyncTask(task_name, [=](AsyncTask *task) {
+    if (delay != 0.0) {
+      Thread::sleep(delay);
+    }
+    if (allow_compression) {
+      get_ram_image();
+    } else {
+      get_uncompressed_ram_image();
+    }
+    return AsyncTask::DS_done;
+  });
+  task->set_task_chain("texture_reload");
+  task->set_priority(priority);
+  task_mgr->add(task);
+  cdataw->_reload_task = task;
+  return (AsyncFuture *)task;
+}
+
 /**
  * Replaces the current system-RAM image with the new data, converting it
  * first if necessary from the indicated component-order format.  See
@@ -5611,6 +5675,12 @@ do_reload_ram_image(CData *cdata, bool allow_compression) {
       cache->store(record);
     }
   }
+
+  // Remove any pending asynchronous reload operation.
+  if (cdata->_reload_task != nullptr) {
+    cdata->_reload_task->remove();
+    cdata->_reload_task = nullptr;
+  }
 }
 
 /**

+ 5 - 1
panda/src/gobj/texture.h

@@ -45,7 +45,7 @@
 #include "bamCacheRecord.h"
 #include "pnmImage.h"
 #include "pfmFile.h"
-#include "asyncFuture.h"
+#include "asyncTask.h"
 
 class TextureContext;
 class FactoryParams;
@@ -448,6 +448,7 @@ PUBLISHED:
   MAKE_PROPERTY(expected_ram_image_size, get_expected_ram_image_size);
   MAKE_PROPERTY(expected_ram_page_size, get_expected_ram_page_size);
 
+  PT(AsyncFuture) async_ensure_ram_image(bool allow_compression = true, int priority = 0);
   INLINE CPTA_uchar get_ram_image();
   INLINE CompressionMode get_ram_image_compression() const;
   INLINE CPTA_uchar get_uncompressed_ram_image();
@@ -784,6 +785,7 @@ protected:
   void do_set_pad_size(CData *cdata, int x, int y, int z);
   virtual bool do_can_reload(const CData *cdata) const;
   bool do_reload(CData *cdata);
+  AsyncFuture *do_async_ensure_ram_image(const CData *cdata, bool allow_compression, int priority);
 
   INLINE AutoTextureScale do_get_auto_texture_scale(const CData *cdata) const;
 
@@ -1032,6 +1034,8 @@ protected:
 
     ModifiedPageRanges _modified_pages;
 
+    PT(AsyncTask) _reload_task;
+
   public:
     static TypeHandle get_class_type() {
       return _type_handle;

+ 2 - 0
panda/src/gobj/textureReloadRequest.h

@@ -27,6 +27,8 @@
  * 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.
+ *
+ * @deprecated Use Texture::async_ensure_ram_image() instead.
  */
 class EXPCL_PANDA_GOBJ TextureReloadRequest : public AsyncTask {
 public: