Virtual texture atlas (#593)
This commit is contained in:
@@ -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
File diff suppressed because it is too large
Load Diff
+48
-49
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user