├── LICENSE ├── README.md ├── include └── vtex │ ├── FeedbackBuffer.h │ ├── PageCache.h │ ├── PageTable.h │ ├── TextureAtlas.h │ ├── VirtualTexture.h │ ├── feedback.frag │ └── final.frag ├── platform └── msvc │ ├── .gitignore │ └── projects │ ├── vtex.vcxproj │ └── vtex.vcxproj.filters └── source ├── FeedbackBuffer.cpp ├── PageCache.cpp ├── PageTable.cpp ├── TextureAtlas.cpp └── VirtualTexture.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Guang Zhu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vtex 2 | 3 | virtual texture 4 | 5 | used code from: 6 | 7 | http://linedef.com/virtual-texture-demo.html 8 | 9 | https://silverspaceship.com/src/svt/ 10 | -------------------------------------------------------------------------------- /include/vtex/FeedbackBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace textile { class PageIndexer; } 12 | namespace ur { class Device; class Framebuffer; } 13 | 14 | namespace vtex 15 | { 16 | 17 | class FeedbackBuffer : private boost::noncopyable 18 | { 19 | public: 20 | FeedbackBuffer(const ur::Device& dev, int size, int page_table_w, 21 | int page_table_h, const textile::PageIndexer& indexer); 22 | ~FeedbackBuffer(); 23 | 24 | void BindRT(); 25 | void UnbindRT(); 26 | 27 | void Download(const ur::Device& dev); 28 | 29 | const std::vector& GetRequests() const { return m_requests; } 30 | 31 | void Clear(); 32 | 33 | auto GetTexture() const { return m_fbo_col_tex; } 34 | 35 | private: 36 | const textile::PageIndexer& m_indexer; 37 | 38 | int m_size; 39 | int m_page_table_w, m_page_table_h; 40 | 41 | ur::TexturePtr m_fbo_col_tex = nullptr; 42 | ur::TexturePtr m_fbo_depth_tex = nullptr; 43 | std::shared_ptr m_fbo = nullptr; 44 | uint8_t* m_data; 45 | 46 | std::vector m_requests; 47 | 48 | }; // FeedbackBuffer 49 | 50 | } -------------------------------------------------------------------------------- /include/vtex/PageCache.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace vtex 6 | { 7 | 8 | class TextureAtlas; 9 | class PageTable; 10 | 11 | class PageCache : public textile::PageCache 12 | { 13 | public: 14 | PageCache(TextureAtlas& atlas, textile::PageLoader& loader, 15 | PageTable& table, const textile::PageIndexer& indexer); 16 | 17 | virtual void LoadComplete(const ur::Device& dev, const textile::Page& page, const uint8_t* data) override; 18 | 19 | private: 20 | TextureAtlas& m_atlas; 21 | PageTable& m_table; 22 | 23 | }; // PageCache 24 | 25 | } -------------------------------------------------------------------------------- /include/vtex/PageTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace textile { struct Page; } 11 | namespace ur { class Device; } 12 | 13 | namespace vtex 14 | { 15 | 16 | class PageTable : private boost::noncopyable 17 | { 18 | public: 19 | PageTable(const ur::Device& dev, int width, int height); 20 | 21 | void AddPage(const textile::Page& page, int mapping_x, int mapping_y); 22 | void RemovePage(const textile::Page& page); 23 | 24 | void Update(); 25 | 26 | auto GetTexture() const { return m_tex; } 27 | 28 | private: 29 | struct Image 30 | { 31 | ~Image() { 32 | delete[] data; 33 | } 34 | 35 | int w = 0, h = 0; 36 | uint8_t* data = nullptr; 37 | }; 38 | 39 | struct Rect 40 | { 41 | Rect(int x, int y, int w, int h) 42 | : x(x), y(y), w(w), h(h) {} 43 | 44 | bool Contain(int px, int py) const { 45 | return px >= x && px < x + w 46 | && py >= y && py < y + h; 47 | } 48 | 49 | int x, y; 50 | int w, h; 51 | }; 52 | 53 | struct QuadNode : private boost::noncopyable 54 | { 55 | QuadNode(int level, const Rect& rect); 56 | 57 | Rect CalcChildRect(int idx) const; 58 | 59 | void Write(int w, int h, uint8_t* data, int mip_level); 60 | 61 | int level; 62 | Rect rect; 63 | 64 | int mapping_x, mapping_y; 65 | 66 | std::unique_ptr children[4]; 67 | 68 | }; // QuadNode 69 | 70 | private: 71 | QuadNode* FindPage(const textile::Page& page, int& index) const; 72 | 73 | size_t CalcMaxLevel() const; 74 | 75 | private: 76 | int m_width, m_height; 77 | 78 | std::unique_ptr m_root; 79 | 80 | std::vector m_data; 81 | 82 | ur::TexturePtr m_tex = nullptr; 83 | 84 | }; // PageTable 85 | 86 | } -------------------------------------------------------------------------------- /include/vtex/TextureAtlas.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace ur { class Device; } 10 | 11 | namespace vtex 12 | { 13 | 14 | class TextureAtlas : private boost::noncopyable 15 | { 16 | public: 17 | TextureAtlas(const ur::Device& dev, size_t atlas_size, 18 | size_t page_size, int tex_channel); 19 | 20 | int GetSize() const { return m_atlas_size; } 21 | 22 | size_t GetPageCount() const { return m_page_count; } 23 | 24 | void Bind(); 25 | 26 | void UploadPage(const uint8_t* pixels, int x, int y); 27 | 28 | auto GetTexture() const { return m_tex; } 29 | 30 | private: 31 | size_t m_atlas_size; 32 | size_t m_page_size; 33 | size_t m_page_count; 34 | 35 | int m_tex_channel; 36 | 37 | ur::TexturePtr m_tex = nullptr; 38 | 39 | }; // TextureAtlas 40 | 41 | } -------------------------------------------------------------------------------- /include/vtex/VirtualTexture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "vtex/FeedbackBuffer.h" 4 | #include "vtex/TextureAtlas.h" 5 | #include "vtex/PageCache.h" 6 | #include "vtex/PageTable.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace ur { class Device; class Context; class ShaderProgram; } 17 | 18 | namespace vtex 19 | { 20 | 21 | class VirtualTexture : private boost::noncopyable 22 | { 23 | public: 24 | VirtualTexture(const ur::Device& dev, const std::string& filepath, 25 | const textile::VTexInfo& info, int atlas_channel, int feedback_size); 26 | 27 | void Draw(const ur::Device& dev, ur::Context& ctx, 28 | std::function draw_cb); 29 | 30 | void ClearCache() { m_cache.Clear(); } 31 | textile::PageLoader& GetPageLoader() { return m_loader; } 32 | 33 | void DecreaseMipBias(); 34 | 35 | auto Width() const { return m_vtex_w; } 36 | auto Height() const { return m_vtex_h; } 37 | 38 | private: 39 | void InitShaders(const ur::Device& dev); 40 | 41 | void Update(const ur::Device& dev, 42 | const std::vector& requests); 43 | 44 | private: 45 | struct PageWithCount 46 | { 47 | PageWithCount(const textile::Page& page, int count) 48 | : page(page), count(count) {} 49 | 50 | textile::Page page; 51 | int count = 0; 52 | }; 53 | 54 | private: 55 | int m_feedback_size; 56 | int m_vtex_w, m_vtex_h; 57 | 58 | textile::VTexInfo m_info; 59 | 60 | std::shared_ptr m_feedback_shader = nullptr; 61 | std::shared_ptr m_final_shader = nullptr; 62 | 63 | TextureAtlas m_atlas; 64 | 65 | textile::PageIndexer m_indexer; 66 | textile::PageLoader m_loader; 67 | 68 | PageTable m_table; 69 | PageCache m_cache; 70 | 71 | FeedbackBuffer m_feedback; 72 | 73 | std::vector m_toload; 74 | 75 | int m_mip_bias; 76 | 77 | }; // VirtualTexture 78 | 79 | } -------------------------------------------------------------------------------- /include/vtex/feedback.frag: -------------------------------------------------------------------------------- 1 | static const char* feedback_frag = R"( 2 | 3 | uniform vec2 u_page_table_size; 4 | uniform vec2 u_virt_tex_size; 5 | uniform float u_mip_sample_bias; 6 | 7 | varying vec2 v_texcoord; 8 | 9 | float tex_mip_level(vec2 coord, vec2 tex_size) 10 | { 11 | vec2 dx_scaled, dy_scaled; 12 | vec2 coord_scaled = coord * tex_size; 13 | 14 | dx_scaled = dFdx(coord_scaled); 15 | dy_scaled = dFdy(coord_scaled); 16 | 17 | vec2 dtex = dx_scaled*dx_scaled + dy_scaled*dy_scaled; 18 | float min_delta = max(dtex.x,dtex.y); 19 | float miplevel = max(0.5 * log2(min_delta), 0.0); 20 | 21 | return miplevel; 22 | } 23 | 24 | void main() 25 | { 26 | float mip = floor(tex_mip_level(v_texcoord, u_virt_tex_size) - u_mip_sample_bias); 27 | mip = clamp(mip, 0, log2(u_page_table_size.x)); 28 | 29 | float times = u_page_table_size.x / exp2(mip); 30 | vec2 offset = floor(v_texcoord * times); 31 | offset = clamp(offset, vec2(0, 0), vec2(times - 1, times - 1)); 32 | gl_FragColor = vec4(vec3(offset / 255.0, mip / 255.0), 1.0); 33 | } 34 | 35 | )"; -------------------------------------------------------------------------------- /include/vtex/final.frag: -------------------------------------------------------------------------------- 1 | static const char* final_frag = R"( 2 | 3 | uniform sampler2D u_page_table_tex; 4 | uniform sampler2D u_texture_atlas_tex; 5 | 6 | uniform vec2 u_page_table_size; 7 | uniform vec2 u_virt_tex_size; 8 | 9 | uniform float u_atlas_scale; // This value is used to scale the uv to the texture atlas. It holds (PageSize/TextureAtlasSize) 10 | 11 | uniform float u_border_scale; // (PageSize-2*BorderSize)/PageSize 12 | uniform float u_border_offset; // BorderSize/PageSize 13 | 14 | varying vec2 v_texcoord; 15 | 16 | float tex_mip_level(vec2 coord, vec2 tex_size) 17 | { 18 | vec2 dx_scaled, dy_scaled; 19 | vec2 coord_scaled = coord * tex_size; 20 | 21 | dx_scaled = dFdx(coord_scaled); 22 | dy_scaled = dFdy(coord_scaled); 23 | 24 | vec2 dtex = dx_scaled*dx_scaled + dy_scaled*dy_scaled; 25 | float min_delta = max(dtex.x,dtex.y); 26 | float miplevel = max(0.5 * log2(min_delta), 0.0); 27 | 28 | return miplevel; 29 | } 30 | 31 | // This function samples the page table and returns the page's 32 | // position and mip level. 33 | vec3 sample_table(vec2 uv, float mip) 34 | { 35 | vec2 offset = fract(uv * u_page_table_size) / u_page_table_size; 36 | return texture2D(u_page_table_tex, uv - offset, mip).xyz; 37 | } 38 | 39 | // This functions samples from the texture atlas and returns the final color 40 | vec4 sample_atlas(vec3 page, vec2 uv) 41 | { 42 | float mipsize = exp2(floor(page.z * 255.0 + 0.5)); 43 | 44 | uv = fract(uv * u_page_table_size / mipsize); 45 | 46 | uv *= u_border_scale; 47 | uv += u_border_offset; 48 | vec2 offset = floor(page.xy * 255 + 0.5); 49 | 50 | return texture2D(u_texture_atlas_tex, (offset + uv) * u_atlas_scale); 51 | } 52 | 53 | vec4 bilinear_sample() 54 | { 55 | float mip = floor(tex_mip_level(v_texcoord, u_virt_tex_size)); 56 | mip = clamp(mip, 0, log2(u_page_table_size.x)); 57 | 58 | vec3 page = sample_table(v_texcoord, mip); 59 | return sample_atlas(page, v_texcoord); 60 | } 61 | 62 | vec4 trilinear_sample() 63 | { 64 | float miplevel = tex_mip_level(v_texcoord, u_virt_tex_size); 65 | miplevel = clamp(miplevel, 0, log2(u_page_table_size.x) - 1); 66 | 67 | float mip1 = floor(miplevel); 68 | float mip2 = mip1 + 1; 69 | float mipfrac = miplevel - mip1; 70 | 71 | vec3 page1 = sample_table(v_texcoord, mip1); 72 | vec3 page2 = sample_table(v_texcoord, mip2); 73 | 74 | vec4 sample1 = sample_atlas(page1, v_texcoord); 75 | vec4 sample2 = sample_atlas(page2, v_texcoord); 76 | 77 | return mix(sample1, sample2, mipfrac); 78 | } 79 | 80 | void main() 81 | { 82 | gl_FragColor = bilinear_sample(); 83 | } 84 | 85 | )"; -------------------------------------------------------------------------------- /platform/msvc/.gitignore: -------------------------------------------------------------------------------- 1 | vtex/ 2 | projects/* 3 | 4 | !projects/vtex.vcxproj 5 | !projects/vtex.vcxproj.filters -------------------------------------------------------------------------------- /platform/msvc/projects/vtex.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 3.vtex 33 | {EB17C700-1495-4066-9722-D62B71C0C55A} 34 | vtex 35 | Win32Proj 36 | 10.0.16299.0 37 | 38 | 39 | 40 | StaticLibrary 41 | v141 42 | Unicode 43 | true 44 | 45 | 46 | StaticLibrary 47 | v141 48 | Unicode 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | <_ProjectFileVersion>15.0.26730.12 62 | 63 | 64 | ..\vtex\x86\Debug\ 65 | ..\vtex\x86\Debug\obj\ 66 | 67 | 68 | ..\vtex\x86\Release\ 69 | ..\vtex\x86\Release\obj\ 70 | 71 | 72 | 73 | Disabled 74 | ..\..\..\include;..\..\..\..\cu\src;..\..\..\..\sm\src\sm;..\..\..\..\memmgr\include;..\..\..\..\guard\include;..\..\..\..\textile\include;..\..\..\..\unirender\include;..\..\..\..\painting2\include;..\..\..\..\painting3\include;..\..\..\..\multitask\include;..\..\..\..\shadertrans\include;..\..\..\..\external\boost\include; 75 | WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 76 | true 77 | EnableFastChecks 78 | MultiThreadedDebugDLL 79 | 80 | Level3 81 | EditAndContinue 82 | 83 | 84 | 85 | 86 | MaxSpeed 87 | true 88 | ..\..\..\include;..\..\..\..\cu\src;..\..\..\..\sm\src\sm;..\..\..\..\memmgr\include;..\..\..\..\guard\include;..\..\..\..\textile\include;..\..\..\..\unirender\include;..\..\..\..\painting2\include;..\..\..\..\painting3\include;..\..\..\..\multitask\include;..\..\..\..\shadertrans\include;..\..\..\..\external\boost\include; 89 | WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 90 | MultiThreadedDLL 91 | true 92 | 93 | Level3 94 | ProgramDatabase 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /platform/msvc/projects/vtex.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /source/FeedbackBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "vtex/FeedbackBuffer.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace vtex 12 | { 13 | 14 | FeedbackBuffer::FeedbackBuffer(const ur::Device& dev, int size, int page_table_w, 15 | int page_table_h, const textile::PageIndexer& indexer) 16 | : m_size(size) 17 | , m_page_table_w(page_table_w) 18 | , m_page_table_h(page_table_h) 19 | , m_indexer(indexer) 20 | { 21 | ur::TextureDescription desc; 22 | desc.target = ur::TextureTarget::Texture2D; 23 | desc.width = m_size; 24 | desc.height = m_size; 25 | desc.format = ur::TextureFormat::RGBA8; 26 | m_fbo_col_tex = dev.CreateTexture(desc, nullptr); 27 | desc.format = ur::TextureFormat::DEPTH; 28 | m_fbo_depth_tex = dev.CreateTexture(desc, nullptr); 29 | 30 | m_fbo = dev.CreateFramebuffer(); 31 | m_fbo->SetAttachment(ur::AttachmentType::Color0, ur::TextureTarget::Texture2D, m_fbo_col_tex, nullptr); 32 | m_fbo->SetAttachment(ur::AttachmentType::Depth, ur::TextureTarget::Texture2D, m_fbo_depth_tex, nullptr); 33 | 34 | m_data = new uint8_t[m_size * m_size * 4]; 35 | 36 | m_requests.resize(indexer.GetPageCount(), 0); 37 | } 38 | 39 | FeedbackBuffer::~FeedbackBuffer() 40 | { 41 | delete[] m_data; 42 | } 43 | 44 | void FeedbackBuffer::BindRT() 45 | { 46 | // m_fbo->Bind(); 47 | } 48 | 49 | void FeedbackBuffer::UnbindRT() 50 | { 51 | } 52 | 53 | void FeedbackBuffer::Download(const ur::Device& dev) 54 | { 55 | dev.ReadPixels(m_data, ur::TextureFormat::RGBA8, 0, 0, m_size, m_size); 56 | 57 | int page_table_size_log2 = static_cast(std::log2(std::min(m_page_table_w, m_page_table_h))); 58 | for (int i = 0, n = m_size * m_size; i < n; ++i) 59 | { 60 | if (m_data[i * 4 + 3] == 255) 61 | { 62 | int x = static_cast(m_data[i * 4]); 63 | int y = static_cast(m_data[i * 4 + 1]); 64 | int mip = static_cast(m_data[i * 4 + 2]); 65 | int count = page_table_size_log2 - mip + 1; 66 | for (int j = 0; j < count; ++j) 67 | { 68 | int _x = x >> j; 69 | int _y = y >> j; 70 | int _mip = mip + j; 71 | int idx = m_indexer.CalcPageIdx(textile::Page(_x, _y, _mip)); 72 | ++m_requests[idx]; 73 | } 74 | 75 | // todo cache 76 | 77 | } 78 | } 79 | } 80 | 81 | void FeedbackBuffer::Clear() 82 | { 83 | std::fill(m_requests.begin(), m_requests.end(), 0); 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /source/PageCache.cpp: -------------------------------------------------------------------------------- 1 | #include "vtex/PageCache.h" 2 | #include "vtex/TextureAtlas.h" 3 | #include "vtex/PageTable.h" 4 | 5 | #include 6 | 7 | namespace vtex 8 | { 9 | 10 | PageCache::PageCache(TextureAtlas& atlas, textile::PageLoader& loader, 11 | PageTable& table, const textile::PageIndexer& indexer) 12 | : textile::PageCache(loader, indexer) 13 | , m_atlas(atlas) 14 | , m_table(table) 15 | { 16 | } 17 | 18 | void PageCache::LoadComplete(const ur::Device& dev, const textile::Page& page, const uint8_t* data) 19 | { 20 | int x, y; 21 | 22 | int page_n = m_atlas.GetPageCount(); 23 | if (m_lru.Size() == page_n * page_n) 24 | { 25 | auto end = m_lru.GetListEnd(); 26 | assert(end); 27 | 28 | m_table.RemovePage(end->page); 29 | 30 | x = end->x; 31 | y = end->y; 32 | m_lru.RemoveBack(); 33 | } 34 | else 35 | { 36 | x = m_lru.Size() % page_n; 37 | y = m_lru.Size() / page_n; 38 | } 39 | m_lru.AddFront(page, x, y); 40 | 41 | m_atlas.UploadPage(data, x, y); 42 | 43 | m_table.AddPage(page, x, y); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /source/PageTable.cpp: -------------------------------------------------------------------------------- 1 | #include "vtex/PageTable.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace vtex 13 | { 14 | 15 | PageTable::PageTable(const ur::Device& dev, int width, int height) 16 | : m_width(width) 17 | , m_height(height) 18 | { 19 | const auto level = CalcMaxLevel(); 20 | m_root = std::make_unique(level, Rect(0, 0, m_width, m_height)); 21 | 22 | m_data.resize(level + 1); 23 | for (size_t i = 0; i < level + 1; ++i) 24 | { 25 | int sw = width >> i; 26 | int sh = height >> i; 27 | 28 | auto& data = m_data[i]; 29 | data.w = sw; 30 | data.h = sh; 31 | data.data = new uint8_t[sw * sh * 4]; 32 | memset(data.data, 0, sw * sh * 4); 33 | } 34 | 35 | ur::TextureDescription desc; 36 | desc.target = ur::TextureTarget::Texture2D; 37 | desc.width = m_width; 38 | desc.height = m_height; 39 | desc.format = ur::TextureFormat::RGBA8; 40 | m_tex = dev.CreateTexture(desc, nullptr); 41 | } 42 | 43 | void PageTable::AddPage(const textile::Page& page, int mapping_x, int mapping_y) 44 | { 45 | int scale = 1 << page.mip; 46 | int x = page.x * scale; 47 | int y = page.y * scale; 48 | 49 | QuadNode* node = m_root.get(); 50 | while (page.mip < node->level) 51 | { 52 | for (int i = 0; i < 4; ++i) 53 | { 54 | Rect cr = node->CalcChildRect(i); 55 | if (!cr.Contain(x, y)) { 56 | continue; 57 | } 58 | if (node->children[i] == nullptr) { 59 | node->children[i] = std::make_unique(node->level - 1, cr); 60 | } 61 | node = node->children[i].get(); 62 | break; 63 | } 64 | } 65 | 66 | node->mapping_x = mapping_x; 67 | node->mapping_y = mapping_y; 68 | } 69 | 70 | void PageTable::RemovePage(const textile::Page& page) 71 | { 72 | int index; 73 | auto node = FindPage(page, index); 74 | if (node != nullptr) { 75 | node->children[index] = nullptr; 76 | } 77 | } 78 | 79 | void PageTable::Update() 80 | { 81 | const auto level = CalcMaxLevel(); 82 | for (size_t i = 0; i < level + 1; ++i) 83 | { 84 | m_root->Write(m_data[i].w, m_data[i].h, m_data[i].data, i); 85 | m_tex->Upload(m_data[i].data, 0, 0, m_data[i].w, m_data[i].h, i); 86 | } 87 | } 88 | 89 | PageTable::QuadNode* PageTable::FindPage(const textile::Page& page, int& index) const 90 | { 91 | QuadNode* node = m_root.get(); 92 | 93 | int scale = 1 << page.mip; 94 | int x = page.x * scale; 95 | int y = page.y * scale; 96 | 97 | bool exitloop = false; 98 | while (!exitloop) 99 | { 100 | exitloop = true; 101 | for (int i = 0; i < 4; ++i) 102 | { 103 | if (node->children[i] != nullptr && node->children[i]->rect.Contain(x, y)) 104 | { 105 | // We found it 106 | if (page.mip == node->level - 1) 107 | { 108 | index = i; 109 | return node; 110 | } 111 | 112 | // Check the children 113 | else 114 | { 115 | node = node->children[i].get(); 116 | exitloop = false; 117 | } 118 | } 119 | } 120 | } 121 | 122 | // We couldn't find it so it must not exist anymore 123 | index = -1; 124 | return nullptr; 125 | } 126 | 127 | size_t PageTable::CalcMaxLevel() const 128 | { 129 | return static_cast(std::min(std::log2(m_width), std::log2(m_height))); 130 | } 131 | 132 | /************************************************************************/ 133 | /* class PageTable::QuadNode */ 134 | /************************************************************************/ 135 | 136 | PageTable::QuadNode::QuadNode(int level, const Rect& rect) 137 | : level(level) 138 | , rect(rect) 139 | , mapping_x(0) 140 | , mapping_y(0) 141 | { 142 | for (int i = 0; i < 4; ++i) { 143 | children[i] = nullptr; 144 | } 145 | } 146 | 147 | PageTable::Rect PageTable::QuadNode::CalcChildRect(int idx) const 148 | { 149 | int x = rect.x; 150 | int y = rect.y; 151 | int w = rect.w / 2; 152 | int h = rect.h / 2; 153 | 154 | switch (idx) 155 | { 156 | case 0: 157 | return Rect(x, y, w, h); 158 | case 1: 159 | return Rect(x + w, y, w, h); 160 | case 2: 161 | return Rect(x + w, y + h, w, h); 162 | case 3: 163 | return Rect(x, y + h, w, h); 164 | default: 165 | assert(0); 166 | return rect; 167 | } 168 | } 169 | 170 | void PageTable::QuadNode::Write(int w, int h, uint8_t* data, int mip_level) 171 | { 172 | if (level < mip_level) { 173 | return; 174 | } 175 | 176 | // fill 177 | int rx = rect.x >> mip_level; 178 | int ry = rect.y >> mip_level; 179 | int rw = rect.w >> mip_level; 180 | int rh = rect.h >> mip_level; 181 | for (int y = ry; y < ry + rh; ++y) { 182 | for (int x = rx; x < rx + rw; ++x) { 183 | int ptr = (y * w + x) * 4; 184 | data[ptr + 0] = mapping_x; 185 | data[ptr + 1] = mapping_y; 186 | data[ptr + 2] = level; 187 | data[ptr + 3] = 255; 188 | } 189 | } 190 | 191 | for (int i = 0; i < 4; ++i) { 192 | if (children[i] != nullptr) { 193 | children[i]->Write(w, h, data, mip_level); 194 | } 195 | } 196 | } 197 | 198 | } -------------------------------------------------------------------------------- /source/TextureAtlas.cpp: -------------------------------------------------------------------------------- 1 | #include "vtex/TextureAtlas.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace vtex 8 | { 9 | 10 | TextureAtlas::TextureAtlas(const ur::Device& dev, size_t atlas_size, 11 | size_t page_size, int tex_channel) 12 | : m_atlas_size(atlas_size) 13 | , m_page_size(page_size) 14 | , m_tex_channel(tex_channel) 15 | { 16 | m_page_count = m_atlas_size / m_page_size; 17 | 18 | uint8_t* pixels = new uint8_t[atlas_size * atlas_size * 4]; 19 | memset(pixels, 0xff, atlas_size * atlas_size * 4); 20 | 21 | ur::TextureDescription desc; 22 | desc.target = ur::TextureTarget::Texture2D; 23 | desc.width = atlas_size; 24 | desc.height = atlas_size; 25 | desc.format = ur::TextureFormat::RGBA8; 26 | m_tex = dev.CreateTexture(desc, pixels); 27 | 28 | delete[] pixels; 29 | } 30 | 31 | void TextureAtlas::Bind() 32 | { 33 | m_tex->Bind(); 34 | } 35 | 36 | void TextureAtlas::UploadPage(const uint8_t* pixels, int x, int y) 37 | { 38 | m_tex->Upload(pixels, x * m_page_size, y * m_page_size, m_page_size, m_page_size); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /source/VirtualTexture.cpp: -------------------------------------------------------------------------------- 1 | #include "vtex/VirtualTexture.h" 2 | #include "vtex/feedback.frag" 3 | #include "vtex/final.frag" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace 18 | { 19 | 20 | const int ATLAS_TEX_SIZE = 4096; 21 | const int UPLOADS_PER_FRAME = 5; 22 | 23 | const int MIP_SAMPLE_BIAS = 3; 24 | 25 | const char* default_vs = R"( 26 | 27 | attribute vec4 position; 28 | attribute vec3 normal; 29 | attribute vec2 texcoord; 30 | 31 | uniform mat4 u_projection; 32 | uniform mat4 u_modelview; 33 | 34 | varying vec2 v_texcoord; 35 | 36 | void main() 37 | { 38 | gl_Position = u_projection * u_modelview * position; 39 | v_texcoord = texcoord; 40 | } 41 | 42 | )"; 43 | 44 | } 45 | 46 | namespace vtex 47 | { 48 | 49 | VirtualTexture::VirtualTexture(const ur::Device& dev, 50 | const std::string& filepath, 51 | const textile::VTexInfo& info, 52 | int atlas_channel, 53 | int feedback_size) 54 | : m_feedback_size(feedback_size) 55 | , m_vtex_w(info.vtex_width) 56 | , m_vtex_h(info.vtex_height) 57 | , m_info(info) 58 | , m_atlas(dev, ATLAS_TEX_SIZE, m_info.PageSize(), atlas_channel) 59 | , m_indexer(m_info) 60 | , m_loader(filepath, m_indexer) 61 | , m_table(dev, m_info.PageTableWidth(), m_info.PageTableHeight()) 62 | , m_cache(m_atlas, m_loader, m_table, m_indexer) 63 | , m_feedback(dev, feedback_size, m_info.PageTableWidth(), m_info.PageTableHeight(), m_indexer) 64 | , m_mip_bias(MIP_SAMPLE_BIAS) 65 | { 66 | InitShaders(dev); 67 | } 68 | 69 | void VirtualTexture::Draw(const ur::Device& dev, ur::Context& ctx, std::function draw_cb) 70 | { 71 | // pass 1 72 | 73 | //pt3::EffectsManager::Instance()->SetUserEffect(m_feedback_shader); 74 | 75 | m_feedback.BindRT(); 76 | 77 | //m_feedback_shader->Use(); 78 | 79 | //auto& wc = pt3::Blackboard::Instance()->GetWindowContext(); 80 | //auto screen_sz = wc->GetScreenSize(); 81 | 82 | //rc.SetViewport(0, 0, m_feedback_size, m_feedback_size); 83 | 84 | //rc.SetZTest(ur::DEPTH_LESS_EQUAL); 85 | //rc.SetClearFlag(ur::MASKC | ur::MASKD); 86 | // rc.SetClearColor(0); 87 | //rc.Clear(); 88 | 89 | draw_cb(); 90 | 91 | m_feedback.Download(dev); 92 | Update(dev, m_feedback.GetRequests()); 93 | m_feedback.Clear(); 94 | 95 | m_feedback.UnbindRT(); 96 | 97 | // rc.SetViewport(0, 0, screen_sz.x, screen_sz.y); 98 | 99 | // pass 2 100 | // rc.Clear(); 101 | 102 | // pt3::EffectsManager::Instance()->SetUserEffect(m_final_shader); 103 | ctx.SetTexture(m_final_shader->QueryTexSlot("u_page_table_tex"), m_table.GetTexture()); 104 | ctx.SetTexture(m_final_shader->QueryTexSlot("u_texture_atlas_tex"), m_atlas.GetTexture()); 105 | draw_cb(); 106 | 107 | // debug 108 | pt2::DebugDraw::Draw(dev, ctx, m_atlas.GetTexture()->GetTexID(), 4); 109 | pt2::DebugDraw::Draw(dev, ctx, m_feedback.GetTexture()->GetTexID(), 3); 110 | //pt2::DebugDraw::Draw(virt_tex->GetPageTableTexID(), 2); 111 | } 112 | 113 | void VirtualTexture::DecreaseMipBias() 114 | { 115 | --m_mip_bias; 116 | if (m_mip_bias < 0) { 117 | m_mip_bias = 0; 118 | } 119 | 120 | auto u_mip_sample_bias = m_feedback_shader->QueryUniform("u_mip_sample_bias"); 121 | assert(u_mip_sample_bias); 122 | float bias = static_cast(m_mip_bias); 123 | u_mip_sample_bias->SetValue(&bias, 1); 124 | } 125 | 126 | void VirtualTexture::InitShaders(const ur::Device& dev) 127 | { 128 | //CU_VEC layout; 129 | //layout.push_back(ur::VertexAttrib("position", 3, 4, 32, 0)); 130 | //layout.push_back(ur::VertexAttrib("normal", 3, 4, 32, 12)); 131 | //layout.push_back(ur::VertexAttrib("texcoord", 2, 4, 32, 24)); 132 | 133 | const float page_table_sz[2] = { 134 | static_cast(m_info.PageTableWidth()), 135 | static_cast(m_info.PageTableHeight()) 136 | }; 137 | const float vtex_sz[2] = { 138 | static_cast(m_vtex_w), 139 | static_cast(m_vtex_h) 140 | }; 141 | 142 | // feedback 143 | { 144 | std::vector vs, fs; 145 | shadertrans::ShaderTrans::GLSL2SpirV(shadertrans::ShaderStage::VertexShader, default_vs, vs); 146 | shadertrans::ShaderTrans::GLSL2SpirV(shadertrans::ShaderStage::PixelShader, feedback_frag, fs); 147 | m_feedback_shader = dev.CreateShaderProgram(vs, fs); 148 | 149 | auto u_page_table_size = m_feedback_shader->QueryUniform("u_page_table_size"); 150 | assert(u_page_table_size); 151 | u_page_table_size->SetValue(page_table_sz, 2); 152 | 153 | auto u_virt_tex_size = m_feedback_shader->QueryUniform("u_virt_tex_size"); 154 | assert(u_virt_tex_size); 155 | u_virt_tex_size->SetValue(vtex_sz, 2); 156 | 157 | auto u_mip_sample_bias = m_feedback_shader->QueryUniform("u_mip_sample_bias"); 158 | assert(u_mip_sample_bias); 159 | float bias = static_cast(m_mip_bias); 160 | u_mip_sample_bias->SetValue(&bias, 1); 161 | } 162 | // final 163 | { 164 | //std::vector textures; 165 | //textures.push_back("u_page_table_tex"); 166 | //textures.push_back("u_texture_atlas_tex"); 167 | 168 | std::vector vs, fs; 169 | shadertrans::ShaderTrans::GLSL2SpirV(shadertrans::ShaderStage::VertexShader, default_vs, vs); 170 | shadertrans::ShaderTrans::GLSL2SpirV(shadertrans::ShaderStage::PixelShader, final_frag, fs); 171 | m_final_shader = dev.CreateShaderProgram(vs, fs); 172 | 173 | auto u_page_table_size = m_final_shader->QueryUniform("u_page_table_size"); 174 | assert(u_page_table_size); 175 | u_page_table_size->SetValue(page_table_sz, 2); 176 | 177 | auto u_virt_tex_size = m_final_shader->QueryUniform("u_virt_tex_size"); 178 | assert(u_virt_tex_size); 179 | u_virt_tex_size->SetValue(vtex_sz, 2); 180 | 181 | auto u_atlas_scale = m_final_shader->QueryUniform("u_atlas_scale"); 182 | assert(u_atlas_scale); 183 | float atlas_scale = static_cast(m_info.PageSize()) / m_atlas.GetSize(); 184 | u_atlas_scale->SetValue(&atlas_scale, 1); 185 | 186 | float page_size = static_cast(m_info.PageSize()); 187 | 188 | auto u_border_scale = m_final_shader->QueryUniform("u_border_scale"); 189 | assert(u_border_scale); 190 | float border_scale = (page_size - 2 * m_info.border_size) / page_size; 191 | u_border_scale->SetValue(&border_scale, 1); 192 | 193 | auto u_border_offset = m_final_shader->QueryUniform("u_border_offset"); 194 | assert(u_border_offset); 195 | float border_offset = m_info.border_size / page_size; 196 | u_border_offset->SetValue(&border_offset, 1); 197 | } 198 | } 199 | 200 | void VirtualTexture::Update(const ur::Device& dev, 201 | const std::vector& requests) 202 | { 203 | m_toload.clear(); 204 | 205 | int touched = 0; 206 | for (int i = 0, n = requests.size(); i < n; ++i) 207 | { 208 | if (requests[i] == 0) { 209 | continue; 210 | } 211 | 212 | auto& page = m_indexer.QueryPageByIdx(i); 213 | if (!m_cache.Touch(page)) { 214 | m_toload.push_back(PageWithCount(page, requests[i])); 215 | } else { 216 | ++touched; 217 | } 218 | } 219 | 220 | int page_n = m_atlas.GetPageCount(); 221 | if (touched < page_n * page_n) 222 | { 223 | std::sort(m_toload.begin(), m_toload.end(), [](const PageWithCount& p0, const PageWithCount& p1)->bool { 224 | if (p0.page.mip != p1.page.mip) { 225 | return p0.page.mip < p1.page.mip; 226 | } else { 227 | return p0.count > p1.count; 228 | } 229 | }); 230 | 231 | int load_n = std::min((int)m_toload.size(), UPLOADS_PER_FRAME); 232 | for (int i = 0; i < load_n; ++i) { 233 | m_cache.Request(dev, m_toload[i].page); 234 | } 235 | } 236 | else 237 | { 238 | DecreaseMipBias(); 239 | } 240 | 241 | m_loader.Update(dev); 242 | 243 | m_table.Update(); 244 | } 245 | 246 | } --------------------------------------------------------------------------------