forked from mirrors/0ad
add high-level dox and updated some comments
This was SVN commit r2249.
This commit is contained in:
+10
-11
@@ -95,11 +95,11 @@ int LDR_BeginRegistering()
|
||||
}
|
||||
|
||||
|
||||
// register a load request (later processed in FIFO order).
|
||||
// register a task (later processed in FIFO order).
|
||||
// <func>: function that will perform the actual work; see LoadFunc.
|
||||
// <param>: (optional) parameter/persistent state; must be freed by func.
|
||||
// <description>: user-visible description of the current task, e.g.
|
||||
// "Loading map".
|
||||
// "Loading Textures".
|
||||
// <estimated_duration_ms>: used to calculate progress, and when checking
|
||||
// whether there is enough of the time budget left to process this task
|
||||
// (reduces timeslice overruns, making the main loop more responsive).
|
||||
@@ -120,7 +120,7 @@ int LDR_Register(LoadFunc func, void* param, const wchar_t* description,
|
||||
}
|
||||
|
||||
|
||||
// call when finished registering load requests; subsequent calls to
|
||||
// call when finished registering tasks; subsequent calls to
|
||||
// LDR_ProgressiveLoad will then work off the queued entries.
|
||||
int LDR_EndRegistering()
|
||||
{
|
||||
@@ -138,8 +138,9 @@ int LDR_EndRegistering()
|
||||
}
|
||||
|
||||
|
||||
// immediately cancel the load. note: no special notification will be
|
||||
// returned by LDR_ProgressiveLoad.
|
||||
// immediately cancel this load; no further tasks will be processed.
|
||||
// used to abort loading upon user request or failure.
|
||||
// note: no special notification will be returned by LDR_ProgressiveLoad.
|
||||
int LDR_Cancel()
|
||||
{
|
||||
// note: calling during registering doesn't make sense - that
|
||||
@@ -178,18 +179,16 @@ static bool HaveTimeForNextTask(double time_left, double time_budget, int estima
|
||||
}
|
||||
|
||||
|
||||
// process as many of the queued load requests as possible within
|
||||
// <time_budget> [s]. if a request is lengthy, the budget may be exceeded.
|
||||
// call from the main loop.
|
||||
// process as many of the queued tasks as possible within <time_budget> [s].
|
||||
// if a task is lengthy, the budget may be exceeded. call from the main loop.
|
||||
//
|
||||
// passes back a description of the next task that will be undertaken
|
||||
// ("" if finished) and the progress value established by the
|
||||
// last request to complete.
|
||||
// ("" if finished) and the current progress value.
|
||||
//
|
||||
// return semantics:
|
||||
// - if loading just completed, return 0.
|
||||
// - if loading is in progress but didn't finish, return ERR_TIMED_OUT.
|
||||
// - if not currently loading (no-op), return 1.
|
||||
// - if not currently loading (no-op), return > 0.
|
||||
// - any other value indicates a failure; the request has been de-queued.
|
||||
//
|
||||
// string interface rationale: for better interoperability, we avoid C++
|
||||
|
||||
+92
-31
@@ -4,49 +4,109 @@
|
||||
// Jan Wassenberg, initial implementation finished 2005-03-21
|
||||
// jan@wildfiregames.com
|
||||
|
||||
|
||||
// intended use:
|
||||
// replace the InitEverything function with the following:
|
||||
// LDR_BeginRegistering()
|
||||
// LDR_Register() for each sub-function (*)
|
||||
// LDR_EndRegistering()
|
||||
// then in the main loop, call LDR_ProgressiveLoad().
|
||||
//
|
||||
// *: splitting up InitEverything is required so that control returns to
|
||||
// the main loop occasionally; that allows displaying the progress.
|
||||
// note that we can't interrupt loading without threads (complex).
|
||||
//
|
||||
// this module is not thread-safe!
|
||||
|
||||
#ifndef LOADER_H_INCLUDED
|
||||
#define LOADER_H_INCLUDED
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
/*
|
||||
|
||||
// call before starting to register load requests.
|
||||
[KEEP IN SYNC WITH WIKI!]
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
"Loading" is the act of preparing a game session, including reading all
|
||||
required data from disk. Ideally, this would be very quick, but for complex
|
||||
maps and/or low-end machines, a duration of several seconds can be expected.
|
||||
Having the game freeze that long is unacceptable; instead, we want to display
|
||||
the current progress and task, which greatly increases user patience.
|
||||
|
||||
|
||||
Allowing for Display
|
||||
--------------------
|
||||
|
||||
To display progress, we need to periodically 'interrupt' loading.
|
||||
Threads come to mind, but there is a problem: since OpenGL graphics calls
|
||||
must come from the main thread, loading would have to happen in a
|
||||
background thread. Everything would thus need to be made thread-safe,
|
||||
which is a considerable complication.
|
||||
|
||||
Therefore, we load from a single thread, and split the operation up into
|
||||
"tasks" (as short as possible). These are typically function calls from the
|
||||
old InitEverything(); instead of being called directly, they are registered
|
||||
with our queue. We are called from the main loop and process as many tasks
|
||||
as possible within one "timeslice".
|
||||
|
||||
After that, progress is updated: an estimated duration for each task
|
||||
(derived from timings on one machine) is used to calculate headway.
|
||||
As long as task lengths only differ by a constant factor between machines,
|
||||
this timing is exact; even if not, only smoothness of update suffers.
|
||||
|
||||
|
||||
Interrupting Lengthy Tasks
|
||||
--------------------------
|
||||
|
||||
The above is sufficient for basic needs, but breaks down if tasks are long
|
||||
(> 500ms). To fix this, we will need to modify the tasks themselves:
|
||||
either make them coroutines, i.e. have them return to the main loop and then
|
||||
resume where they left off, or re-enter a limited version of the main loop.
|
||||
The former requires persistent state and careful implementation,
|
||||
but yields significant advantages:
|
||||
- progress calculation is easy and smooth,
|
||||
- all services of the main loop (especially input*) are available, and
|
||||
- complexity due to reentering the main loop is avoided.
|
||||
|
||||
* input is important, since we want to be able to abort long loads or
|
||||
even exit the game immediately.
|
||||
|
||||
Examples of tasks that take so long, and typical 'coroutine' (more correcly
|
||||
'generator') implementations may be seen in MapReader.cpp.
|
||||
|
||||
|
||||
Intended Use
|
||||
------------
|
||||
|
||||
Replace the InitEverything() function with the following:
|
||||
LDR_BeginRegistering();
|
||||
LDR_Register(..) for each sub-function (*)
|
||||
LDR_EndRegistering();
|
||||
Then in the main loop, call LDR_ProgressiveLoad().
|
||||
|
||||
* RegMemFun from LoaderThunks.h may be used instead; it takes care of
|
||||
registering member functions, which would otherwise be messy.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// NOTE: this module is not thread-safe!
|
||||
|
||||
|
||||
// call before starting to register tasks.
|
||||
// this routine is provided so we can prevent 2 simultaneous load operations,
|
||||
// which is bogus. that can happen by clicking the load button quickly,
|
||||
// or issuing via console while already loading.
|
||||
extern int LDR_BeginRegistering();
|
||||
|
||||
|
||||
// callback function of a load request; performs the actual work.
|
||||
// callback function of a task; performs the actual work.
|
||||
// it receives a param (see below) and the exact time remaining [s].
|
||||
//
|
||||
// return semantics:
|
||||
// - if the entire task was successfully completed, return 0:
|
||||
// the load request will then be de-queued.
|
||||
// - if the entire task was successfully completed, return 0;
|
||||
// it will then be de-queued.
|
||||
// - if the work can be split into smaller subtasks, process those until
|
||||
// <time_left> is reached or exceeded and then return an estimate
|
||||
// of progress in percent (> 0 or it's treated as "finished").
|
||||
// of progress in percent (!= 0, or it's treated as "finished").
|
||||
// - on failure, return a negative error code; LDR_ProgressiveLoad
|
||||
// will abort immediately and return that.
|
||||
typedef int (*LoadFunc)(void* param, double time_left);
|
||||
|
||||
// register a load request (later processed in FIFO order).
|
||||
// register a task (later processed in FIFO order).
|
||||
// <func>: function that will perform the actual work; see LoadFunc.
|
||||
// <param>: (optional) parameter/persistent state; must be freed by func.
|
||||
// <description>: user-visible description of the current task, e.g.
|
||||
// "Loading map".
|
||||
// "Loading Textures".
|
||||
// <estimated_duration_ms>: used to calculate progress, and when checking
|
||||
// whether there is enough of the time budget left to process this task
|
||||
// (reduces timeslice overruns, making the main loop more responsive).
|
||||
@@ -54,28 +114,27 @@ extern int LDR_Register(LoadFunc func, void* param, const wchar_t* description,
|
||||
int estimated_duration_ms);
|
||||
|
||||
|
||||
// call when finished registering load requests; subsequent calls to
|
||||
// call when finished registering tasks; subsequent calls to
|
||||
// LDR_ProgressiveLoad will then work off the queued entries.
|
||||
extern int LDR_EndRegistering();
|
||||
|
||||
|
||||
// immediately cancel the load. note: no special notification will be
|
||||
// returned by LDR_ProgressiveLoad.
|
||||
// immediately cancel this load; no further tasks will be processed.
|
||||
// used to abort loading upon user request or failure.
|
||||
// note: no special notification will be returned by LDR_ProgressiveLoad.
|
||||
extern int LDR_Cancel();
|
||||
|
||||
|
||||
// process as many of the queued load requests as possible within
|
||||
// <time_budget> [s]. if a request is lengthy, the budget may be exceeded.
|
||||
// call from the main loop.
|
||||
// process as many of the queued tasks as possible within <time_budget> [s].
|
||||
// if a task is lengthy, the budget may be exceeded. call from the main loop.
|
||||
//
|
||||
// passes back a description of the next task that will be undertaken
|
||||
// ("" if finished) and the progress value established by the
|
||||
// last request to complete.
|
||||
// ("" if finished) and the current progress value.
|
||||
//
|
||||
// return semantics:
|
||||
// - if loading just completed, return 0.
|
||||
// - if loading is in progress but didn't finish, return ERR_TIMED_OUT.
|
||||
// - if not currently loading (no-op), return 1.
|
||||
// - if not currently loading (no-op), return > 0.
|
||||
// - any other value indicates a failure; the request has been de-queued.
|
||||
//
|
||||
// string interface rationale: for better interoperability, we avoid C++
|
||||
@@ -87,7 +146,7 @@ extern int LDR_ProgressiveLoad(double time_budget, wchar_t* next_description,
|
||||
size_t max_chars, int* progress_percent);
|
||||
|
||||
// immediately process all queued load requests.
|
||||
// returns 0 on success, something else on failure.
|
||||
// returns 0 on success or a negative error code.
|
||||
extern int LDR_NonprogressiveLoad();
|
||||
|
||||
|
||||
@@ -105,3 +164,5 @@ extern int LDR_NonprogressiveLoad();
|
||||
assert2(0 < progress_percent && progress_percent <= 100);\
|
||||
return progress_percent;\
|
||||
}
|
||||
|
||||
#endif // #ifndef LOADER_H_INCLUDED
|
||||
|
||||
Reference in New Issue
Block a user