bgfx/examples/40-svt/vt.h
2019-01-05 08:44:36 +01:00

440 lines
9.0 KiB
C++

/*
* Copyright 2018 Aleš Mlakar. All rights reserved.
* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
*/
/*
* 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 "common.h"
#include "bgfx_utils.h"
#include "bimg/decode.h"
#include <tinystl/allocator.h>
#include <tinystl/unordered_set.h>
#include <tinystl/vector.h>
#include <functional>
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<bgfx::TextureHandle> 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<int> m_offsets; // This stores the offsets to the first page of the start of a mipmap level
tinystl::vector<int> m_sizes; // This stores the sizes of various mip levels
tinystl::vector<Page> 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<uint8_t>& _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<uint8_t> 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<SimpleImage*> m_images;
tinystl::vector<bgfx::TextureHandle> m_stagingTextures;
};
// PageLoader
class PageLoader
{
public:
struct ReadState
{
Page m_page;
tinystl::vector<uint8_t> 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<void(Page, uint8_t*)> 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<void(Page, Point)> removed;
std::function<void(Page, Point)> 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<Page> m_lru_used;
tinystl::vector<LruPage> m_lru;
tinystl::unordered_set<Page> 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<int>& 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<int> m_requests;
tinystl::vector<uint8_t> 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<int>& 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<PageCount> 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