forked from 0ad/0ad
waio: fix spurious WARN_ERR
vfs: integrate hotload code fixed a few warnings as well. This was SVN commit r2609.
This commit is contained in:
parent
6c4aab2e55
commit
0e20c08969
@ -1082,7 +1082,6 @@ debug_printf("file_io fd=%d size=%d ofs=%d\n", f->fd, data_size, data_ofs);
|
||||
|
||||
// only align if we allocate the buffer and in AIO mode
|
||||
const bool do_align = temp;
|
||||
const bool cache = temp;
|
||||
|
||||
|
||||
//
|
||||
|
@ -244,39 +244,25 @@ enum VfsMountFlags
|
||||
VFS_MOUNT_RECURSIVE = 2,
|
||||
|
||||
// all real directories mounted during this operation will be watched
|
||||
// for changes (using hotload.h). this flag is provided to avoid
|
||||
// watches in output-only directories, e.g. screenshots/
|
||||
// (only causes unnecessary overhead).
|
||||
// for changes. this flag is provided to avoid watches in output-only
|
||||
// directories, e.g. screenshots/ (only causes unnecessary overhead).
|
||||
VFS_MOUNT_WATCH = 4
|
||||
};
|
||||
|
||||
// mount <p_real_dir> into the VFS at <V_mount_point>,
|
||||
// mount <P_real_dir> into the VFS at <V_mount_point>,
|
||||
// which is created if it does not yet exist.
|
||||
// files in that directory override the previous VFS contents if
|
||||
// <pri>(ority) is not lower.
|
||||
// all archives in <p_real_dir> are also mounted, in alphabetical order.
|
||||
// all archives in <P_real_dir> are also mounted, in alphabetical order.
|
||||
//
|
||||
// flags determines extra actions to perform; see VfsMountFlags.
|
||||
//
|
||||
// p_real_dir = "." or "./" isn't allowed - see implementation for rationale.
|
||||
// P_real_dir = "." or "./" isn't allowed - see implementation for rationale.
|
||||
extern int vfs_mount(const char* V_mount_point, const char* P_real_dir, int flags = 0, uint pri = 0);
|
||||
|
||||
// rebuild the VFS, i.e. re-mount everything. open files are not affected.
|
||||
// necessary after loose files or directories change, so that the VFS
|
||||
// "notices" the changes and updates file locations. res calls this after
|
||||
// dir_watch reports changes; can also be called from the console after a
|
||||
// rebuild command. there is no provision for updating single VFS dirs -
|
||||
// it's not worth the trouble.
|
||||
extern int vfs_rebuild(void);
|
||||
|
||||
// unmount a previously mounted item, and rebuild the VFS afterwards.
|
||||
extern int vfs_unmount(const char* name);
|
||||
|
||||
// if <path> or its ancestors are mounted,
|
||||
// return a VFS path that accesses it.
|
||||
// used when receiving paths from external code.
|
||||
extern int vfs_make_vfs_path(const char* path, char* vfs_path);
|
||||
|
||||
|
||||
//
|
||||
// directory entry
|
||||
@ -427,4 +413,14 @@ extern int vfs_map(Handle hf, uint flags, void*& p, size_t& size);
|
||||
// may be removed when no longer needed.
|
||||
extern int vfs_unmap(Handle hf);
|
||||
|
||||
|
||||
//
|
||||
// hotloading
|
||||
//
|
||||
|
||||
extern int vfs_reload(const char* fn);
|
||||
|
||||
// this must be called from the main thread? (wdir_watch problem)
|
||||
extern int vfs_reload_changed_files(void);
|
||||
|
||||
#endif // #ifndef __VFS_H__
|
||||
|
@ -1,10 +1,11 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "../res.h"
|
||||
#include "vfs_mount.h"
|
||||
#include "vfs_path.h"
|
||||
#include "vfs_tree.h"
|
||||
#include "zip.h"
|
||||
#include "../res.h"
|
||||
#include "sysdep/dir_watch.h"
|
||||
|
||||
struct Stats
|
||||
{
|
||||
@ -396,12 +397,14 @@ static int add_ent(TDir* td, DirEnt* ent, const char* P_parent_path, const Mount
|
||||
}
|
||||
|
||||
|
||||
// note: full path is needed for the dir watch.
|
||||
static int populate_dir(TDir* td, const char* P_path, const Mount* m,
|
||||
DirQueue* dir_queue, Archives* archives, int flags)
|
||||
{
|
||||
int err;
|
||||
|
||||
CHECK_ERR(tree_attach_real_dir(td, P_path, flags, m));
|
||||
RealDir* rd = tree_get_real_dir(td);
|
||||
WARN_ERR(mount_attach_real_dir(rd, P_path, m, flags));
|
||||
|
||||
DirIterator d;
|
||||
RETURN_ERR(dir_open(P_path, &d));
|
||||
@ -620,7 +623,7 @@ int vfs_mount(const char* V_mount_point, const char* P_real_path, int flags, uin
|
||||
// dir_watch reports changes; can also be called from the console after a
|
||||
// rebuild command. there is no provision for updating single VFS dirs -
|
||||
// it's not worth the trouble.
|
||||
int vfs_rebuild()
|
||||
static int rebuild()
|
||||
{
|
||||
tree_clear();
|
||||
tree_init();
|
||||
@ -645,7 +648,7 @@ int vfs_unmount(const char* P_name)
|
||||
// trim list and actually remove 'invalidated' entries.
|
||||
mounts.erase(last, end);
|
||||
|
||||
return vfs_rebuild();
|
||||
return rebuild();
|
||||
}
|
||||
|
||||
|
||||
@ -656,7 +659,7 @@ int vfs_unmount(const char* P_name)
|
||||
// if <path> or its ancestors are mounted,
|
||||
// return a VFS path that accesses it.
|
||||
// used when receiving paths from external code.
|
||||
int vfs_make_vfs_path(const char* P_path, char* V_path)
|
||||
static int make_vfs_path(const char* P_path, char* V_path)
|
||||
{
|
||||
for(MountIt it = mounts.begin(); it != mounts.end(); ++it)
|
||||
{
|
||||
@ -691,14 +694,166 @@ void mount_shutdown()
|
||||
|
||||
|
||||
|
||||
int mount_populate(TDir* td, const Mount* m)
|
||||
|
||||
int mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int flags)
|
||||
{
|
||||
// more than one real dir mounted into VFS dir
|
||||
// (=> can't create files for writing here)
|
||||
if(rd->m)
|
||||
rd->m = (const Mount*)-1;
|
||||
else
|
||||
rd->m = m;
|
||||
|
||||
#ifndef NO_DIR_WATCH
|
||||
if(flags & VFS_MOUNT_WATCH)
|
||||
{
|
||||
char N_path[PATH_MAX];
|
||||
CHECK_ERR(file_make_full_native_path(P_path, N_path));
|
||||
CHECK_ERR(dir_add_watch(N_path, &rd->watch));
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void mount_detach_real_dir(RealDir* rd)
|
||||
{
|
||||
rd->m = 0;
|
||||
|
||||
#ifndef NO_DIR_WATCH
|
||||
if(rd->watch) // avoid dir_cancel_watch complaining
|
||||
WARN_ERR(dir_cancel_watch(rd->watch));
|
||||
rd->watch = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int mount_populate(TDir* td, RealDir* rd)
|
||||
{
|
||||
UNUSED2(td);
|
||||
UNUSED2(rd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// hotloading
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// called by vfs_reload and vfs_reload_changed_files (which will already
|
||||
// have rebuilt the VFS - doing so more than once a frame is unnecessary).
|
||||
static int reload_without_rebuild(const char* fn)
|
||||
{
|
||||
// invalidate this file's cached blocks to make sure its contents are
|
||||
// loaded anew.
|
||||
CHECK_ERR(file_invalidate_cache(fn));
|
||||
|
||||
CHECK_ERR(h_reload(fn));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// called via console command.
|
||||
int vfs_reload(const char* fn)
|
||||
{
|
||||
// if <fn> currently maps to an archive, the VFS must switch
|
||||
// over to using the loose file (that was presumably changed).
|
||||
CHECK_ERR(rebuild());
|
||||
|
||||
return reload_without_rebuild(fn);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// get directory change notifications, and reload all affected files.
|
||||
// must be called regularly (e.g. once a frame). this is much simpler
|
||||
// than asynchronous notifications: everything would need to be thread-safe.
|
||||
int vfs_reload_changed_files()
|
||||
{
|
||||
// array of reloads requested this frame (see 'do we really need to
|
||||
// reload' below). go through gyrations to avoid heap allocs.
|
||||
const size_t MAX_RELOADS_PER_FRAME = 12;
|
||||
typedef char Path[VFS_MAX_PATH];
|
||||
typedef Path PathList[MAX_RELOADS_PER_FRAME];
|
||||
PathList pending_reloads;
|
||||
|
||||
uint num_pending = 0;
|
||||
// process only as many notifications as we have room for; the others
|
||||
// will be handled next frame. it's not imagineable that they'll pile up.
|
||||
while(num_pending < MAX_RELOADS_PER_FRAME)
|
||||
{
|
||||
// get next notification
|
||||
char N_path[PATH_MAX];
|
||||
int ret = dir_get_changed_file(N_path);
|
||||
CHECK_ERR(ret); // error? (doesn't cover 'none available')
|
||||
if(ret != 0) // none available; done.
|
||||
break;
|
||||
|
||||
// convert to VFS path
|
||||
char P_path[PATH_MAX];
|
||||
CHECK_ERR(file_make_full_portable_path(N_path, P_path));
|
||||
char* V_path = pending_reloads[num_pending];
|
||||
CHECK_ERR(make_vfs_path(P_path, V_path));
|
||||
|
||||
// do we really need to reload? try to avoid the considerable cost of
|
||||
// rebuilding VFS and scanning all Handles.
|
||||
//
|
||||
// note: be careful to avoid 'race conditions' depending on the
|
||||
// timeframe in which notifications reach us.
|
||||
// example: editor deletes a.tga; we are notified; reload is
|
||||
// triggered but fails since the file isn't found; further
|
||||
// notifications (e.g. renamed a.tmp to a.tga) come within x [ms] and
|
||||
// are ignored due to a time limit.
|
||||
// therefore, we can only check for multiple reload requests a frame;
|
||||
// to that purpose, an array is built and duplicates ignored.
|
||||
const char* ext = strrchr(V_path, '.');
|
||||
// .. directory change notification; ignore because we get
|
||||
// per-file notifications anyway. (note: assume no extension =>
|
||||
// it's a directory). this also protects the strcmp calls below.
|
||||
if(!ext)
|
||||
continue;
|
||||
// .. compiled XML files the engine writes out by the hundreds;
|
||||
// skipping them is a big performance gain.
|
||||
if(!strcmp(ext, ".xmb"))
|
||||
continue;
|
||||
// .. temp files, usually created when an editor saves a file
|
||||
// (delete, create temp, rename temp); no need to reload those.
|
||||
if(!strcmp(ext, ".tmp"))
|
||||
continue;
|
||||
// .. more than one notification for a file; only reload once.
|
||||
// note: this doesn't suffer from the 'reloaded too early'
|
||||
// problem described above; if there's more than one
|
||||
// request in the array, the file has since been written.
|
||||
for(uint i = 0; i < num_pending; i++)
|
||||
if(!strcmp(pending_reloads[i], V_path))
|
||||
continue;
|
||||
|
||||
// path has already been written to pending_reloads,
|
||||
// so just mark it valid.
|
||||
num_pending++;
|
||||
}
|
||||
|
||||
// rebuild VFS, in case a file that has been changed is currently
|
||||
// mounted from an archive (reloading would just grab the unchanged
|
||||
// version in the archive). the rebuild sees differing mtimes and
|
||||
// always choses the loose file version. only do this once
|
||||
// (instead of per reload request) because it's slow (> 1s)!
|
||||
if(num_pending != 0)
|
||||
CHECK_ERR(rebuild());
|
||||
|
||||
// now actually reload all files in the array we built
|
||||
for(uint i = 0; i < num_pending; i++)
|
||||
CHECK_ERR(reload_without_rebuild(pending_reloads[i]));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
@ -77,9 +77,6 @@ extern int x_io_close(XIo* xio);
|
||||
|
||||
|
||||
|
||||
struct TDir;
|
||||
extern int mount_populate(TDir* td, const Mount* m);
|
||||
|
||||
|
||||
//
|
||||
// accessor routines that obviate the need to access Mount fields directly:
|
||||
@ -90,4 +87,29 @@ extern bool mount_should_replace(const Mount* m_old, const Mount* m_new,
|
||||
|
||||
extern char mount_get_type(const Mount* m);
|
||||
|
||||
|
||||
|
||||
// stored by vfs_tree in TDir
|
||||
struct RealDir
|
||||
{
|
||||
// if exactly one real directory is mounted into this virtual dir,
|
||||
// this points to its location. used to add files to VFS when writing.
|
||||
//
|
||||
// the Mount is actually in the mount info and is invalid when
|
||||
// that's unmounted, but the VFS would then be rebuilt anyway.
|
||||
//
|
||||
// = 0 if no real dir mounted here; = -1 if more than one.
|
||||
const Mount* m;
|
||||
#ifndef NO_DIR_WATCH
|
||||
intptr_t watch;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern int mount_attach_real_dir(RealDir* rd, const char* P_path, const Mount* m, int flags);
|
||||
extern void mount_detach_real_dir(RealDir* rd);
|
||||
|
||||
struct TDir;
|
||||
extern int mount_populate(TDir* td, RealDir* rd);
|
||||
|
||||
|
||||
#endif // #ifndef VFS_MOUNT_H__
|
||||
|
@ -7,11 +7,9 @@
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "lib.h"
|
||||
#include "../res.h"
|
||||
#include "vfs_path.h"
|
||||
#include "vfs_tree.h"
|
||||
#include "../hotload.h" // see NO_DIR_WATCH
|
||||
|
||||
|
||||
// we add/cancel directory watches from the VFS mount code for convenience -
|
||||
@ -355,20 +353,10 @@ enum TDirFlags
|
||||
// must be declared before TNode
|
||||
struct TDir
|
||||
{
|
||||
// if exactly one real directory is mounted into this virtual dir,
|
||||
// this points to its location. used to add files to VFS when writing.
|
||||
//
|
||||
// the Mount is actually in the mount info and is invalid when
|
||||
// that's unmounted, but the VFS would then be rebuilt anyway.
|
||||
//
|
||||
// = 0 if no real dir mounted here; = -1 if more than one.
|
||||
const Mount* m;
|
||||
|
||||
int flags; // enum TDirFlags
|
||||
|
||||
#ifndef NO_DIR_WATCH
|
||||
intptr_t watch;
|
||||
#endif
|
||||
RealDir rd;
|
||||
|
||||
DynHashTbl children;
|
||||
|
||||
void init();
|
||||
@ -435,7 +423,7 @@ static inline Key GetKey(const T t)
|
||||
|
||||
void TDir::init()
|
||||
{
|
||||
m = 0;
|
||||
rd.m = 0;
|
||||
children.init();
|
||||
}
|
||||
|
||||
@ -486,24 +474,6 @@ done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// note: full path is needed for the dir watch.
|
||||
int TDir::attach_real_dir(const char* P_path, int flags, const Mount* new_m)
|
||||
{
|
||||
// more than one real dir mounted into VFS dir
|
||||
// (=> can't create files for writing here)
|
||||
if(m)
|
||||
m = (Mount*)-1;
|
||||
else
|
||||
m = new_m;
|
||||
|
||||
#ifndef NO_DIR_WATCH
|
||||
if(flags & VFS_MOUNT_WATCH)
|
||||
CHECK_ERR(res_watch_dir(P_path, &watch));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
|
||||
{
|
||||
// cleared on failure / if returning root dir node (= "")
|
||||
@ -560,7 +530,7 @@ int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
|
||||
// make sure it has been populated with loose files/directories.
|
||||
if(!(td->flags & TD_POPULATED))
|
||||
{
|
||||
WARN_ERR(mount_populate(td, td->m));
|
||||
WARN_ERR(mount_populate(td, &td->rd));
|
||||
td->flags |= TD_POPULATED;
|
||||
}
|
||||
|
||||
@ -577,7 +547,7 @@ int TDir::lookup(const char* path, uint flags, TNode** pnode, char* exact_path)
|
||||
// so we special-case its init.
|
||||
if(type == N_FILE)
|
||||
{
|
||||
node->u.file.m = td->m;
|
||||
node->u.file.m = td->rd.m;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -623,10 +593,10 @@ void TDir::clearR()
|
||||
|
||||
// wipe out this directory
|
||||
children.clear();
|
||||
#ifndef NO_DIR_WATCH
|
||||
res_cancel_watch(watch);
|
||||
watch = 0;
|
||||
#endif
|
||||
|
||||
// the watch is restored when this directory is repopulated; we must
|
||||
// remove it in case the real directory backing this one was deleted.
|
||||
mount_detach_real_dir(&rd);
|
||||
}
|
||||
|
||||
void TDir::displayR(int indent_level)
|
||||
@ -716,11 +686,11 @@ void vfs_display()
|
||||
|
||||
|
||||
|
||||
int tree_add_file(TDir* dir, const char* name, const Mount* m,
|
||||
int tree_add_file(TDir* td, const char* name, const Mount* m,
|
||||
off_t size, time_t mtime)
|
||||
{
|
||||
TNode* node;
|
||||
RETURN_ERR(dir->add(name, N_FILE, &node));
|
||||
RETURN_ERR(td->add(name, N_FILE, &node));
|
||||
TFile* tf = &node->u.file;
|
||||
|
||||
// assume they're the same if size and last-modified time match.
|
||||
@ -737,33 +707,29 @@ int tree_add_file(TDir* dir, const char* name, const Mount* m,
|
||||
}
|
||||
|
||||
|
||||
int tree_add_dir(TDir* dir, const char* name, TDir** ptd)
|
||||
int tree_add_dir(TDir* td, const char* name, TDir** ptd)
|
||||
{
|
||||
TNode* node;
|
||||
RETURN_ERR(dir->add(name, N_DIR, &node));
|
||||
RETURN_ERR(td->add(name, N_DIR, &node));
|
||||
*ptd = &node->u.dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int tree_attach_real_dir(TDir* dir, const char* path, int flags, const Mount* m)
|
||||
{
|
||||
return dir->attach_real_dir(path, flags, m);
|
||||
}
|
||||
|
||||
|
||||
int tree_lookup_dir(const char* path, TDir** pdir, uint flags, char* exact_path)
|
||||
int tree_lookup_dir(const char* path, TDir** ptd, uint flags, char* exact_path)
|
||||
{
|
||||
// TDir::lookup would return a file node
|
||||
if(path[0] != '\0' && path[strlen(path)-1] != '/')
|
||||
return -1;
|
||||
|
||||
TDir* dir = (flags & LF_START_DIR)? *pdir : tree_root_dir;
|
||||
TDir* td = (flags & LF_START_DIR)? *ptd : tree_root_dir;
|
||||
TNode* node;
|
||||
CHECK_ERR(dir->lookup(path, flags, &node, exact_path));
|
||||
CHECK_ERR(td->lookup(path, flags, &node, exact_path));
|
||||
// directories should exist, so warn if this fails
|
||||
*pdir = &node->u.dir;
|
||||
*ptd = &node->u.dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -866,7 +832,7 @@ int tree_dir_close(TreeDirIterator* UNUSED(d))
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// get/set for TFile
|
||||
// get/set
|
||||
|
||||
const Mount* tree_get_mount(const TFile* tf)
|
||||
{
|
||||
@ -892,3 +858,9 @@ int tree_stat(const TFile* tf, struct stat* s)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
RealDir* tree_get_real_dir(TDir* td)
|
||||
{
|
||||
return &td->rd;
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ extern int tree_add_file(TDir* td, const char* fn, const Mount* m,
|
||||
off_t size, time_t mtime);
|
||||
|
||||
extern int tree_add_dir(TDir* dir, const char* name, TDir** ptd);
|
||||
extern int tree_attach_real_dir(TDir* dir, const char* path, int flags, const Mount* m);
|
||||
|
||||
enum TreeLookupFlags
|
||||
{
|
||||
@ -104,6 +103,10 @@ extern const Mount* tree_get_mount(const TFile* tf);
|
||||
|
||||
extern void tree_update_file(TFile* tf, off_t size, time_t mtime);
|
||||
|
||||
struct RealDir;
|
||||
extern RealDir* tree_get_real_dir(TDir* td);
|
||||
|
||||
|
||||
// for use in vfs_mount
|
||||
extern void tree_lock();
|
||||
extern void tree_unlock();
|
||||
|
@ -1,13 +0,0 @@
|
||||
|
||||
extern int res_reload(const char* fn);
|
||||
|
||||
|
||||
// the following functions must be called from the same thread!
|
||||
// (wdir_watch limitation)
|
||||
|
||||
|
||||
extern int res_watch_dir(const char* path, intptr_t* watch);
|
||||
|
||||
extern int res_cancel_watch(const intptr_t watch);
|
||||
|
||||
extern int res_reload_changed_files(void);
|
@ -1,133 +0,0 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "res.h"
|
||||
#include "file/file.h" // file_make_native_path, file_invalidate_cache
|
||||
#include "timer.h"
|
||||
#include "hotload.h" // we implement that interface
|
||||
|
||||
#include "sysdep/dir_watch.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// called by res_reload and res_reload_changed_files (which will already
|
||||
// have rebuilt the VFS - doing so more than once a frame is unnecessary).
|
||||
static int reload_without_rebuild(const char* fn)
|
||||
{
|
||||
// invalidate this file's cached blocks to make sure its contents are
|
||||
// loaded anew.
|
||||
CHECK_ERR(file_invalidate_cache(fn));
|
||||
|
||||
CHECK_ERR(h_reload(fn));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// called via console command.
|
||||
int res_reload(const char* fn)
|
||||
{
|
||||
// if <fn> currently maps to an archive, the VFS must switch
|
||||
// over to using the loose file (that was presumably changed).
|
||||
CHECK_ERR(vfs_rebuild());
|
||||
|
||||
return reload_without_rebuild(fn);
|
||||
}
|
||||
|
||||
|
||||
int res_watch_dir(const char* path, intptr_t* watch)
|
||||
{
|
||||
char n_path[PATH_MAX];
|
||||
CHECK_ERR(file_make_full_native_path(path, n_path));
|
||||
return dir_add_watch(n_path, watch);
|
||||
}
|
||||
|
||||
|
||||
int res_cancel_watch(const intptr_t watch)
|
||||
{
|
||||
return dir_cancel_watch(watch);
|
||||
}
|
||||
|
||||
|
||||
// get directory change notifications, and reload all affected files.
|
||||
// must be called regularly (e.g. once a frame). this is much simpler
|
||||
// than asynchronous notifications: everything would need to be thread-safe.
|
||||
int res_reload_changed_files()
|
||||
{
|
||||
// array of reloads requested this frame (see 'do we really need to
|
||||
// reload' below). go through gyrations to avoid heap allocs.
|
||||
const size_t MAX_RELOADS_PER_FRAME = 12;
|
||||
typedef char Path[VFS_MAX_PATH];
|
||||
typedef Path PathList[MAX_RELOADS_PER_FRAME];
|
||||
PathList pending_reloads;
|
||||
|
||||
uint num_pending = 0;
|
||||
// process only as many notifications as we have room for; the others
|
||||
// will be handled next frame. it's not imagineable that they'll pile up.
|
||||
while(num_pending < MAX_RELOADS_PER_FRAME)
|
||||
{
|
||||
// get next notification
|
||||
char n_path[PATH_MAX];
|
||||
int ret = dir_get_changed_file(n_path);
|
||||
CHECK_ERR(ret); // error? (doesn't cover 'none available')
|
||||
if(ret != 0) // none available; done.
|
||||
break;
|
||||
|
||||
// convert to VFS path
|
||||
char p_path[PATH_MAX];
|
||||
CHECK_ERR(file_make_full_portable_path(n_path, p_path));
|
||||
char* vfs_path = pending_reloads[num_pending];
|
||||
CHECK_ERR(vfs_make_vfs_path(p_path, vfs_path));
|
||||
|
||||
// do we really need to reload? try to avoid the considerable cost of
|
||||
// rebuilding VFS and scanning all Handles.
|
||||
//
|
||||
// note: be careful to avoid 'race conditions' depending on the
|
||||
// timeframe in which notifications reach us.
|
||||
// example: editor deletes a.tga; we are notified; reload is
|
||||
// triggered but fails since the file isn't found; further
|
||||
// notifications (e.g. renamed a.tmp to a.tga) come within x [ms] and
|
||||
// are ignored due to a time limit.
|
||||
// therefore, we can only check for multiple reload requests a frame;
|
||||
// to that purpose, an array is built and duplicates ignored.
|
||||
const char* ext = strrchr(vfs_path, '.');
|
||||
// .. directory change notification; ignore because we get
|
||||
// per-file notifications anyway. (note: assume no extension =>
|
||||
// it's a directory). this also protects the strcmp calls below.
|
||||
if(!ext)
|
||||
continue;
|
||||
// .. compiled XML files the engine writes out by the hundreds;
|
||||
// skipping them is a big performance gain.
|
||||
if(!strcmp(ext, ".xmb"))
|
||||
continue;
|
||||
// .. temp files, usually created when an editor saves a file
|
||||
// (delete, create temp, rename temp); no need to reload those.
|
||||
if(!strcmp(ext, ".tmp"))
|
||||
continue;
|
||||
// .. more than one notification for a file; only reload once.
|
||||
// note: this doesn't suffer from the 'reloaded too early'
|
||||
// problem described above; if there's more than one
|
||||
// request in the array, the file has since been written.
|
||||
for(uint i = 0; i < num_pending; i++)
|
||||
if(!strcmp(pending_reloads[i], vfs_path))
|
||||
continue;
|
||||
|
||||
// path has already been written to pending_reloads,
|
||||
// so just mark it valid.
|
||||
num_pending++;
|
||||
}
|
||||
|
||||
// rebuild VFS, in case a file that has been changed is currently
|
||||
// mounted from an archive (reloading would just grab the unchanged
|
||||
// version in the archive). the rebuild sees differing mtimes and
|
||||
// always choses the loose file version. only do this once
|
||||
// (instead of per reload request) because it's slow (> 1s)!
|
||||
if(num_pending != 0)
|
||||
CHECK_ERR(vfs_rebuild());
|
||||
|
||||
// now actually reload all files in the array we built
|
||||
for(uint i = 0; i < num_pending; i++)
|
||||
CHECK_ERR(reload_without_rebuild(pending_reloads[i]));
|
||||
|
||||
return 0;
|
||||
}
|
@ -233,8 +233,10 @@ fail:
|
||||
int aio_close(int fd)
|
||||
{
|
||||
// early out for files that were never re-opened for AIO.
|
||||
// since there is no way for wposix close to know this, we mustn't
|
||||
// return an error (which would cause it to WARN_ERR).
|
||||
if(!aio_h_is_set(fd))
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
HANDLE h = aio_h_get(fd);
|
||||
// out of bounds or already closed
|
||||
|
@ -280,6 +280,7 @@ static void* prof_thread_func(void* UNUSED(data))
|
||||
debug_warn("wpcu prof_thread_func: sem_timedwait failed");
|
||||
|
||||
uintptr_t pc = get_target_pc();
|
||||
UNUSED2(pc);
|
||||
|
||||
// ADD TO LIST
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ func_and_arg;
|
||||
|
||||
|
||||
// bridge calling conventions required by _beginthreadex and POSIX.
|
||||
static unsigned __stdcall thread_start(void* param)
|
||||
static unsigned __stdcall thread_start(void* UNUSED(param))
|
||||
{
|
||||
void*(*func)(void*) = func_and_arg.func;
|
||||
void* arg = func_and_arg.arg;
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "lib/timer.h"
|
||||
#include "lib/input.h"
|
||||
#include "lib/res/res.h"
|
||||
#include "lib/res/hotload.h" // xxx
|
||||
#include "lib/res/sound/snd.h"
|
||||
#include "lib/res/graphics/tex.h"
|
||||
#include "lib/res/graphics/cursor.h"
|
||||
@ -1377,7 +1376,7 @@ static void Frame()
|
||||
|
||||
PROFILE_START( "reload changed files" );
|
||||
MICROLOG(L"reload files");
|
||||
res_reload_changed_files();
|
||||
vfs_reload_changed_files();
|
||||
PROFILE_END( "reload changed files" );
|
||||
|
||||
oglCheck();
|
||||
|
@ -312,7 +312,6 @@ void CModelRData::RenderModels(u32 streamflags,u32 flags)
|
||||
#if 1
|
||||
// submit batches for each model to the vertex buffer
|
||||
for (i=0;i<m_Models.size();++i) {
|
||||
u32 mflags=m_Models[i]->GetFlags(); // TODO2
|
||||
if (!flags || (m_Models[i]->GetFlags()&flags)) {
|
||||
CModelRData* modeldata=(CModelRData*) m_Models[i]->GetRenderData();
|
||||
modeldata->SubmitBatches();
|
||||
|
@ -41,11 +41,14 @@
|
||||
|
||||
#define LOG_CATEGORY "graphics"
|
||||
|
||||
/*
|
||||
// jw: unused
|
||||
static bool saveTGA(const char* filename,int width,int height,int bpp,unsigned char* data)
|
||||
{
|
||||
int err = tex_write(filename, width, height, bpp, TEX_BGR, data);
|
||||
return (err == 0);
|
||||
}
|
||||
*/
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// CRenderer destructor
|
||||
|
Loading…
Reference in New Issue
Block a user