From 0f2ae614a17a7c231b2a6e129633a99d9d1f12e3 Mon Sep 17 00:00:00 2001 From: Lukas Stockner Date: Thu, 14 Jan 2021 22:02:48 +0100 Subject: [PATCH] Use mmap() IO for reading uncompressed .blends Instead of submitting tons of tiny IO syscalls, we can speed things up significantly by `mmap`ing the .blend file into virtual memory and directly accessing it. In my local testing, this speeds up loading the Dweebs file with all its linked files from 19sec to 10sec (on Linux). As far as I can see, this should be supported on Linux, OSX and BSD. For Windows, a second code path uses `CreateFileMapping` and `MapViewOfFile` to achieve the same result. Reviewed By: mont29, brecht Differential Revision: https://developer.blender.org/D8246 --- intern/guardedalloc/CMakeLists.txt | 6 - intern/guardedalloc/intern/mmap_win.c | 294 ------------------ intern/guardedalloc/mmap_win.h | 50 --- source/blender/blenlib/BLI_mmap.h | 59 ++++ source/blender/blenlib/CMakeLists.txt | 2 + source/blender/blenlib/intern/BLI_mmap.c | 233 ++++++++++++++ source/blender/blenloader/intern/readfile.c | 68 +++- source/blender/blenloader/intern/readfile.h | 4 +- source/blender/imbuf/intern/IMB_allocimbuf.h | 2 +- source/blender/imbuf/intern/allocimbuf.c | 2 +- source/blender/imbuf/intern/readimage.c | 24 +- source/blender/makesdna/intern/CMakeLists.txt | 6 - source/blender/makesrna/intern/CMakeLists.txt | 1 - 13 files changed, 376 insertions(+), 375 deletions(-) delete mode 100644 intern/guardedalloc/intern/mmap_win.c delete mode 100644 intern/guardedalloc/mmap_win.h create mode 100644 source/blender/blenlib/BLI_mmap.h create mode 100644 source/blender/blenlib/intern/BLI_mmap.c diff --git a/intern/guardedalloc/CMakeLists.txt b/intern/guardedalloc/CMakeLists.txt index 0d46e81cd87..b47565b8ef9 100644 --- a/intern/guardedalloc/CMakeLists.txt +++ b/intern/guardedalloc/CMakeLists.txt @@ -49,12 +49,6 @@ set(LIB ) if(WIN32 AND NOT UNIX) - list(APPEND SRC - intern/mmap_win.c - - mmap_win.h - ) - list(APPEND INC_SYS ${PTHREADS_INC} ) diff --git a/intern/guardedalloc/intern/mmap_win.c b/intern/guardedalloc/intern/mmap_win.c deleted file mode 100644 index a02a0f88fa9..00000000000 --- a/intern/guardedalloc/intern/mmap_win.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup MEM - */ - -#ifdef WIN32 - -# include -# include -# include -# include -# include - -# include "mmap_win.h" - -# ifndef FILE_MAP_EXECUTE -// not defined in earlier versions of the Platform SDK (before February 2003) -# define FILE_MAP_EXECUTE 0x0020 -# endif - -/* copied from BLI_utildefines.h, ugh */ -# ifdef __GNUC__ -# define UNUSED(x) UNUSED_##x __attribute__((__unused__)) -# else -# define UNUSED(x) x -# endif - -/* --------------------------------------------------------------------- */ -/* local storage definitions */ -/* --------------------------------------------------------------------- */ -/* all memory mapped chunks are put in linked lists */ -typedef struct mmapLink { - struct mmapLink *next, *prev; -} mmapLink; - -typedef struct mmapListBase { - void *first, *last; -} mmapListBase; - -typedef struct MemMap { - struct MemMap *next, *prev; - void *mmap; - HANDLE fhandle; - HANDLE maphandle; -} MemMap; - -/* --------------------------------------------------------------------- */ -/* local functions */ -/* --------------------------------------------------------------------- */ - -static void mmap_addtail(volatile mmapListBase *listbase, void *vlink); -static void mmap_remlink(volatile mmapListBase *listbase, void *vlink); -static void *mmap_findlink(volatile mmapListBase *listbase, void *ptr); - -static int mmap_get_prot_flags(int flags); -static int mmap_get_access_flags(int flags); - -/* --------------------------------------------------------------------- */ -/* vars */ -/* --------------------------------------------------------------------- */ -volatile static struct mmapListBase _mmapbase; -volatile static struct mmapListBase *mmapbase = &_mmapbase; - -/* --------------------------------------------------------------------- */ -/* implementation */ -/* --------------------------------------------------------------------- */ - -/* mmap for windows */ -void *mmap(void *UNUSED(start), size_t len, int prot, int flags, int fd, off_t offset) -{ - HANDLE fhandle = INVALID_HANDLE_VALUE; - HANDLE maphandle; - int prot_flags = mmap_get_prot_flags(prot); - int access_flags = mmap_get_access_flags(prot); - MemMap *mm = NULL; - void *ptr = NULL; - - if (flags & MAP_FIXED) { - return MAP_FAILED; - } - -# if 0 - if (fd == -1) { - _set_errno(EBADF); - return MAP_FAILED; - } -# endif - - if (fd != -1) { - fhandle = (HANDLE)_get_osfhandle(fd); - } - if (fhandle == INVALID_HANDLE_VALUE) { - if (!(flags & MAP_ANONYMOUS)) { - errno = EBADF; - return MAP_FAILED; - } - } - else { - if (!DuplicateHandle(GetCurrentProcess(), - fhandle, - GetCurrentProcess(), - &fhandle, - 0, - FALSE, - DUPLICATE_SAME_ACCESS)) { - return MAP_FAILED; - } - } - - /* Split 64 bit size into low and high bits. */ - DWORD len_bits_high = len >> 32; - DWORD len_bits_low = len & 0xFFFFFFFF; - - maphandle = CreateFileMapping(fhandle, NULL, prot_flags, len_bits_high, len_bits_low, NULL); - if (maphandle == 0) { - errno = EBADF; - return MAP_FAILED; - } - - ptr = MapViewOfFile(maphandle, access_flags, 0, offset, 0); - if (ptr == NULL) { - DWORD dwLastErr = GetLastError(); - if (dwLastErr == ERROR_MAPPED_ALIGNMENT) { - errno = EINVAL; - } - else { - errno = EACCES; - } - CloseHandle(maphandle); - return MAP_FAILED; - } - - mm = (MemMap *)malloc(sizeof(MemMap)); - if (!mm) { - errno = ENOMEM; - } - mm->fhandle = fhandle; - mm->maphandle = maphandle; - mm->mmap = ptr; - mmap_addtail(mmapbase, mm); - - return ptr; -} - -/* munmap for windows */ -intptr_t munmap(void *ptr, size_t UNUSED(size)) -{ - MemMap *mm = mmap_findlink(mmapbase, ptr); - if (!mm) { - errno = EINVAL; - return -1; - } - UnmapViewOfFile(mm->mmap); - CloseHandle(mm->maphandle); - CloseHandle(mm->fhandle); - mmap_remlink(mmapbase, mm); - free(mm); - return 0; -} - -/* --------------------------------------------------------------------- */ -/* local functions */ -/* --------------------------------------------------------------------- */ - -static void mmap_addtail(volatile mmapListBase *listbase, void *vlink) -{ - struct mmapLink *link = vlink; - - if (link == NULL) { - return; - } - if (listbase == NULL) { - return; - } - - link->next = 0; - link->prev = listbase->last; - - if (listbase->last) { - ((struct mmapLink *)listbase->last)->next = link; - } - if (listbase->first == NULL) { - listbase->first = link; - } - listbase->last = link; -} - -static void mmap_remlink(volatile mmapListBase *listbase, void *vlink) -{ - struct mmapLink *link = vlink; - - if (link == NULL) { - return; - } - if (listbase == NULL) { - return; - } - if (link->next) { - link->next->prev = link->prev; - } - if (link->prev) { - link->prev->next = link->next; - } - - if (listbase->last == link) { - listbase->last = link->prev; - } - if (listbase->first == link) { - listbase->first = link->next; - } -} - -static void *mmap_findlink(volatile mmapListBase *listbase, void *ptr) -{ - MemMap *mm; - - if (ptr == NULL) { - return NULL; - } - if (listbase == NULL) { - return NULL; - } - - mm = (MemMap *)listbase->first; - while (mm) { - if (mm->mmap == ptr) { - return mm; - } - mm = mm->next; - } - return NULL; -} - -static int mmap_get_prot_flags(int flags) -{ - int prot = PAGE_NOACCESS; - - if ((flags & PROT_READ) == PROT_READ) { - if ((flags & PROT_WRITE) == PROT_WRITE) { - prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; - } - else { - prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READ : PAGE_READONLY; - } - } - else if ((flags & PROT_WRITE) == PROT_WRITE) { - prot = (flags & PROT_EXEC) ? PAGE_EXECUTE_READ : PAGE_WRITECOPY; - } - else if ((flags & PROT_EXEC) == PROT_EXEC) { - prot = PAGE_EXECUTE_READ; - } - return prot; -} - -static int mmap_get_access_flags(int flags) -{ - int access = 0; - - if ((flags & PROT_READ) == PROT_READ) { - if ((flags & PROT_WRITE) == PROT_WRITE) { - access = FILE_MAP_WRITE; - } - else { - access = (flags & PROT_EXEC) ? FILE_MAP_EXECUTE : FILE_MAP_READ; - } - } - else if ((flags & PROT_WRITE) == PROT_WRITE) { - access = FILE_MAP_COPY; - } - else if ((flags & PROT_EXEC) == PROT_EXEC) { - access = FILE_MAP_EXECUTE; - } - return access; -} - -#endif // WIN32 diff --git a/intern/guardedalloc/mmap_win.h b/intern/guardedalloc/mmap_win.h deleted file mode 100644 index c0cbaa0e512..00000000000 --- a/intern/guardedalloc/mmap_win.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup MEM - */ - -#ifndef __MMAP_WIN_H__ -#define __MMAP_WIN_H__ - -#define PROT_NONE 0 -#define PROT_READ 1 -#define PROT_WRITE 2 -#define PROT_EXEC 4 - -#define MAP_FILE 0 -#define MAP_SHARED 1 -#define MAP_PRIVATE 2 -#define MAP_TYPE 0xF -#define MAP_FIXED 0x10 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS - -#define MAP_FAILED ((void *)-1) - -/* needed for uintptr_t, exception, dont use BLI anywhere else in MEM_* */ -#include "../../source/blender/blenlib/BLI_sys_types.h" - -#include - -void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t offset); -intptr_t munmap(void *ptr, size_t size); - -#endif diff --git a/source/blender/blenlib/BLI_mmap.h b/source/blender/blenlib/BLI_mmap.h new file mode 100644 index 00000000000..385c56bd10a --- /dev/null +++ b/source/blender/blenlib/BLI_mmap.h @@ -0,0 +1,59 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +#ifndef __BLI_MMAP_H__ +#define __BLI_MMAP_H__ + +/** \file + * \ingroup bli + */ + +#include "BLI_compiler_attrs.h" +#include "BLI_utildefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Memory-mapped file IO that implements all the OS-specific details and error handling. */ + +struct BLI_mmap_file; + +typedef struct BLI_mmap_file BLI_mmap_file; + +/* Prepares an opened file for memory-mapped IO. + * May return NULL if the operation fails. + * Note that this seeks to the end of the file to determine its length. */ +BLI_mmap_file *BLI_mmap_open(int fd) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT; + +/* Reads length bytes from file at the given offset into dest. + * Returns whether the operation was successful (may fail when reading beyond the file + * end or when IO errors occur). */ +bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length) + ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); + +void *BLI_mmap_get_pointer(BLI_mmap_file *file) ATTR_WARN_UNUSED_RESULT; + +void BLI_mmap_free(BLI_mmap_file *file) ATTR_NONNULL(1); + +#ifdef __cplusplus +} +#endif + +#endif /* __BLI_MEMPOOL_H__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index aa4c4efe7d4..b7a13596dd0 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -54,6 +54,7 @@ set(SRC intern/BLI_memblock.c intern/BLI_memiter.c intern/BLI_mempool.c + intern/BLI_mmap.c intern/BLI_timer.c intern/DLRB_tree.c intern/array_store.c @@ -243,6 +244,7 @@ set(SRC BLI_mempool.h BLI_mesh_boolean.hh BLI_mesh_intersect.hh + BLI_mmap.h BLI_mpq2.hh BLI_mpq3.hh BLI_multi_value_map.hh diff --git a/source/blender/blenlib/intern/BLI_mmap.c b/source/blender/blenlib/intern/BLI_mmap.c new file mode 100644 index 00000000000..f0b13183c68 --- /dev/null +++ b/source/blender/blenlib/intern/BLI_mmap.c @@ -0,0 +1,233 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bli + */ + +#include "BLI_mmap.h" +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "MEM_guardedalloc.h" + +#include + +#ifndef WIN32 +# include +# include +# include // for mmap +# include // for read close +#else +# include "BLI_winstuff.h" +# include // for open close read +#endif + +struct BLI_mmap_file { + /* The address to which the file was mapped. */ + char *memory; + + /* The length of the file (and therefore the mapped region). */ + size_t length; + + /* Platform-specific handle for the mapping. */ + void *handle; + + /* Flag to indicate IO errors. Needs to be volatile since it's being set from + * within the signal handler, which is not part of the normal execution flow. */ + volatile bool io_error; +}; + +#ifndef WIN32 +/* When using memory-mapped files, any IO errors will result in a SIGBUS signal. + * Therefore, we need to catch that signal and stop reading the file in question. + * To do so, we keep a list of all current FileDatas that use memory-mapped files, + * and if a SIGBUS is caught, we check if the failed address is inside one of the + * mapped regions. + * If it is, we set a flag to indicate a failed read and remap the memory in + * question to a zero-backed region in order to avoid additional signals. + * The code that actually reads the memory area has to check whether the flag was + * set after it's done reading. + * If the error occurred outside of a memory-mapped region, we call the previous + * handler if one was configured and abort the process otherwise. + */ + +struct error_handler_data { + ListBase open_mmaps; + char configured; + void (*next_handler)(int, siginfo_t *, void *); +} error_handler = {0}; + +static void sigbus_handler(int sig, siginfo_t *siginfo, void *ptr) +{ + /* We only handle SIGBUS here for now. */ + BLI_assert(sig == SIGBUS); + + char *error_addr = (char *)siginfo->si_addr; + /* Find the file that this error belongs to. */ + LISTBASE_FOREACH (LinkData *, link, &error_handler.open_mmaps) { + BLI_mmap_file *file = link->data; + + /* Is the address where the error occurred in this file's mapped range? */ + if (error_addr >= file->memory && error_addr < file->memory + file->length) { + file->io_error = true; + + /* Replace the mapped memory with zeroes. */ + mmap(file->memory, file->length, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + + return; + } + } + + /* Fall back to other handler if there was one. */ + if (error_handler.next_handler) { + error_handler.next_handler(sig, siginfo, ptr); + } + else { + fprintf(stderr, "Unhandled SIGBUS caught\n"); + abort(); + } +} + +/* Ensures that the error handler is set up and ready. */ +static bool sigbus_handler_setup(void) +{ + if (!error_handler.configured) { + struct sigaction newact = {0}, oldact = {0}; + + newact.sa_sigaction = sigbus_handler; + newact.sa_flags = SA_SIGINFO; + + if (sigaction(SIGBUS, &newact, &oldact)) { + return false; + } + + /* Remember the previously configured handler to fall back to it if the error + * does not belong to any of the mapped files. */ + error_handler.next_handler = oldact.sa_sigaction; + error_handler.configured = 1; + } + + return true; +} + +/* Adds a file to the list that the error handler checks. */ +static void sigbus_handler_add(BLI_mmap_file *file) +{ + BLI_addtail(&error_handler.open_mmaps, BLI_genericNodeN(file)); +} + +/* Removes a file from the list that the error handler checks. */ +static void sigbus_handler_remove(BLI_mmap_file *file) +{ + LinkData *link = BLI_findptr(&error_handler.open_mmaps, file, offsetof(LinkData, data)); + BLI_freelinkN(&error_handler.open_mmaps, link); +} +#endif + +BLI_mmap_file *BLI_mmap_open(int fd) +{ + void *memory, *handle = NULL; + size_t length = BLI_lseek(fd, 0, SEEK_END); + +#ifndef WIN32 + /* Ensure that the SIGBUS handler is configured. */ + if (!sigbus_handler_setup()) { + return NULL; + } + + /* Map the given file to memory. */ + memory = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0); + if (memory == MAP_FAILED) { + return NULL; + } +#else + /* Convert the POSIX-style file descriptor to a Windows handle. */ + void *file_handle = (void *)_get_osfhandle(fd); + /* Memory mapping on Windows is a two-step process - first we create a mapping, + * then we create a view into that mapping. + * In our case, one view that spans the entire file is enough. */ + handle = CreateFileMapping(file_handle, NULL, PAGE_READONLY, 0, 0, NULL); + if (handle == NULL) { + return NULL; + } + memory = MapViewOfFile(handle, FILE_MAP_READ, 0, 0, 0); + if (memory == NULL) { + CloseHandle(handle); + return NULL; + } +#endif + + /* Now that the mapping was successful, allocate memory and set up the BLI_mmap_file. */ + BLI_mmap_file *file = MEM_callocN(sizeof(BLI_mmap_file), __func__); + file->memory = memory; + file->handle = handle; + file->length = length; + +#ifndef WIN32 + /* Register the file with the error handler. */ + sigbus_handler_add(file); +#endif + + return file; +} + +bool BLI_mmap_read(BLI_mmap_file *file, void *dest, size_t offset, size_t length) +{ + /* If a previous read has already failed or we try to read past the end, + * don't even attempt to read any further. */ + if (file->io_error || (offset + length > file->length)) { + return false; + } + +#ifndef WIN32 + /* If an error occurs in this call, sigbus_handler will be called and will set + * file->io_error to true. */ + memcpy(dest, file->memory + offset, length); +#else + /* On Windows, we use exception handling to be notified of errors. */ + __try { + memcpy(dest, file->memory + offset, length); + } + __except (GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR ? EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH) { + file->io_error = true; + return false; + } +#endif + + return !file->io_error; +} + +void *BLI_mmap_get_pointer(BLI_mmap_file *file) +{ + return file->memory; +} + +void BLI_mmap_free(BLI_mmap_file *file) +{ +#ifndef WIN32 + munmap((void *)file->memory, file->length); + sigbus_handler_remove(file); +#else + UnmapViewOfFile(file->memory); + CloseHandle(file->handle); +#endif + + MEM_freeN(file); +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index b61abd4ed06..2192e9a378f 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -68,6 +68,7 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_mempool.h" +#include "BLI_mmap.h" #include "BLI_threads.h" #include "BLT_translation.h" @@ -1179,6 +1180,53 @@ static ssize_t fd_read_from_memory(FileData *filedata, return readsize; } +/* Memory-mapped file reading. + * By using mmap(), we can map a file so that it can be treated like normal memory, + * meaning that we can just read from it with memcpy() etc. + * This avoids system call overhead and can significantly speed up file loading. + */ + +static ssize_t fd_read_from_mmap(FileData *filedata, + void *buffer, + size_t size, + bool *UNUSED(r_is_memchunck_identical)) +{ + /* don't read more bytes than there are available in the buffer */ + size_t readsize = MIN2(size, (size_t)(filedata->buffersize - filedata->file_offset)); + + if (!BLI_mmap_read(filedata->mmap_file, buffer, filedata->file_offset, readsize)) { + return 0; + } + + filedata->file_offset += readsize; + + return readsize; +} + +static off64_t fd_seek_from_mmap(FileData *filedata, off64_t offset, int whence) +{ + off64_t new_pos; + if (whence == SEEK_CUR) { + new_pos = filedata->file_offset + offset; + } + else if (whence == SEEK_SET) { + new_pos = offset; + } + else if (whence == SEEK_END) { + new_pos = filedata->buffersize + offset; + } + else { + return -1; + } + + if (new_pos < 0 || new_pos > filedata->buffersize) { + return -1; + } + + filedata->file_offset = new_pos; + return filedata->file_offset; +} + /* MemFile reading. */ static ssize_t fd_read_from_memfile(FileData *filedata, @@ -1306,6 +1354,8 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, { FileDataReadFn *read_fn = NULL; FileDataSeekFn *seek_fn = NULL; /* Optional. */ + size_t buffersize = 0; + BLI_mmap_file *mmap_file = NULL; gzFile gzfile = (gzFile)Z_NULL; @@ -1322,14 +1372,21 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, return NULL; } - BLI_lseek(file, 0, SEEK_SET); - /* Regular file. */ if (memcmp(header, "BLENDER", sizeof(header)) == 0) { read_fn = fd_read_data_from_file; seek_fn = fd_seek_data_from_file; + + mmap_file = BLI_mmap_open(file); + if (mmap_file != NULL) { + read_fn = fd_read_from_mmap; + seek_fn = fd_seek_from_mmap; + buffersize = BLI_lseek(file, 0, SEEK_END); + } } + BLI_lseek(file, 0, SEEK_SET); + /* Gzip file. */ errno = 0; if ((read_fn == NULL) && @@ -1363,6 +1420,8 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, fd->read = read_fn; fd->seek = seek_fn; + fd->mmap_file = mmap_file; + fd->buffersize = buffersize; return fd; } @@ -1531,6 +1590,11 @@ void blo_filedata_free(FileData *fd) fd->buffer = NULL; } + if (fd->mmap_file) { + BLI_mmap_free(fd->mmap_file); + fd->mmap_file = NULL; + } + /* Free all BHeadN data blocks */ #ifndef NDEBUG BLI_freelistN(&fd->bhead_list); diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index c724cc32051..86f05eda7b7 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -41,6 +41,7 @@ struct Object; struct OldNewMap; struct ReportList; struct UserDef; +struct BLI_mmap_file; typedef struct IDNameLib_Map IDNameLib_Map; @@ -83,8 +84,9 @@ typedef struct FileData { /** Regular file reading. */ int filedes; - /** Variables needed for reading from memory / stream. */ + /** Variables needed for reading from memory / stream / memory-mapped files. */ const char *buffer; + struct BLI_mmap_file *mmap_file; /** Variables needed for reading from memfile (undo). */ struct MemFile *memfile; /** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use diff --git a/source/blender/imbuf/intern/IMB_allocimbuf.h b/source/blender/imbuf/intern/IMB_allocimbuf.h index 08aa1936a6f..c92d764a104 100644 --- a/source/blender/imbuf/intern/IMB_allocimbuf.h +++ b/source/blender/imbuf/intern/IMB_allocimbuf.h @@ -32,7 +32,7 @@ struct ImBuf; void imb_refcounter_lock_init(void); void imb_refcounter_lock_exit(void); -#ifdef WIN32 +#ifndef WIN32 void imb_mmap_lock_init(void); void imb_mmap_lock_exit(void); void imb_mmap_lock(void); diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 8dfb3ada7d6..bfb7bc93ac7 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -53,7 +53,7 @@ void imb_refcounter_lock_exit(void) BLI_spin_end(&refcounter_spin); } -#ifdef WIN32 +#ifndef WIN32 static SpinLock mmap_spin; void imb_mmap_lock_init(void) diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index f0daa4543e1..50210650f05 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -23,13 +23,13 @@ */ #ifdef _WIN32 -# include "mmap_win.h" # include # include # include #endif #include "BLI_fileops.h" +#include "BLI_mmap.h" #include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -186,20 +186,19 @@ ImBuf *IMB_loadifffile( size = BLI_file_descriptor_size(file); imb_mmap_lock(); - mem = mmap(NULL, size, PROT_READ, MAP_SHARED, file, 0); + BLI_mmap_file *mmap_file = BLI_mmap_open(file); imb_mmap_unlock(); - - if (mem == (unsigned char *)-1) { + if (mmap_file == NULL) { fprintf(stderr, "%s: couldn't get mapping %s\n", __func__, descr); return NULL; } + mem = BLI_mmap_get_pointer(mmap_file); + ibuf = IMB_ibImageFromMemory(mem, size, flags, colorspace, descr); imb_mmap_lock(); - if (munmap(mem, size)) { - fprintf(stderr, "%s: couldn't unmap file %s\n", __func__, descr); - } + BLI_mmap_free(mmap_file); imb_mmap_unlock(); return ibuf; @@ -292,14 +291,15 @@ static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int size = BLI_file_descriptor_size(file); imb_mmap_lock(); - mem = mmap(NULL, size, PROT_READ, MAP_SHARED, file, 0); + BLI_mmap_file *mmap_file = BLI_mmap_open(file); imb_mmap_unlock(); - - if (mem == (unsigned char *)-1) { + if (mmap_file == NULL) { fprintf(stderr, "Couldn't get memory mapping for %s\n", ibuf->cachename); return; } + mem = BLI_mmap_get_pointer(mmap_file); + const ImFileType *type = IMB_file_type_from_ibuf(ibuf); if (type != NULL) { if (type->load_tile != NULL) { @@ -308,9 +308,7 @@ static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int } imb_mmap_lock(); - if (munmap(mem, size)) { - fprintf(stderr, "Couldn't unmap memory for %s.\n", ibuf->cachename); - } + BLI_mmap_free(mmap_file); imb_mmap_unlock(); } diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt index a7adaa7e258..5d1ed7b44a1 100644 --- a/source/blender/makesdna/intern/CMakeLists.txt +++ b/source/blender/makesdna/intern/CMakeLists.txt @@ -48,12 +48,6 @@ set(SRC ../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c ) -if(WIN32 AND NOT UNIX) - list(APPEND SRC - ../../../../intern/guardedalloc/intern/mmap_win.c - ) -endif() - # SRC_DNA_INC is defined in the parent dir add_cc_flags_custom_test(makesdna) diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 72cdaecb2c3..94cfd8464ae 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -189,7 +189,6 @@ set(SRC ../../../../intern/guardedalloc/intern/mallocn.c ../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c ../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c - ../../../../intern/guardedalloc/intern/mmap_win.c # Needed for defaults. ../../../../release/datafiles/userdef/userdef_default.c