Virtual texture atlas (#593)

This commit is contained in:
Turánszki János
2022-11-16 18:57:21 +01:00
committed by GitHub
parent 965277f4ac
commit 69442cd14c
6 changed files with 513 additions and 878 deletions
+104 -102
View File
@@ -89,12 +89,22 @@ static const uint CLEARCOATNORMALMAP = 11;
static const uint SPECULARMAP = 12;
static const uint TEXTURESLOT_COUNT = 13;
static const uint SVT_TILE_SIZE = 256u;
static const uint SVT_TILE_BORDER = 4u;
static const uint SVT_TILE_SIZE_PADDED = SVT_TILE_BORDER + SVT_TILE_SIZE + SVT_TILE_BORDER;
#ifndef __cplusplus
#ifdef TEXTURE_SLOT_NONUNIFORM
#define UniformTextureSlot(x) NonUniformResourceIndex(x)
#else
#define UniformTextureSlot(x) (x)
#endif // TEXTURE_SLOT_NONUNIFORM
inline float get_lod(in uint2 dim, in float2 uv_dx, in float2 uv_dy)
{
// https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
return log2(max(length(uv_dx * dim), length(uv_dy * dim)));
}
#endif // __cplusplus
struct ShaderTextureSlot
@@ -130,47 +140,84 @@ struct ShaderTextureSlot
{
return bindless_textures[UniformTextureSlot(texture_descriptor)];
}
float4 Sample(in SamplerState sam, in float4 uvsets)
float4 SampleVirtual(
in Texture2D tex,
in SamplerState sam,
in float2 uv,
in Texture2D<uint> residency_map,
in uint2 virtual_tile_count,
in uint2 virtual_image_dim,
in float virtual_lod
)
{
Texture2D tex = GetTexture();
float2 uv = GetUVSet() == 0 ? uvsets.xy : uvsets.zw;
#ifdef DISABLE_SVT
float4 value = tex.Sample(sam, uv);
#else
uint svt_status;
float4 value = tex.Sample(sam, uv, 0, 0, svt_status);
[branch]
if (!CheckAccessFullyMapped(svt_status))
{
float lod_clamp = GetLodClamp();
[branch]
if (sparse_residencymap_descriptor >= 0)
{
Texture2D<uint> residency_map = bindless_textures_uint[UniformTextureSlot(sparse_residencymap_descriptor)];
uint4 residency = residency_map.GatherRed(sam, uv);
lod_clamp = max(residency.x, max(residency.y, max(residency.z, residency.w)));
}
value = tex.Sample(sam, uv, 0, lod_clamp);
}
#ifdef SVT_FEEDBACK
[branch]
if (sparse_feedbackmap_descriptor >= 0)
{
RWTexture2D<uint> feedback_map = bindless_rwtextures_uint[UniformTextureSlot(sparse_feedbackmap_descriptor)];
uint2 dim;
feedback_map.GetDimensions(dim.x, dim.y);
uint2 pixel = uv * dim;
float lod = tex.CalculateLevelOfDetail(sam, uv);
InterlockedMin(feedback_map[pixel], uint(lod));
uint2 pixel = uv * virtual_tile_count;
InterlockedMin(feedback_map[pixel], uint(virtual_lod));
}
#endif // SVT_FEEDBACK
float2 atlas_dim;
tex.GetDimensions(atlas_dim.x, atlas_dim.y);
uint2 pixel = uv * virtual_tile_count;
uint min_lod = (residency_map.Load(uint3(pixel >> uint(virtual_lod), uint(virtual_lod))) >> 16u) & 0xFF;
float clamped_lod = clamp(virtual_lod, min_lod, GetLodClamp());
// Mip - more detailed:
float4 value0;
{
uint lod0 = floor(clamped_lod);
uint residency = residency_map.Load(uint3(pixel >> lod0, lod0));
uint2 tile = uint2(residency & 0xFF, (residency >> 8u) & 0xFF);
uint2 tile_pixel_upperleft = tile * SVT_TILE_SIZE_PADDED + SVT_TILE_BORDER;
uint2 virtual_lod_dim = virtual_image_dim >> lod0;
float2 virtual_pixel = uv * virtual_lod_dim;
float2 virtual_tile_pixel = fmod(virtual_pixel, SVT_TILE_SIZE);
float2 atlas_tile_pixel = tile_pixel_upperleft + 0.5 + virtual_tile_pixel;
float2 atlas_uv = atlas_tile_pixel / atlas_dim;
value0 = tex.SampleLevel(sam, atlas_uv, 0);
}
// Mip - less detailed:
float4 value1;
{
uint lod1 = ceil(clamped_lod);
uint residency = residency_map.Load(uint3(pixel >> lod1, lod1));
uint2 tile = uint2(residency & 0xFF, (residency >> 8u) & 0xFF);
uint2 tile_pixel_upperleft = tile * SVT_TILE_SIZE_PADDED + SVT_TILE_BORDER;
uint2 virtual_lod_dim = virtual_image_dim >> lod1;
float2 virtual_pixel = uv * virtual_lod_dim;
float2 virtual_tile_pixel = fmod(virtual_pixel, SVT_TILE_SIZE);
float2 atlas_tile_pixel = tile_pixel_upperleft + 0.5 + virtual_tile_pixel;
float2 atlas_uv = atlas_tile_pixel / atlas_dim;
value1 = tex.SampleLevel(sam, atlas_uv, 0);
}
return lerp(value0, value1, frac(virtual_lod)); // custom trilinear filtering
}
float4 Sample(in SamplerState sam, in float4 uvsets)
{
Texture2D tex = GetTexture();
float2 uv = GetUVSet() == 0 ? uvsets.xy : uvsets.zw;
#ifndef DISABLE_SVT
[branch]
if (sparse_residencymap_descriptor >= 0)
{
Texture2D<uint> residency_map = bindless_textures_uint[UniformTextureSlot(sparse_residencymap_descriptor)];
float2 virtual_tile_count;
residency_map.GetDimensions(virtual_tile_count.x, virtual_tile_count.y);
float2 virtual_image_dim = virtual_tile_count * SVT_TILE_SIZE;
float virtual_lod = get_lod(virtual_image_dim, ddx(uv), ddy(uv));
return SampleVirtual(tex, sam, uv, residency_map, virtual_tile_count, virtual_image_dim, virtual_lod);
}
#endif // DISABLE_SVT
return value;
return tex.Sample(sam, uv);
}
float4 SampleLevel(in SamplerState sam, in float4 uvsets, in float lod)
@@ -178,41 +225,19 @@ struct ShaderTextureSlot
Texture2D tex = GetTexture();
float2 uv = GetUVSet() == 0 ? uvsets.xy : uvsets.zw;
#ifdef DISABLE_SVT
float4 value = tex.SampleLevel(sam, uv, lod);
#else
uint svt_status;
float4 value = tex.SampleLevel(sam, uv, lod, 0, svt_status);
#ifndef DISABLE_SVT
[branch]
if (!CheckAccessFullyMapped(svt_status))
if (sparse_residencymap_descriptor >= 0)
{
float lod_clamp = GetLodClamp();
[branch]
if (sparse_residencymap_descriptor >= 0)
{
Texture2D<uint> residency_map = bindless_textures_uint[UniformTextureSlot(sparse_residencymap_descriptor)];
uint4 residency = residency_map.GatherRed(sam, uv);
lod_clamp = max(residency.x, max(residency.y, max(residency.z, residency.w)));
}
value = tex.SampleLevel(sam, uv, max(lod, lod_clamp));
Texture2D<uint> residency_map = bindless_textures_uint[UniformTextureSlot(sparse_residencymap_descriptor)];
float2 virtual_tile_count;
residency_map.GetDimensions(virtual_tile_count.x, virtual_tile_count.y);
float2 virtual_image_dim = virtual_tile_count * SVT_TILE_SIZE;
return SampleVirtual(tex, sam, uv, residency_map, virtual_tile_count, virtual_image_dim, lod);
}
#ifdef SVT_FEEDBACK
[branch]
if (sparse_feedbackmap_descriptor >= 0)
{
RWTexture2D<uint> feedback_map = bindless_rwtextures_uint[UniformTextureSlot(sparse_feedbackmap_descriptor)];
uint2 dim;
feedback_map.GetDimensions(dim.x, dim.y);
uint2 pixel = uv * dim;
InterlockedMin(feedback_map[pixel], uint(lod));
}
#endif // SVT_FEEDBACK
#endif // DISABLE_SVT
return value;
return tex.SampleLevel(sam, uv, lod);
}
float4 SampleGrad(in SamplerState sam, in float4 uvsets, in float4 uvsets_dx, in float4 uvsets_dy)
@@ -222,43 +247,20 @@ struct ShaderTextureSlot
float2 uv_dx = GetUVSet() == 0 ? uvsets_dx.xy : uvsets_dx.zw;
float2 uv_dy = GetUVSet() == 0 ? uvsets_dy.xy : uvsets_dy.zw;
#ifdef DISABLE_SVT
float4 value = tex.SampleGrad(sam, uv, uv_dx, uv_dy);
#else
uint svt_status;
float4 value = tex.SampleGrad(sam, uv, uv_dx, uv_dy, 0, 0, svt_status);
#ifndef DISABLE_SVT
[branch]
if (!CheckAccessFullyMapped(svt_status))
if (sparse_residencymap_descriptor >= 0)
{
float lod_clamp = GetLodClamp();
[branch]
if (sparse_residencymap_descriptor >= 0)
{
Texture2D<uint> residency_map = bindless_textures_uint[UniformTextureSlot(sparse_residencymap_descriptor)];
uint4 residency = residency_map.GatherRed(sam, uv);
lod_clamp = max(residency.x, max(residency.y, max(residency.z, residency.w)));
}
value = tex.SampleGrad(sam, uv, uv_dx, uv_dy, 0, lod_clamp);
Texture2D<uint> residency_map = bindless_textures_uint[UniformTextureSlot(sparse_residencymap_descriptor)];
float2 virtual_tile_count;
residency_map.GetDimensions(virtual_tile_count.x, virtual_tile_count.y);
float2 virtual_image_dim = virtual_tile_count * SVT_TILE_SIZE;
float virtual_lod = get_lod(virtual_image_dim, uv_dx, uv_dy);
return SampleVirtual(tex, sam, uv, residency_map, virtual_tile_count, virtual_image_dim, virtual_lod);
}
#ifdef SVT_FEEDBACK
[branch]
if (sparse_feedbackmap_descriptor >= 0)
{
RWTexture2D<uint> feedback_map = bindless_rwtextures_uint[UniformTextureSlot(sparse_feedbackmap_descriptor)];
uint2 dim;
feedback_map.GetDimensions(dim.x, dim.y);
uint2 pixel = uv * dim;
tex.GetDimensions(dim.x, dim.y);
float lod = log2(max(length(uv_dx * dim), length(uv_dy * dim))); // https://microsoft.github.io/DirectX-Specs/d3d/archive/D3D11_3_FunctionalSpec.htm#7.18.11%20LOD%20Calculations
InterlockedMin(feedback_map[pixel], uint(lod));
}
#endif // SVT_FEEDBACK
#endif // DISABLE_SVT
return value;
return tex.SampleGrad(sam, uv, uv_dx, uv_dy);
}
#endif // __cplusplus
};
@@ -1097,19 +1099,19 @@ struct VolumetricCloudCapturePushConstants
struct TerrainVirtualTexturePush
{
uint2 offset;
float2 resolution_rcp;
uint2 write_size;
int2 offset;
uint2 write_offset;
float resolution_rcp;
int region_weights_textureRO;
int output_textureRW;
};
struct VirtualTextureResidencyUpdatePush
struct VirtualTextureResidencyUpdateCB
{
uint lodCount;
uint width;
uint height;
int pageBufferRO;
int residencyTextureRW;
int4 residencyTextureRW_mips[9]; // because this can be indexed in shader, this structure cannot be push constant, it has to be constant buffer!
};
struct VirtualTextureTileAllocatePush
{
@@ -15,9 +15,9 @@ ConstantBuffer<Terrain> terrain : register(b0);
#if !defined(UPDATE_NORMALMAP) && !defined(UPDATE_SURFACEMAP)
#define UPDATE_BASECOLORMAP
RWTexture2D<uint2> bindless_rwtextures_uint2[] : register(space19);
RWTexture2D<uint2> output : register(u0);
#else
RWTexture2D<uint4> bindless_rwtextures_uint4[] : register(space19);
RWTexture2D<uint4> output : register(u0);
#endif // UPDATE_NORMALMAP
static const uint2 block_offsets[BLOCK_SIZE_4X4] = {
@@ -27,21 +27,10 @@ static const uint2 block_offsets[BLOCK_SIZE_4X4] = {
uint2(0, 3), uint2(1, 3), uint2(2, 3), uint2(3, 3),
};
#undef WICKED_ENGINE_DEFAULT_ROOTSIGNATURE // don't use auto root signature!
[RootSignature(
"RootConstants(num32BitConstants=8, b999), "
"CBV(b0), "
"DescriptorTable( "
"SRV(t0, space = 2, offset = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE),"
"UAV(u0, space = 19, offset = 0, numDescriptors = unbounded, flags = DESCRIPTORS_VOLATILE | DATA_VOLATILE)"
"), "
"StaticSampler(s100, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_MIN_MAG_MIP_LINEAR),"
"StaticSampler(s101, addressU = TEXTURE_ADDRESS_WRAP, addressV = TEXTURE_ADDRESS_WRAP, addressW = TEXTURE_ADDRESS_WRAP, filter = FILTER_MIN_MAG_MIP_LINEAR),"
)]
[numthreads(8, 8, 1)]
void main(uint3 DTid : SV_DispatchThreadID)
{
if (DTid.x >= push.write_size.x || DTid.y >= push.write_size.y)
if (DTid.x >= SVT_TILE_SIZE_PADDED / 4 || DTid.y >= SVT_TILE_SIZE_PADDED / 4)
return;
Texture2D<float4> region_weights_texture = bindless_textures[push.region_weights_textureRO];
@@ -63,7 +52,7 @@ void main(uint3 DTid : SV_DispatchThreadID)
for(uint idx = 0; idx < BLOCK_SIZE_4X4; ++idx)
{
const uint2 block_offset = block_offsets[idx];
const uint2 pixel = push.offset + DTid.xy * 4 + block_offset;
const int2 pixel = push.offset + DTid.xy * 4 + block_offset;
const float2 uv = (pixel.xy + 0.5f) * push.resolution_rcp;
float4 region_weights = region_weights_texture.SampleLevel(sampler_linear_clamp, uv, 0);
@@ -91,6 +80,8 @@ void main(uint3 DTid : SV_DispatchThreadID)
baseColor *= baseColorMap;
}
total_color += baseColor * weight;
//if (DTid.x == 0 || DTid.y == 0)
// total_color = 0;
#endif // UPDATE_BASECOLORMAP
#ifdef UPDATE_NORMALMAP
@@ -146,20 +137,17 @@ void main(uint3 DTid : SV_DispatchThreadID)
#endif // UPDATE_SURFACEMAP
}
const uint2 write_coord = push.offset / 4 + DTid.xy;
const uint2 write_coord = push.write_offset + DTid.xy;
#ifdef UPDATE_BASECOLORMAP
RWTexture2D<uint2> output = bindless_rwtextures_uint2[push.output_textureRW];
output[write_coord] = CompressBlockBC1_UNORM(block_rgb, CMP_QUALITY0, /*isSRGB =*/ true);
#endif // UPDATE_BASECOLORMAP
#ifdef UPDATE_NORMALMAP
RWTexture2D<uint4> output = bindless_rwtextures_uint4[push.output_textureRW];
output[write_coord] = CompressBlockBC5_UNORM(block_x, block_y, CMP_QUALITY0);
#endif // UPDATE_NORMALMAP
#ifdef UPDATE_SURFACEMAP
RWTexture2D<uint4> output = bindless_rwtextures_uint4[push.output_textureRW];
output[write_coord] = CompressBlockBC3_UNORM(block_rgb, block_a, CMP_QUALITY2, /*isSRGB =*/ false);
#endif // UPDATE_SURFACEMAP
}
@@ -1,47 +1,75 @@
#include "globals.hlsli"
#include "ShaderInterop_Renderer.h"
PUSHCONSTANT(push, VirtualTextureResidencyUpdatePush);
ConstantBuffer<VirtualTextureResidencyUpdateCB> cb : register(b0);
[numthreads(8, 8, 1)]
void main(uint3 DTid : SV_DispatchThreadID, uint groupIndex : SV_GroupIndex)
{
if (DTid.x >= push.width || DTid.y >= push.height)
if (DTid.x >= cb.width || DTid.y >= cb.height)
return;
ByteAddressBuffer pageBuffer = bindless_buffers[push.pageBufferRO];
RWTexture2D<uint> residencyTexture = bindless_rwtextures_uint[push.residencyTextureRW];
ByteAddressBuffer pageBuffer = bindless_buffers[cb.pageBufferRO];
uint residency = 0xFF;
uint minLod = 0xFF;
uint tile_x = 0;
uint tile_y = 0;
const uint x = DTid.x;
const uint y = DTid.y;
uint lod_offset = 0;
for (uint lod = 0; lod < push.lodCount; ++lod)
uint lod_pages[9];
uint lod = 0;
for (lod = 0; lod < cb.lodCount; ++lod)
{
const uint l_x = x >> lod;
const uint l_y = y >> lod;
const uint l_width = max(1u, push.width >> lod);
const uint l_height = max(1u, push.height >> lod);
const uint l_width = max(1u, cb.width >> lod);
const uint l_height = max(1u, cb.height >> lod);
const uint l_page_offset = lod_offset;
const uint l_index = l_page_offset + l_x + l_y * l_width;
const uint page = pageBuffer.Load(l_index * sizeof(uint));
lod_pages[lod] = page;
if (page == 0xFFFF)
{
// invalid page
// normally this wouldn't happen after a resident mip was encountered, but
// it can happen on allocation failures, in this case reset the residency to invalid
// this will mean that while there was a higher res resident page, but we can't use that because lower levels might be non resident
residency = 0xFF;
minLod = 0xFF;
}
else
else if(lod < minLod)
{
// valid page
// normally this would be the highest lod and we could exit, but failed allocations can cause holes in the mip chain
residency = min(residency, lod);
minLod = lod;
tile_x = page & 0xFF;
tile_y = (page >> 8u) & 0xFF;
}
lod_offset += l_width * l_height;
}
residencyTexture[DTid.xy] = residency;
// Update the mip chain:
for (lod = 0; lod < cb.lodCount; ++lod)
{
uint lod_check = 1u << lod;
if ((DTid.x % lod_check == 0) && (DTid.y % lod_check == 0))
{
uint page = lod_pages[lod];
RWTexture2D<uint> residencyTexture = bindless_rwtextures_uint[cb.residencyTextureRW_mips[lod].x];
uint2 write_coord = DTid.xy >> lod;
if (lod < minLod || page == 0xFFFF)
{
residencyTexture[write_coord] = (tile_x & 0xFF) | ((tile_y & 0xFF) << 8u) | ((minLod & 0xFF) << 16u);
}
else
{
uint x = page & 0xFF;
uint y = (page >> 8u) & 0xFF;
residencyTexture[write_coord] = (x & 0xFF) | ((y & 0xFF) << 8u) | ((lod & 0xFF) << 16u);
}
}
}
}
+313 -695
View File
File diff suppressed because it is too large Load Diff
+48 -49
View File
@@ -47,33 +47,52 @@ namespace wi::terrain
static constexpr float chunk_width_rcp = 1.0f / (chunk_width - 1);
static constexpr uint32_t vertexCount = chunk_width * chunk_width;
struct GPUPageAllocator
struct VirtualTextureAtlas
{
size_t block_size = 0;
size_t page_size = 0;
wi::vector<std::shared_ptr<wi::graphics::GPUBuffer>> blocks; // storing pointers to avoid pointer changes on realloc, because they can be referenced by multiple threads
struct Page
struct Map
{
uint16_t block = 0; // index into blocks array
uint16_t index = 0xFFFF; // index into block's pages
constexpr bool IsValid() const { return index < 0xFFFF; }
wi::graphics::Texture texture;
wi::graphics::Texture texture_raw_block;
};
wi::vector<Page> pages;
Map maps[3];
wi::graphics::GPUBuffer tile_pool;
void init(size_t block_size, size_t page_size);
void new_block();
Page allocate();
void free(Page page);
struct Tile
{
uint8_t x = 0xFF;
uint8_t y = 0xFF;
constexpr bool IsValid() const
{
return x != 0xFF && y != 0xFF;
}
};
wi::vector<Tile> free_tiles;
Tile allocate()
{
if (free_tiles.empty())
return {};
Tile tile = free_tiles.back();
free_tiles.pop_back();
return tile;
}
void free(Tile& tile)
{
if (!tile.IsValid())
return;
free_tiles.push_back(tile);
tile = {};
}
inline bool IsValid() const
{
return tile_pool.IsValid();
}
};
//#define TERRAIN_VIRTUAL_TEXTURE_DEBUG
struct VirtualTexture
{
wi::graphics::Texture texture;
wi::graphics::Texture texture_raw_block;
wi::graphics::Texture residencyMap;
wi::graphics::Texture feedbackMap;
wi::graphics::Texture residencyMap;
wi::graphics::GPUBuffer requestBuffer;
wi::graphics::GPUBuffer allocationBuffer;
wi::graphics::GPUBuffer allocationBuffer_CPU_readback[wi::graphics::GraphicsDevice::GetBufferCount() + 1];
@@ -81,51 +100,32 @@ namespace wi::terrain
wi::graphics::GPUBuffer pageBuffer_CPU_upload[wi::graphics::GraphicsDevice::GetBufferCount() + 1];
bool data_available_CPU[wi::graphics::GraphicsDevice::GetBufferCount() + 1] = {};
int cpu_resource_id = 0;
wi::vector<GPUPageAllocator::Page> pages;
wi::vector<VirtualTextureAtlas::Tile> tiles;
uint32_t lod_count = 0;
uint32_t resolution = 0;
#ifdef TERRAIN_VIRTUAL_TEXTURE_DEBUG
uint32_t tile_allocation_count = 0;
uint32_t tile_deallocation_count = 0;
wi::graphics::Texture feedbackMap_CPU_readback[wi::graphics::GraphicsDevice::GetBufferCount() + 1];
wi::graphics::Texture residencyMap_CPU_readback[wi::graphics::GraphicsDevice::GetBufferCount() + 1];
wi::graphics::GPUBuffer requestBuffer_CPU_readback[wi::graphics::GraphicsDevice::GetBufferCount() + 1];
#endif // TERRAIN_VIRTUAL_TEXTURE_DEBUG
void init(VirtualTextureAtlas& atlas, uint resolution);
void init(GPUPageAllocator& page_allocator, const wi::graphics::TextureDesc& desc);
void free(GPUPageAllocator& page_allocator)
void free(VirtualTextureAtlas& atlas)
{
for (auto& page : pages)
for (auto& tile : tiles)
{
page_allocator.free(page);
page = {};
atlas.free(tile);
}
tiles.clear();
}
void DrawDebug(wi::graphics::CommandList cmd);
// Attach this data to Virtual Texture because we will record these by separate CPU thread:
struct UpdateRequest
{
uint16_t x = 0;
uint16_t y = 0;
uint16_t lod = 0;
uint8_t tile_x = 0;
uint8_t tile_y = 0;
};
mutable wi::vector<UpdateRequest> update_requests;
wi::graphics::Texture region_weights_texture;
uint32_t map_type = 0;
void SparseMap(GPUPageAllocator& allocator, GPUPageAllocator::Page page, uint32_t x, uint32_t y, uint32_t mip);
void SparseFlush(GPUPageAllocator& allocator);
private:
wi::vector<wi::graphics::SparseResourceCoordinate> sparse_coordinate;
wi::vector<wi::graphics::SparseRegionSize> sparse_size;
wi::vector<wi::graphics::TileRangeFlags> tile_range_flags;
wi::vector<uint32_t> tile_range_offset;
wi::vector<uint32_t> tile_range_count;
uint32_t last_block = 0;
};
struct ChunkData
@@ -142,8 +142,7 @@ namespace wi::terrain
wi::primitive::Sphere sphere;
XMFLOAT3 position = XMFLOAT3(0, 0, 0);
bool visible = true;
wi::vector<VirtualTexture> vt;
std::shared_ptr<VirtualTexture> vt;
};
struct Prop
@@ -202,8 +201,8 @@ namespace wi::terrain
wi::vector<wi::graphics::GPUBarrier> virtual_texture_barriers_before_allocation;
wi::vector<wi::graphics::GPUBarrier> virtual_texture_barriers_after_allocation;
wi::vector<const VirtualTexture*> virtual_textures_in_use;
GPUPageAllocator page_allocator;
wi::graphics::Sampler sampler;
VirtualTextureAtlas atlas;
constexpr bool IsCenterToCamEnabled() const { return _flags & CENTER_TO_CAM; }
constexpr bool IsRemovalEnabled() const { return _flags & REMOVAL; }
+1 -1
View File
@@ -9,7 +9,7 @@ namespace wi::version
// minor features, major updates, breaking compatibility changes
const int minor = 71;
// minor bug fixes, alterations, refactors, updates
const int revision = 94;
const int revision = 95;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);