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.
This commit is contained in:
rdb 2021-11-30 10:32:33 +01:00
parent 9e7ca63662
commit fa0ea312ea
6 changed files with 99 additions and 26 deletions

View File

@ -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);
}
/**

View File

@ -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 "

View File

@ -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;

View File

@ -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;
}
}
/**

View File

@ -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;

View File

@ -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: