diff --git a/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs b/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs index 669cddec2..7e8740972 100644 --- a/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs +++ b/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs @@ -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 diff --git a/src/Aardvark.Rendering/Resources/Textures/Formats.fs b/src/Aardvark.Rendering/Resources/Textures/Formats.fs index 7b1e25c67..0586902c7 100644 --- a/src/Aardvark.Rendering/Resources/Textures/Formats.fs +++ b/src/Aardvark.Rendering/Resources/Textures/Formats.fs @@ -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 diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Create.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Create.fs index ccac0fadd..691a2d04e 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Create.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Create.fs @@ -115,11 +115,99 @@ module TextureCreate = finally runtime.DeleteTexture(t) + let memoryUsage (runtime : IRuntime) = + let runtime = unbox 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" \ No newline at end of file