Skip to content

Commit

Permalink
Add: MappedGltfFile
Browse files Browse the repository at this point in the history
  • Loading branch information
spnda committed Mar 23, 2024
1 parent 0ec5dc8 commit f0726e0
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 2 deletions.
46 changes: 46 additions & 0 deletions include/fastgltf/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ namespace fastgltf {
};

class GltfDataBuffer : public GltfDataGetter {
protected:
std::unique_ptr<std::byte[]> buffer;

std::size_t allocatedSize = 0;
Expand Down Expand Up @@ -687,6 +688,51 @@ namespace fastgltf {
}
};

#if defined(__APPLE__) || defined(__linux__)
/** Maps a glTF file into memory using mmap */
class MappedGltfFile : public GltfDataGetter {
void* mappedFile = nullptr;
std::uint64_t fileSize = 0;

std::size_t idx = 0;

Error error = Error::None;

explicit MappedGltfFile(const std::filesystem::path& path) noexcept;

public:
explicit MappedGltfFile() = default;
MappedGltfFile(const MappedGltfFile& other) = delete;
MappedGltfFile& operator=(const MappedGltfFile& other) = delete;
MappedGltfFile(MappedGltfFile&& other) noexcept;
MappedGltfFile& operator=(MappedGltfFile&& other) noexcept;

static Expected<MappedGltfFile> FromPath(const std::filesystem::path& path) noexcept {
MappedGltfFile buffer(path);
if (buffer.error != fastgltf::Error::None) {
return buffer.error;
}
return std::move(buffer);
}

~MappedGltfFile() noexcept;

void read(void* ptr, std::size_t count) override;

[[nodiscard]] span<std::byte> read(std::size_t count, std::size_t padding) override;

void reset() override;

[[nodiscard]] std::size_t bytesRead() override;

[[nodiscard]] std::size_t totalSize() override;

[[nodiscard]] explicit operator span<std::byte>() {
return span<std::byte>(static_cast<std::byte*>(mappedFile), fileSize);
}
};
#endif

class GltfFileStream : public GltfDataGetter {
std::ifstream fileStream;
std::vector<std::ifstream::char_type> buf;
Expand Down
92 changes: 90 additions & 2 deletions src/fastgltf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@
#include <android/asset_manager.h>
#endif

#if defined(__APPLE__) || defined(__linux__)
#include <unistd.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/stat.h>
#endif

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 5030) // attribute 'x' is not recognized
Expand Down Expand Up @@ -3808,8 +3815,89 @@ std::size_t fg::GltfDataBuffer::totalSize() {
return dataSize;
}

fg::GltfFileStream::GltfFileStream(const std::filesystem::path &path) : fileStream(path, std::ios::binary) {
fileSize = std::filesystem::file_size(path);
#if defined(__APPLE__) || defined(__linux__)
fg::MappedGltfFile::MappedGltfFile(const fs::path& path) noexcept {
// Open the file
int fd = open(path.c_str(), O_RDONLY, 0);
if (fd == 0) {
// TODO: Cover actual error messages using std::strerror(errno)?
error = Error::InvalidPath;
return;
}

// Get the file size
struct stat statInfo;
if (fstat(fd, &statInfo) != 0) {
error = Error::InvalidPath;
return;
}

// Map the file
mappedFile = mmap(nullptr,
statInfo.st_size,
PROT_READ,
MAP_PRIVATE,
fd,
0);
if (mappedFile != MAP_FAILED) {
fileSize = static_cast<std::uint64_t>(statInfo.st_size);
} else {
std::perror("Mapping file");
error = Error::FileBufferAllocationFailed;
}

// The file descriptor is not used for the memory mapped and is not required anymore.
close(fd);
}

fg::MappedGltfFile::MappedGltfFile(fastgltf::MappedGltfFile &&other) noexcept {
// Make sure that munmap is never called when other gets destructud.
mappedFile = std::exchange(other.mappedFile, MAP_FAILED);
fileSize = other.fileSize;
idx = other.idx;
error = other.error;
}

fg::MappedGltfFile& fg::MappedGltfFile::operator=(fastgltf::MappedGltfFile &&other) noexcept {
mappedFile = std::exchange(other.mappedFile, MAP_FAILED);
fileSize = other.fileSize;
idx = other.idx;
error = other.error;
return *this;
}

fg::MappedGltfFile::~MappedGltfFile() noexcept {
if (mappedFile != MAP_FAILED) {
munmap(mappedFile, fileSize);
}
}

void fg::MappedGltfFile::read(void *ptr, std::size_t count) {
std::memcpy(ptr, static_cast<std::byte*>(mappedFile) + idx, count);
idx += count;
}

fg::span<std::byte> fg::MappedGltfFile::read(std::size_t count, std::size_t padding) {
span<std::byte> sub(static_cast<std::byte*>(mappedFile) + idx, count);
idx += count;
return sub;
}

void fg::MappedGltfFile::reset() {
idx = 0;
}

std::size_t fg::MappedGltfFile::bytesRead() {
return idx;
}

std::size_t fg::MappedGltfFile::totalSize() {
return fileSize;
}
#endif

fg::GltfFileStream::GltfFileStream(const fs::path& path) : fileStream(path, std::ios::binary) {
fileSize = fs::file_size(path);
}

bool fg::GltfFileStream::isOpen() const {
Expand Down
16 changes: 16 additions & 0 deletions tests/basic_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,3 +666,19 @@ TEST_CASE("Test extras callback", "[gltf-loader]") {
REQUIRE(nodeNames[2] == "Shoe.obj");
}
}

#if defined(__APPLE__) || defined(__linux__)
TEST_CASE("Test glTF file loading", "[gltf-loader]") {
SECTION("Mapped files") {
auto cubePath = sampleModels / "2.0" / "Cube" / "glTF";
auto mappedFile = fastgltf::MappedGltfFile::FromPath(cubePath / "Cube.gltf");
REQUIRE(mappedFile.error() == fastgltf::Error::None);

REQUIRE(fastgltf::determineGltfFileType(mappedFile.get()) == fastgltf::GltfType::glTF);

fastgltf::Parser parser;
auto asset = parser.loadGltfJson(mappedFile.get(), cubePath);
REQUIRE(asset.error() == fastgltf::Error::None);
}
}
#endif

0 comments on commit f0726e0

Please sign in to comment.