diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index be8dabaa..f0855921 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -8,7 +8,25 @@
### 5.4.10-prerelease0001
- [Text] improved Font resolver for Windows and MacOS
-- [PathSegment] fixed several PathSegment tools and added a few new ones
+- [PathSegment] fixed several PathSegment tools and added a few new ones### 5.4.7
+- Fixed Frustum.withAspect and Frustum.withHorizontalFieldOfViewInDegrees
+- [GL] Fixed InvalidEnum error due to GL_POINT_SPRITE
+- [GL] Removed validation via proxy textures (resulted in errors on AMD with multisampled textures)
+- [GL] Removed swizzle for multisampled textures (not supported)
+- [GL] Added simple parameter device limit checks for textures and renderbuffers
+- [GL] Improved texture memory usage tracking
+- [GL] Made retrieval of program binaries more robust
+- [GL] Improved driver information and error formatting
+- [GL] Disabled Dispose() for Program
+- [GL] Fixed resource leaks in ContextHandleOpenTK.create
+- [GL] Fixed ComputeCommand.SetBufferCmd
+- [GL] Fixed issue with texture targets and multisampling
+- [Vulkan] Fixed swapchain creation if maxImages is zero
+- [Vulkan] Fixed issue with image format queries and external memory
+- [Vulkan] Improved error formatting
+- [GLFW] Use no error context only when indicated by debug config
+- Added IRenderTask.GetRuntime() and IRenderTask.GetFramebufferSignature()
+
### 5.4.6
- [ContextHandles] GL.Enable(EnableCap.PointSprite)
diff --git a/paket.dependencies b/paket.dependencies
index 12763937..0d2c7ecd 100644
--- a/paket.dependencies
+++ b/paket.dependencies
@@ -1,12 +1,11 @@
framework: auto-detect
source https://api.nuget.org/v3/index.json
-source https://vrvis.myget.org/F/aardvark_public/api/v2
storage: none
nuget FSharp.Core >= 5.0.1 lowest_matching: true
nuget Microsoft.NETFramework.ReferenceAssemblies >= 1.0.0 lowest_matching: true
-nuget Aardvark.Build ~> 1.0.18
+nuget Aardvark.Build ~> 1.0.21
nuget FSharp.Data.Adaptive ~> 1.2.13
nuget CSharp.Data.Adaptive ~> 1.2.13
@@ -23,9 +22,9 @@ nuget Aardvark.Base.Tensors ~> 5.2.27
nuget Aardvark.Assembler ~> 0.0.8
-nuget FShade.Core ~> 5.5.0
-nuget FShade ~> 5.5.0
-nuget FShade.Debug ~> 5.5.0
+nuget FShade.Core ~> 5.5.3
+nuget FShade ~> 5.5.3
+nuget FShade.Debug ~> 5.5.3
nuget Unofficial.OpenVR ~> 1.1.0
nuget Unofficial.Typography ~> 0.1.0
diff --git a/paket.lock b/paket.lock
index fc6fdd36..3e9e9a5d 100644
--- a/paket.lock
+++ b/paket.lock
@@ -10,53 +10,53 @@ NUGET
Aardvark.Base.Runtime (>= 5.2.7 < 5.3)
FSharp.Core (>= 5.0)
FSharp.Data.Adaptive (>= 1.2.13 < 1.3)
- Aardvark.Base (5.2.27)
- Aardvark.Base.Telemetry (5.2.27)
+ Aardvark.Base (5.2.28)
+ Aardvark.Base.Telemetry (5.2.28)
System.Collections.Immutable (>= 5.0)
System.Reflection.Metadata (>= 5.0) - restriction: || (== net471) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0)
System.Text.Json (>= 4.7.2)
- Aardvark.Base.Essentials (5.2.27)
- Aardvark.Base (5.2.27)
+ Aardvark.Base.Essentials (5.2.28)
+ Aardvark.Base (5.2.28)
System.Collections.Immutable (>= 5.0)
- Aardvark.Base.FSharp (5.2.27)
- Aardvark.Base (5.2.27)
+ Aardvark.Base.FSharp (5.2.28)
+ Aardvark.Base (5.2.28)
Aardvark.Base.TypeProviders (>= 4.5.15 < 4.6)
FSharp.Core (>= 5.0)
FSharp.Data.Adaptive (>= 1.2 < 1.3)
FsPickler (>= 5.3.2 < 5.4)
System.Dynamic.Runtime (>= 4.3 < 4.4)
- Aardvark.Base.Incremental (5.2.27)
- Aardvark.Base (5.2.27)
- Aardvark.Base.FSharp (5.2.27)
+ Aardvark.Base.Incremental (5.2.28)
+ Aardvark.Base (5.2.28)
+ Aardvark.Base.FSharp (5.2.28)
Aardvark.Base.TypeProviders (>= 4.5.15 < 4.6)
FSharp.Core (>= 5.0)
FSharp.Data.Adaptive (>= 1.2 < 1.3)
FsPickler (>= 5.3.2 < 5.4)
- Aardvark.Base.IO (5.2.27)
- Aardvark.Base (5.2.27)
- Aardvark.Base.Tensors (5.2.27)
+ Aardvark.Base.IO (5.2.28)
+ Aardvark.Base (5.2.28)
+ Aardvark.Base.Tensors (5.2.28)
System.Dynamic.Runtime (>= 4.3 < 4.4)
- Aardvark.Base.Runtime (5.2.27)
- Aardvark.Base.FSharp (5.2.27)
- Aardvark.Base.Incremental (5.2.27)
+ Aardvark.Base.Runtime (5.2.28)
+ Aardvark.Base.FSharp (5.2.28)
+ Aardvark.Base.Incremental (5.2.28)
Aardvark.Base.TypeProviders (>= 4.5.15 < 4.6)
FSharp.Core (>= 5.0)
FSharp.Data.Adaptive (>= 1.2 < 1.3)
FsPickler (>= 5.3.2 < 5.4)
- Aardvark.Base.Telemetry (5.2.27)
- Aardvark.Base.Tensors (5.2.27)
- Aardvark.Base (5.2.27)
- Aardvark.Base.FSharp (5.2.27)
+ Aardvark.Base.Telemetry (5.2.28)
+ Aardvark.Base.Tensors (5.2.28)
+ Aardvark.Base (5.2.28)
+ Aardvark.Base.FSharp (5.2.28)
FSharp.Core (>= 5.0)
SixLabors.ImageSharp (>= 2.1.3 < 2.2)
Aardvark.Base.TypeProviders (4.5.15)
FSharp.Core (>= 3.1.2.5) - restriction: || (== net471) (&& (== net6.0) (>= net45)) (&& (== net6.0-windows7.0) (>= net45)) (&& (== netstandard2.0) (>= net45))
FSharp.Core (>= 4.2.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0)
- Aardvark.Build (1.0.19)
- Aardvark.Geometry (5.2.27)
- Aardvark.Base (5.2.27)
- Aardvark.Base.FSharp (5.2.27)
- Aardvark.Base.Tensors (5.2.27)
+ Aardvark.Build (1.0.21)
+ Aardvark.Geometry (5.2.28)
+ Aardvark.Base (5.2.28)
+ Aardvark.Base.FSharp (5.2.28)
+ Aardvark.Base.Tensors (5.2.28)
Aardvark.Base.TypeProviders (>= 4.5.15 < 4.6)
FSharp.Core (>= 5.0)
FSharp.Data.Adaptive (>= 1.2 < 1.3)
@@ -69,38 +69,38 @@ NUGET
FSharp.Core (>= 4.7)
FSharp.Data.Adaptive (1.2.14)
System.Reflection.Emit.Lightweight (>= 4.6)
- FShade (5.5)
- FShade.Core (5.5)
- FShade.GLSL (5.5)
- FShade.Imperative (5.5)
- FShade.SpirV (5.5)
- FShade.Core (5.5)
+ FShade (5.5.3)
+ FShade.Core (5.5.3)
+ FShade.GLSL (5.5.3)
+ FShade.Imperative (5.5.3)
+ FShade.SpirV (5.5.3)
+ FShade.Core (5.5.3)
Aardvark.Base (>= 5.2.19 < 5.3)
Aardvark.Base.FSharp (>= 5.2.19 < 5.3)
- FShade.Imperative (5.5)
+ FShade.Imperative (5.5.3)
FSharp.Core (>= 5.0)
- FShade.Debug (5.5)
+ FShade.Debug (5.5.3)
Aardvark.Base (>= 5.2.19 < 5.3)
Aardvark.Base.FSharp (>= 5.2.19 < 5.3)
- FShade.Core (5.5)
- FShade.Imperative (5.5)
+ FShade.Core (5.5.3)
+ FShade.Imperative (5.5.3)
FSharp.Core (>= 5.0)
- FShade.GLSL (5.5)
+ FShade.GLSL (5.5.3)
Aardvark.Base (>= 5.2.19 < 5.3)
Aardvark.Base.FSharp (>= 5.2.19 < 5.3)
- FShade.Core (5.5)
- FShade.Imperative (5.5)
+ FShade.Core (5.5.3)
+ FShade.Imperative (5.5.3)
FSharp.Core (>= 5.0)
- FShade.Imperative (5.5)
+ FShade.Imperative (5.5.3)
Aardvark.Base (>= 5.2.19 < 5.3)
Aardvark.Base.FSharp (>= 5.2.19 < 5.3)
FSharp.Core (>= 5.0)
FsPickler (>= 5.3.2 < 5.4)
- FShade.SpirV (5.5)
+ FShade.SpirV (5.5.3)
Aardvark.Base (>= 5.2.19 < 5.3)
Aardvark.Base.FSharp (>= 5.2.19 < 5.3)
- FShade.Core (5.5)
- FShade.Imperative (5.5)
+ FShade.Core (5.5.3)
+ FShade.Imperative (5.5.3)
FSharp.Core (>= 5.0)
FSharp.Core (5.0.1)
FSharp.Data (4.2.10)
diff --git a/src/Aardvark.Rendering.GL/Aardvark.Rendering.GL.fsproj b/src/Aardvark.Rendering.GL/Aardvark.Rendering.GL.fsproj
index 64673e94..daa476f6 100644
--- a/src/Aardvark.Rendering.GL/Aardvark.Rendering.GL.fsproj
+++ b/src/Aardvark.Rendering.GL/Aardvark.Rendering.GL.fsproj
@@ -56,8 +56,9 @@
-
+
+
diff --git a/src/Aardvark.Rendering.GL/Core/Context.fs b/src/Aardvark.Rendering.GL/Core/Context.fs
index df67edf2..719a2d1e 100644
--- a/src/Aardvark.Rendering.GL/Core/Context.fs
+++ b/src/Aardvark.Rendering.GL/Core/Context.fs
@@ -201,10 +201,22 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this
let mutable unpackAlignment : Option = None
+ let mutable maxTextureSize : Option = None
+
+ let mutable maxTextureSize3d : Option = None
+
+ let mutable maxTextureSizeCube : Option = None
+
+ let mutable maxTextureArrayLayers : Option = None
+
+ let mutable maxRenderbufferSize : Option = None
+
let mutable maxComputeWorkGroupSize : Option = None
let mutable maxComputeWorkGroupInvocations : Option = None
+ let mutable numProgramBinaryFormats : Option = None
+
let mutable shaderCachePath : Option = Some defaultShaderCachePath
let formatSampleCounts = FastConcurrentDict()
@@ -259,6 +271,34 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this
GL.GetInteger(GetPName.UnpackAlignment)
)
+ member x.MaxTextureSize =
+ getOrQuery &maxTextureSize (fun _ ->
+ let s = GL.GetInteger(GetPName.MaxTextureSize)
+ V2i s
+ )
+
+ member x.MaxTextureSize3D =
+ getOrQuery &maxTextureSize3d (fun _ ->
+ let s = GL.GetInteger(GetPName.Max3DTextureSize)
+ V3i s
+ )
+
+ member x.MaxTextureSizeCube =
+ getOrQuery &maxTextureSizeCube (fun _ ->
+ GL.GetInteger(GetPName.MaxCubeMapTextureSize)
+ )
+
+ member x.MaxTextureArrayLayers =
+ getOrQuery &maxTextureArrayLayers (fun _ ->
+ GL.GetInteger(GetPName.MaxArrayTextureLayers)
+ )
+
+ member x.MaxRenderbufferSize =
+ getOrQuery &maxRenderbufferSize (fun _ ->
+ let s = GL.GetInteger(GetPName.MaxRenderbufferSize)
+ V2i s
+ )
+
member x.MaxComputeWorkGroupSize =
getOrQuery &maxComputeWorkGroupSize (fun _ ->
let mutable res = V3i.Zero
@@ -273,6 +313,11 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this
GL.GetInteger(GetPName.MaxComputeWorkGroupInvocations)
)
+ member x.NumProgramBinaryFormats =
+ getOrQuery &numProgramBinaryFormats (fun _ ->
+ GL.GetInteger(GetPName.NumProgramBinaryFormats)
+ )
+
member internal x.ImportMemoryBlock(external : ExternalMemoryBlock) =
sharedMemoryManager.Import external
diff --git a/src/Aardvark.Rendering.GL/Core/ContextHandles.fs b/src/Aardvark.Rendering.GL/Core/ContextHandles.fs
index 467c0f6f..9437f7d0 100644
--- a/src/Aardvark.Rendering.GL/Core/ContextHandles.fs
+++ b/src/Aardvark.Rendering.GL/Core/ContextHandles.fs
@@ -31,12 +31,23 @@ module ContextHandleGLExtensions =
type GL with
static member SetDefaultStates() =
GL.Enable(EnableCap.TextureCubeMapSeamless)
+ GL.Check "cannot enable GL_TEXTURE_CUBE_MAP_SEAMLESS"
+
+ // Note: This is supposed to be deprecated since OpenGL 3.2 and enabled by default.
+ // However, for some AMD drivers you still need to enable it even though it should not exist anymore.
GL.Enable(EnableCap.PointSprite)
+ GL.GetError() |> ignore
+
GL.Disable(EnableCap.PolygonSmooth)
+ GL.Check "cannot disable GL_POLYGON_SMOOTH"
+
GL.Hint(HintTarget.FragmentShaderDerivativeHint, HintMode.Nicest)
+ GL.Check "cannot set GL_FRAGMENT_SHADER_DERIVATIVE_HINT to GL_NICEST"
+
if RuntimeConfig.DepthRange = DepthRange.ZeroToOne then
if GL.ARB_clip_control then
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.ZeroToOne)
+ GL.Check "failed to set depth range to [0, 1]"
else
failf "cannot set depth range to [0, 1] without GL_ARB_clip_control or OpenGL 4.5"
@@ -50,6 +61,7 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) =
static let contextError = new Event()
let l = obj()
+ let onDisposed = Event()
let mutable debugOutput = None
let mutable onMakeCurrent : ConcurrentHashSet unit> = null
let mutable driverInfo = None
@@ -66,6 +78,9 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) =
[]
static member ContextError = contextError.Publish
+ []
+ member x.OnDisposed = onDisposed.Publish
+
member x.GetProcAddress(name : string) =
(handle |> unbox).GetAddress(name)
@@ -187,6 +202,7 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) =
member x.Dispose() =
debugOutput |> Option.iter (fun dbg -> dbg.Dispose())
+ onDisposed.Trigger()
interface IDisposable with
member x.Dispose() = x.Dispose()
@@ -204,9 +220,6 @@ module ContextHandle =
if ctx.IsCurrent then
ctx.ReleaseCurrent()
ctx.Dispose()
- //match windows.TryRemove ctx with
- // | (true, w) -> w.Dispose()
- // | _ -> ()
///
/// checks whether the given context is current on the calling thread
@@ -225,8 +238,45 @@ module ContextHandle =
let releaseCurrent (ctx : ContextHandle) = ctx.ReleaseCurrent()
module ContextHandleOpenTK =
-
- let private windows = System.Collections.Concurrent.ConcurrentDictionary()
+
+ // This is a workaround for closing the X11 display, since OpenTK leaks the display even when the
+ // window is disposed. This leads to problems when running multiple unit tests one after another.
+ // We currently use a custom version of OpenTK in which this issue isn't fixed yet.
+ // https://github.com/krauthaufen/OpenTKHack
+ // See: https://github.com/opentk/opentk/pull/773
+ module private X11 =
+ open OpenTK.Platform.X11
+ open System.Reflection
+
+ []
+ module private Internals =
+ let asm = typeof.Assembly
+
+ let fiImplementation =
+ typeof.GetField("implementation", BindingFlags.NonPublic ||| BindingFlags.Instance)
+
+ let fiWindow =
+ let t = asm.GetType("OpenTK.Platform.X11.X11GLNative")
+ if isNull t then null
+ else t.GetField("window", BindingFlags.NonPublic ||| BindingFlags.Instance)
+
+ let fCloseDisplay =
+ let t = asm.GetType("OpenTK.Platform.X11.Functions")
+ if isNull t then null
+ else t.GetMethod("XCloseDisplay")
+
+ let closeDisplay (window : NativeWindow) =
+ if RuntimeInformation.IsOSPlatform OSPlatform.Linux then
+ try
+ let x11GLNative = fiImplementation.GetValue(window)
+ let window = unbox <| fiWindow.GetValue(x11GLNative)
+
+ if window.Display <> 0n then
+ fCloseDisplay.Invoke(null, [| window.Display |]) |> ignore
+ with exn ->
+ Log.warn "[GL] Failed to close X11 display: %A" exn.Message
+ else
+ ()
///
/// creates a new context using the default configuration
@@ -250,15 +300,14 @@ module ContextHandleOpenTK =
| ValueNone -> context.MakeCurrent(null)
window, context
-
-
- let handle = new ContextHandle(context, window.WindowInfo)
- handle.Initialize(debug, setDefaultStates = false)
- // add the window to the windows-table to save it from being
- // garbage collected.
- if not <| windows.TryAdd(handle, window) then failwith "failed to add new context to live-set"
-
- handle
+ let dispose() =
+ context.Dispose()
+ window.Dispose()
+ X11.closeDisplay window
+ let handle = new ContextHandle(context, window.WindowInfo)
+ handle.OnDisposed.Add dispose
+ handle.Initialize(debug, setDefaultStates = false)
+ handle
\ No newline at end of file
diff --git a/src/Aardvark.Rendering.GL/Core/DebugOutput.fs b/src/Aardvark.Rendering.GL/Core/DebugOutput.fs
index 254f6734..d5689d70 100644
--- a/src/Aardvark.Rendering.GL/Core/DebugOutput.fs
+++ b/src/Aardvark.Rendering.GL/Core/DebugOutput.fs
@@ -12,19 +12,37 @@ open Aardvark.Rendering.GL
module Error =
exception OpenGLException of ec : ErrorCode * msg : string with
- override x.Message = sprintf "%A: %s" x.ec x.msg
+ override x.Message = $"{x.msg} (Error: {x.ec})"
type GL with
+ static member private Check(str, throwOnError) =
+ let err = GL.GetError()
+ if err <> ErrorCode.NoError then
+ let str = $"{str}"
+ let msg =
+ if String.IsNullOrEmpty str then "An error occurred"
+ else string (Char.ToUpper str.[0]) + str.Substring(1)
+
+ Report.Error($"[GL] {msg} (Error: {err})")
+
+ if throwOnError then
+ raise <| OpenGLException(err, msg)
+
+ /// Gets the value of the GL error flag and logs it
+ /// with the given message string if it is not equal to GL_NO_ERROR.
+ /// Throws an exception after logging if DebugConfig.ErrorFlagCheck = ThrowOnError.
+ /// Does nothing if DebugConfig.ErrorFlagCheck = Disabled.
static member Check str =
let mode = GL.CheckErrors
if mode <> ErrorFlagCheck.Disabled then
- let err = GL.GetError()
- if err <> ErrorCode.NoError then
- Report.Error("{0}: {1}", err, str)
+ let throwOnError = (mode = ErrorFlagCheck.ThrowOnError)
+ GL.Check(str, throwOnError)
- if mode = ErrorFlagCheck.ThrowOnError then
- raise <| OpenGLException(err, sprintf "%A" str)
+ /// Gets the value of the GL error flag and throws an exception
+ /// with the given message string if it is not equal to GL_NO_ERROR.
+ static member Assert str =
+ GL.Check(str, true)
[]
module private IGraphicsContextDebugExtensions =
diff --git a/src/Aardvark.Rendering.GL/Core/Extensions/ARB_get_program_binary.fs b/src/Aardvark.Rendering.GL/Core/Extensions/ARB_get_program_binary.fs
index 52369656..b03c131e 100644
--- a/src/Aardvark.Rendering.GL/Core/Extensions/ARB_get_program_binary.fs
+++ b/src/Aardvark.Rendering.GL/Core/Extensions/ARB_get_program_binary.fs
@@ -22,4 +22,20 @@ module ARB_get_program_binary =
if GL.ARB_get_program_binary then
GL.GetProgramBinary(program, bufSize, &length, &binaryFormat, binary)
else
- failwith "glGetProgramBinary is not available."
\ No newline at end of file
+ failwith "glGetProgramBinary is not available."
+
+ static member GetProgramBinary(program : int, length : int) =
+ let data : byte[] = Array.zeroCreate length
+ let mutable format = Unchecked.defaultof
+ let mutable returnedLength = length
+ GL.Dispatch.GetProgramBinary(program, length, &returnedLength, &format, data)
+
+ if returnedLength = length then
+ data, format
+ else
+ null, format
+
+ static member GetProgramBinaryLength(program : int) =
+ let mutable result = 0
+ GL.GetProgram(program, GetProgramParameterName.ProgramBinaryLength, &result)
+ result
\ No newline at end of file
diff --git a/src/Aardvark.Rendering.GL/Core/ShaderCache.fs b/src/Aardvark.Rendering.GL/Core/ShaderCache.fs
index a2b0ca72..5b366087 100644
--- a/src/Aardvark.Rendering.GL/Core/ShaderCache.fs
+++ b/src/Aardvark.Rendering.GL/Core/ShaderCache.fs
@@ -24,27 +24,45 @@ module internal ShaderCacheKeys =
deviceCount : int
}
+type internal ShaderCacheEntry(surface : IBackendSurface, destroy : unit -> unit) =
+ member x.Surface = surface
+ member x.Dispose() = destroy()
+
+ member x.Equals(other : ShaderCacheEntry) =
+ surface = other.Surface
+
+ override x.Equals(other : obj) =
+ match other with
+ | :? ShaderCacheEntry as o -> x.Equals(o)
+ | _ -> false
+
+ override x.GetHashCode() =
+ surface.GetHashCode()
+
+ interface IDisposable with
+ member x.Dispose() = x.Dispose()
+
type internal ShaderCache() =
- let codeCache = ConcurrentDictionary>()
- let effectCache = ConcurrentDictionary>()
+ let codeCache = ConcurrentDictionary>()
+ let effectCache = ConcurrentDictionary>()
- static let box (value : Error<'T>) : Error =
+ static let box (destroy : 'T -> unit) (value : Error<'T>) : Error =
match value with
- | Success v -> Success (v :> IBackendSurface)
+ | Success v -> Success (new ShaderCacheEntry(v, fun () -> destroy v))
| Error err -> Error err
- static let unbox (value : Error) : Error<'T> =
+ static let unbox (value : Error) : Error<'T> =
match value with
- | Success v -> Success (unbox v)
+ | Success v -> Success (unbox v.Surface)
| Error err -> Error err
- member x.GetOrAdd<'T when 'T :> IBackendSurface>(key : CodeCacheKey, create : CodeCacheKey -> Error<'T>) : Error<'T> =
- codeCache.GetOrAdd(key, create >> box) |> unbox
+ member x.GetOrAdd<'T when 'T :> IBackendSurface>(key : CodeCacheKey, create : CodeCacheKey -> Error<'T>, destroy : 'T -> unit) : Error<'T> =
+ codeCache.GetOrAdd(key, create >> box destroy) |> unbox
- member x.GetOrAdd<'T when 'T :> IBackendSurface>(key : EffectCacheKey, create : EffectCacheKey -> Error<'T>) : Error<'T> =
- effectCache.GetOrAdd(key, create >> box) |> unbox
+ member x.GetOrAdd<'T when 'T :> IBackendSurface>(key : EffectCacheKey, create : EffectCacheKey -> Error<'T>, destroy : 'T -> unit) : Error<'T> =
+ effectCache.GetOrAdd(key, create >> box destroy) |> unbox
- member x.Programs =
+ member x.Entries =
[ codeCache.Values; effectCache.Values ]
|> Seq.concat
|> Seq.choose (function
@@ -54,7 +72,7 @@ type internal ShaderCache() =
|> Seq.distinct
member x.Dispose() =
- for p in x.Programs do p.Dispose()
+ for e in x.Entries do e.Dispose()
codeCache.Clear()
effectCache.Clear()
diff --git a/src/Aardvark.Rendering.GL/Core/Utilities/Common.fs b/src/Aardvark.Rendering.GL/Core/Utilities/Common.fs
index 0ea654cc..ee795ba4 100644
--- a/src/Aardvark.Rendering.GL/Core/Utilities/Common.fs
+++ b/src/Aardvark.Rendering.GL/Core/Utilities/Common.fs
@@ -11,8 +11,13 @@ module private ErrorUtilities =
let inline failf fmt =
Printf.kprintf (fun str ->
- Report.Error $"[GL] {str}"
- failwith ("[GL] " + str)
+ let str =
+ if String.IsNullOrEmpty str then "An error occurred"
+ else string (Char.ToUpper str.[0]) + str.Substring(1)
+
+ let msg = $"[GL] {str}"
+ Report.Error msg
+ failwith msg
) fmt
[]
diff --git a/src/Aardvark.Rendering.GL/Instructions/OpenGL.fs b/src/Aardvark.Rendering.GL/Instructions/OpenGL.fs
index f06fdfe4..404c74bc 100644
--- a/src/Aardvark.Rendering.GL/Instructions/OpenGL.fs
+++ b/src/Aardvark.Rendering.GL/Instructions/OpenGL.fs
@@ -380,45 +380,43 @@ module OpenGl =
///
let getProcAddressInternal (name : string) =
match System.Environment.OSVersion with
- | Linux ->
- if opengl32 = 0n then
- opengl32Lib <- DynamicLinker.loadLibrary "libGL.so.1"
- opengl32 <- opengl32Lib.Handle
+ | Linux ->
+ if opengl32 = 0n then
+ opengl32Lib <- DynamicLinker.loadLibrary "libGL.so.1"
+ opengl32 <- opengl32Lib.Handle
- match GLX.GetProcAddress name with
- | 0n -> 0n
- | ptr -> ptr
- | Mac ->
+ match GLX.GetProcAddress name with
+ | 0n -> 0n
+ | ptr -> ptr
- let ctx = OpenTK.Graphics.GraphicsContext.CurrentContext :?> OpenTK.Graphics.IGraphicsContextInternal
+ | Mac ->
+ let ctx = OpenTK.Graphics.GraphicsContext.CurrentContext :?> OpenTK.Graphics.IGraphicsContextInternal
+ ctx.GetAddress name
- let handle = ctx.GetAddress name
- if handle = 0n then printfn "could not grab: %s" name
- handle
- (*if opengl32 = 0n then
- let ctx : OpenTK.Graphics.IGraphicsContextInternal = failwith ""
+ (*if opengl32 = 0n then
+ let ctx : OpenTK.Graphics.IGraphicsContextInternal = failwith ""
- ctx.GetAddress(name)
+ ctx.GetAddress(name)
- opengl32Lib <- DynamicLinker.loadLibrary "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"
- opengl32 <- opengl32Lib.Handle
+ opengl32Lib <- DynamicLinker.loadLibrary "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"
+ opengl32 <- opengl32Lib.Handle
- let handle = opengl32Lib.GetFunction(name).Handle
- if handle <> 0n then Log.warn "could not get function ptr: %A" name
- handle*)
+ let handle = opengl32Lib.GetFunction(name).Handle
+ if handle <> 0n then Log.warn "could not get function ptr: %A" name
+ handle*)
- | Windows ->
- if opengl32 = 0n then
- opengl32Lib <- DynamicLinker.loadLibrary "Opengl32.dll"
- opengl32 <- opengl32Lib.Handle
+ | Windows ->
+ if opengl32 = 0n then
+ opengl32Lib <- DynamicLinker.loadLibrary "Opengl32.dll"
+ opengl32 <- opengl32Lib.Handle
- match Wgl.GetDefaultProcAddress name with
- | 0n -> match Wgl.GLGetProcAddress name with
- | 0n -> match Wgl.GetProcAddress(opengl32, name) with
- | 0n -> 0n
- | ptr -> ptr
- | ptr -> ptr
- | ptr -> ptr
+ match Wgl.GetDefaultProcAddress name with
+ | 0n -> match Wgl.GLGetProcAddress name with
+ | 0n -> match Wgl.GetProcAddress(opengl32, name) with
+ | 0n -> 0n
+ | ptr -> ptr
+ | ptr -> ptr
+ | ptr -> ptr
let rec getProcAddressProbing (suffixes : list) (name : string) =
diff --git a/src/Aardvark.Rendering.GL/Resources/Program.fs b/src/Aardvark.Rendering.GL/Resources/Program.fs
index c90cad47..2a5272be 100644
--- a/src/Aardvark.Rendering.GL/Resources/Program.fs
+++ b/src/Aardvark.Rendering.GL/Resources/Program.fs
@@ -50,13 +50,22 @@ type Program =
member x.WritesPointSize =
FShade.GLSL.GLSLProgramInterface.usesPointSize x.Interface
- member x.Dispose() =
+ // Deletes the program handle
+ // Called by the program cache of the Context
+ member x.Free() =
using x.Context.ResourceLock (fun _ ->
ResourceCounts.removeProgram x.Context
GL.DeleteProgram(x.Handle)
GL.Check "could not delete program"
)
+ member x.Dispose() =
+ // Programs are kept alive in the cache of the Context
+ // Disposing them manually leads to issues because they are not removed
+ // from the cache. As a workaround we just do nothing when Dispose() is called.
+ // The real solution is to use reference counting like in the Vulkan backend (breaking change).
+ ()
+
interface IBackendSurface with
member x.Handle = x.Handle :> obj
member x.Dispose() = x.Dispose()
@@ -435,6 +444,10 @@ module ProgramExtensions =
GL.Check "could not create program"
try
+ if GL.ARB_get_program_binary then
+ GL.ProgramParameter(handle, ProgramParameterName.ProgramBinaryRetrievableHint, 1)
+ GL.Check "could not set program binary retrievable hint"
+
for s in shaders do
GL.AttachShader(handle, s.Handle)
GL.Check "could not attach shader to program"
@@ -634,29 +647,28 @@ module ProgramExtensions =
module Program =
let tryGetBinary (program : Program) =
- if GL.ARB_get_program_binary then
- GL.GetError() |> ignore
+ if GL.ARB_get_program_binary && program.Context.NumProgramBinaryFormats > 0 then
+ let length = GL.Dispatch.GetProgramBinaryLength program.Handle
+ GL.Check "failed to get program binary length"
- let mutable length = 0
- GL.GetProgram(program.Handle, GetProgramParameterName.ProgramBinaryLength, &length)
-
- let err = GL.GetError()
- if err <> ErrorCode.NoError then
- Log.warn "[GL] Failed to query program binary length: %A" err
- None
- else
- let data : byte[] = Array.zeroCreate length
- let mutable format = Unchecked.defaultof
- GL.GetProgramBinary(program.Handle, length, &length, &format, data)
+ if length > 0 then
+ let data, format = GL.Dispatch.GetProgramBinary(program.Handle, length)
+ GL.Check "failed to get program binary"
- let err = GL.GetError()
- if err <> ErrorCode.NoError then
- Log.warn "[GL] Failed to retrieve program binary: %A" err
+ if isNull data then
+ Log.warn "[GL] Failed to retrieve program binary"
None
else
Some (format, data)
+ else
+ Log.warn "[GL] Program binary length is zero bytes"
+ None
else
- Report.Line(4, "[GL] Cannot read shader cache because GL_ARB_get_program_binary is not supported")
+ let reason =
+ if GL.ARB_get_program_binary then "no binary formats are supported"
+ else "GL_ARB_get_program_binary is not supported"
+
+ Report.Line(4, $"[GL] Cannot read shader cache because {reason}")
None
let ofShaderCacheEntry (context : Context) (fixBindings : bool) (entry : ShaderCacheEntry) =
@@ -763,8 +775,23 @@ module ProgramExtensions =
None
)
+ []
+ module internal ShaderCacheExtensions =
+ type ShaderCache with
+ member inline x.GetOrAdd(key : CodeCacheKey, create : CodeCacheKey -> Error) =
+ x.GetOrAdd(key, create, fun p -> p.Free())
+
+ member inline x.GetOrAdd(key : EffectCacheKey, create : EffectCacheKey -> Error) =
+ x.GetOrAdd(key, create, fun p -> p.Free())
+
type Aardvark.Rendering.GL.Context with
+ /// Returns whether the inputs gl_Layer and gl_ViewportIndex can be used
+ /// in fragment shaders. If not a custom output / input must be used for
+ /// layered effects.
+ member internal x.SupportsLayeredEffects =
+ x.Driver.glsl >= Version(4, 3, 0)
+
member x.TryGetProgramBinary(prog : Program) =
use __ = prog.Context.ResourceLock
Program.tryGetBinary prog
@@ -856,7 +883,7 @@ module ProgramExtensions =
let glsl =
lazy (
try
- let module_ = key.layout.Link(key.effect, key.deviceCount, Range1d(-1.0, 1.0), false, key.topology)
+ let module_ = key.layout.Link(key.effect, key.deviceCount, Range1d(-1.0, 1.0), false, key.topology, not x.SupportsLayeredEffects)
ModuleCompiler.compileGLSL x.FShadeBackend module_
with exn ->
Log.error "%s" exn.Message
diff --git a/src/Aardvark.Rendering.GL/Resources/Textures/Image.fs b/src/Aardvark.Rendering.GL/Resources/Textures/Image.fs
new file mode 100644
index 00000000..d31ba04a
--- /dev/null
+++ b/src/Aardvark.Rendering.GL/Resources/Textures/Image.fs
@@ -0,0 +1,141 @@
+namespace Aardvark.Rendering.GL
+
+open Aardvark.Base
+open Aardvark.Rendering
+open OpenTK.Graphics.OpenGL4
+open Aardvark.Rendering.GL
+
+[]
+type internal Image =
+ | Texture of Texture
+ | Renderbuffer of Renderbuffer
+
+ member x.Handle =
+ match x with
+ | Texture t -> t.Handle
+ | Renderbuffer rb -> rb.Handle
+
+ member x.Dimension =
+ match x with
+ | Texture t -> t.Dimension
+ | Renderbuffer _ -> TextureDimension.Texture2D
+
+ member x.Format =
+ match x with
+ | Texture t -> t.Format
+ | Renderbuffer rb -> rb.Format
+
+ member x.Target =
+ match x with
+ | Texture t -> unbox <| TextureTarget.ofTexture t
+ | Renderbuffer _ -> ImageTarget.Renderbuffer
+
+ member x.GetSize(level : int) =
+ match x with
+ | Texture t -> t.GetSize(level)
+ | Renderbuffer rb -> V3i(rb.Size, 1)
+
+ member x.Samples =
+ match x with
+ | Texture t -> t.Multisamples
+ | Renderbuffer rb -> rb.Samples
+
+ member x.IsMultisampled =
+ x.Samples > 1
+
+ member private x.IsDepth =
+ match x with
+ | Texture t -> t.Format.IsDepth
+ | Renderbuffer rb -> rb.Format.IsDepth
+
+ member private x.IsStencil =
+ match x with
+ | Texture _ -> false
+ | Renderbuffer rb -> rb.Format.IsStencil
+
+ member private x.IsDepthStencil =
+ match x with
+ | Texture t -> t.Format.IsDepthStencil
+ | Renderbuffer rb -> rb.Format.IsDepthStencil
+
+ member x.Attachment =
+ if x.IsDepth then FramebufferAttachment.DepthAttachment
+ elif x.IsStencil then FramebufferAttachment.StencilAttachment
+ elif x.IsDepthStencil then FramebufferAttachment.DepthStencilAttachment
+ else FramebufferAttachment.ColorAttachment0
+
+ member x.Mask =
+ if x.IsDepth then ClearBufferMask.DepthBufferBit
+ elif x.IsStencil then ClearBufferMask.StencilBufferBit
+ elif x.IsDepthStencil then ClearBufferMask.DepthBufferBit ||| ClearBufferMask.StencilBufferBit
+ else ClearBufferMask.ColorBufferBit
+
+ member x.WindowOffset(level : int, window : Box3i) =
+ match x with
+ | Image.Texture t -> t.WindowOffset(level, window)
+ | Image.Renderbuffer rb -> window |> WindowOffset.flipY rb.Size.Y
+
+ member x.WindowOffset(level : int, offset : V3i, size : V3i) =
+ x.WindowOffset(level, Box3i.FromMinAndSize(offset, size))
+
+ member x.WindowOffset(level : int, window : Box2i) =
+ let window = Box3i(V3i(window.Min, 0), V3i(window.Max, 1))
+ x .WindowOffset(level, window).XY
+
+ member x.WindowOffset(level : int, offset : V2i, size : V2i) =
+ x.WindowOffset(level, Box2i.FromMinAndSize(offset, size))
+
+module internal Image =
+
+ /// Attaches an image to the framebuffer bound at the given framebuffer target
+ let attach (framebufferTarget : FramebufferTarget) (level : int) (slice : int) (image : Image) =
+ let attachment = image.Attachment
+
+ match image with
+ | Image.Texture texture ->
+ let target = texture |> TextureTarget.ofTexture
+ let targetSlice = target |> TextureTarget.toSliceTarget slice
+
+ match texture.Dimension, texture.IsArray with
+ | TextureDimension.Texture1D, true
+ | TextureDimension.Texture2D, true
+ | TextureDimension.TextureCube, true
+ | TextureDimension.Texture3D, false ->
+ GL.FramebufferTextureLayer(framebufferTarget, attachment, texture.Handle, level, slice)
+
+ | TextureDimension.Texture1D, false ->
+ GL.FramebufferTexture1D(framebufferTarget, attachment, targetSlice, texture.Handle, level)
+
+ | TextureDimension.Texture2D, false
+ | TextureDimension.TextureCube, false ->
+ GL.FramebufferTexture2D(framebufferTarget, attachment, targetSlice, texture.Handle, level)
+
+ | d, a ->
+ failwithf "[GL] cannot attach %A%s to framebuffer" d (if a then "[]" else "")
+
+ | Image.Renderbuffer renderBuffer ->
+ GL.FramebufferRenderbuffer(framebufferTarget, attachment, RenderbufferTarget.Renderbuffer, renderBuffer.Handle)
+
+ GL.Check "could not attach texture to framebuffer"
+
+ /// Uses a framebuffer to the read the image layers of the given level from slice baseSlice to baseSlice + slices.
+ let readLayers (image : Image) (level : int) (baseSlice : int) (slices : int) (f : int -> unit) =
+ let attachment = image.Attachment
+
+ let readBuffer =
+ if attachment = FramebufferAttachment.ColorAttachment0 then ReadBufferMode.ColorAttachment0
+ else ReadBufferMode.None
+
+ Framebuffer.temporary FramebufferTarget.ReadFramebuffer (fun fbo ->
+ GL.ReadBuffer(readBuffer)
+ GL.Check "could not set buffer"
+
+ try
+ for slice = baseSlice to baseSlice + slices - 1 do
+ image |> attach FramebufferTarget.ReadFramebuffer level slice
+ Framebuffer.check FramebufferTarget.ReadFramebuffer
+ f slice
+
+ finally
+ GL.ReadBuffer(ReadBufferMode.None)
+ )
\ No newline at end of file
diff --git a/src/Aardvark.Rendering.GL/Resources/Textures/Renderbuffer.fs b/src/Aardvark.Rendering.GL/Resources/Textures/Renderbuffer.fs
index 08c9324b..c9361e9f 100644
--- a/src/Aardvark.Rendering.GL/Resources/Textures/Renderbuffer.fs
+++ b/src/Aardvark.Rendering.GL/Resources/Textures/Renderbuffer.fs
@@ -55,32 +55,32 @@ type Renderbuffer =
new (ctx : Context, handle : int, size : V2i, format : TextureFormat, samples : int, sizeInBytes : int64) =
{ Context = ctx; Handle = handle; Size = size; Format = format; Samples = samples; SizeInBytes = sizeInBytes }
+
+ new (ctx : Context, handle : int, size : V2i, format : TextureFormat, samples : int) =
+ let sizeInBytes = ResourceCounts.texSizeInBytes TextureDimension.Texture2D size.XYI format samples 1 1
+ new Renderbuffer(ctx, handle, size, format, samples, sizeInBytes)
end
[]
module RenderbufferExtensions =
- let private updateRenderbuffer (cxt : Context) (handle : int) (size : V2i) (format : TextureFormat) (samples : int) =
- GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, handle)
- GL.Check "could not bind renderbuffer"
+ let private updateRenderbuffer (ctx : Context) (handle : int) (size : V2i) (format : TextureFormat) (samples : int) =
+ if Vec.anyGreater size ctx.MaxRenderbufferSize then
+ failf $"cannot create renderbuffer with size {size} (maximum is {ctx.MaxRenderbufferSize})"
let samples =
- if samples > 1 then
- let counts = cxt.GetFormatSamples(ImageTarget.Renderbuffer, format)
- if counts.Contains samples then samples
- else
- let max = Set.maxElement counts
- Log.warn "[GL] cannot create %A render buffer with %d samples (using %d instead)" format samples max
- max
- else
- 1
+ Image.validateSampleCount ctx ImageTarget.Renderbuffer format samples
+
+ GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, handle)
+ GL.Check "could not bind renderbuffer"
if samples > 1 then
GL.RenderbufferStorageMultisample(RenderbufferTarget.Renderbuffer, samples, TextureFormat.toRenderbufferStorage format, size.X, size.Y)
else
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, TextureFormat.toRenderbufferStorage format, size.X, size.Y)
- GL.Check "could not set renderbuffer storage"
+
+ GL.Check $"failed to allocate renderbuffer storage (format = {format}, size = {size}, samples = {samples})"
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, 0)
GL.Check "could not unbind renderbuffer"
@@ -92,15 +92,14 @@ module RenderbufferExtensions =
let samples = defaultArg samples 1
using x.ResourceLock (fun _ ->
-
let handle = GL.GenRenderbuffer()
GL.Check "could not create renderbuffer"
let samples = updateRenderbuffer x handle size format samples
- let sizeInBytes = (int64 size.X * int64 size.Y * int64 format.PixelSizeInBits) / 8L
- ResourceCounts.addRenderbuffer x sizeInBytes
- new Renderbuffer(x, handle, size, format, samples, sizeInBytes)
+ let rb = new Renderbuffer(x, handle, size, format, samples)
+ ResourceCounts.addRenderbuffer x rb.SizeInBytes
+ rb
)
member x.Update(r : Renderbuffer, size : V2i, format : TextureFormat, ?samples : int) =
@@ -109,7 +108,7 @@ module RenderbufferExtensions =
if r.Size <> size || r.Format <> format || r.Samples <> samples then
using x.ResourceLock (fun _ ->
let samples = updateRenderbuffer x r.Handle size format samples
- let sizeInBytes = (int64 size.X * int64 size.Y * int64 format.PixelSizeInBits) / 8L
+ let sizeInBytes = ResourceCounts.texSizeInBytes TextureDimension.Texture2D size.XYI format samples 1 1
ResourceCounts.resizeRenderbuffer x r.SizeInBytes sizeInBytes
r.SizeInBytes <- sizeInBytes
diff --git a/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs b/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs
index d50d1ddd..6a1f6e2e 100644
--- a/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs
+++ b/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs
@@ -32,14 +32,29 @@ module internal TextureResourceCounts =
let updateTexture (ctx:Context) oldSize newSize =
Interlocked.Add(&ctx.MemoryUsage.TextureMemory,newSize-oldSize) |> ignore
- let texSizeInBytes (size : V3i, t : TextureFormat, samples : int, levels : int) =
- let pixelCount = (int64 size.X) * (int64 size.Y) * (int64 size.Z) * (int64 samples)
- let mutable size = pixelCount * (int64 t.PixelSizeInBits) / 8L
- let mutable temp = size
- for i in 1..levels-1 do
- temp <- temp >>> 2
- size <- size + temp
- 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
@@ -98,6 +113,11 @@ type Texture =
Format = format
SizeInBytes = sizeInBytes }
+ new(ctx : Context, handle : int, dimension : TextureDimension, mipMapLevels : int, multisamples : int,
+ size : V3i, count : int, isArray : bool, format : TextureFormat) =
+ let sizeInBytes = ResourceCounts.texSizeInBytes dimension size format multisamples mipMapLevels count
+ new Texture(ctx, handle, dimension, mipMapLevels, multisamples, size, count, isArray, format, sizeInBytes)
+
new(ctx : Context, handle : int, dimension : TextureDimension, mipMapLevels : int, multisamples : int,
size : V3i, count : Option, format : TextureFormat, sizeInBytes : int64) =
let cnt, isArray =
@@ -106,6 +126,15 @@ type Texture =
| None -> 1, false
new Texture(ctx, handle, dimension, mipMapLevels, multisamples, size, cnt, isArray, format, sizeInBytes)
+
+ new(ctx : Context, handle : int, dimension : TextureDimension, mipMapLevels : int, multisamples : int,
+ size : V3i, count : Option, format : TextureFormat) =
+ let cnt, isArray =
+ match count with
+ | Some cnt -> cnt, true
+ | None -> 1, false
+
+ new Texture(ctx, handle, dimension, mipMapLevels, multisamples, size, cnt, isArray, format)
end
type internal SharedTexture(ctx : Context, handle : int, external : IExportedBackendTexture, memory : SharedMemoryBlock) =
@@ -154,6 +183,26 @@ module internal TextureUtilitiesAndExtensions =
IsMultisampled = x.isMS
IsArray = x.isArray }
+ module Image =
+
+ /// Validates the sample count for the given image parameters and returns an appropiate fallback
+ /// if the sample count is not supported.
+ let validateSampleCount (ctx : Context) (target : ImageTarget) (format : TextureFormat) (samples : int) =
+ if samples > 1 then
+ let counts = ctx.GetFormatSamples(target, format)
+ if counts.Contains samples then samples
+ else
+ let fallback =
+ counts
+ |> Set.toList
+ |> List.sortBy ((-) samples >> abs)
+ |> List.head
+
+ Log.warn "[GL] cannot create %A image with %d samples (using %d instead)" format samples fallback
+ fallback
+ else
+ 1
+
[]
module TensorExtensions =
@@ -195,7 +244,7 @@ module internal TextureUtilitiesAndExtensions =
member x.AsBytes(elementSize) = x |> Tensor4Info.asBytes elementSize
member x.AsBytes<'T>() = x.AsBytes(sizeof<'T>)
- module private WindowOffset =
+ module WindowOffset =
let flipY (height : int) (window : Box3i) =
V3i(window.Min.X, height - window.Max.Y, window.Min.Z)
@@ -250,143 +299,6 @@ module internal TextureUtilitiesAndExtensions =
GL.BindFramebuffer(target, old)
GL.DeleteFramebuffer(fbo)
-
- []
- type Image =
- | Texture of Texture
- | Renderbuffer of Renderbuffer
-
- member x.Handle =
- match x with
- | Texture t -> t.Handle
- | Renderbuffer rb -> rb.Handle
-
- member x.Dimension =
- match x with
- | Texture t -> t.Dimension
- | Renderbuffer _ -> TextureDimension.Texture2D
-
- member x.Format =
- match x with
- | Texture t -> t.Format
- | Renderbuffer rb -> rb.Format
-
- member x.Target =
- match x with
- | Texture t -> unbox <| TextureTarget.ofTexture t
- | Renderbuffer _ -> ImageTarget.Renderbuffer
-
- member x.GetSize(level : int) =
- match x with
- | Texture t -> t.GetSize(level)
- | Renderbuffer rb -> V3i(rb.Size, 1)
-
- member x.Samples =
- match x with
- | Texture t -> t.Multisamples
- | Renderbuffer rb -> rb.Samples
-
- member x.IsMultisampled =
- x.Samples > 1
-
- member private x.IsDepth =
- match x with
- | Texture t -> t.Format.IsDepth
- | Renderbuffer rb -> rb.Format.IsDepth
-
- member private x.IsStencil =
- match x with
- | Texture _ -> false
- | Renderbuffer rb -> rb.Format.IsStencil
-
- member private x.IsDepthStencil =
- match x with
- | Texture t -> t.Format.IsDepthStencil
- | Renderbuffer rb -> rb.Format.IsDepthStencil
-
- member x.Attachment =
- if x.IsDepth then FramebufferAttachment.DepthAttachment
- elif x.IsStencil then FramebufferAttachment.StencilAttachment
- elif x.IsDepthStencil then FramebufferAttachment.DepthStencilAttachment
- else FramebufferAttachment.ColorAttachment0
-
- member x.Mask =
- if x.IsDepth then ClearBufferMask.DepthBufferBit
- elif x.IsStencil then ClearBufferMask.StencilBufferBit
- elif x.IsDepthStencil then ClearBufferMask.DepthBufferBit ||| ClearBufferMask.StencilBufferBit
- else ClearBufferMask.ColorBufferBit
-
- member x.WindowOffset(level : int, window : Box3i) =
- match x with
- | Image.Texture t -> t.WindowOffset(level, window)
- | Image.Renderbuffer rb -> window |> WindowOffset.flipY rb.Size.Y
-
- member x.WindowOffset(level : int, offset : V3i, size : V3i) =
- x.WindowOffset(level, Box3i.FromMinAndSize(offset, size))
-
- member x.WindowOffset(level : int, window : Box2i) =
- let window = Box3i(V3i(window.Min, 0), V3i(window.Max, 1))
- x .WindowOffset(level, window).XY
-
- member x.WindowOffset(level : int, offset : V2i, size : V2i) =
- x.WindowOffset(level, Box2i.FromMinAndSize(offset, size))
-
- module Image =
-
- /// Attaches an image to the framebuffer bound at the given framebuffer target
- let attach (framebufferTarget : FramebufferTarget) (level : int) (slice : int) (image : Image) =
- let attachment = image.Attachment
-
- match image with
- | Image.Texture texture ->
- let target = texture |> TextureTarget.ofTexture
- let targetSlice = target |> TextureTarget.toSliceTarget slice
-
- match texture.Dimension, texture.IsArray with
- | TextureDimension.Texture1D, true
- | TextureDimension.Texture2D, true
- | TextureDimension.TextureCube, true
- | TextureDimension.Texture3D, false ->
- GL.FramebufferTextureLayer(framebufferTarget, attachment, texture.Handle, level, slice)
-
- | TextureDimension.Texture1D, false ->
- GL.FramebufferTexture1D(framebufferTarget, attachment, targetSlice, texture.Handle, level)
-
- | TextureDimension.Texture2D, false
- | TextureDimension.TextureCube, false ->
- GL.FramebufferTexture2D(framebufferTarget, attachment, targetSlice, texture.Handle, level)
-
- | d, a ->
- failwithf "[GL] cannot attach %A%s to framebuffer" d (if a then "[]" else "")
-
- | Image.Renderbuffer renderBuffer ->
- GL.FramebufferRenderbuffer(framebufferTarget, attachment, RenderbufferTarget.Renderbuffer, renderBuffer.Handle)
-
- GL.Check "could not attach texture to framebuffer"
-
- /// Uses a framebuffer to the read the image layers of the given level from slice baseSlice to baseSlice + slices.
- let readLayers (image : Image) (level : int) (baseSlice : int) (slices : int) (f : int -> unit) =
- let attachment = image.Attachment
-
- let readBuffer =
- if attachment = FramebufferAttachment.ColorAttachment0 then ReadBufferMode.ColorAttachment0
- else ReadBufferMode.None
-
- Framebuffer.temporary FramebufferTarget.ReadFramebuffer (fun fbo ->
- GL.ReadBuffer(readBuffer)
- GL.Check "could not set buffer"
-
- try
- for slice = baseSlice to baseSlice + slices - 1 do
- image |> attach FramebufferTarget.ReadFramebuffer level slice
- Framebuffer.check FramebufferTarget.ReadFramebuffer
- f slice
-
- finally
- GL.ReadBuffer(ReadBufferMode.None)
- )
-
-
[]
module TextureCreationExtensions =
@@ -428,6 +340,27 @@ module TextureCreationExtensions =
type Context with
+ // ================================================================================================================
+ // SetDefaultTextureParams
+ // ================================================================================================================
+
+ member internal x.SetDefaultTextureParams(target : TextureTarget, format : TextureFormat, mipMapLevels : int) =
+ match target with
+ | TextureTarget.Texture2DMultisample
+ | TextureTarget.Texture2DMultisampleArray -> ()
+ | _ ->
+ // For gray scale textures, duplicate channel
+ if TextureFormat.toColFormat format = Col.Format.Gray then
+ GL.TexParameter(target, TextureParameterName.TextureSwizzleG, int PixelFormat.Red)
+ GL.TexParameter(target, TextureParameterName.TextureSwizzleB, int PixelFormat.Red)
+
+ GL.TexParameter(target, TextureParameterName.TextureMaxLevel, mipMapLevels - 1)
+ GL.TexParameter(target, TextureParameterName.TextureBaseLevel, 0)
+ GL.TexParameter(target, TextureParameterName.TextureWrapS, int TextureWrapMode.ClampToEdge)
+ GL.TexParameter(target, TextureParameterName.TextureWrapT, int TextureWrapMode.ClampToEdge)
+ GL.TexParameter(target, TextureParameterName.TextureMinFilter, int TextureMinFilter.Linear)
+ GL.TexParameter(target, TextureParameterName.TextureMagFilter, int TextureMagFilter.Linear)
+
// ================================================================================================================
// CreateTexture
// ================================================================================================================
@@ -437,7 +370,7 @@ module TextureCreationExtensions =
let isArray = slices > 0
if format = TextureFormat.StencilIndex8 && not GL.ARB_texture_stencil8 then
- failwithf "[GL] textures with format %A not supported" format
+ failf "textures with format %A not supported" format
match dim, isArray with
| TextureDimension.Texture1D, false -> x.CreateTexture1D(size.X, levels, format)
@@ -448,383 +381,222 @@ module TextureCreationExtensions =
| TextureDimension.Texture3D, true -> raise <| ArgumentException("3D textures cannot be arrayed")
| TextureDimension.TextureCube, false -> x.CreateTextureCube(size.X, levels, format)
| TextureDimension.TextureCube, true -> x.CreateTextureCubeArray(size.X, slices, levels, format)
- | _ -> failwith "[GL] Invalid texture dimension"
+ | _ -> failf "Invalid texture dimension"
)
member x.CreateTexture1D(size : int, mipMapLevels : int, format : TextureFormat) =
using x.ResourceLock (fun _ ->
- let h = GL.GenTexture()
- GL.Check "could not create texture"
-
- ResourceCounts.addTexture x 0L
- let tex = new Texture(x, h, TextureDimension.Texture1D, mipMapLevels, 1, V3i.Zero, None, format, 0L)
- x.UpdateTexture1D(tex, size, mipMapLevels, format)
-
- tex
- )
+ if size > x.MaxTextureSize.X then
+ failf $"cannot create 1D texture with size {size} (maximum is {x.MaxTextureSize.X})"
- member x.CreateTexture2D(size : V2i, mipMapLevels : int, format : TextureFormat, samples : int) =
- using x.ResourceLock (fun _ ->
let h = GL.GenTexture()
GL.Check "could not create texture"
- ResourceCounts.addTexture x 0L
- let tex = new Texture(x, h, TextureDimension.Texture2D, mipMapLevels, 1, V3i.Zero, None, format, 0L)
-
- x.UpdateTexture2D(tex, size, mipMapLevels, format, samples)
+ GL.BindTexture(TextureTarget.Texture1D, h)
+ GL.Check "could not bind texture"
- tex
- )
+ x.SetDefaultTextureParams(TextureTarget.Texture1D, format, mipMapLevels)
+ GL.Check "could not set default texture parameters"
- member x.CreateTexture3D(size : V3i, mipMapLevels : int, format : TextureFormat) =
- using x.ResourceLock (fun _ ->
- let h = GL.GenTexture()
- GL.Check "could not create texture"
+ GL.Dispatch.TexStorage1D(TextureTarget1d.Texture1D, mipMapLevels, TextureFormat.toSizedInternalFormat format, size)
+ GL.Check $"failed to allocate 1D texture storage (format = {format}, size = {size}, levels = {mipMapLevels})"
- ResourceCounts.addTexture x 0L
- let tex = new Texture(x, h, TextureDimension.Texture3D, mipMapLevels, 1, V3i.Zero, None, format, 0L)
- x.UpdateTexture3D(tex, size, mipMapLevels, format)
+ GL.BindTexture(TextureTarget.Texture1D, 0)
+ GL.Check "could not unbind texture"
+ let tex = new Texture(x, h, TextureDimension.Texture1D, mipMapLevels, 1, V3i(size, 1, 1), None, format)
+ ResourceCounts.addTexture x tex.SizeInBytes
tex
)
- member x.CreateTextureCube(size : int, mipMapLevels : int, format : TextureFormat) =
+ member x.CreateTexture2D(size : V2i, mipMapLevels : int, format : TextureFormat, samples : int) =
using x.ResourceLock (fun _ ->
- let h = GL.GenTexture()
- GL.Check "could not create texture"
+ if Vec.anyGreater size x.MaxTextureSize then
+ failf $"cannot create 2D texture with size {size} (maximum is {x.MaxTextureSize})"
- ResourceCounts.addTexture x 0L
- let tex = new Texture(x, h, TextureDimension.TextureCube, mipMapLevels, 1, V3i(size, size, 0), None, format, 0L)
- x.UpdateTextureCube(tex, size, mipMapLevels, format)
+ let samples =
+ if samples <= 1 then 1
+ else Image.validateSampleCount x ImageTarget.Texture2DMultisample format samples
- tex
- )
+ let target =
+ if samples = 1 then TextureTarget.Texture2D
+ else TextureTarget.Texture2DMultisample
- member x.CreateTexture1DArray(size : int, count : int, mipMapLevels : int, format : TextureFormat) =
- using x.ResourceLock (fun _ ->
let h = GL.GenTexture()
GL.Check "could not create texture"
- ResourceCounts.addTexture x 0L
- let tex = new Texture(x, h, TextureDimension.Texture1D, mipMapLevels, 1, V3i.Zero, Some count, format, 0L)
- x.UpdateTexture1DArray(tex, size, count, mipMapLevels, format)
+ GL.BindTexture(target, h)
+ GL.Check "could not bind texture"
- tex
- )
+ x.SetDefaultTextureParams(target, format, mipMapLevels)
+ GL.Check "could not set default texture parameters"
- member x.CreateTexture2DArray(size : V2i, count : int, mipMapLevels : int, format : TextureFormat, samples : int) =
- using x.ResourceLock (fun _ ->
- let h = GL.GenTexture()
- GL.Check "could not create texture"
+ let ifmt = TextureFormat.toSizedInternalFormat format
- ResourceCounts.addTexture x 0L
- let tex = new Texture(x, h, TextureDimension.Texture2D, mipMapLevels, 1, V3i.Zero, Some count, format, 0L)
+ if samples = 1 then
+ GL.Dispatch.TexStorage2D(unbox target, mipMapLevels, ifmt, size.X, size.Y)
+ GL.Check $"failed to allocate 2D texture storage (format = {format}, size = {size}, levels = {mipMapLevels})"
+ else
+ GL.Dispatch.TexStorage2DMultisample(unbox target, samples, ifmt, size.X, size.Y, true)
+ GL.Check $"failed to allocate 2D texture storage (format = {format}, size = {size}, samples = {samples})"
- x.UpdateTexture2DArray(tex, size, count, mipMapLevels, format, samples)
+ GL.BindTexture(target, 0)
+ GL.Check "could not unbind texture"
+ let tex = new Texture(x, h, TextureDimension.Texture2D, mipMapLevels, samples, size.XYI, None, format)
+ ResourceCounts.addTexture x tex.SizeInBytes
tex
)
- member x.CreateTextureCubeArray(size : int, count : int, mipMapLevels : int, format : TextureFormat) =
+ member x.CreateTexture3D(size : V3i, mipMapLevels : int, format : TextureFormat) =
using x.ResourceLock (fun _ ->
+ if Vec.anyGreater size x.MaxTextureSize3D then
+ failf $"cannot create 3D texture with size {size} (maximum is {x.MaxTextureSize3D})"
+
let h = GL.GenTexture()
GL.Check "could not create texture"
- ResourceCounts.addTexture x 0L
- let tex = new Texture(x, h, TextureDimension.TextureCube, mipMapLevels, 1, V3i(size, size, 0), Some count, format, 0L)
- x.UpdateTextureCubeArray(tex, size, count, mipMapLevels, format)
-
- tex
- )
-
-
- // ================================================================================================================
- // AllocateTexture
- // ================================================================================================================
-
- member internal x.SetDefaultTextureParams(target : TextureTarget, format : TextureFormat, mipMapLevels : int) =
- // For gray scale textures, duplicate channel
- if TextureFormat.toColFormat format = Col.Format.Gray then
- GL.TexParameter(target, TextureParameterName.TextureSwizzleG, int PixelFormat.Red)
- GL.TexParameter(target, TextureParameterName.TextureSwizzleB, int PixelFormat.Red)
-
- match target with
- | TextureTarget.Texture2DMultisample
- | TextureTarget.Texture2DMultisampleArray -> ()
- | _ ->
- GL.TexParameter(target, TextureParameterName.TextureMaxLevel, mipMapLevels - 1)
- GL.TexParameter(target, TextureParameterName.TextureBaseLevel, 0)
- GL.TexParameter(target, TextureParameterName.TextureWrapS, int TextureWrapMode.ClampToEdge)
- GL.TexParameter(target, TextureParameterName.TextureWrapT, int TextureWrapMode.ClampToEdge)
- GL.TexParameter(target, TextureParameterName.TextureMinFilter, int TextureMinFilter.Linear)
- GL.TexParameter(target, TextureParameterName.TextureMagFilter, int TextureMagFilter.Linear)
-
- member private x.ValidateAndAllocateTexture(target : TextureTarget, size : 'T, create : TextureTarget -> unit) =
- // Allocate using proxy target
- let proxyTarget = TextureTarget.toProxy target
- create proxyTarget
-
- // Check for success
- let mutable width = 0
- GL.GetTexLevelParameter(proxyTarget, 0, GetTextureParameter.TextureWidth, &width)
- GL.Check "could not get texture parameter"
-
- if width = 0 then
- failwithf "[GL] cannot create a texture with size %A as it exceeds device limits" size
-
- // Allocate for real using regular target
- create target
-
- member inline private x.AllocateTexture1D(target : TextureTarget, mipMapLevels : int, format : SizedInternalFormat, size : int) =
- x.SetDefaultTextureParams(target, unbox format, mipMapLevels)
-
- x.ValidateAndAllocateTexture(target, size, fun target ->
- GL.Dispatch.TexStorage1D(unbox target, mipMapLevels, format, size)
- )
-
- member inline private x.AllocateTexture2D(target : TextureTarget, mipMapLevels : int, format : SizedInternalFormat, size : V2i) =
- x.SetDefaultTextureParams(target, unbox format, mipMapLevels)
-
- x.ValidateAndAllocateTexture(target, size, fun target ->
- GL.Dispatch.TexStorage2D(unbox target, mipMapLevels, format, size.X, size.Y)
- )
-
- member inline private x.AllocateTexture2DMultisample(target : TextureTarget, samples : int,
- format : SizedInternalFormat, size : V2i, fixedSampleLocations : bool) =
- x.SetDefaultTextureParams(target, unbox format, 1)
-
- x.ValidateAndAllocateTexture(target, size, fun target ->
- GL.Dispatch.TexStorage2DMultisample(unbox target, samples, format, size.X, size.Y, fixedSampleLocations)
- )
-
- member inline private x.AllocateTexture3D(target : TextureTarget, mipMapLevels : int, format : SizedInternalFormat, size : V3i) =
- x.SetDefaultTextureParams(target, unbox format, mipMapLevels)
-
- x.ValidateAndAllocateTexture(target, size, fun target ->
- GL.Dispatch.TexStorage3D(unbox target, mipMapLevels, format, size.X, size.Y, size.Z)
- )
-
- member inline private x.AllocateTexture3DMultisample(target : TextureTarget, samples : int,
- format : SizedInternalFormat, size : V3i, fixedSampleLocations : bool) =
- x.SetDefaultTextureParams(target, unbox format, 1)
-
- x.ValidateAndAllocateTexture(target, size, fun target ->
- GL.Dispatch.TexStorage3DMultisample(unbox target, samples, format, size.X, size.Y, size.Z, fixedSampleLocations)
- )
-
-
- // ================================================================================================================
- // UpdateTexture
- // ================================================================================================================
-
- member private x.UpdateTexture1D(tex : Texture, size : int, mipMapLevels : int, format : TextureFormat) =
- using x.ResourceLock (fun _ ->
- GL.BindTexture(TextureTarget.Texture1D, tex.Handle)
+ GL.BindTexture(TextureTarget.Texture3D, h)
GL.Check "could not bind texture"
- x.AllocateTexture1D(TextureTarget.Texture1D, mipMapLevels, TextureFormat.toSizedInternalFormat format, size)
- GL.Check "could not allocate texture"
+ x.SetDefaultTextureParams(TextureTarget.Texture3D, format, mipMapLevels)
+ GL.Check "could not set default texture parameters"
- GL.BindTexture(TextureTarget.Texture1D, 0)
- GL.Check "could not unbind texture"
+ GL.Dispatch.TexStorage3D(TextureTarget3d.Texture3D, mipMapLevels, TextureFormat.toSizedInternalFormat format, size.X, size.Y, size.Z)
+ GL.Check $"failed to allocate 3D texture storage (format = {format}, size = {size}, levels = {mipMapLevels})"
- let sizeInBytes = ResourceCounts.texSizeInBytes(V3i(size, 1, 1), format, 1, mipMapLevels)
- ResourceCounts.updateTexture tex.Context tex.SizeInBytes sizeInBytes
- tex.SizeInBytes <- sizeInBytes
+ GL.BindTexture(TextureTarget.Texture3D, 0)
+ GL.Check "could not unbind texture"
- tex.MipMapLevels <- mipMapLevels
- tex.Dimension <- TextureDimension.Texture1D
- tex.Size <- V3i(size, 1, 1)
- tex.Format <- format
+ let tex = new Texture(x, h, TextureDimension.Texture3D, mipMapLevels, 1, size, None, format)
+ ResourceCounts.addTexture x tex.SizeInBytes
+ tex
)
- member private x.UpdateTexture2D(tex : Texture, size : V2i, mipMapLevels : int, format : TextureFormat, samples : int) =
- let ifmt = TextureFormat.toSizedInternalFormat format
-
+ member x.CreateTextureCube(size : int, mipMapLevels : int, format : TextureFormat) =
using x.ResourceLock (fun _ ->
- let target =
- if samples = 1 then TextureTarget.Texture2D
- else TextureTarget.Texture2DMultisample
+ if size > x.MaxTextureSizeCube then
+ failf $"cannot create cube texture with size {size} (maximum is {x.MaxTextureSizeCube})"
- let samples =
- if samples > 1 then
- let counts = x.GetFormatSamples(unbox target, format)
- if counts.Contains samples then samples
- else
- let max = Set.maxElement counts
- Log.warn "[GL] cannot create %A texture with %d samples (using %d instead)" format samples max
- max
- else
- 1
+ let h = GL.GenTexture()
+ GL.Check "could not create texture"
- GL.BindTexture(target, tex.Handle)
+ GL.BindTexture(TextureTarget.TextureCubeMap, h)
GL.Check "could not bind texture"
- if samples = 1 then
- x.AllocateTexture2D(target, mipMapLevels, ifmt, size)
- else
- x.AllocateTexture2DMultisample(target, samples, ifmt, size, true)
+ x.SetDefaultTextureParams(TextureTarget.TextureCubeMap, format, mipMapLevels)
+ GL.Check "could not set default texture parameters"
- GL.Check "could not allocate texture"
+ GL.Dispatch.TexStorage2D(TextureTarget2d.TextureCubeMap, mipMapLevels, TextureFormat.toSizedInternalFormat format, size, size)
+ GL.Check $"failed to allocate cube texture storage (format = {format}, size = {size}, levels = {mipMapLevels})"
- GL.BindTexture(target, 0)
+ GL.BindTexture(TextureTarget.TextureCubeMap, 0)
GL.Check "could not unbind texture"
- let sizeInBytes = ResourceCounts.texSizeInBytes(size.XYI, format, samples, mipMapLevels)
- ResourceCounts.updateTexture tex.Context tex.SizeInBytes sizeInBytes
- tex.SizeInBytes <- sizeInBytes
-
- tex.MipMapLevels <- mipMapLevels
- tex.Dimension <- TextureDimension.Texture2D
- tex.Multisamples <- samples
- tex.Count <- 1
- tex.Size <- V3i(size.X, size.Y, 1)
- tex.Format <- format
+ let tex = new Texture(x, h, TextureDimension.TextureCube, mipMapLevels, 1, V3i(size, size, 1), None, format)
+ ResourceCounts.addTexture x tex.SizeInBytes
+ tex
)
- member private x.UpdateTexture3D(tex : Texture, size : V3i, mipMapLevels : int, format : TextureFormat) =
- let ifmt = TextureFormat.toSizedInternalFormat format
-
+ member x.CreateTexture1DArray(size : int, count : int, mipMapLevels : int, format : TextureFormat) =
using x.ResourceLock (fun _ ->
- GL.BindTexture(TextureTarget.Texture3D, tex.Handle)
- GL.Check "could not bind texture"
-
- x.AllocateTexture3D(TextureTarget.Texture3D, mipMapLevels, ifmt, size)
- GL.Check "could not allocate texture"
-
- GL.BindTexture(TextureTarget.Texture3D, 0)
- GL.Check "could not unbind texture"
+ if size > x.MaxTextureSize.X then
+ failf $"cannot create 1D array texture with size {size} (maximum is {x.MaxTextureSize.X})"
- let sizeInBytes = ResourceCounts.texSizeInBytes(size, format, 1, mipMapLevels)
- ResourceCounts.updateTexture tex.Context tex.SizeInBytes sizeInBytes
- tex.SizeInBytes <- sizeInBytes
+ if count > x.MaxTextureArrayLayers then
+ failf $"cannot create 1D array texture with {count} layers (maximum is {x.MaxTextureArrayLayers})"
- tex.MipMapLevels <- mipMapLevels
- tex.Dimension <- TextureDimension.Texture3D
- tex.Count <- 1
- tex.Multisamples <- 1
- tex.Size <- size
- tex.Format <- format
- )
-
- member private x.UpdateTextureCube(tex : Texture, size : int, mipMapLevels : int, format : TextureFormat) =
- let ifmt = TextureFormat.toSizedInternalFormat format
+ let h = GL.GenTexture()
+ GL.Check "could not create texture"
- using x.ResourceLock (fun _ ->
- GL.BindTexture(TextureTarget.TextureCubeMap, tex.Handle)
+ GL.BindTexture(TextureTarget.Texture1DArray, h)
GL.Check "could not bind texture"
- x.AllocateTexture2D(TextureTarget.TextureCubeMap, mipMapLevels, ifmt, V2i(size))
- GL.Check "could not allocate texture"
+ x.SetDefaultTextureParams(TextureTarget.Texture1DArray, format, mipMapLevels)
+ GL.Check "could not set default texture parameters"
- GL.BindTexture(TextureTarget.TextureCubeMap, 0)
- GL.Check "could not unbind texture"
+ GL.Dispatch.TexStorage2D(TextureTarget2d.Texture1DArray, mipMapLevels, TextureFormat.toSizedInternalFormat format, size, count)
+ GL.Check $"failed to allocate 1D array texture storage (format = {format}, size = {size}, count = {count}, levels = {mipMapLevels})"
- let sizeInBytes = ResourceCounts.texSizeInBytes(V3i(size, size, 1), format, 1, mipMapLevels) * 6L
- ResourceCounts.updateTexture tex.Context tex.SizeInBytes sizeInBytes
- tex.SizeInBytes <- sizeInBytes
+ GL.BindTexture(TextureTarget.Texture1DArray, 0)
+ GL.Check "could not unbind texture"
- tex.MipMapLevels <- mipMapLevels
- tex.Dimension <- TextureDimension.TextureCube
- tex.Size <- V3i(size, size, 1)
- tex.Count <- 1
- tex.Format <- format
+ let tex = new Texture(x, h, TextureDimension.Texture1D, mipMapLevels, 1, V3i(size, 1, 1), Some count, format)
+ ResourceCounts.addTexture x tex.SizeInBytes
+ tex
)
- member private x.UpdateTexture1DArray(tex : Texture, size : int, count : int, mipMapLevels : int, format : TextureFormat) =
- let ifmt = TextureFormat.toSizedInternalFormat format
-
+ member x.CreateTexture2DArray(size : V2i, count : int, mipMapLevels : int, format : TextureFormat, samples : int) =
using x.ResourceLock (fun _ ->
- GL.BindTexture(TextureTarget.Texture1DArray, tex.Handle)
- GL.Check "could not bind texture"
+ if Vec.anyGreater size x.MaxTextureSize then
+ failf $"cannot create 2D array texture with size {size} (maximum is {x.MaxTextureSize})"
- x.AllocateTexture2D(TextureTarget.Texture1DArray, mipMapLevels, ifmt, V2i(size, count))
- GL.Check "could not allocate texture"
+ if count > x.MaxTextureArrayLayers then
+ failf $"cannot create 2D array texture with {count} layers (maximum is {x.MaxTextureArrayLayers})"
- GL.BindTexture(TextureTarget.Texture1DArray, 0)
- GL.Check "could not unbind texture"
-
- let sizeInBytes = ResourceCounts.texSizeInBytes(V3i(size, 1, 1), format, 1, mipMapLevels) * (int64 count)
- ResourceCounts.updateTexture tex.Context tex.SizeInBytes sizeInBytes
- tex.SizeInBytes <- sizeInBytes
-
- tex.IsArray <- true
- tex.MipMapLevels <- mipMapLevels
- tex.Dimension <- TextureDimension.Texture1D
- tex.Count <- count
- tex.Multisamples <- 1
- tex.Size <- V3i(size, 1, 1)
- tex.Format <- format
- )
+ let samples =
+ if samples <= 1 then 1
+ else Image.validateSampleCount x ImageTarget.Texture2DMultisampleArray format samples
- member private x.UpdateTexture2DArray(tex : Texture, size : V2i, count : int, mipMapLevels : int, format : TextureFormat, samples : int) =
- using x.ResourceLock (fun _ ->
let target =
if samples = 1 then TextureTarget.Texture2DArray
else TextureTarget.Texture2DMultisampleArray
- let samples =
- if samples > 1 then
- let counts = x.GetFormatSamples(unbox target, format)
- if counts.Contains samples then samples
- else
- let max = Set.maxElement counts
- Log.warn "[GL] cannot create %A texture with %d samples (using %d instead)" format samples max
- max
- else
- 1
-
- let ifmt = TextureFormat.toSizedInternalFormat format
+ let h = GL.GenTexture()
+ GL.Check "could not create texture"
- GL.BindTexture(target, tex.Handle)
+ GL.BindTexture(target, h)
GL.Check "could not bind texture"
+ x.SetDefaultTextureParams(unbox target, format, mipMapLevels)
+ GL.Check "could not set default texture parameters"
+
+ let ifmt = TextureFormat.toSizedInternalFormat format
+
if samples = 1 then
- x.AllocateTexture3D(TextureTarget.Texture2DArray, mipMapLevels, ifmt, V3i(size.X, size.Y, count))
+ GL.Dispatch.TexStorage3D(unbox target, mipMapLevels, ifmt, size.X, size.Y, count)
+ GL.Check $"failed to allocate 2D array texture storage (format = {format}, size = {size}, count = {count}, levels = {mipMapLevels})"
else
- x.AllocateTexture3DMultisample(TextureTarget.Texture2DMultisampleArray, samples, ifmt, V3i(size.X, size.Y, count), true)
-
- GL.Check "could not allocate texture"
+ GL.Dispatch.TexStorage3DMultisample(unbox target, samples, ifmt, size.X, size.Y, count, true)
+ GL.Check $"failed to allocate 2D array texture storage (format = {format}, size = {size}, count = {count}, samples = {samples})"
GL.BindTexture(target, 0)
GL.Check "could not unbind texture"
- let sizeInBytes = ResourceCounts.texSizeInBytes(size.XYI, format, samples, mipMapLevels) * (int64 count)
- ResourceCounts.updateTexture tex.Context tex.SizeInBytes sizeInBytes
- tex.SizeInBytes <- sizeInBytes
-
- tex.MipMapLevels <- mipMapLevels
- tex.Dimension <- TextureDimension.Texture2D
- tex.IsArray <- true
- tex.Count <- count
- tex.Multisamples <- samples
- tex.Size <- V3i(size.X, size.Y, 1)
- tex.Format <- format
+ let tex = new Texture(x, h, TextureDimension.Texture2D, mipMapLevels, samples, size.XYI, Some count, format)
+ ResourceCounts.addTexture x tex.SizeInBytes
+ tex
)
- member private x.UpdateTextureCubeArray(tex : Texture, size : int, count : int, mipMapLevels : int, format : TextureFormat) =
+ member x.CreateTextureCubeArray(size : int, count : int, mipMapLevels : int, format : TextureFormat) =
using x.ResourceLock (fun _ ->
- GL.BindTexture(TextureTarget.TextureCubeMapArray, tex.Handle)
+ if size > x.MaxTextureSizeCube then
+ failf $"cannot create cube array texture with size {size} (maximum is {x.MaxTextureSizeCube})"
+
+ if count > x.MaxTextureArrayLayers then
+ failf $"cannot create cube array texture with {count} layers (maximum is {x.MaxTextureArrayLayers})"
+
+ let h = GL.GenTexture()
+ GL.Check "could not create texture"
+
+ GL.BindTexture(TextureTarget.TextureCubeMapArray, h)
GL.Check "could not bind texture"
- x.AllocateTexture3D(TextureTarget.TextureCubeMapArray, mipMapLevels, TextureFormat.toSizedInternalFormat format, V3i(size, size, count * 6))
- GL.Check "could not allocate texture"
+ GL.Dispatch.TexStorage3D(TextureTarget3d.TextureCubeMapArray, mipMapLevels, TextureFormat.toSizedInternalFormat format, size, size, count * 6)
+ GL.Check $"failed to allocate cube array texture storage (format = {format}, size = {size}, count = {count}, levels = {mipMapLevels})"
GL.BindTexture(TextureTarget.TextureCubeMapArray, 0)
GL.Check "could not unbind texture"
- let sizeInBytes = ResourceCounts.texSizeInBytes(V3i(size, size, 1), format, 1, mipMapLevels) * 6L * (int64 count)
- ResourceCounts.updateTexture tex.Context tex.SizeInBytes sizeInBytes
- tex.SizeInBytes <- sizeInBytes
-
- tex.MipMapLevels <- mipMapLevels
- tex.Dimension <- TextureDimension.TextureCube
- tex.IsArray <- true
- tex.Count <- count
- tex.Size <- V3i(size, size, 1)
- tex.Format <- format
+ let tex = new Texture(x, h, TextureDimension.TextureCube, mipMapLevels, 1, V3i(size, size, 1), Some count, format)
+ ResourceCounts.addTexture x tex.SizeInBytes
+ tex
)
+ // ================================================================================================================
+ // CreateTextureView
+ // ================================================================================================================
+
member x.CreateTextureView(orig : Texture, levels : Range1i, slices : Range1i, isArray : bool) =
using x.ResourceLock (fun _ ->
let handle = GL.GenTexture()
diff --git a/src/Aardvark.Rendering.GL/Runtime/Compute.fs b/src/Aardvark.Rendering.GL/Runtime/Compute.fs
index 902eaced..7025ab52 100644
--- a/src/Aardvark.Rendering.GL/Runtime/Compute.fs
+++ b/src/Aardvark.Rendering.GL/Runtime/Compute.fs
@@ -453,14 +453,14 @@ module internal ComputeTaskInternals =
if GL.ARB_direct_state_access then
s.ClearNamedBufferSubData(
buffer.Handle, PixelInternalFormat.R32ui,
- range.Offset, range.SizeInBytes, PixelFormat.Red, PixelType.UnsignedInt,
+ range.Offset, range.SizeInBytes, PixelFormat.RedInteger, PixelType.UnsignedInt,
pValue
)
else
s.BindBuffer(BufferTarget.CopyWriteBuffer, buffer.Handle)
s.ClearBufferSubData(
BufferTarget.CopyWriteBuffer, PixelInternalFormat.R32ui,
- range.Offset, range.SizeInBytes, PixelFormat.Red, PixelType.UnsignedInt,
+ range.Offset, range.SizeInBytes, PixelFormat.RedInteger, PixelType.UnsignedInt,
pValue
)
)
diff --git a/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs b/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs
index beb6a9e0..c9ec0e4f 100644
--- a/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs
+++ b/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs
@@ -1234,6 +1234,8 @@ module GeometryPoolData =
NativePtr.free mem
ctx.Delete buffer
if bounds then
+ culling.Dispose()
+ boxShader.Dispose()
NativePtr.free bmem
ctx.Delete bbuffer
capacity <- 0
diff --git a/src/Aardvark.Rendering.GL/Runtime/Runtime.fs b/src/Aardvark.Rendering.GL/Runtime/Runtime.fs
index 2195d688..2faf886e 100644
--- a/src/Aardvark.Rendering.GL/Runtime/Runtime.fs
+++ b/src/Aardvark.Rendering.GL/Runtime/Runtime.fs
@@ -51,10 +51,10 @@ type Runtime(debug : IDebugConfig) =
// GL_CONTEXT_CORE_PROFILE_BIT 1
// GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 2
- let profileType = if driver.profileMask = 1 then " Core" elif driver.profileMask = 2 then " Compatibility" else ""
+ let profileType = if driver.profileMask = 1 then "Core" elif driver.profileMask = 2 then "Compatibility" else ""
- Log.line "vendor: %A" driver.vendor
- Log.line "renderer: %A" driver.renderer
+ Log.line "vendor: %s" driver.vendor
+ Log.line "renderer: %s" driver.renderer
Log.line "version: OpenGL %A / GLSL %A %s" driver.version driver.glsl profileType
let major = driver.version.Major
@@ -401,7 +401,7 @@ type Runtime(debug : IDebugConfig) =
member x.OnDispose = onDispose.Publish
member x.AssembleModule (effect : Effect, signature : IFramebufferSignature, topology : IndexedGeometryMode) =
- signature.Link(effect, Range1d(-1.0, 1.0), false, topology)
+ signature.Link(effect, Range1d(-1.0, 1.0), false, topology, not x.Context.SupportsLayeredEffects)
member x.ResourceManager = manager
diff --git a/src/Aardvark.Rendering.Vulkan/Core/Flags.fs b/src/Aardvark.Rendering.Vulkan/Core/Flags.fs
index edec9fdb..3f3afde3 100644
--- a/src/Aardvark.Rendering.Vulkan/Core/Flags.fs
+++ b/src/Aardvark.Rendering.Vulkan/Core/Flags.fs
@@ -372,4 +372,11 @@ module VkExternalMemoryHandleTypeFlags =
if isWindows then
VkExternalMemoryHandleTypeFlags.OpaqueWin32Bit
else
- VkExternalMemoryHandleTypeFlags.OpaqueFdBit
\ No newline at end of file
+ VkExternalMemoryHandleTypeFlags.OpaqueFdBit
+
+[]
+module VkExternalMemoryPropertiesExtensions =
+ open Vulkan11
+
+ type VkExternalMemoryProperties with
+ member inline x.IsExportable = x.externalMemoryFeatures.HasFlag VkExternalMemoryFeatureFlags.ExportableBit
\ No newline at end of file
diff --git a/src/Aardvark.Rendering.Vulkan/Core/Platform.fs b/src/Aardvark.Rendering.Vulkan/Core/Platform.fs
index 8f34a2f0..8f9cebfd 100644
--- a/src/Aardvark.Rendering.Vulkan/Core/Platform.fs
+++ b/src/Aardvark.Rendering.Vulkan/Core/Platform.fs
@@ -660,31 +660,34 @@ and PhysicalDevice internal(instance : Instance, handle : VkPhysicalDevice) =
| VkImageTiling.Linear -> formatProperties.[fmt].linearTilingFeatures
| _ -> formatProperties.[fmt].optimalTilingFeatures
- member private x.GetImageProperties(format : VkFormat, typ : VkImageType, tiling : VkImageTiling, usage : VkImageUsageFlags, flags : VkImageCreateFlags) =
- let key = (format, typ, tiling, usage, flags)
+ member internal x.GetImageProperties(format : VkFormat, typ : VkImageType, tiling : VkImageTiling, usage : VkImageUsageFlags,
+ flags : VkImageCreateFlags, external : VkExternalMemoryHandleTypeFlags) =
+ let key = (format, typ, tiling, usage, flags, external)
imageFormatProperties.GetOrCreate(key, fun _ ->
native {
let! pExternalImageFormatInfo =
- VkPhysicalDeviceExternalImageFormatInfo VkExternalMemoryHandleTypeFlags.OpaqueBit
+ VkPhysicalDeviceExternalImageFormatInfo external
let! pImageFormatInfo =
- VkPhysicalDeviceImageFormatInfo2(
- NativePtr.toNativeInt pExternalImageFormatInfo,
- format, typ, tiling, usage, flags
- )
+ let pNext =
+ if external = VkExternalMemoryHandleTypeFlags.None then 0n
+ else NativePtr.toNativeInt pExternalImageFormatInfo
+
+ VkPhysicalDeviceImageFormatInfo2(pNext, format, typ, tiling, usage, flags)
let! pExternalImageFormatProperties =
VkExternalImageFormatProperties.Empty
let! pImageFormatProperties =
- VkImageFormatProperties2(
- NativePtr.toNativeInt pExternalImageFormatProperties,
- VkImageFormatProperties.Empty
- )
+ let pNext =
+ if external = VkExternalMemoryHandleTypeFlags.None then 0n
+ else NativePtr.toNativeInt pExternalImageFormatProperties
+
+ VkImageFormatProperties2(pNext, VkImageFormatProperties.Empty)
VkRaw.vkGetPhysicalDeviceImageFormatProperties2(x.Handle, pImageFormatInfo, pImageFormatProperties)
- |> check "could not query image format properties"
+ |> check $"could not query image format properties (format = {format}, type = {typ}, tiling = {tiling}, usage = {usage}, flags = {flags}, external = {external})"
let imageFormatProperties = (!!pImageFormatProperties).imageFormatProperties
let externalMemoryProperties = (!!pExternalImageFormatProperties).externalMemoryProperties
@@ -693,11 +696,11 @@ and PhysicalDevice internal(instance : Instance, handle : VkPhysicalDevice) =
)
member x.GetImageFormatProperties(format : VkFormat, typ : VkImageType, tiling : VkImageTiling, usage : VkImageUsageFlags, flags : VkImageCreateFlags) =
- fst <| x.GetImageProperties(format, typ, tiling, usage, flags)
+ fst <| x.GetImageProperties(format, typ, tiling, usage, flags, VkExternalMemoryHandleTypeFlags.None)
member x.GetImageExportable(format : VkFormat, typ : VkImageType, tiling : VkImageTiling, usage : VkImageUsageFlags, flags : VkImageCreateFlags) =
- let _, externalMemoryProperties = x.GetImageProperties(format, typ, tiling, usage, flags)
- externalMemoryProperties.externalMemoryFeatures.HasFlag VkExternalMemoryFeatureFlags.ExportableBit
+ let _, externalMemoryProperties = x.GetImageProperties(format, typ, tiling, usage, flags, VkExternalMemoryHandleTypeFlags.OpaqueBit)
+ externalMemoryProperties.IsExportable
member x.GetBufferFormatFeatures(fmt : VkFormat) =
formatProperties.[fmt].bufferFeatures
@@ -721,7 +724,7 @@ and PhysicalDevice internal(instance : Instance, handle : VkPhysicalDevice) =
member x.GetBufferExportable(flags : VkBufferCreateFlags, usage : VkBufferUsageFlags) =
let properties = x.GetExternalBufferProperties(flags, usage)
- properties.externalMemoryProperties.externalMemoryFeatures.HasFlag VkExternalMemoryFeatureFlags.ExportableBit
+ properties.externalMemoryProperties.IsExportable
member x.AvailableLayers = availableLayers
member x.GlobalExtensions : ExtensionInfo[] = globalExtensions
diff --git a/src/Aardvark.Rendering.Vulkan/Core/Utilities.fs b/src/Aardvark.Rendering.Vulkan/Core/Utilities.fs
index 614cc0ee..5c7892ab 100644
--- a/src/Aardvark.Rendering.Vulkan/Core/Utilities.fs
+++ b/src/Aardvark.Rendering.Vulkan/Core/Utilities.fs
@@ -10,6 +10,15 @@ open Aardvark.Rendering
#nowarn "9"
+type VulkanException(error : VkResult, message : string, [] innerException : Exception) =
+ inherit Exception(message, innerException)
+
+ new(message : string, [] innerException : Exception) =
+ VulkanException(VkResult.ErrorUnknown, message, innerException)
+
+ member x.Error = error
+ override x.Message = $"{message} (Error: {error})"
+
[]
module private Utilities =
@@ -130,21 +139,26 @@ module private Utilities =
}
let check (str : string) (err : VkResult) =
- if err <> VkResult.Success then
- Log.error "[Vulkan] %s (%A)" str err
- failwithf "[Vulkan] %s (%A)" str err
+ if err <> VkResult.Success then
+ let msg =
+ if String.IsNullOrEmpty str then "An error occurred"
+ else string (Char.ToUpper str.[0]) + str.Substring(1)
+
+ Report.Error $"[Vulkan] {msg} (Error: {err})"
+ raise <| VulkanException(err, msg)
let checkf (fmt : Printf.StringFormat<'a, VkResult -> unit>) =
- Printf.kprintf (fun (str : string) (res : VkResult) ->
- if res <> VkResult.Success then
- Log.error "[Vulkan] %s (%A)" str res
- failwithf "[Vulkan] %s (%A)" str res
- ) fmt
+ Printf.kprintf check fmt
let inline failf fmt =
- Printf.kprintf (fun str ->
- Log.error $"[Vulkan] {str}"
- failwith ("[Vulkan] " + str)
+ Printf.kprintf (fun str ->
+ let str =
+ if String.IsNullOrEmpty str then "An error occurred"
+ else string (Char.ToUpper str.[0]) + str.Substring(1)
+
+ let msg = $"[Vulkan] {str}"
+ Report.Error msg
+ failwith msg
) fmt
module Map =
diff --git a/src/Aardvark.Rendering.Vulkan/Resources/Image/Image.fs b/src/Aardvark.Rendering.Vulkan/Resources/Image/Image.fs
index 383cbde1..42865620 100644
--- a/src/Aardvark.Rendering.Vulkan/Resources/Image/Image.fs
+++ b/src/Aardvark.Rendering.Vulkan/Resources/Image/Image.fs
@@ -103,6 +103,7 @@ module VkImageAspectFlags =
type ImageExportMode =
| None
| Export of preferArray: bool
+ member inline x.Enabled = x <> None
type Image =
class
@@ -1059,8 +1060,20 @@ module Image =
let usage =
usage |> VkImageUsageFlags.filterSupported features
- let properties =
- device.PhysicalDevice.GetImageFormatProperties(fmt, typ, VkImageTiling.Optimal, usage, flags)
+ let properties, externalMemoryProperties =
+ let external =
+ if export.Enabled then
+ if not <| device.IsExtensionEnabled ExternalMemory.Extension then
+ failf $"Cannot export image memory because {ExternalMemory.Extension} is not supported"
+
+ VkExternalMemoryHandleTypeFlags.OpaqueBit
+ else
+ VkExternalMemoryHandleTypeFlags.None
+
+ device.PhysicalDevice.GetImageProperties(fmt, typ, VkImageTiling.Optimal, usage, flags, external)
+
+ if export.Enabled && not externalMemoryProperties.IsExportable then
+ failf $"Cannot export {fmt} image with usage {usage}"
let maxExtent = V3l.OfExtent properties.maxExtent
@@ -1085,19 +1098,11 @@ module Image =
let! pExternalMemoryInfo =
VkExternalMemoryImageCreateInfo VkExternalMemoryHandleTypeFlags.OpaqueBit
- let pNext =
- if export <> ImageExportMode.None then
- if device.IsExtensionEnabled ExternalMemory.Extension then
- if device.PhysicalDevice.GetImageExportable(fmt, typ, VkImageTiling.Optimal, usage, flags) then
- NativePtr.toNativeInt pExternalMemoryInfo
- else
- failf $"Cannot export {fmt} image with usage {usage}"
- else
- failf $"Cannot export image memory because {ExternalMemory.Extension} is not supported"
- else
- 0n
-
let! pInfo =
+ let pNext =
+ if export.Enabled then NativePtr.toNativeInt pExternalMemoryInfo
+ else 0n
+
VkImageCreateInfo(
pNext,
flags,
diff --git a/src/Aardvark.Rendering.Vulkan/Swapchain/SwapchainDescription.fs b/src/Aardvark.Rendering.Vulkan/Swapchain/SwapchainDescription.fs
index c9cdd776..5d2216ea 100644
--- a/src/Aardvark.Rendering.Vulkan/Swapchain/SwapchainDescription.fs
+++ b/src/Aardvark.Rendering.Vulkan/Swapchain/SwapchainDescription.fs
@@ -135,7 +135,8 @@ type GraphicsMode(format : Col.Format, bits : int, depthBits : int, stencilBits
modes |> Seq.maxBy presentModeScore
override x.ChooseBufferCount(min, max) =
- clamp min max buffers
+ if max = 0 then Fun.Max(min, buffers)
+ else clamp min max buffers
type SwapchainDescription =
{
diff --git a/src/Aardvark.Rendering/Effects/Interop/FShade.fs b/src/Aardvark.Rendering/Effects/Interop/FShade.fs
index 113d5321..2a9f128d 100644
--- a/src/Aardvark.Rendering/Effects/Interop/FShade.fs
+++ b/src/Aardvark.Rendering/Effects/Interop/FShade.fs
@@ -437,8 +437,7 @@ module FShadeInterop =
flipHandedness = flip
}
-
- member x.Link(effect : Effect, deviceCount : int, depthRange : Range1d, flip : bool, top : IndexedGeometryMode) =
+ member x.Link(effect : Effect, deviceCount : int, depthRange : Range1d, flip : bool, top : IndexedGeometryMode, useCustomSemantic : bool) =
let outputs =
x.ColorAttachments
|> Map.toList
@@ -454,26 +453,38 @@ module FShadeInterop =
if deviceCount > 1 then
if x.LayerCount > 1 then
+ let semantic = Intrinsics.Layer
+ let customSemantic = if useCustomSemantic then "GeometryInvocationId" else semantic
+
effect
- // TODO: other topologies????
- |> Effect.toLayeredEffect x.LayerCount (x.PerLayerUniforms |> Seq.map (fun n -> n, n) |> Map.ofSeq) top
- |> withDeviceIndex deviceCount
- |> Effect.toModule config
+ // TODO: other topologies????
+ |> Effect.toLayered semantic customSemantic x.LayerCount (x.PerLayerUniforms |> Seq.map (fun n -> n, n) |> Map.ofSeq) top
+ |> withDeviceIndex deviceCount
+ |> Effect.toModule config
else
+ let semantic = Intrinsics.ViewportIndex
+ let customSemantic = if useCustomSemantic then "GeometryInvocationId" else semantic
+
effect
- // TODO: other topologies????
- |> Effect.toMultiViewportEffect deviceCount Map.empty top
- |> withDeviceIndex deviceCount
- |> Effect.toModule config
+ // TODO: other topologies????
+ |> Effect.toLayered semantic customSemantic deviceCount Map.empty top
+ |> withDeviceIndex deviceCount
+ |> Effect.toModule config
else
if x.LayerCount > 1 then
+ let semantic = Intrinsics.Layer
+ let customSemantic = if useCustomSemantic then "GeometryInvocationId" else semantic
+
effect
- // TODO: other topologies????
- |> Effect.toLayeredEffect x.LayerCount (x.PerLayerUniforms |> Seq.map (fun n -> n, n) |> Map.ofSeq) top
- |> Effect.toModule config
+ // TODO: other topologies????
+ |> Effect.toLayered semantic customSemantic x.LayerCount (x.PerLayerUniforms |> Seq.map (fun n -> n, n) |> Map.ofSeq) top
+ |> Effect.toModule config
else
effect |> Effect.toModule config
+ member x.Link(effect : Effect, deviceCount : int, depthRange : Range1d, flip : bool, top : IndexedGeometryMode) =
+ x.Link(effect, deviceCount, depthRange, flip, top, false)
+
type IFramebufferSignature with
member x.Layout : FramebufferLayout =
{
@@ -484,8 +495,14 @@ module FShadeInterop =
PerLayerUniforms = x.PerLayerUniforms
}
- member x.EffectConfig(depthRange : Range1d, flip : bool) = x.Layout.EffectConfig(depthRange, flip)
- member x.Link(effect : Effect, depthRange : Range1d, flip : bool, top : IndexedGeometryMode) = x.Layout.Link(effect, x.Runtime.DeviceCount, depthRange, flip, top)
+ member x.EffectConfig(depthRange : Range1d, flip : bool) =
+ x.Layout.EffectConfig(depthRange, flip)
+
+ member x.Link(effect : Effect, depthRange : Range1d, flip : bool, top : IndexedGeometryMode) =
+ x.Layout.Link(effect, x.Runtime.DeviceCount, depthRange, flip, top, false)
+
+ member x.Link(effect : Effect, depthRange : Range1d, flip : bool, top : IndexedGeometryMode, useCustomSemantic : bool) =
+ x.Layout.Link(effect, x.Runtime.DeviceCount, depthRange, flip, top, useCustomSemantic)
type FShadeSurface private(effect : FShadeEffect) =
diff --git a/src/Aardvark.Rendering/Pipeline/Camera.fs b/src/Aardvark.Rendering/Pipeline/Camera.fs
index 4d3890de..f97cca10 100644
--- a/src/Aardvark.Rendering/Pipeline/Camera.fs
+++ b/src/Aardvark.Rendering/Pipeline/Camera.fs
@@ -259,30 +259,15 @@ module Frustum =
(r - l) / (t - b)
let withAspect (newAspect : float) ( { left = l; right = r; top = t; bottom = b } as f ) =
- let factor = newAspect / aspect f
- { f with right = factor * r; left = factor * l }
+ let factor = aspect f / newAspect
+ { f with top = factor * t; bottom = factor * b }
let withHorizontalFieldOfViewInDegrees (angleInDegrees : float) (frustum : Frustum) =
if frustum.isOrtho then
frustum
else
- let lt = atan2 frustum.left frustum.near
- let rt = atan2 frustum.right frustum.near
-
- let total = rt - lt
- let f = angleInDegrees / total
-
- let ll = lt * f
- let rr = rt * f
-
- let l = tan ll * frustum.near
- let r = tan rr * frustum.near
-
-
- let t = tan (atan2 frustum.top frustum.near * f) * frustum.near
- let b = tan (atan2 frustum.bottom frustum.near * f) * frustum.near
-
- { frustum with left = l; right = r; top = t; bottom = b }
+ let aspect = aspect frustum
+ perspective angleInDegrees frustum.near frustum.far aspect
let horizontalFieldOfViewInDegrees { left = l; right = r; near = near } =
let l,r = atan2 l near, atan2 r near
diff --git a/src/Aardvark.Rendering/RenderTasks/RenderTask.fs b/src/Aardvark.Rendering/RenderTasks/RenderTask.fs
index 4cb6a5a3..54d92296 100644
--- a/src/Aardvark.Rendering/RenderTasks/RenderTask.fs
+++ b/src/Aardvark.Rendering/RenderTasks/RenderTask.fs
@@ -96,8 +96,8 @@ module RenderTask =
/// Runs a render task for the given adaptive size and returns a map containing the textures specified as output.
/// The resulting framebuffer is cleared according to the given adaptive clear values before the render task is executed.
let renderSemanticsWithAdaptiveClear (output : Set) (size : aval) (clearValues : aval) (task : IRenderTask) =
- let runtime = task.Runtime.Value
- let signature = task.FramebufferSignature.Value
+ let runtime = task.GetRuntime()
+ let signature = task.GetFramebufferSignature()
let attachments = signature.GetSemantics()
let fbo = runtime.CreateFramebuffer(signature, size, Set.difference attachments output)
@@ -188,8 +188,8 @@ module RenderTask =
/// The resulting framebuffers are cleared according to the given clear values before the render tasks are executed.
let renderSemanticsCubeMipWithClear (output : Set) (size : aval) (clearValues : CubeMap) (tasks : CubeMap) =
let task = tasks.Data.[0]
- let runtime = task.Runtime.Value
- let signature = task.FramebufferSignature.Value
+ let runtime = task.GetRuntime()
+ let signature = task.GetFramebufferSignature()
let fbo = runtime.CreateFramebufferCube(signature, size, tasks.Levels)
let res = tasks.RenderTo(fbo, clearValues)
diff --git a/src/Aardvark.Rendering/RenderTasks/RenderToExtensions.fs b/src/Aardvark.Rendering/RenderTasks/RenderToExtensions.fs
index 873e3019..0d467ee8 100644
--- a/src/Aardvark.Rendering/RenderTasks/RenderToExtensions.fs
+++ b/src/Aardvark.Rendering/RenderTasks/RenderToExtensions.fs
@@ -113,8 +113,8 @@ type RenderToExtensions private() =
/// Renders the given task to the given framebuffer, after clearing it according to the given adaptive clear values.
[]
static member RenderTo(this : IRenderTask, output : IAdaptiveResource, clearValues : aval) =
- let runtime = this.Runtime.Value
- let signature = this.FramebufferSignature.Value
+ let runtime = this.GetRuntime()
+ let signature = this.GetFramebufferSignature()
let task =
let mutable clear = Unchecked.defaultof
@@ -155,8 +155,8 @@ type RenderToExtensions private() =
[]
static member RenderTo(this : CubeMap<#IRenderTask>, output : IAdaptiveResource>, clearValues : CubeMap>) =
let task = this.Data.[0]
- let runtime = task.Runtime.Value
- let signature = task.FramebufferSignature.Value
+ let runtime = task.GetRuntime()
+ let signature = task.GetFramebufferSignature()
let compiled =
let cache = Dict, IRenderTask>()
diff --git a/src/Aardvark.Rendering/Resources/Textures/Formats.fs b/src/Aardvark.Rendering/Resources/Textures/Formats.fs
index 7b1e25c6..0586902c 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/Aardvark.Rendering/Runtime/Runtime.fs b/src/Aardvark.Rendering/Runtime/Runtime.fs
index c85d0832..15bc9e97 100644
--- a/src/Aardvark.Rendering/Runtime/Runtime.fs
+++ b/src/Aardvark.Rendering/Runtime/Runtime.fs
@@ -123,3 +123,15 @@ type RenderTaskRunExtensions() =
[]
static member Run(t : IRenderTask, token : RenderToken, fbo : OutputDescription) =
t.Run(AdaptiveToken.Top, token, fbo)
+
+ []
+ static member inline GetRuntime(t : IRenderTask) =
+ match t.Runtime with
+ | Some r -> r
+ | _ -> raise <| InvalidOperationException("Render task does not have a runtime.")
+
+ []
+ static member inline GetFramebufferSignature(t : IRenderTask) =
+ match t.FramebufferSignature with
+ | Some s -> s
+ | _ -> raise <| InvalidOperationException("Render task does not have a framebuffer signature.")
\ No newline at end of file
diff --git a/src/Application/Aardvark.Application.Slim.GL/Application.fs b/src/Application/Aardvark.Application.Slim.GL/Application.fs
index d102431e..9d7e68fd 100644
--- a/src/Application/Aardvark.Application.Slim.GL/Application.fs
+++ b/src/Application/Aardvark.Application.Slim.GL/Application.fs
@@ -28,7 +28,7 @@ module private OpenGL =
open FSharp.Data.Adaptive
let mutable version = System.Version(3,3)
- let mutable useNoError = false
+ let mutable supportsNoError = false
let private tryCreateOffscreenWindow (version : Version) (useNoError : bool) (glfw : Glfw) =
glfw.DefaultWindowHints()
@@ -50,13 +50,13 @@ module private OpenGL =
else
glfw.DestroyWindow w
true
-
- let supportsNoError (version : Version) (glfw : Glfw) =
+
+ let queryNoErrorSupport (version : Version) (glfw : Glfw) =
if tryCreateOffscreenWindow version true glfw then
true
else
let error, _ = glfw.GetError()
- Report.Line(2, "OpenGL does not support KHR_no_error: {0}", error)
+ Report.Line(2, $"OpenGL does not support KHR_no_error ({error})")
false
let initVersion (glfw : Glfw) =
@@ -90,7 +90,7 @@ module private OpenGL =
match best with
| Some b ->
version <- b
- useNoError <- glfw |> supportsNoError b
+ supportsNoError <- glfw |> queryNoErrorSupport b
| None -> failwith "no compatible OpenGL version found"
type MyWindowInfo(win : nativeptr) =
@@ -292,7 +292,10 @@ module private OpenGL =
()
}
- let interop =
+ let interop (debug : DebugConfig) =
+ let disableErrorChecks =
+ debug.ErrorFlagCheck = ErrorFlagCheck.Disabled
+
{ new IWindowInterop with
override __.Boot(glfw) =
initVersion glfw
@@ -307,7 +310,6 @@ module private OpenGL =
glfw.WindowHint(WindowHintInt.DepthBits, 24)
glfw.WindowHint(WindowHintInt.StencilBits, 8)
-
let m = glfw.GetPrimaryMonitor()
let mode = glfw.GetVideoMode(m) |> NativePtr.read
glfw.WindowHint(WindowHintInt.RedBits, 8)
@@ -320,7 +322,7 @@ module private OpenGL =
glfw.WindowHint(WindowHintBool.OpenGLForwardCompat, true)
glfw.WindowHint(WindowHintBool.DoubleBuffer, true)
glfw.WindowHint(WindowHintBool.OpenGLDebugContext, false)
- if useNoError then glfw.WindowHint(WindowHintBool.ContextNoError, true)
+ glfw.WindowHint(WindowHintBool.ContextNoError, disableErrorChecks && supportsNoError)
glfw.WindowHint(WindowHintBool.SrgbCapable, false)
if RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then
glfw.WindowHint(WindowHintBool.CocoaRetinaFramebuffer, cfg.physicalSize)
@@ -330,7 +332,7 @@ module private OpenGL =
}
type OpenGlApplication private (runtime : Runtime, shaderCachePath : Option, hideCocoaMenuBar : bool) as this =
- inherit Application(runtime, OpenGL.interop, hideCocoaMenuBar)
+ inherit Application(runtime, OpenGL.interop runtime.DebugConfig, hideCocoaMenuBar)
let createContext() =
let w = this.Instance.CreateWindow WindowConfig.Default
diff --git a/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj b/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj
index 6eaffeee..d8c58255 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj
+++ b/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj
@@ -56,6 +56,8 @@
+
+
@@ -68,6 +70,7 @@
+
diff --git a/src/Tests/Aardvark.Rendering.Tests/Program.fs b/src/Tests/Aardvark.Rendering.Tests/Program.fs
index ad32697f..bbf20024 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Program.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Program.fs
@@ -1,281 +1,75 @@
open Aardvark.Rendering.Tests
-open Aardvark.Rendering.GL.Tests
+open Aardvark.Application
+open Expecto
-open System
-open Aardvark.Base
-
-open FSharp.Data.Adaptive
-open Aardvark.Rendering
-open Aardvark.Rendering.GL
-open OpenTK.Graphics.OpenGL4
-open Aardvark.Application.Slim
-
-open BenchmarkDotNet.Running;
-open BenchmarkDotNet.Configs
-open BenchmarkDotNet.Jobs
-open BenchmarkDotNet.Toolchains
+[]
+let main argv =
-let testCompile() =
- use runtime = new Runtime(DebugLevel.None)
- let ctx = new Context(runtime, fun () -> ContextHandleOpenTK.create runtime.DebugConfig)
- runtime.Initialize(ctx)
+ let backendTests backend =
+ let bufferTests =
+ testBackend backend "Buffers" [
+ Buffer.BufferCopy.tests
+ Buffer.BufferUpload.tests
+ Buffer.BufferDownload.tests
+ Buffer.AttributeBuffer.tests
+ ]
- let signature =
- runtime.CreateFramebufferSignature(
- [
- DefaultSemantic.Colors, TextureFormat.Rgba8
- DefaultSemantic.DepthStencil, TextureFormat.Depth24Stencil8
+ let textureTests =
+ testBackend backend "Textures" [
+ Texture.TextureUpload.tests
+ Texture.TextureDownload.tests
+ Texture.TextureCreate.tests
+ Texture.TextureCopy.tests
+ Texture.TextureClear.tests
]
- )
- let callInfo =
- DrawCallInfo(
- FaceVertexCount = 6,
- InstanceCount = 1
- )
+ let renderingTests =
+ testBackend backend "Rendering" [
+ Rendering.Culling.tests
+ Rendering.Blending.tests
+ Rendering.ColorMasks.tests
+ Rendering.RenderTasks.tests
+ Rendering.FramebufferSignature.tests
+ Rendering.IntegerAttachments.tests
+ Rendering.Samplers.tests
+ Rendering.Uniforms.tests
+ ]
- let surface =
- runtime.PrepareEffect(
- signature,
- [
- DefaultSurfaces.constantColor C4f.Red |> toEffect
+ let computeTests =
+ testBackend backend "Compute" [
+ Compute.ComputeImages.tests
+ Compute.ComputeBuffers.tests
+ Compute.ComputePrimitives.tests
+ Compute.ComputeSorting.tests
+ Compute.ComputeJpeg.tests
+ Compute.MutableInputBinding.tests
]
- )
- let uniforms (t : V3d) =
- UniformProvider.ofList [
- Symbol.Create "ModelTrafo", AVal.constant (Trafo3d.Translation t) :> IAdaptiveValue
- Symbol.Create "ViewProjTrafo", AVal.constant Trafo3d.Identity :> IAdaptiveValue
+ testList $"Tests ({backend})" [
+ bufferTests
+ textureTests
+ renderingTests
+ computeTests
]
- let attributes =
- AttributeProvider.ofList [
- DefaultSemantic.Positions, [| V3f(-1,-1,0); V3f(1,-1,0); V3f(1,1,0); V3f(1,-1,0); V3f(1,1,0); V3f(-1,1,0) |] :> Array
+ let otherTests =
+ testList "Other tests" [
+ ``SceneGraph Tests``.tests
+ ``CompactSet Tests``.tests
+ ``AdaptiveResource Tests``.tests
+ ``Camera Tests``.tests
]
- let prototype =
- let ro = RenderObject()
- ro.DrawCalls <- Direct(AVal.constant [ callInfo ])
- ro.Surface <- Surface.Backend (surface :> ISurface)
- ro.RasterizerState <- { RasterizerState.Default with FrontFacing = AVal.constant WindingOrder.CounterClockwise }
- ro.VertexAttributes <- attributes
- ro.Uniforms <- uniforms V3d.Zero
- ro
-
- let framebuffer = runtime.CreateFramebuffer(signature, AVal.constant(V2i(1024, 1024)))
- framebuffer.Acquire()
-
- let objects =
- Array.init (1 <<< 20) (fun i ->
- let ro = RenderObject.Clone prototype
- ro.Uniforms <- uniforms (V3d(i,0,0))
- ro :> IRenderObject
- )
-
- let fbo = framebuffer.GetValue()
-
- //let set = ASet.ofArray (Array.take (1 <<< 16) objects)
- //let commands = AList.ofArray (Array.take (1 <<< 16) objects |> Array.map RenderCommand.Render)
-
-// Log.line "starting"
-// while true do
-// //let task = runtime.CompileRender(signature, BackendConfiguration.Default, set)
-// let task = runtime.Compile(signature, commands)
-// task.Run(RenderToken.Empty, fbo)
-// //task.Dispose()
-
-
- let log = @"C:\Users\Schorsch\Desktop\perfOld.csv"
-
- for cnt in 1000 .. 1000 .. 100000 do
- let set = ASet.ofArray (Array.take cnt objects)
- //let commands = AList.ofArray (Array.take cnt objects |> Array.map RenderCommand.Render)
-
- Log.startTimed "compile %d" cnt
- let sw = System.Diagnostics.Stopwatch.StartNew()
- //let task = runtime.Compile(signature, commands)
- let task = runtime.CompileRender(signature, set)
- task.Run(RenderToken.Empty, fbo)
- sw.Stop()
- Log.stop()
- task.Dispose()
- System.IO.File.AppendAllLines(log, [sprintf "%d:%d" cnt sw.MicroTime.TotalNanoseconds])
-
-
- ()
-
-let clearTexture(runtime : Aardvark.Rendering.GL.Runtime, texture : IBackendTexture, color : C4f, level : int) =
-
- using runtime.Context.ResourceLock (fun _ ->
-
- // create temporary fbo
- let fbo = GL.GenFramebuffer()
- GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, fbo)
- GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, FramebufferAttachment.ColorAttachment0, unbox texture.Handle, level)
- GL.Check "[GL] ClearTexture: could not create FramebufferTexture"
-
- // perform clear
- GL.ClearColor(color.R, color.G, color.B, color.A)
- GL.Clear(ClearBufferMask.ColorBufferBit)
- GL.Check "[GL] ClearTexture: could not clear texture"
-
- // unbind and delete of fbo
- GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0)
- GL.Check "[GL] ClearTexture: could not unbind Framebuffer"
- GL.DeleteFramebuffer(fbo)
- GL.Check "[GL] ClearTexture: could not delete FramebufferTexture"
- )
-
-let testDownloadSlice() =
- let app = new OpenGlApplication(false, true)
- let runtime = app.Runtime
- let texRt = runtime :> ITextureRuntime
- let tex = texRt.CreateTexture2DArray(V2i(222,333), TextureFormat.Rgba8, 1, 8, 5)
- let path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
-
- clearTexture(runtime, tex, C4f.Red, 0)
- runtime.Download(tex, 0, 0).Save(Path.combine [path; "slice0.bmp"])
-
- clearTexture(runtime, tex, C4f.Blue, 0)
- runtime.Download(tex, 0, 1).Save(Path.combine [path; "slice1.bmp"])
-
- ()
-
-let testTextureCubeArray() =
- let app = new OpenGlApplication(false, DebugLevel.Normal)
- let runtime = app.Runtime
- let texRt = runtime :> ITextureRuntime
-
- let cta = texRt.CreateTextureCubeArray(128, TextureFormat.Rgba8, 1, 4)
-
- let cube0View = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(0,5), false) // create TextureCube view of [0]
- let cube1View = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(6,11), false) // create TextureCube view of [1]
- let cube0Face0View = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(0,0), false) // create Texture2d view of cube[0].face[0]
- let cube0Face1View = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(1,1), false) // create Texture2d view of cube[0].face[1]
- let cube1Face2View = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(8,8), false) // create Texture2d view of cube[1].face[2]
- let cube2Face0To2View = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(12,14), true) // create Texture2dArray of range
- let cube2Face0To5View = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(12,17), true) // create Texture2dArray of range spanning cube
- let cube0to1FacesView = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(0,11), true) // create Texture2dArray of complete cube 0 & 1
- let cubeArrayFacesView = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(0,23), true) // create Texture2dArray of complete TextureCubeArray
- //let cube0Cube1ArrayView = texRt.CreateTextureView(cta, Range1i(0,0), Range1i(0,11), true) // API does not allow to create TextureCubeArray sub-range
-
- texRt.DeleteTexture(cta)
- texRt.DeleteTexture(cube0View)
- texRt.DeleteTexture(cube1View)
- texRt.DeleteTexture(cube0Face0View)
- texRt.DeleteTexture(cube0Face1View)
- texRt.DeleteTexture(cube1Face2View)
- texRt.DeleteTexture(cube2Face0To2View)
- texRt.DeleteTexture(cube2Face0To5View)
- texRt.DeleteTexture(cube0to1FacesView)
- texRt.DeleteTexture(cubeArrayFacesView)
-
-
-let testCopySlice() =
- let app = new OpenGlApplication(false, DebugLevel.Normal)
- let runtime = app.Runtime
- let texRt = runtime :> ITextureRuntime
- let texSrc = texRt.CreateTexture2DArray(V2i(222,333), TextureFormat.Rgba8, 1, 1, 2)
- let texDst = texRt.CreateTexture2DArray(V2i(222,333), TextureFormat.Rgba8, 1, 1, 2)
-
- clearTexture(runtime, texSrc, C4f.Red, 0)
- clearTexture(runtime, texDst, C4f.Blue, 0)
-
- // NOTE: can only be test when testDownloadSlice is fixed
- runtime.Download(texSrc, 0, 0).Save("C:\\Debug\\testCopySlice_src_slice0.bmp") // -> should be Red
- runtime.Download(texSrc, 0, 1).Save("C:\\Debug\\testCopySlice_src_slice1.bmp") // -> should be Red
-
- // alternative:
- let texSrcTile0View = texRt.CreateTextureView(texSrc, Range1i(0,0), Range1i(0,0), false)
- runtime.Download(texSrcTile0View).Save("C:\\Debug\\testCopySlice_src_slice0_asView.bmp")
- let texSrcTile1View = texRt.CreateTextureView(texSrc, Range1i(0,0), Range1i(1,1), false)
- runtime.Download(texSrcTile1View).Save("C:\\Debug\\testCopySlice_src_slice1_asView.bmp")
-
- // copy first slice
- runtime.Copy(texSrc, 0, 0, texDst, 0, 0, 1, 1)
-
- // NOTE: can only be test when testDownloadSlice is fixed
- runtime.Download(texDst, 0, 0).Save("C:\\Debug\\testCopySlice_dst_slice0.bmp") // -> should be Red
- runtime.Download(texDst, 0, 1).Save("C:\\Debug\\testCopySlice_dst_slice1.bmp") // -> should be Blue
-
- // alternative:
- let texDstTile0View = texRt.CreateTextureView(texDst, Range1i(0,0), Range1i(0,0), false)
- runtime.Download(texDstTile0View).Save("C:\\Debug\\testCopySlice_dst_slice0_asView.bmp") // -> should be Red
- let texDstTile1View = texRt.CreateTextureView(texDst, Range1i(0,0), Range1i(1,1), false)
- runtime.Download(texDstTile1View).Save("C:\\Debug\\testCopySlice_dst_slice1_asView.bmp") // -> should be Blue
-
- ()
-
-module CSTest =
-
- module Shader =
- open FShade
-
- []
- let write (img : Image2d) =
- compute {
- let id = getGlobalId().XY
- let s = img.Size
- if id.X < s.X && id.Y < s.Y then
- img.[id] <- V4d(V2d id / V2d s, 1.0, 1.0)
- }
-
- let run() =
-
- let app = new OpenGlApplication()
- let runtime = app.Runtime :> IRuntime
-
- let dst = runtime.CreateTexture2D(V2i(1024, 1024), TextureFormat.Rgba8, 1, 1)
-
- let sh = runtime.CreateComputeShader Shader.write
- let ip = runtime.CreateInputBinding sh
- ip.["img"] <- dst.[TextureAspect.Color, 0, 0]
- ip.Flush()
-
- runtime.Run [
- ComputeCommand.Bind sh
- ComputeCommand.SetInput ip
- ComputeCommand.Dispatch (dst.Size.XY / 8)
+ let allTests =
+ testList "Tests" [
+ otherTests
+ backendTests Backend.GL
+ backendTests Backend.Vulkan
]
- let img = PixImage(Col.Format.RGBA, dst.Size.XY)
- dst.Download(img)
- img.SaveImageSharp @"C:\Users\Schorsch\Desktop\bla.png"
-
-
-
-[]
-let main argv =
- //Aardvark.Init()
- //CSTest.run()
-
- //let cfg =
- // let job = Job.Default.WithToolchain(InProcess.Emit.InProcessEmitToolchain.Instance)
- // ManualConfig.Create(DefaultConfig.Instance).AddJob(job)
-
- //BenchmarkSwitcher.FromAssembly(typeof.Assembly).Run(argv, cfg) |> ignore;
-
- //exit 0
- //``Texture Tests``.runAllTests()
- //testCompile()
-
- //RadixSortTest.run()
-
- //testDownloadSlice()
- //testCopySlice()
-
- BenchmarkRunner.Run() |> ignore
-
-
- //testTextureCubeArray()
-
- //RenderingTests.``[GL] concurrent group change``()
- //RenderingTests.``[GL] memory leak test``()
- //MultipleStageAgMemoryLeakTest.run() |> ignore
+ let runManuallyInMain = true
- //PerformanceTests.PerformanceTest.runConsole()
- //Examples.PerformanceTest.run()
- //PerformanceTests.StartupPerformance.runConsole args
- //PerformanceTests.IsActiveFlagPerformance.run args
- //UseTest.bla()
- 0
+ if runManuallyInMain then
+ runTestsSynchronously false allTests
+ else
+ runTestsWithCLIArgs [ CLIArguments.No_Spinner ] argv allTests
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Application.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Application.fs
index c36ccddb..445f5f90 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Application.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Application.fs
@@ -30,7 +30,7 @@ module TestApplication =
RuntimeConfig.UseNewRenderTask <- true
RuntimeConfig.PreferHostSideTextureCompression <- true
- Toolkit.Init(ToolkitOptions(Backend = PlatformBackend.PreferNative)) |> ignore
+ let toolkit = Toolkit.Init(ToolkitOptions(Backend = PlatformBackend.PreferNative))
let runtime = new Runtime(debug)
let ctx = new Context(runtime, fun () -> ContextHandleOpenTK.create runtime.DebugConfig)
@@ -59,10 +59,10 @@ module TestApplication =
runtime,
{ new IDisposable with
member x.Dispose() =
+ checkForErrors()
runtime.Dispose()
checkForDebugErrors()
- checkForErrors()
- ctx.Dispose()
+ toolkit.Dispose()
}
)
@@ -70,6 +70,7 @@ module TestApplication =
open Aardvark.Rendering.Vulkan
let create (debug : IDebugConfig) =
+ CustomDeviceChooser.Register Seq.head
let app = new HeadlessVulkanApplication(debug)
let onExit =
{ new IDisposable with
@@ -91,9 +92,13 @@ module TestApplication =
app.Runtime, onExit
)
+ let mutable private aardvarkInitialized = false
+
let create' (debug : IDebugConfig) (backend : Backend) =
- IntrospectionProperties.CustomEntryAssembly <- Assembly.GetAssembly(typeof)
- Aardvark.Init()
+ if not aardvarkInitialized then
+ IntrospectionProperties.CustomEntryAssembly <- Assembly.GetAssembly(typeof)
+ Aardvark.Init()
+ aardvarkInitialized <- true
match backend with
| Backend.GL -> GL.create debug
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Common.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Common.fs
index 6b9ab79c..9aae94c1 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Common.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Common.fs
@@ -22,6 +22,7 @@ module ``Unit Test Utilities`` =
let prepareCases (backend : Backend) (name : string) (cases : List unit)>) =
cases |> List.map (fun (name, test) ->
testCase name (fun () -> TestApplication.createUse test backend)
+ |> testSequenced
)
|> testList name
@@ -32,5 +33,6 @@ module ``Unit Test Utilities`` =
Aardvark.Init()
test()
)
+ |> testSequenced
)
|> testList name
\ No newline at end of file
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Buffers.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Buffers.fs
index 7b4ea82d..00c151d1 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Buffers.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Buffers.fs
@@ -1,4 +1,4 @@
-namespace Aardvark.Rendering.Tests.Rendering
+namespace Aardvark.Rendering.Tests.Compute
open System
open Aardvark.Base
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/ComputeTests.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/ComputeTests.fs
index 434d9359..b5726b04 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/ComputeTests.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/ComputeTests.fs
@@ -1,7 +1,7 @@
namespace Aardvark.Rendering.Tests
open Aardvark.Application
-open Aardvark.Rendering.Tests.Rendering
+open Aardvark.Rendering.Tests.Compute
open Expecto
module ``Compute Tests`` =
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Images.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Images.fs
index 6ce0e97c..d6110eac 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Images.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Images.fs
@@ -1,4 +1,4 @@
-namespace Aardvark.Rendering.Tests.Rendering
+namespace Aardvark.Rendering.Tests.Compute
open System
open Aardvark.Base
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Jpeg.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Jpeg.fs
index 552f3e44..269a812b 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Jpeg.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Jpeg.fs
@@ -1,4 +1,4 @@
-namespace Aardvark.Rendering.Tests.Rendering
+namespace Aardvark.Rendering.Tests.Compute
open Aardvark.Base
open Aardvark.GPGPU
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/MutableInputBinding.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/MutableInputBinding.fs
index 1fb02b8d..2e530cdc 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/MutableInputBinding.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/MutableInputBinding.fs
@@ -1,4 +1,4 @@
-namespace Aardvark.Rendering.Tests.Rendering
+namespace Aardvark.Rendering.Tests.Compute
open Aardvark.Base
open Aardvark.Rendering
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Primitives.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Primitives.fs
index f3dc5420..df8471e1 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Primitives.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Primitives.fs
@@ -1,4 +1,4 @@
-namespace Aardvark.Rendering.Tests.Rendering
+namespace Aardvark.Rendering.Tests.Compute
open Aardvark.Base
open Aardvark.GPGPU
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Sorting.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Sorting.fs
index 1a81b20d..c1490a90 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Sorting.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Compute/Sorting.fs
@@ -1,4 +1,4 @@
-namespace Aardvark.Rendering.Tests.Rendering
+namespace Aardvark.Rendering.Tests.Compute
open Aardvark.Base
open Aardvark.GPGPU
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Other/Camera.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/Camera.fs
new file mode 100644
index 00000000..a0e3660d
--- /dev/null
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/Camera.fs
@@ -0,0 +1,68 @@
+namespace Aardvark.Rendering.Tests
+
+open Aardvark.Base
+open Aardvark.Rendering
+open Expecto
+
+module ``Camera Tests`` =
+
+ module private Expect =
+
+ let frustumClose (accuracy : Accuracy) (a : Frustum) (b : Frustum) (message : string) =
+ Expect.floatClose accuracy a.near b.near (message + " (near)")
+ Expect.floatClose accuracy a.far b.far (message + " (far)")
+ Expect.floatClose accuracy a.left b.left (message + " (left)")
+ Expect.floatClose accuracy a.right b.right (message + " (right)")
+ Expect.floatClose accuracy a.top b.top (message + " (top)")
+ Expect.floatClose accuracy a.bottom b.bottom (message + " (bottom)")
+ Expect.equal a.isOrtho b.isOrtho (message + " (isOrtho)")
+
+ let aspect =
+ test "aspect" {
+ let f = Frustum.perspective 75.0 0.1 100.0 1.77
+ let a = Frustum.aspect f
+ Expect.floatClose Accuracy.high a 1.77 "Aspect is wrong"
+ }
+
+ let fieldOfView =
+ test "fieldOfView" {
+ let f = Frustum.perspective 75.0 0.1 100.0 1.77
+ let fov = Frustum.horizontalFieldOfViewInDegrees f
+ Expect.floatClose Accuracy.high fov 75.0 "Field of view is wrong"
+ }
+
+ let withAspect =
+ test "withAspect" {
+ let a = Frustum.perspective 75.0 0.1 100.0 1.77
+ let b = Frustum.perspective 75.0 0.1 100.0 1.5
+ let c = a |> Frustum.withAspect 1.5
+ Expect.frustumClose Accuracy.high b c "Frustums do not match"
+ }
+
+ let withNear =
+ test "withNear" {
+ let a = Frustum.perspective 75.0 0.1 100.0 1.77
+ let b = Frustum.perspective 75.0 0.01 100.0 1.77
+ let c = a |> Frustum.withNear 0.01
+ Expect.frustumClose Accuracy.high b c "Frustums do not match"
+ }
+
+ let withFieldOfView =
+ test "withFieldOfView" {
+ let a = Frustum.perspective 75.0 0.1 100.0 1.77
+ let b = Frustum.perspective 90.0 0.1 100.0 1.77
+ let c = a |> Frustum.withHorizontalFieldOfViewInDegrees 90.0
+ Expect.frustumClose Accuracy.high b c "Frustums do not match"
+ }
+
+ []
+ let tests =
+ testList "Camera" [
+ testList "Frustum" [
+ aspect
+ fieldOfView
+ withAspect
+ withNear
+ withFieldOfView
+ ]
+ ]
\ No newline at end of file
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Blending.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Blending.fs
new file mode 100644
index 00000000..ecbe6404
--- /dev/null
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Blending.fs
@@ -0,0 +1,125 @@
+namespace Aardvark.Rendering.Tests.Rendering
+
+open Aardvark.Base
+open Aardvark.Rendering
+open Aardvark.Rendering.Tests
+open Aardvark.SceneGraph
+open Aardvark.Application
+open FSharp.Data.Adaptive
+open FSharp.Data.Adaptive.Operators
+open Expecto
+
+module Blending =
+
+ module private Semantic =
+ let Output0 = Sym.ofString "Output0"
+ let Output1 = Sym.ofString "Output1"
+
+ module private BlendMode =
+ let ColorMulAlphaAdd =
+ { BlendMode.Blend with
+ SourceColorFactor = BlendFactor.Zero
+ SourceAlphaFactor = BlendFactor.One
+ DestinationColorFactor = BlendFactor.SourceColor
+ DestinationAlphaFactor = BlendFactor.One }
+
+ module private Shader =
+ open FShade
+
+ let inline output01 (c1 : ^Color) (c2 : ^Color) (v : Effects.Vertex) =
+ let c1 = v4d c1
+ let c2 = v4d c2
+ fragment {
+ return {| Output0 = c1; Output1 = c2 |}
+ }
+
+ module Cases =
+
+ let globalBlend (mode : BlendMode) (colorOp : float32 -> float32 -> float32) (alphaOp : float32 -> float32 -> float32) (runtime : IRuntime) =
+ let clearColor = C4f(0.1, 0.2, 0.3, 0.4)
+ let blendedColor = C4f(0.1, 0.1, 0.1, 0.1)
+ let expectedColor =
+ C4f(
+ colorOp clearColor.R blendedColor.R,
+ colorOp clearColor.G blendedColor.G,
+ colorOp clearColor.B blendedColor.B,
+ alphaOp clearColor.A blendedColor.A
+ )
+
+ use signature =
+ runtime.CreateFramebufferSignature([
+ DefaultSemantic.Colors, TextureFormat.Rgba32f
+ ])
+
+ use task =
+ Sg.fullScreenQuad
+ |> Sg.shader {
+ do! DefaultSurfaces.constantColor blendedColor
+ }
+ |> Sg.blendMode' mode
+ |> Sg.compile runtime signature
+
+ let output =
+ let clear = clear { color clearColor }
+ task |> RenderTask.renderToColorWithClear (~~V2i(256)) clear
+
+ output.Acquire()
+
+ try
+ let result = output.GetValue().Download().AsPixImage()
+ result |> PixImage.isColor32f Accuracy.medium (expectedColor.ToArray())
+ finally
+ output.Release()
+
+ let perAttachmentBlend (runtime : IRuntime) =
+ let clearColor = C4f(0.1, 0.2, 0.3, 0.4)
+ let blendedColor1 = C4f(0.1, 0.1, 0.1, 0.1)
+ let blendedColor2 = C4f(0.5, 0.5, 0.5, 0.5)
+ let expectedColor1 = clearColor + blendedColor1
+ let expectedColor2 = clearColor * blendedColor2
+
+ use signature =
+ runtime.CreateFramebufferSignature([
+ Semantic.Output0, TextureFormat.Rgba32f
+ Semantic.Output1, TextureFormat.Rgba32f
+ ])
+
+ let modes =
+ Map.ofList [
+ Semantic.Output0, BlendMode.Add
+ Semantic.Output1, BlendMode.Multiply
+ ]
+
+ use task =
+ Sg.fullScreenQuad
+ |> Sg.shader {
+ do! Shader.output01 blendedColor1 blendedColor2
+ }
+ |> Sg.blendModes' modes
+ |> Sg.compile runtime signature
+
+ let r1, r2 =
+ let fbo = runtime.CreateFramebuffer(signature, ~~V2i(256))
+ let clear = clear { color clearColor }
+ let output = task |> RenderTask.renderToWithClear fbo clear
+ output.GetOutputTexture(Semantic.Output0), output.GetOutputTexture(Semantic.Output1)
+
+ r1.Acquire(); r2.Acquire()
+
+ try
+ let p1 = r1.GetValue().Download().AsPixImage()
+ p1 |> PixImage.isColor (expectedColor1.ToArray())
+
+ let p2 = r2.GetValue().Download().AsPixImage()
+ p2 |> PixImage.isColor (expectedColor2.ToArray())
+
+ finally
+ r1.Release(); r2.Release()
+
+ let tests (backend : Backend) =
+ [
+ "Global (add)", Cases.globalBlend BlendMode.Add (+) (+)
+ "Global (color multiply, alpha add)", Cases.globalBlend BlendMode.ColorMulAlphaAdd (*) (+)
+ "Per attachment", Cases.perAttachmentBlend
+ ]
+ |> prepareCases backend "Blending"
\ No newline at end of file
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/ColorMasks.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/ColorMasks.fs
new file mode 100644
index 00000000..1643d479
--- /dev/null
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/ColorMasks.fs
@@ -0,0 +1,95 @@
+namespace Aardvark.Rendering.Tests.Rendering
+
+open Aardvark.Base
+open Aardvark.Rendering
+open Aardvark.Rendering.Tests
+open Aardvark.SceneGraph
+open Aardvark.Application
+open FSharp.Data.Adaptive
+open FSharp.Data.Adaptive.Operators
+
+module ColorMasks =
+
+ module private Semantic =
+ let Output0 = Sym.ofString "Output0"
+ let Output1 = Sym.ofString "Output1"
+
+ module private Shader =
+ open FShade
+
+ let output01White (v : Effects.Vertex) =
+ fragment {
+ return {| Output0 = V4d.One; Output1 = V4d.One |}
+ }
+
+ module Cases =
+
+ let globalMask (runtime : IRuntime) =
+ use signature =
+ runtime.CreateFramebufferSignature([
+ DefaultSemantic.Colors, TextureFormat.Rgba8
+ ])
+
+ use task =
+ Sg.fullScreenQuad
+ |> Sg.shader {
+ do! DefaultSurfaces.constantColor C4f.White
+ }
+ |> Sg.colorMask' (ColorMask.Green ||| ColorMask.Alpha)
+ |> Sg.compile runtime signature
+
+ let size = ~~V2i(256)
+ let output = task |> RenderTask.renderToColor size
+ output.Acquire()
+
+ try
+ let result = output.GetValue().Download().AsPixImage()
+ result |> PixImage.isColor [| 0uy; 255uy; 0uy; 255uy |]
+ finally
+ output.Release()
+
+ let perAttachmentMask (runtime : IRuntime) =
+ use signature =
+ runtime.CreateFramebufferSignature([
+ Semantic.Output0, TextureFormat.Rgba8
+ Semantic.Output1, TextureFormat.Rgba8
+ ])
+
+ let masks =
+ Map.ofList [
+ Semantic.Output0, ColorMask.Green ||| ColorMask.Alpha
+ Semantic.Output1, ColorMask.Red ||| ColorMask.Blue ||| ColorMask.Alpha
+ ]
+
+ use task =
+ Sg.fullScreenQuad
+ |> Sg.shader {
+ do! Shader.output01White
+ }
+ |> Sg.colorMasks' masks
+ |> Sg.compile runtime signature
+
+ let r1, r2 =
+ let fbo = runtime.CreateFramebuffer(signature, ~~V2i(256))
+ let clear = ClearValues.ofColor V4f.Zero
+ let output = task |> RenderTask.renderToWithClear fbo clear
+ output.GetOutputTexture(Semantic.Output0), output.GetOutputTexture(Semantic.Output1)
+
+ r1.Acquire(); r2.Acquire()
+
+ try
+ let p1 = r1.GetValue().Download().AsPixImage()
+ p1 |> PixImage.isColor [| 0uy; 255uy; 0uy; 255uy |]
+
+ let p2 = r2.GetValue().Download().AsPixImage()
+ p2 |> PixImage.isColor [| 255uy; 0uy; 255uy; 255uy |]
+
+ finally
+ r1.Release(); r2.Release()
+
+ let tests (backend : Backend) =
+ [
+ "Global", Cases.globalMask
+ "Per attachment", Cases.perAttachmentMask
+ ]
+ |> prepareCases backend "Color masks"
\ No newline at end of file
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Samplers.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Samplers.fs
index 891149eb..b68400c3 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Samplers.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Samplers.fs
@@ -20,11 +20,13 @@ module Samplers =
let private diffuseIntSampler =
intSampler2d {
texture uniform?DiffuseColorTexture
+ filter Filter.MinMagPoint
}
let private diffuseUIntSampler =
uintSampler2d {
texture uniform?DiffuseColorTexture
+ filter Filter.MinMagPoint
}
let diffuseIntTexture (v : Vertex) =
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Signatures.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Signatures.fs
index 7653e3c2..64b17fbb 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Signatures.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Signatures.fs
@@ -101,7 +101,8 @@ module FramebufferSignature =
let r1, r3 =
let fbo = runtime.CreateFramebuffer(signature, ~~V2i(256))
- let output = task |> RenderTask.renderTo fbo
+ let clear = ClearValues.ofColor V4f.Zero
+ let output = task |> RenderTask.renderToWithClear fbo clear
output.GetOutputTexture(Semantic.Output1), output.GetOutputTexture(Semantic.Output3)
r1.Acquire(); r3.Acquire()
@@ -141,7 +142,8 @@ module FramebufferSignature =
let result =
let fbo = runtime.CreateFramebuffer(signature, ~~V2i(256))
- let output = task |> RenderTask.renderTo fbo
+ let clear = ClearValues.ofColor V4f.Zero
+ let output = task |> RenderTask.renderToWithClear fbo clear
output.GetOutputTexture(Semantic.Output1)
result.Acquire()
@@ -164,6 +166,9 @@ module FramebufferSignature =
4, { Name = Semantic.Output4; Format = TextureFormat.Rgba8 }
], Some TextureFormat.Depth24Stencil8)
+ use tclear =
+ runtime.CompileClear(signature, ClearValues.ofColor V4f.Zero)
+
use t0sig =
runtime.CreateFramebufferSignature(Map.ofList [
1, { Name = Semantic.Output1; Format = TextureFormat.Rgba8 }
@@ -221,7 +226,7 @@ module FramebufferSignature =
|> Sg.compile runtime t2sig
use task =
- RenderTask.ofList [t0; t1; t2]
+ RenderTask.ofList [tclear; t0; t1; t2]
// Combining tasks with varying signatures leads to task with no signature
Expect.equal task.FramebufferSignature None "Unexpected render task signature"
@@ -279,7 +284,8 @@ module FramebufferSignature =
let result =
let fbo = runtime.CreateFramebuffer(signature, ~~V2i(256))
- let output = task |> RenderTask.renderTo fbo
+ let clear = ClearValues.ofColor V4f.Zero
+ let output = task |> RenderTask.renderToWithClear fbo clear
output.GetOutputTexture(Semantic.Output1)
result.Acquire()
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Clear.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Clear.fs
index 1b4a3f92..714bfb9f 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Clear.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Clear.fs
@@ -23,7 +23,7 @@ module TextureClear =
let rgba8 (runtime : IRuntime) =
let data = createAndClearColor runtime TextureFormat.Rgba8 <| C3f(0.5)
- data.AsPixImage() |> PixImage.isColor (C4b(127uy).ToArray())
+ data.AsPixImage() |> PixImage.isColorWithDist 1L (C4b(127uy).ToArray())
let rgba32i (runtime : IRuntime) =
let data = createAndClearColor runtime TextureFormat.Rgba32i <| V4i(-1)
@@ -135,7 +135,7 @@ module TextureClear =
do
let c = C4b(127uy)
let pi = textures.[c3].Download().AsPixImage()
- pi |> PixImage.isColor (c.ToArray())
+ pi |> PixImage.isColorWithDist 1L (c.ToArray())
do
let c = C3b.AliceBlue |> V4i
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Create.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Create.fs
index ccac0fad..691a2d04 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
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Download.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Download.fs
index 524aa2d3..3524a045 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Download.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Download.fs
@@ -32,6 +32,10 @@ module TextureDownload =
let private texture2DWithFormat<'T when 'T : equality> (runtime : IRuntime) (format : TextureFormat) (data : PixImage<'T>) =
texture2DWithFormatWithComparer runtime Expect.equal format data
+ let inline private texture2DWithFormatNorm (runtime : IRuntime) (format : TextureFormat) (data : PixImage<'T>) =
+ let comp a b = Expect.isLessThanOrEqual (abs (int64 a - int64 b)) 1L
+ texture2DWithFormatWithComparer runtime comp format data
+
let private texture2DWithFormat32f (runtime : IRuntime) (accuracy : Accuracy) (format : TextureFormat) (data : PixImage) =
let comp a b = Expect.floatClose accuracy (float a) (float b)
texture2DWithFormatWithComparer runtime comp format data
@@ -54,7 +58,7 @@ module TextureDownload =
let texture2Drgba16snorm (runtime : IRuntime) =
let data = PixImage.random16i <| V2i(256)
- data |> texture2DWithFormat runtime TextureFormat.Rgba16Snorm
+ data |> texture2DWithFormatNorm runtime TextureFormat.Rgba16Snorm
let texture2Drgba32ui (runtime : IRuntime) =
let data = PixImage.random32ui <| V2i(256)
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs
index fc85eb7e..10861e09 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs
@@ -153,14 +153,12 @@ module TextureUpload =
|> shader
|> Sg.compile runtime signature
+ // Render sampling the null texture.
+ // We can't check the result since null textures are uninitialized, just make sure there are no errors.
let buffer = task |> RenderTask.renderToColor (AVal.init <| V2i(256))
buffer.Acquire()
-
- try
- let result = buffer.GetValue().Download().AsPixImage().ToFormat(Col.Format.RGB)
- PixImage.isColor [| 0uy; 0uy; 0uy |] result
- finally
- buffer.Release()
+ try buffer.GetValue() |> ignore
+ finally buffer.Release()
let texture1DNull (runtime : IRuntime) =
let diffuseSampler =
diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Utilities.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Utilities.fs
index b0826532..7e947408 100644
--- a/src/Tests/Aardvark.Rendering.Tests/Tests/Utilities.fs
+++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Utilities.fs
@@ -230,6 +230,9 @@ module PixData =
let isColor (color : 'T[]) (pi : PixImage<'T>) =
isColorWithComparer Expect.equal color pi
+ let inline isColorWithDist (maxDist : int64) (color : 'T[]) (pi : PixImage<'T>) =
+ isColorWithComparer (fun x y -> Expect.isLessThanOrEqual (abs (int64 x - int64 y)) maxDist) color pi
+
let isColor32f (accuracy : Accuracy) (color : float32[]) (pi : PixImage) =
let comp a b = Expect.floatClose accuracy (float a) (float b)
isColorWithComparer comp color pi
@@ -290,6 +293,71 @@ module ``Expecto Extensions`` =
Expect.floatClose accuracy actual.Z expected.Z message
Expect.floatClose accuracy actual.W expected.W message
+ // Utility to run Expecto tests synchronously in the current thread.
+ // Prints a summary at the end.
+ let runTestsSynchronously (stopOnFail : bool) (tests : Test) =
+ let failed = ResizeArray()
+ let skipped = ResizeArray()
+ let mutable passed = 0
+
+ let tests = Test.toTestCodeList tests
+ let count = tests.Length
+
+ let printSummary() =
+ let print fmt =
+ Printf.kprintf (fun str ->
+ if failed.Count = 0 then Log.line "%s" str
+ else Log.error "%s" str
+ ) fmt
+
+ Report.Line()
+ print $"Total: {count}, Passed: {passed}, Failed: {failed.Count}, Skipped: {skipped.Count}"
+
+ if failed.Count > 0 then
+ Report.Line()
+ print "Failed tests:"
+ for t in failed do
+ print " %s" t
+
+ if skipped.Count > 0 then
+ Report.Line()
+ print "Skipped tests:"
+ for t in skipped do
+ print " %s" t
+
+ let rec run (index : int) (tests : FlatTest list) =
+ match tests with
+ | t :: rem ->
+ let name = t.fullName "."
+ let mutable success = false
+
+ Report.Begin $"({index + 1} / {count}) {name}"
+
+ match t.test with
+ | Sync f ->
+ try
+ f()
+ success <- true
+ inc &passed
+ with exn ->
+ Log.error "%A" exn
+ failed.Add name
+ | _ ->
+ Log.error "Test not supported"
+ skipped.Add name
+
+ Report.End(if success then " - passed" else " - failed") |> ignore
+
+ if success || not stopOnFail then
+ run (index + 1) rem
+
+ | _ ->
+ ()
+
+ run 0 tests
+ printSummary()
+ if failed.Count = 0 then 0 else 1
+
[]
module ``RenderTo Utilities`` =
open Aardvark.SceneGraph