Skip to content

Commit

Permalink
[GL] Improve texture memory usage tracking
Browse files Browse the repository at this point in the history
Properly handles compressed textures now, irregular formats
are assumed to have a normal layout in memory (e.g. RGB is RGBA)
  • Loading branch information
hyazinthh committed Dec 9, 2023
1 parent f1a6974 commit 53f029e
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 11 deletions.
33 changes: 23 additions & 10 deletions src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,29 @@ module internal TextureResourceCounts =
let updateTexture (ctx:Context) oldSize newSize =
Interlocked.Add(&ctx.MemoryUsage.TextureMemory,newSize-oldSize) |> ignore

let inline texSizeInBytes (dimension : TextureDimension) (size : V3i) (format : TextureFormat) (samples : int) (levels : int) (count : int) =
let pixelCount = (int64 size.X) * (int64 size.Y) * (int64 size.Z) * (int64 samples)
let mutable size = pixelCount * (int64 format.PixelSizeInBits) / 8L
let mutable temp = size
for _ in 1..levels-1 do
temp <- temp >>> 2
size <- size + temp
size <- size * (int64 count)
if dimension = TextureDimension.TextureCube then size * 6L
else size
/// Computes an estimate of the memory usage of a texture with the given parameters.
/// Assumes that per-pixel size in bits is a power of two, so for example RGB textures are layed out as RGBX.
let texSizeInBytes (dimension : TextureDimension) (size : V3i) (format : TextureFormat) (samples : int) (levels : int) (count : int) =
let count =
if dimension = TextureDimension.TextureCube then 6 * count
else count

let getLevelSizeInBytes : V3i -> int64 =
match format.CompressionMode with
| CompressionMode.None ->
let bitsPerPixel = format.PixelSizeInBits |> Fun.NextPowerOfTwo |> max 8 |> int64
fun size -> (int64 size.X) * (int64 size.Y) * (int64 size.Z) * (int64 samples) * (bitsPerPixel / 8L)

| mode ->
fun size -> int64 <| CompressionMode.sizeInBytes size mode

let mutable layerSizeInBytes = 0L

for level = 0 to levels - 1 do
let levelSize = Fun.MipmapLevelSize(size, level)
layerSizeInBytes <- layerSizeInBytes + getLevelSizeInBytes levelSize

layerSizeInBytes * (int64 count)

type Texture =
class
Expand Down
2 changes: 1 addition & 1 deletion src/Aardvark.Rendering/Resources/Textures/Formats.fs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ module TextureFormat =
LookupTable.lookupTable [
TextureFormat.Bgr8, 24
TextureFormat.Bgra8, 32
TextureFormat.R3G3B2, 6
TextureFormat.R3G3B2, 8
TextureFormat.Rgb4, 12
TextureFormat.Rgb5, 15
TextureFormat.Rgb8, 24
Expand Down
88 changes: 88 additions & 0 deletions src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Create.fs
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,99 @@ module TextureCreate =
finally
runtime.DeleteTexture(t)

let memoryUsage (runtime : IRuntime) =
let runtime = unbox<GL.Runtime> runtime
let context = runtime.Context

let mutable count = 0
let mutable memory = 0L

let check() =
Expect.equal context.MemoryUsage.TextureCount count "unexpected texture count"
Expect.equal context.MemoryUsage.TextureMemory memory "unexpected memory usage"

// Simple 2D
let t1 = runtime.CreateTexture2D(V2i(512), TextureFormat.Rgba8, levels = 1)
count <- count + 1
memory <- memory + (512L * 512L * 4L)
check()

// Multisampled 2D
let t2 = runtime.CreateTexture2D(V2i(512), TextureFormat.R8, samples = 2)
count <- count + 1
memory <- memory + (512L * 512L * 2L)
check()

// Mipmapped 2D
let t3 = runtime.CreateTexture2D(V2i(512), TextureFormat.Rgba32f, levels = Fun.MipmapLevels 512)
count <- count + 1
for i = 1 to t3.MipMapLevels do
let size = v3l <| t3.GetSize(i - 1)
memory <- memory + (size.X * size.Y * 16L)
check()

// Simple 1D
let t4 = runtime.CreateTexture1D(123, TextureFormat.R32f)
count <- count + 1
memory <- memory + (123L * 4L)
check()

// Simple 3D
let t5 = runtime.CreateTexture3D(V3i(3, 12, 64), TextureFormat.Rgba8)
count <- count + 1
memory <- memory + (3L * 12L * 64L * 4L)
check()

// Cube
let t6 = runtime.CreateTextureCube(64, TextureFormat.Rgba8)
count <- count + 1
memory <- memory + (64L * 64L * 4L * 6L)
check()

// Cube array
let t7 = runtime.CreateTextureCubeArray(123, TextureFormat.Rgba8, levels = Fun.MipmapLevels 123, count = 7)
count <- count + 1
for _ = 1 to t7.Count * 6 do
for i = 1 to t7.MipMapLevels do
let size = v3l <| t7.GetSize(i - 1)
memory <- memory + (size.X * size.X * 4L)
check()

// Compressed
let t8 = runtime.PrepareTexture <| EmbeddedResource.getTexture TextureParams.mipmappedCompressed "data/bc1.dds"

let sizeInBytes =
let mode = t8.Format.CompressionMode

(0L, [0 .. t8.MipMapLevels - 1]) ||> List.fold (fun sizeInBytes level ->
let size = Fun.MipmapLevelSize(t8.Size, level)
sizeInBytes + (int64 <| CompressionMode.sizeInBytes size mode)
)

count <- count + 1
memory <- memory + sizeInBytes
check()

t1.Dispose()
t2.Dispose()
t3.Dispose()
t4.Dispose()
t5.Dispose()
t6.Dispose()
t7.Dispose()
t8.Dispose()
count <- 0
memory <- 0L
check()

let tests (backend : Backend) =
[
"Non-positive arguments", Cases.nonPositiveArguments
"Invalid usage", Cases.invalidUsage
"Valid usage", Cases.validUsage
"Unsupported multisample count", Cases.unsupportedMultisamples

if backend = Backend.GL then
"Memory usage", Cases.memoryUsage
]
|> prepareCases backend "Create"

0 comments on commit 53f029e

Please sign in to comment.