/* * Copyright 2018 Ales Mlakar. All rights reserved. * License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE */ /* * Reference(s): * - Sparse Virtual Textures by Sean Barrett * http://web.archive.org/web/20190103162611/http://silverspaceship.com/src/svt/ * - Based on Virtual Texture Demo by Brad Blanchard * http://web.archive.org/web/20190103162638/http://linedef.com/virtual-texture-demo.html * - Mars texture * http://web.archive.org/web/20190103162730/http://www.celestiamotherlode.net/catalog/mars.php */ #pragma once #include #include #include #include #include #include "common.h" #include "bgfx_utils.h" #include // FILE namespace vt { // Forward declarations class PageCache; class TextureAtlas; class TileDataFile; // Point struct Point { int m_x, m_y; }; // Rect struct Rect { int minX() const { return m_x; } int minY() const { return m_y; } int maxX() const { return m_x + m_width; } int maxY() const { return m_y + m_height; } bool contains(const Point& p) const { return p.m_x >= minX() && p.m_y >= minY() && p.m_x < maxX() && p.m_y < maxY(); } int m_x, m_y, m_width, m_height; }; // Color struct Color { uint8_t m_r, m_g, m_b, m_a; }; // Page struct Page { operator size_t() const; int m_x; int m_y; int m_mip; }; // PageCount struct PageCount { Page m_page; int m_count; PageCount(Page _page, int _count); int compareTo(const PageCount& other) const; }; // VirtualTextureInfo struct VirtualTextureInfo { VirtualTextureInfo(); int GetPageSize() const; int GetPageTableSize() const; int m_virtualTextureSize = 0; int m_tileSize = 0; int m_borderSize = 0; }; // StagingPool class StagingPool { public: StagingPool(int _width, int _height, int _count, bool _readBack); ~StagingPool(); void grow(int count); bgfx::TextureHandle getTexture(); void next(); private: tinystl::vector m_stagingTextures; int m_stagingTextureIndex; int m_width; int m_height; uint64_t m_flags; }; // PageIndexer struct PageIndexer { public: PageIndexer(VirtualTextureInfo* _info); int getIndexFromPage(Page page); Page getPageFromIndex(int index); bool isValid(Page page); int getCount() const; int getMipCount() const; private: VirtualTextureInfo* m_info; int m_mipcount; int m_count; tinystl::vector m_offsets; // This stores the offsets to the first page of the start of a mipmap level tinystl::vector m_sizes; // This stores the sizes of various mip levels tinystl::vector m_reverse; }; // SimpleImage struct SimpleImage { SimpleImage(int _width, int _height, int _channelCount, uint8_t _clearValue = 0); SimpleImage(int _width, int _height, int _channelCount, tinystl::vector& _data); void copy(Point dest_offset, SimpleImage& src, Rect src_rect); void clear(uint8_t clearValue = 0); void fill(Rect rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a); static void mipmap(uint8_t* source, int size, int channels, uint8_t* dest); int m_width = 0; int m_height = 0; int m_channelCount = 0; tinystl::vector m_data; }; // Quadtree struct Quadtree { Quadtree(Rect _rect, int _level); ~Quadtree(); void add(Page request, Point mapping); void remove(Page request); void write(SimpleImage& image, int miplevel); Rect getRectangle(int index); void write(Quadtree* node, SimpleImage& image, int miplevel); static Quadtree* findPage(Quadtree* node, Page request, int* index); Rect m_rectangle; int m_level; Point m_mapping; Quadtree* m_children[4]; }; // PageTable class PageTable { public: PageTable(PageCache* _cache, VirtualTextureInfo* _info, PageIndexer* _indexer); ~PageTable(); void update(bgfx::ViewId blitViewId); bgfx::TextureHandle getTexture(); private: VirtualTextureInfo* m_info; bgfx::TextureHandle m_texture; PageIndexer* m_indexer; Quadtree* m_quadtree; bool m_quadtreeDirty; tinystl::vector m_images; tinystl::vector m_stagingTextures; }; // PageLoader class PageLoader { public: struct ReadState { Page m_page; tinystl::vector m_data; }; PageLoader(TileDataFile* _tileDataFile, PageIndexer* _indexer, VirtualTextureInfo* _info); void submit(Page request); void loadPage(ReadState& state); void onPageLoadComplete(ReadState& state); void copyBorder(uint8_t* image); void copyColor(uint8_t* image, Page request); std::function loadComplete; bool m_colorMipLevels; bool m_showBorders; private: TileDataFile* m_tileDataFile; PageIndexer* m_indexer; VirtualTextureInfo* m_info; }; // PageCache class PageCache { public: PageCache(TextureAtlas* _atlas, PageLoader* _loader, int _count); bool touch(Page page); bool request(Page request, bgfx::ViewId blitViewId); void clear(); void loadComplete(Page page, uint8_t* data); // These callbacks are used to notify the other systems std::function removed; std::function added; private: TextureAtlas* m_atlas; PageLoader* m_loader; int m_count; struct LruPage { Page m_page; Point m_point; bool operator==(const Page& other) const { return m_page == other; } }; int m_current; // This is used for generating the texture atlas indices before the lru is full tinystl::unordered_set m_lru_used; tinystl::vector m_lru; tinystl::unordered_set m_loading; bgfx::ViewId m_blitViewId; }; // TextureAtlas class TextureAtlas { public: TextureAtlas(VirtualTextureInfo* _info, int count, int uploadsperframe); ~TextureAtlas(); void setUploadsPerFrame(int count); void uploadPage(Point pt, uint8_t* data, bgfx::ViewId blitViewId); bgfx::TextureHandle getTexture(); private: VirtualTextureInfo* m_info; bgfx::TextureHandle m_texture; StagingPool m_stagingPool; }; // FeedbackBuffer class FeedbackBuffer { public: FeedbackBuffer(VirtualTextureInfo* _info, int _width, int _height); ~FeedbackBuffer(); void clear(); void copy(bgfx::ViewId viewId); void download(); // This function validates the pages and adds the page's parents // We do this so that we can fall back to them if we run out of memory void addRequestAndParents(Page request); const tinystl::vector& getRequests() const; bgfx::FrameBufferHandle getFrameBuffer(); int getWidth() const; int getHeight() const; private: VirtualTextureInfo* m_info; PageIndexer* m_indexer; int m_width = 0; int m_height = 0; StagingPool m_stagingPool; bgfx::TextureHandle m_lastStagingTexture; bgfx::FrameBufferHandle m_feedbackFrameBuffer; // This stores the pages by index. The int value is number of requests. tinystl::vector m_requests; tinystl::vector m_downloadBuffer; }; // VirtualTexture class VirtualTexture { public: VirtualTexture(TileDataFile* _tileDataFile, VirtualTextureInfo* _info, int _atlassize, int _uploadsperframe, int _mipBias = 4); ~VirtualTexture(); int getMipBias() const; void setMipBias(int value); void setUploadsPerFrame(int count); int getUploadsPerFrame() const; void enableShowBoarders(bool enable); bool isShowBoardersEnabled() const; void enableColorMipLevels(bool enable); bool isColorMipLevelsEnabled() const; bgfx::TextureHandle getAtlastTexture(); bgfx::TextureHandle getPageTableTexture(); void clear(); void update(const tinystl::vector& requests, bgfx::ViewId blitViewId); void setUniforms(); static void setAllocator(bx::AllocatorI* allocator); static bx::AllocatorI* getAllocator(); private: TileDataFile* m_tileDataFile; VirtualTextureInfo* m_info; PageIndexer* m_indexer; PageTable* m_pageTable; TextureAtlas* m_atlas; PageLoader* m_loader; PageCache* m_cache; int m_atlasCount; int m_uploadsPerFrame; tinystl::vector m_pagesToLoad; int m_mipBias; bgfx::UniformHandle u_vt_settings_1; bgfx::UniformHandle u_vt_settings_2; bgfx::UniformHandle s_vt_page_table; bgfx::UniformHandle s_vt_texture_atlas; static bx::AllocatorI* s_allocator; }; // TileDataFile class TileDataFile { public: TileDataFile(const bx::FilePath& filename, VirtualTextureInfo* _info, bool _readWrite = false); ~TileDataFile(); void readInfo(); void writeInfo(); void readPage(int index, uint8_t* data); void writePage(int index, uint8_t* data); private: VirtualTextureInfo* m_info; int m_size; FILE* m_file; }; // TileGenerator class TileGenerator { public: TileGenerator(VirtualTextureInfo* _info); ~TileGenerator(); bool generate(const bx::FilePath& filename); private: void CopyTile(SimpleImage& image, Page request); private: VirtualTextureInfo* m_info; PageIndexer* m_indexer; TileDataFile* m_tileDataFile; int m_tilesize; int m_pagesize; bimg::ImageContainer* m_sourceImage; SimpleImage* m_page1Image; SimpleImage* m_page2Image; SimpleImage* m_2xtileImage; SimpleImage* m_4xtileImage; SimpleImage* m_tileImage; }; } // namespace vt