diff --git a/README.md b/README.md index 2bf2ea738..620a226b7 100644 --- a/README.md +++ b/README.md @@ -3,49 +3,14 @@ ![Linux](https://github.com/aardvark-platform/aardvark.rendering/workflows/Linux/badge.svg) [![Discord](https://badgen.net/discord/online-members/UyecnhM)](https://discord.gg/UyecnhM) -[![license](https://img.shields.io/github/license/aardvark-platform/aardvark.rendering.svg)](https://github.com/aardvark-platform/aardvark.rendering/blob/master/LICENSE) +[![license](https://img.shields.io/github/license/aardvark-platform/aardvark.base.svg)](https://github.com/aardvark-platform/aardvark.rendering/blob/master/LICENSE) [The Aardvark Platform](https://aardvarkians.com/) | -[Platform Wiki](https://github.com/aardvarkplatform/aardvark.docs/wiki) | -[aardvark.rendering Examples](%2Fsrc%2FExamples%20(netcore)) | -[Technical Walkthrough and Examples](https://github.com/aardvark-platform/walkthrough) | -[Platform Examples](https://github.com/aardvark-platform/aardvark.docs/wiki/Examples) | -[Gallery](https://github.com/aardvarkplatform/aardvark.docs/wiki/Gallery) | -[Quickstart](https://github.com/aardvarkplatform/aardvark.docs/wiki/Quickstart-Windows) | -[Status](https://github.com/aardvarkplatform/aardvark.docs/wiki/Status) +[Gallery](https://github.com/aardvark-platform/aardvark.docs/wiki/Gallery) | +[Packages&Repositories](https://github.com/aardvark-platform/aardvark.docs/wiki/Packages-and-Repositories) -Aardvark.Rendering is part of the open-source [Aardvark platform](https://github.com/aardvark-platform/aardvark.docs/wiki) for visual computing, real-time graphics and visualization. This repository heavily builds on datastructures and tools from [aardvark.base](https://github.com/aardvark-platform/aardvark.base). The engine can be used standalone or using [aardvark.media](https://github.com/aardvark-platform/aardvark.media) which provides purely functional ELM style application abstraction. +The Aardvark.Rendering engine is a high-performance engine that tries to bridge the gap between efficiency and high-level easy-to-use abstractions. The engine is used in applied research and industry as well as basic research, and heavily embraces incremental computation. It tracks all changes in the scene description and automatically updates affected parts in the incrementally maintained optimization data structures. The engine currently has two backends: OpenGL and Vulkan, runs on netstandard, and is basically platform independent. It was born in 2006 and was mostly written in C#, but later moved towards functional programming. Now, most of the code is written in F#. Supported platforms are windows, linux, macOS. Render backends exist for OpenGL and Vulkan. -![Alt text](./data/context.svg) - - -The aardvark rendering engine was the vital spark which finally led to the complete ecosystem of libraries, we now call aardvark-platform. The engine as such was born in 2006. At that time, the engine was written mostly in C# (which was rather unusual in a world of highly optimized C++ engines). Later we more and more moved towards functional programming. In various rewrites we modernized the engine over and over again. Now most code is written in F#. The unique features of the engine are: - - The engine tries to bridge the gap between *efficiency* and *high-level easy to use abstractions*. We used a lot of energy to get out good performance for a lot cases. We did a lot but we look forward to getting better and better in this regard. [This video](https://www.youtube.com/watch?v=QjVRJworUOw) demonstrates the rapid prototyping features of aardvark. - - It is used in applied research and industry but it is also used as vehicle for basic research. - - The engine heavily embraces *incremental computation*. Rendering engines typically use some form of scene description which is then interpreted by the rendering kernel. The interpretation of large scenes quickly becomes a [bottleneck](https://www.cg.tuwien.ac.at/courses/RendEng/2015/RendEng-2015-11-16-paper2.pdf). Aardvark by contrast tracks all changes in the scene description and automatically updates affected parts in the *incrementally maintained optimization datstructures*. The approach was published in a paper [An Incremental Rendering VM](https://www.vrvis.at/publications/pdfs/PB-VRVis-2015-015.pdf). The scene graph concept and implementation is published in the paper [Attribute Grammars for Incremental Scene Graph Rendering](https://www.vrvis.at/publications/pdfs/PB-VRVis-2019-004.pdf). - - The engine currently has two backends: OpenGL and Vulkan, runs on netstandard and is basically platform independent - - Not like classic rendering engines, the aardvark rendering engine does not provide any tooling such as level editors etc. but lives from the aardvark platform as whole which provides tools to *create customized tooling for various needs*. - - Aardvark does not understand light, shadows or particular material workflows as in most game engines. Instead, the codebase provides a *rich set of tools* to customize those features to fit the needs. - - For application and UI programming we recommend to climb the abstraction ladder up towards [aardvark.media](https://github.com/aardvark-platform/aardvark.media) which provides easy-to-use ELM style API to both UI and high-performance computer graphics. - -We are constantly looking for cool contributions ideas etc! Meet us on [Discord](https://discord.gg/UyecnhM) - -To list some, the most important [packages found on nuget](https://www.nuget.org/packages?q=aardvark.Rendering.*) are: -- Aardvark.Rendering -- Aardvark.Rendering.GL -- Aardvark.Rendering.Vulkan -- Aardvark.SceneGraph -- Aardvark.Application.WPF.GL -- Aardvark.Application.WPF -- Aardvark.Application.WinForms.GL -- Aardvark.Application.WinForms.Vulkan -- Aardvark.Application.WinForms -- Aardvark.Application -- Aardvark.GPGPU -- Aardvark.Application.OpenVR -- Aardvark.Application.Slim -- Aardvark.Application.Slim.GL -- Aardvark.Application.Slim.Vulkan -- Aardvark.Rendering.Text -- Aardvark.SceneGraph.IO +You can find demos and code in the Gallery and Packages&Repositories links above. Additionally, examples are available in the `src/Examples*` folder in this repository. For more information, please refer to the [aardvark.docs wiki](https://github.com/aardvark-platform/aardvark.docs/wiki). +Aardvark.Rendering is part of the open-source [Aardvark Platform](https://github.com/aardvark-platform) for visual computing, real-time graphics, and visualization. Aardvark.Rendering is a stand-alone rendering engine that builds on basic data structures and tools from [Aardvark.Base](https://github.com/aardvark-platform/aardvark.base). It is also integrated into [Aardvark.Media](https://github.com/aardvark-platform/aardvark.media), which provides web-based user interfaces and ELM-style application development. [Aardvark.Algodat](https://github.com/aardvark-platform/aardvark.algodat) provides advanced geometry tooling and algorithms, including out-of-core point cloud rendering and PolyMesh processing algorithms. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8fa68b40e..dbb19920b 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,24 @@ +### 5.4.12 +- [GL] Fixed potential memory leak after ContextHandle is disposed +- Optimized generic dispatch +- Fixed potential leaks with ConcurrentDictionary.GetOrAdd + +### 5.4.11 +- [Application.WPF.GL] SharingRenderControl implementation now uses Silk.NET.Direct3D9 instead of SharpDX +- Removed SharpDX dependency +- Re-added dynamic shader caches +- Fixed multi-threading issue in PrimitiveValueConverter +- [Sg] Use single value attributes for IndexedGeometry +- [IndexedGeometry] Fixed Union() and added ToIndexed() overload +- [IndexedGeometry] Added overload Clone() for deep copy + +### 5.4.10 +- [OpenVR] changed GL texture submit to 2 textures (previously side by side, issue with Quest 3) +- [GL] Improved querying of supported sample counts +- [GL] Fixed double disposal of Context +- [GLFW] Fixed OpenTK context interop +- [Vulkan] Fixed conservative raster validation error + ### 5.4.10-prerelease0006 - rebuild glvm for ARM64 @@ -9,12 +30,29 @@ ### 5.4.10-prerelease0003 - [FontResolve] fixed null family name failure + ### 5.4.10-prerelease0002 - [PathSegment] minor fixes ### 5.4.10-prerelease0001 - [Text] improved Font resolver for Windows and MacOS -- [PathSegment] fixed several PathSegment tools and added a few new ones### 5.4.7 +- [PathSegment] fixed several PathSegment tools and added a few new ones + +### 5.4.9 +- [LodRenderer] Handle exceptions in background threads +- [GL] Implemented GLSL shader caches for platforms that do not support program binaries (e.g. MacOS) + +### 5.4.8 +- [GL] Fixed locking order of GlobalResourceLock and context locks to avoid potential deadlocks +- [GL] Added workaround for layered rendering and GLSL < 430 +- [GL] Made context creation and sharing more robust (see RuntimeConfig.RobustContextSharing) +- [GL] Improved disposal of ContextHandle +- [GLVM / VKVM] Updated ARM64 binaries +- [GLFW] Fixed context resource leaks +- [GLFW] Reset GetCurrentContext on disposal +- [WinForms / WPF] Removed double dispose of context + +### 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) @@ -33,7 +71,6 @@ - [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) - [ManagedPool] Avoid evaluating draw call set if not active diff --git a/lib/Native/Aardvark.Rendering.GL/mac/ARM64/libglvm.dylib b/lib/Native/Aardvark.Rendering.GL/mac/ARM64/libglvm.dylib index cb05c7af8..227c9cb8a 100755 Binary files a/lib/Native/Aardvark.Rendering.GL/mac/ARM64/libglvm.dylib and b/lib/Native/Aardvark.Rendering.GL/mac/ARM64/libglvm.dylib differ diff --git a/lib/Native/Aardvark.Rendering.Vulkan/mac/ARM64/libvkvm.dylib b/lib/Native/Aardvark.Rendering.Vulkan/mac/ARM64/libvkvm.dylib index a9330f6e3..3d07ff0e5 100755 Binary files a/lib/Native/Aardvark.Rendering.Vulkan/mac/ARM64/libvkvm.dylib and b/lib/Native/Aardvark.Rendering.Vulkan/mac/ARM64/libvkvm.dylib differ diff --git a/paket.dependencies b/paket.dependencies index 0d2c7ecd9..1ea45874b 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -32,7 +32,6 @@ nuget CommonMark.NET ~> 0.15.1 nuget GLSLangSharp ~> 0.4.14 nuget Unofficial.LibTessDotNet ~> 2.0.2 -nuget SharpDX.Direct3D9 ~> 4.0.1 nuget AssimpNet ~> 5.0.0-beta1 nuget Offler ~> 2.0.3 nuget FSharp.Data ~> 4.2.10 @@ -41,10 +40,13 @@ nuget Unofficial.OpenTK ~> 3.0.21 nuget Unofficial.OpenTK.GLControl ~> 3.0.21 nuget Silk.NET.GLFW = 2.15.0 nuget Silk.NET.Core = 2.15.0 +nuget Silk.NET.Direct3D9 = 2.15.0 nuget SharpZipLib ~> 1.4.1 nuget FuzzySharp ~> 2.0.2 +nuget Aardvark.PixImage.Pfim ~> 0.9.2 + group Test framework: net6.0 source https://api.nuget.org/v3/index.json diff --git a/paket.lock b/paket.lock index 3e9e9a5df..c6bc48a56 100644 --- a/paket.lock +++ b/paket.lock @@ -10,102 +10,106 @@ 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.28) - Aardvark.Base.Telemetry (5.2.28) + Aardvark.Base (5.2.32) + Aardvark.Base.Telemetry (5.2.32) 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.28) - Aardvark.Base (5.2.28) + Aardvark.Base.Essentials (5.2.32) + Aardvark.Base (5.2.32) System.Collections.Immutable (>= 5.0) - Aardvark.Base.FSharp (5.2.28) - Aardvark.Base (5.2.28) + Aardvark.Base.FSharp (5.2.32) + Aardvark.Base (5.2.32) 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.28) - Aardvark.Base (5.2.28) - Aardvark.Base.FSharp (5.2.28) + Aardvark.Base.Incremental (5.2.32) + Aardvark.Base (5.2.32) + Aardvark.Base.FSharp (5.2.32) 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.28) - Aardvark.Base (5.2.28) - Aardvark.Base.Tensors (5.2.28) + Aardvark.Base.IO (5.2.32) + Aardvark.Base (5.2.32) + Aardvark.Base.Tensors (5.2.32) System.Dynamic.Runtime (>= 4.3 < 4.4) - Aardvark.Base.Runtime (5.2.28) - Aardvark.Base.FSharp (5.2.28) - Aardvark.Base.Incremental (5.2.28) + Aardvark.Base.Runtime (5.2.32) + Aardvark.Base.FSharp (5.2.32) + Aardvark.Base.Incremental (5.2.32) 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.28) - Aardvark.Base.Tensors (5.2.28) - Aardvark.Base (5.2.28) - Aardvark.Base.FSharp (5.2.28) + Aardvark.Base.Telemetry (5.2.32) + Aardvark.Base.Tensors (5.2.32) + Aardvark.Base (5.2.32) + Aardvark.Base.FSharp (5.2.32) 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.21) - Aardvark.Geometry (5.2.28) - Aardvark.Base (5.2.28) - Aardvark.Base.FSharp (5.2.28) - Aardvark.Base.Tensors (5.2.28) + Aardvark.Build (1.0.25) + Aardvark.Geometry (5.2.32) + Aardvark.Base (5.2.32) + Aardvark.Base.FSharp (5.2.32) + Aardvark.Base.Tensors (5.2.32) 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) Unofficial.LibTessDotNet (>= 2.0.2 < 2.1) + Aardvark.PixImage.Pfim (0.9.2) + Aardvark.Base.Tensors (>= 5.2.32 < 5.3) + FSharp.Core (>= 5.0.1) + Pfim (>= 0.11.2 < 0.12) AssimpNet (5.0.0-beta1) NETStandard.Library (>= 1.6.1) - restriction: || (&& (== net471) (< net35)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) CommonMark.NET (0.15.1) - CSharp.Data.Adaptive (1.2.14) + CSharp.Data.Adaptive (1.2.15) FSharp.Core (>= 4.7) - FSharp.Data.Adaptive (1.2.14) + FSharp.Data.Adaptive (1.2.15) System.Reflection.Emit.Lightweight (>= 4.6) - 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) + FShade (5.5.4) + FShade.Core (5.5.4) + FShade.GLSL (5.5.4) + FShade.Imperative (5.5.4) + FShade.SpirV (5.5.4) + FShade.Core (5.5.4) Aardvark.Base (>= 5.2.19 < 5.3) Aardvark.Base.FSharp (>= 5.2.19 < 5.3) - FShade.Imperative (5.5.3) + FShade.Imperative (5.5.4) FSharp.Core (>= 5.0) - FShade.Debug (5.5.3) + FShade.Debug (5.5.4) Aardvark.Base (>= 5.2.19 < 5.3) Aardvark.Base.FSharp (>= 5.2.19 < 5.3) - FShade.Core (5.5.3) - FShade.Imperative (5.5.3) + FShade.Core (5.5.4) + FShade.Imperative (5.5.4) FSharp.Core (>= 5.0) - FShade.GLSL (5.5.3) + FShade.GLSL (5.5.4) Aardvark.Base (>= 5.2.19 < 5.3) Aardvark.Base.FSharp (>= 5.2.19 < 5.3) - FShade.Core (5.5.3) - FShade.Imperative (5.5.3) + FShade.Core (5.5.4) + FShade.Imperative (5.5.4) FSharp.Core (>= 5.0) - FShade.Imperative (5.5.3) + FShade.Imperative (5.5.4) 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.3) + FShade.SpirV (5.5.4) Aardvark.Base (>= 5.2.19 < 5.3) Aardvark.Base.FSharp (>= 5.2.19 < 5.3) - FShade.Core (5.5.3) - FShade.Imperative (5.5.3) + FShade.Core (5.5.4) + FShade.Imperative (5.5.4) FSharp.Core (>= 5.0) FSharp.Core (5.0.1) FSharp.Data (4.2.10) FSharp.Core (>= 5.0.1) - FSharp.Data.Adaptive (1.2.14) + FSharp.Data.Adaptive (1.2.15) FSharp.Core (>= 4.7) System.Reflection.Emit.Lightweight (>= 4.6) FsPickler (5.3.2) @@ -116,49 +120,32 @@ NUGET FuzzySharp (2.0.2) GLSLangSharp (0.4.15) FSharp.Core (>= 5.0) - Microsoft.Bcl.AsyncInterfaces (6.0) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Bcl.AsyncInterfaces (8.0) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) Microsoft.Bcl.HashCode (1.1.1) - restriction: || (== net471) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) Microsoft.CSharp (4.7) Microsoft.DotNet.PlatformAbstractions (3.1.6) System.Runtime.InteropServices.RuntimeInformation (>= 4.0) - restriction: || (== net471) (&& (== net6.0) (>= net45)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (>= net45)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== netstandard2.0) (>= net45)) - Microsoft.Extensions.DependencyModel (6.0) - System.Buffers (>= 4.5.1) - System.Memory (>= 4.5.4) - System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Runtime.InteropServices.RuntimeInformation (>= 4.3) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== netstandard2.0) (>= net461)) - System.Text.Encodings.Web (>= 6.0) - System.Text.Json (>= 6.0) - Microsoft.NETCore.App (2.2.8) - restriction: || (&& (== net471) (>= netcoreapp1.0)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.NETCore.DotNetHostPolicy (>= 2.2.8) - restriction: || (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp2.2)) - Microsoft.NETCore.Platforms (>= 2.2.4) - restriction: || (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp2.2)) - Microsoft.NETCore.Targets (>= 2.0) - restriction: || (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp2.2)) - NETStandard.Library (>= 2.0.3) - restriction: || (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp2.2)) - Microsoft.NETCore.DotNetAppHost (6.0.5) - restriction: || (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp2.2)) - Microsoft.NETCore.DotNetHostPolicy (6.0.5) - restriction: || (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp2.2)) - Microsoft.NETCore.DotNetHostResolver (>= 6.0.5) - Microsoft.NETCore.DotNetHostResolver (6.0.5) - restriction: || (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp2.2)) - Microsoft.NETCore.DotNetAppHost (>= 6.0.5) - Microsoft.NETCore.Platforms (6.0.3) - restriction: || (&& (== net471) (< net45)) (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) - Microsoft.NETCore.Targets (5.0) - restriction: || (&& (== net471) (< net45)) (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) + Microsoft.Extensions.DependencyModel (8.0.1) + System.Buffers (>= 4.5.1) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.5) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) + System.Text.Encodings.Web (>= 8.0) + System.Text.Json (>= 8.0.4) + Microsoft.NETCore.Platforms (7.0.4) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) + Microsoft.NETCore.Targets (5.0) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETFramework.ReferenceAssemblies (1.0) Microsoft.NETFramework.ReferenceAssemblies.net471 (>= 1.0) - restriction: || (== net471) (&& (== net6.0) (>= net471)) (&& (== net6.0-windows7.0) (>= net471)) (&& (== netstandard2.0) (>= net471)) - Microsoft.NETFramework.ReferenceAssemblies.net471 (1.0.2) - restriction: || (== net471) (&& (== net6.0) (>= net471)) (&& (== net6.0-windows7.0) (>= net471)) (&& (== netstandard2.0) (>= net471)) - NETStandard.Library (2.0.3) - restriction: || (&& (== net471) (< net35)) (&& (== net471) (< net40)) (&& (== net471) (>= netcoreapp2.2)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) + Microsoft.NETFramework.ReferenceAssemblies.net471 (1.0.3) - restriction: || (== net471) (&& (== net6.0) (>= net471)) (&& (== net6.0-windows7.0) (>= net471)) (&& (== netstandard2.0) (>= net471)) + NETStandard.Library (2.0.3) - restriction: || (&& (== net471) (< net35)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Platforms (>= 1.1) - Newtonsoft.Json (13.0.1) + Newtonsoft.Json (13.0.3) Offler (2.0.9) Aardium (2.0.9) Aardvark.Base.Tensors (>= 5.1.9) FSharp.Core (>= 4.7) FSys (>= 0.0.1 < 0.1) Newtonsoft.Json (>= 12.0.3) - SharpDX (4.2) - NETStandard.Library (>= 1.6.1) - restriction: || (&& (== net471) (< net40)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) - SharpDX.Direct3D9 (4.0.1) - Microsoft.NETCore.App (>= 1.0.5) - restriction: || (&& (== net471) (>= netcoreapp1.0)) (== net6.0) (== net6.0-windows7.0) (&& (== netstandard2.0) (>= netcoreapp1.0)) - NETStandard.Library (>= 1.6.1) - restriction: || (&& (== net471) (== net6.0)) (&& (== net471) (== net6.0-windows7.0)) (&& (== net471) (< net40)) (&& (== net6.0) (< netcoreapp1.0)) (&& (== net6.0-windows7.0) (< netcoreapp1.0)) (== netstandard2.0) - SharpDX (>= 4.0.1) + Pfim (0.11.2) SharpZipLib (1.4.2) System.Memory (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.2) - restriction: || (== net471) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) @@ -170,22 +157,28 @@ NUGET System.Memory (>= 4.5.4) System.Numerics.Vectors (>= 4.5) System.Runtime.CompilerServices.Unsafe (>= 6.0) + Silk.NET.Direct3D9 (2.15) + Silk.NET.Core (>= 2.15) + Silk.NET.Maths (>= 2.15) Silk.NET.GLFW (2.15) Silk.NET.Core (>= 2.15) Ultz.Native.GLFW (>= 3.3.3.1) - SixLabors.ImageSharp (2.1.3) + Silk.NET.Maths (2.21) + Microsoft.Bcl.HashCode (>= 1.1.1) - restriction: || (== net471) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) + Ultz.Bcl.Half (>= 1.0) - restriction: || (== net471) (&& (== net6.0) (< net5.0)) (&& (== net6.0) (>= net6.0-android)) (&& (== net6.0) (>= net6.0-ios)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< net5.0)) (&& (== net6.0-windows7.0) (>= net6.0-android)) (&& (== net6.0-windows7.0) (>= net6.0-ios)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) + SixLabors.ImageSharp (2.1.9) System.Buffers (>= 4.5.1) - restriction: || (== net471) (&& (== net6.0) (>= net472)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0-windows7.0) (>= net472)) (&& (== net6.0-windows7.0) (< netcoreapp2.1)) (== netstandard2.0) System.Memory (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net472)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0-windows7.0) (>= net472)) (&& (== net6.0-windows7.0) (< netcoreapp2.1)) (== netstandard2.0) System.Numerics.Vectors (>= 4.5) - restriction: || (== net471) (&& (== net6.0) (>= net472)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0-windows7.0) (>= net472)) (&& (== net6.0-windows7.0) (< netcoreapp2.1)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 5.0) System.Text.Encoding.CodePages (>= 5.0) - System.Buffers (4.5.1) + System.Buffers (4.5.1) - restriction: || (== net471) (&& (== net6.0) (>= net472)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0-windows7.0) (>= net472)) (&& (== net6.0-windows7.0) (< netcoreapp2.1)) (== netstandard2.0) System.Collections (4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Targets (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Runtime (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) - System.Collections.Immutable (6.0) - System.Memory (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) + System.Collections.Immutable (8.0) + System.Memory (>= 4.5.5) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) System.Diagnostics.Debug (4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) @@ -242,7 +235,7 @@ NUGET System.Threading (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Memory (4.5.5) System.Buffers (>= 4.5.1) - restriction: || (== net471) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net6.0-windows7.0) (>= monotouch)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp2.0)) (&& (== net6.0-windows7.0) (< netstandard1.1)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (>= xamarinios)) (&& (== net6.0-windows7.0) (>= xamarinmac)) (&& (== net6.0-windows7.0) (>= xamarintvos)) (&& (== net6.0-windows7.0) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net471) (== net6.0)) (&& (== net471) (== net6.0-windows7.0)) (&& (== net471) (< net45)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0-windows7.0) (< netcoreapp2.0)) (== netstandard2.0) + System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net471) (< net45)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0-windows7.0) (< netcoreapp2.0)) (== netstandard2.0) System.Numerics.Vectors (>= 4.5) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== netstandard2.0) (>= net461)) System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (== net471) (&& (== net6.0) (>= monotouch)) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.0)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0) (>= xamarinios)) (&& (== net6.0) (>= xamarinmac)) (&& (== net6.0) (>= xamarintvos)) (&& (== net6.0) (>= xamarinwatchos)) (&& (== net6.0-windows7.0) (>= monotouch)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp2.0)) (&& (== net6.0-windows7.0) (< netcoreapp2.1)) (&& (== net6.0-windows7.0) (< netstandard1.1)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (>= uap10.1)) (&& (== net6.0-windows7.0) (>= xamarinios)) (&& (== net6.0-windows7.0) (>= xamarinmac)) (&& (== net6.0-windows7.0) (>= xamarintvos)) (&& (== net6.0-windows7.0) (>= xamarinwatchos)) (== netstandard2.0) System.Numerics.Vectors (4.5) @@ -259,17 +252,18 @@ NUGET System.Reflection.Primitives (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Runtime (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Reflection.Emit (4.7) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) - System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net471) (== net6.0)) (&& (== net471) (== net6.0-windows7.0)) (&& (== net471) (< net45)) (&& (== net471) (< netstandard1.1)) (&& (== net471) (< netstandard2.0) (>= wpa81)) (&& (== net471) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0-windows7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< netstandard1.1)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (>= uap10.1)) (== netstandard2.0) + System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net471) (< net45)) (&& (== net471) (< netstandard1.1)) (&& (== net471) (< netstandard2.0) (>= wpa81)) (&& (== net471) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard1.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0-windows7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< netstandard1.1)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (>= uap10.1)) (== netstandard2.0) System.Reflection.Emit.ILGeneration (4.7) - restriction: || (&& (== net471) (< net45)) (&& (== net471) (< netstandard2.0) (>= wpa81)) (&& (== net471) (< portable-net45+wp8)) (&& (== net471) (>= uap10.1)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Reflection.Emit.Lightweight (4.7) - System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net471) (== net6.0)) (&& (== net471) (== net6.0-windows7.0)) (&& (== net471) (< net45)) (&& (== net471) (< netstandard2.0) (>= wpa81)) (&& (== net471) (< portable-net45+wp8)) (&& (== net471) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0-windows7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (< portable-net45+wp8)) (&& (== net6.0-windows7.0) (>= uap10.1)) (== netstandard2.0) + System.Reflection.Emit.ILGeneration (>= 4.7) - restriction: || (&& (== net471) (< net45)) (&& (== net471) (< netstandard2.0) (>= wpa81)) (&& (== net471) (< portable-net45+wp8)) (&& (== net471) (>= uap10.1)) (&& (== net6.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (< portable-net45+wp8)) (&& (== net6.0) (>= uap10.1)) (&& (== net6.0-windows7.0) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (< portable-net45+wp8)) (&& (== net6.0-windows7.0) (>= uap10.1)) (== netstandard2.0) System.Reflection.Extensions (4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Targets (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Reflection (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Runtime (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) - System.Reflection.Metadata (6.0.1) - restriction: || (== net471) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Collections.Immutable (>= 6.0) + System.Reflection.Metadata (8.0) - restriction: || (== net471) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) + System.Collections.Immutable (>= 8.0) + System.Memory (>= 4.5.5) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) System.Reflection.Primitives (4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Targets (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) @@ -289,27 +283,26 @@ NUGET Microsoft.NETCore.Platforms (>= 1.1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Targets (>= 1.1.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Runtime (>= 4.3.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) - System.Runtime.InteropServices.RuntimeInformation (4.3) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== netstandard2.0) (>= net461)) + System.Runtime.InteropServices.RuntimeInformation (4.3) - restriction: || (== net471) (&& (== net6.0) (>= net45)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (>= net45)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== netstandard2.0) (>= net45)) System.Text.Encoding (4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Targets (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Runtime (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) - System.Text.Encoding.CodePages (6.0) - System.Memory (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) + System.Text.Encoding.CodePages (8.0) + System.Memory (>= 4.5.5) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Text.Encodings.Web (6.0) - System.Buffers (>= 4.5.1) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) + System.Text.Encodings.Web (8.0) + System.Buffers (>= 4.5.1) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.5) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Text.Json (6.0.4) - Microsoft.Bcl.AsyncInterfaces (>= 6.0) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.Numerics.Vectors (>= 4.5) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) + System.Text.Json (8.0.4) + Microsoft.Bcl.AsyncInterfaces (>= 8.0) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) + System.Memory (>= 4.5.5) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Text.Encodings.Web (>= 6.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (== netstandard2.0) - System.ValueTuple (>= 4.5) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== netstandard2.0) (>= net461)) + System.Text.Encodings.Web (>= 8.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (== netstandard2.0) + System.ValueTuple (>= 4.5) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== netstandard2.0) (>= net462)) System.Threading (4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Runtime (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Threading.Tasks (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) @@ -317,10 +310,15 @@ NUGET Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) Microsoft.NETCore.Targets (>= 1.1) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) System.Runtime (>= 4.3) - restriction: || (&& (== net471) (< net45)) (== net6.0) (== net6.0-windows7.0) (== netstandard2.0) - System.Threading.Tasks.Extensions (4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (4.5.4) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== net6.0-windows7.0) (< net6.0)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0) (< netcoreapp2.1)) (&& (== net6.0) (< netstandard1.0)) (&& (== net6.0) (< netstandard2.0)) (&& (== net6.0) (>= wp8)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== net6.0-windows7.0) (< netcoreapp2.1)) (&& (== net6.0-windows7.0) (< netstandard1.0)) (&& (== net6.0-windows7.0) (< netstandard2.0)) (&& (== net6.0-windows7.0) (>= wp8)) (== netstandard2.0) - System.ValueTuple (4.5) - restriction: || (== net471) (&& (== net6.0) (>= net461)) (&& (== net6.0-windows7.0) (>= net461)) (&& (== netstandard2.0) (>= net461)) - Ultz.Native.GLFW (3.3.3.1) + System.ValueTuple (4.5) - restriction: || (== net471) (&& (== net6.0) (>= net462)) (&& (== net6.0-windows7.0) (>= net462)) (&& (== netstandard2.0) (>= net462)) + Ultz.Bcl.Half (1.0) - restriction: || (== net471) (&& (== net6.0) (< net5.0)) (&& (== net6.0) (>= net6.0-android)) (&& (== net6.0) (>= net6.0-ios)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< net5.0)) (&& (== net6.0-windows7.0) (>= net6.0-android)) (&& (== net6.0-windows7.0) (>= net6.0-ios)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) + Ultz.Bcl.Half.Fallback (>= 1.0) - restriction: || (== net471) (&& (== net6.0) (< net5.0)) (&& (== net6.0-windows7.0) (< net5.0)) (== netstandard2.0) + Ultz.Bcl.Half.Fallback (1.0) - restriction: || (== net471) (&& (== net6.0) (< net5.0)) (&& (== net6.0) (< netcoreapp3.1)) (&& (== net6.0) (< netstandard2.1)) (&& (== net6.0-windows7.0) (< net5.0)) (&& (== net6.0-windows7.0) (< netcoreapp3.1)) (&& (== net6.0-windows7.0) (< netstandard2.1)) (== netstandard2.0) + System.Memory (>= 4.5.4) + System.Runtime.CompilerServices.Unsafe (>= 4.7.1) + Ultz.Native.GLFW (3.4) Unofficial.LibTessDotNet (2.0.2) Unofficial.OpenTK (3.0.21) Unofficial.OpenTK.GLControl (3.0.21) @@ -334,23 +332,18 @@ STORAGE: NONE RESTRICTION: == net6.0 NUGET remote: https://api.nuget.org/v3/index.json - BenchmarkDotNet (0.13.1) - BenchmarkDotNet.Annotations (>= 0.13.1) - CommandLineParser (>= 2.4.3) - Iced (>= 1.8) - Microsoft.CodeAnalysis.CSharp (>= 2.10) - Microsoft.Diagnostics.NETCore.Client (>= 0.2.61701) - Microsoft.Diagnostics.Runtime (>= 1.1.126102) - Microsoft.Diagnostics.Tracing.TraceEvent (>= 2.0.61) - Microsoft.DotNet.PlatformAbstractions (>= 2.1) - Microsoft.Win32.Registry (>= 4.5) - Perfolizer (>= 0.2.1) - System.Management (>= 4.5) - System.Reflection.Emit (>= 4.3) - System.Reflection.Emit.Lightweight (>= 4.3) - System.Threading.Tasks.Extensions (>= 4.5.2) - System.ValueTuple (>= 4.5) - BenchmarkDotNet.Annotations (0.13.1) + BenchmarkDotNet (0.13.12) + BenchmarkDotNet.Annotations (>= 0.13.12) + CommandLineParser (>= 2.9.1) + Gee.External.Capstone (>= 2.3) + Iced (>= 1.17) + Microsoft.CodeAnalysis.CSharp (>= 4.1) + Microsoft.Diagnostics.Runtime (>= 2.2.332302) + Microsoft.Diagnostics.Tracing.TraceEvent (>= 3.0.2) + Microsoft.DotNet.PlatformAbstractions (>= 3.1.6) + Perfolizer (0.2.1) + System.Management (>= 5.0) + BenchmarkDotNet.Annotations (0.13.12) CommandLineParser (2.9.1) Expecto (9.0.4) FSharp.Core (>= 4.6) @@ -361,87 +354,82 @@ NUGET Expecto.Hopac (9.0.4) Expecto (>= 9.0.4) Hopac (>= 0.4.1) - FsCheck (2.16.5) + FsCheck (2.16.6) FSharp.Core (>= 4.2.3) FSharp.Core (5.0.1) + Gee.External.Capstone (2.3) Hopac (0.5.1) FSharp.Core (>= 4.5.2) - Iced (1.17) - Microsoft.Bcl.AsyncInterfaces (6.0) - Microsoft.CodeAnalysis.Analyzers (3.3.3) - Microsoft.CodeAnalysis.Common (4.2) - Microsoft.CodeAnalysis.Analyzers (>= 3.3.3) - System.Collections.Immutable (>= 5.0) - System.Memory (>= 4.5.4) - System.Reflection.Metadata (>= 5.0) + Iced (1.21) + Microsoft.CodeAnalysis.Analyzers (3.3.4) + Microsoft.CodeAnalysis.Common (4.10) + Microsoft.CodeAnalysis.Analyzers (>= 3.3.4) + System.Buffers (>= 4.5.1) + System.Collections.Immutable (>= 8.0) + System.Memory (>= 4.5.5) + System.Numerics.Vectors (>= 4.5) + System.Reflection.Metadata (>= 8.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Text.Encoding.CodePages (>= 6.0) + System.Text.Encoding.CodePages (>= 7.0) System.Threading.Tasks.Extensions (>= 4.5.4) - Microsoft.CodeAnalysis.CSharp (4.2) - Microsoft.CodeAnalysis.Common (4.2) - Microsoft.CodeCoverage (17.8) - Microsoft.Diagnostics.NETCore.Client (0.2.328102) - Microsoft.Bcl.AsyncInterfaces (>= 1.1) - Microsoft.Extensions.Logging (>= 2.1.1) - Microsoft.Diagnostics.Runtime (2.1.327703) - Microsoft.Diagnostics.NETCore.Client (>= 0.2.251802) - System.Collections.Immutable (>= 5.0) - System.Runtime.CompilerServices.Unsafe (>= 5.0) - Microsoft.Diagnostics.Tracing.TraceEvent (3.0.1) - System.Runtime.CompilerServices.Unsafe (>= 4.5.2) - Microsoft.DotNet.PlatformAbstractions (3.1.6) - Microsoft.Extensions.DependencyInjection (6.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) + Microsoft.CodeAnalysis.CSharp (4.10) + Microsoft.CodeAnalysis.Analyzers (>= 3.3.4) + Microsoft.CodeAnalysis.Common (4.10) + System.Buffers (>= 4.5.1) + System.Collections.Immutable (>= 8.0) + System.Memory (>= 4.5.5) + System.Numerics.Vectors (>= 4.5) + System.Reflection.Metadata (>= 8.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - Microsoft.Extensions.DependencyInjection.Abstractions (6.0) - Microsoft.Extensions.Logging (6.0) - Microsoft.Extensions.DependencyInjection (>= 6.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) - Microsoft.Extensions.Logging.Abstractions (>= 6.0) - Microsoft.Extensions.Options (>= 6.0) - System.Diagnostics.DiagnosticSource (>= 6.0) - Microsoft.Extensions.Logging.Abstractions (6.0.1) - Microsoft.Extensions.Options (6.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0) - Microsoft.Extensions.Primitives (>= 6.0) - Microsoft.Extensions.Primitives (6.0) + System.Text.Encoding.CodePages (>= 7.0) + System.Threading.Tasks.Extensions (>= 4.5.4) + Microsoft.CodeCoverage (17.10) + Microsoft.Diagnostics.NETCore.Client (0.2.532401) + Microsoft.Extensions.Logging.Abstractions (>= 6.0.4) + Microsoft.Diagnostics.Runtime (3.1.512801) + Microsoft.Diagnostics.NETCore.Client (>= 0.2.410101) + Microsoft.Diagnostics.Tracing.TraceEvent (3.1.13) + Microsoft.Win32.Registry (>= 5.0) + System.Collections.Immutable (>= 8.0) + System.Reflection.Metadata (>= 8.0) + System.Reflection.TypeExtensions (>= 4.7) System.Runtime.CompilerServices.Unsafe (>= 6.0) + Microsoft.DotNet.PlatformAbstractions (3.1.6) + Microsoft.Extensions.DependencyInjection.Abstractions (8.0.1) + Microsoft.Extensions.Logging.Abstractions (8.0.1) + Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.1) Microsoft.NET.Test.Sdk (17.8) Microsoft.CodeCoverage (>= 17.8) Microsoft.TestPlatform.TestHost (>= 17.8) - Microsoft.TestPlatform.ObjectModel (17.8) - NuGet.Frameworks (>= 6.5) + Microsoft.TestPlatform.ObjectModel (17.10) System.Reflection.Metadata (>= 1.6) - Microsoft.TestPlatform.TestHost (17.8) - Microsoft.TestPlatform.ObjectModel (>= 17.8) + Microsoft.TestPlatform.TestHost (17.10) + Microsoft.TestPlatform.ObjectModel (>= 17.10) Newtonsoft.Json (>= 13.0.1) Microsoft.Win32.Registry (5.0) System.Security.AccessControl (>= 5.0) System.Security.Principal.Windows (>= 5.0) - Mono.Cecil (0.11.4) - Newtonsoft.Json (13.0.1) - NuGet.Frameworks (6.8) + Mono.Cecil (0.11.5) + Newtonsoft.Json (13.0.3) Perfolizer (0.2.1) System.Memory (>= 4.5.3) - System.CodeDom (6.0) - System.Collections.Immutable (6.0) - System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Diagnostics.DiagnosticSource (6.0) + System.Buffers (4.5.1) + System.CodeDom (8.0) + System.Collections.Immutable (8.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - System.Management (6.0) - System.CodeDom (>= 6.0) + System.Management (8.0) + System.CodeDom (>= 8.0) System.Memory (4.5.5) - System.Reflection.Emit (4.7) - System.Reflection.Emit.Lightweight (4.7) - System.Reflection.Metadata (6.0.1) - System.Collections.Immutable (>= 6.0) + System.Numerics.Vectors (4.5) + System.Reflection.Metadata (8.0) + System.Collections.Immutable (>= 8.0) + System.Reflection.TypeExtensions (4.7) System.Runtime.CompilerServices.Unsafe (6.0) - System.Security.AccessControl (6.0) + System.Security.AccessControl (6.0.1) System.Security.Principal.Windows (5.0) - System.Text.Encoding.CodePages (6.0) + System.Text.Encoding.CodePages (8.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) System.Threading.Tasks.Extensions (4.5.4) - System.ValueTuple (4.5) YoloDev.Expecto.TestSdk (0.13.3) Expecto (>= 9.0 < 10.0) FSharp.Core (>= 4.6.2) diff --git a/src/Aardvark.GPGPU/Primitives.fs b/src/Aardvark.GPGPU/Primitives.fs index 7017275a4..fc9b50b0d 100644 --- a/src/Aardvark.GPGPU/Primitives.fs +++ b/src/Aardvark.GPGPU/Primitives.fs @@ -1133,23 +1133,23 @@ type private MapReduceImage<'b when 'b : unmanaged>(runtime : IComputeRuntime, r member x.Dispose() = x.Dispose() type private ExpressionCache() = - let store = System.Collections.Concurrent.ConcurrentDictionary, obj>() + let store = System.Collections.Concurrent.ConcurrentDictionary, Lazy>() member x.GetOrCreate(e : Expr<'a>, create : Expr<'a> -> 'b) = let hash = [ Expr.ComputeHash e ] store.GetOrAdd(hash, fun _ -> - create e :> obj - ) |> unbox<'b> + lazy (create e :> obj) + ).Value |> unbox<'b> member x.GetOrCreate(a : Expr<'a>, b : Expr<'b>, create : Expr<'a> -> Expr<'b> -> 'c) = let hash = [ Expr.ComputeHash a; Expr.ComputeHash b ] store.GetOrAdd(hash, fun _ -> - create a b :> obj - ) |> unbox<'c> + lazy (create a b :> obj) + ).Value |> unbox<'c> member x.Dispose() = for KeyValue(_, obj) in store do - match obj with + match obj.Value with | :? IDisposable as d -> d.Dispose() | _ -> () diff --git a/src/Aardvark.Rendering.GL/Core/Config.fs b/src/Aardvark.Rendering.GL/Core/Config.fs index 213906d0d..1ff526c37 100644 --- a/src/Aardvark.Rendering.GL/Core/Config.fs +++ b/src/Aardvark.Rendering.GL/Core/Config.fs @@ -58,6 +58,16 @@ module RuntimeConfig = /// let mutable NumberOfResourceContexts = 2 + /// + /// Determines if context creation and sharing is robust to work properly in some edge cases. + /// When a context is created a parent context has to be provided to enable sharing. If that parent + /// context happens to be currently in use by another thread, context creation or sharing may fail. + /// If this flag is false, a resource context is used as parent without checking if it is actually available. + /// If true, an additional hidden context will be used as parent. + /// Default is true. + /// + let mutable RobustContextSharing = true + /// /// Specifies the expected depth range of normalized device coordinates. /// Setting a depth range of [0, 1] requires GL_ARB_clip_control or OpenGL 4.5. diff --git a/src/Aardvark.Rendering.GL/Core/Context.fs b/src/Aardvark.Rendering.GL/Core/Context.fs index 719a2d1e6..614de5458 100644 --- a/src/Aardvark.Rendering.GL/Core/Context.fs +++ b/src/Aardvark.Rendering.GL/Core/Context.fs @@ -64,6 +64,14 @@ type ResourceLockDisposable = bag = bag bagCount = bagCount } +[] +type ResourceLockDisposableOptional(inner : ResourceLockDisposable, success : bool) = + static let invalid = new ResourceLockDisposableOptional(new ResourceLockDisposable(), false) + static member Invalid = invalid + member x.Success = success + member x.Dispose() = inner.Dispose() + interface IDisposable with + member x.Dispose() = x.Dispose() type MemoryUsage() = class @@ -176,25 +184,34 @@ type MemoryUsage() = /// multiple threads to submit GL calls concurrently. /// [] -type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this = +type Context(runtime : IRuntime, createContext : ContextHandle option -> ContextHandle) as this = - static let defaultShaderCachePath = + static let defaultShaderCachePath = Path.combine [ CachingProperties.CacheDirectory "Shaders" "OpenGL" ] - let resourceContexts = Array.init RuntimeConfig.NumberOfResourceContexts (fun _ -> createContext()) - let resourceContextCount = resourceContexts.Length + // Hidden unused context for sharing. + // Note: If None is passed to createContext, it's up to the implementation how to choose the parent. + let parentContext = + if RuntimeConfig.RobustContextSharing then Some <| createContext None + else None + + let resourceContexts = + let n = max 1 RuntimeConfig.NumberOfResourceContexts + Array.init n (fun _ -> createContext parentContext) let memoryUsage = MemoryUsage() let bag = ConcurrentBag(resourceContexts) - let bagCount = new SemaphoreSlim(resourceContextCount) + let bagCount = new SemaphoreSlim(resourceContexts.Length) let shaderCache = new ShaderCache() + let mutable isDisposed = 0 + let mutable driverInfo : Option = None let mutable packAlignment : Option = None @@ -217,6 +234,16 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this let mutable numProgramBinaryFormats : Option = None + let mutable maxSamples : Option = None + + let mutable maxColorTextureSamples : Option = None + + let mutable maxIntegerSamples : Option = None + + let mutable maxDepthTextureSamples : Option = None + + let mutable maxFramebufferSamples : Option = None + let mutable shaderCachePath : Option = Some defaultShaderCachePath let formatSampleCounts = FastConcurrentDict() @@ -225,21 +252,26 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this let sharedMemoryManager = SharedMemoryManager(fun _ -> this.ResourceLock) - let getOrQuery (var : byref<'T option>) (query : unit -> 'T) = + let getOrQuery (description : string) (var : byref<'T option>) (query : unit -> 'T) = match var with | None -> use __ = this.ResourceLock let value = query() + GL.Check $"Failed to query {description}" var <- Some value value | Some v -> v + [] + new (runtime : IRuntime, createContext : unit -> ContextHandle) = + new Context(runtime, fun (_ : ContextHandle option) -> createContext()) + /// /// Creates custom OpenGl context. Usage: /// let customCtx = app.Context.CreateContext() /// use __ = app.Context.RenderingLock(customCtx) /// - member x.CreateContext() = createContext() + member x.CreateContext() = createContext parentContext member internal x.ShaderCache = shaderCache @@ -258,49 +290,51 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this member x.Runtime = runtime + member x.IsDisposed = isDisposed = 1 + member x.Driver = - getOrQuery &driverInfo Driver.readInfo + getOrQuery "driver info" &driverInfo Driver.readInfo member x.PackAlignment = - getOrQuery &packAlignment (fun _ -> + getOrQuery "pack alignment" &packAlignment (fun _ -> GL.GetInteger(GetPName.PackAlignment) ) member x.UnpackAlignment = - getOrQuery &unpackAlignment (fun _ -> + getOrQuery "unpack alignment" &unpackAlignment (fun _ -> GL.GetInteger(GetPName.UnpackAlignment) ) member x.MaxTextureSize = - getOrQuery &maxTextureSize (fun _ -> + getOrQuery "max texture size" &maxTextureSize (fun _ -> let s = GL.GetInteger(GetPName.MaxTextureSize) V2i s ) member x.MaxTextureSize3D = - getOrQuery &maxTextureSize3d (fun _ -> + getOrQuery "max 3D texture size" &maxTextureSize3d (fun _ -> let s = GL.GetInteger(GetPName.Max3DTextureSize) V3i s ) member x.MaxTextureSizeCube = - getOrQuery &maxTextureSizeCube (fun _ -> + getOrQuery "max cube texture size" &maxTextureSizeCube (fun _ -> GL.GetInteger(GetPName.MaxCubeMapTextureSize) ) member x.MaxTextureArrayLayers = - getOrQuery &maxTextureArrayLayers (fun _ -> + getOrQuery "max texture array layers" &maxTextureArrayLayers (fun _ -> GL.GetInteger(GetPName.MaxArrayTextureLayers) ) member x.MaxRenderbufferSize = - getOrQuery &maxRenderbufferSize (fun _ -> + getOrQuery "max renderbuffer size" &maxRenderbufferSize (fun _ -> let s = GL.GetInteger(GetPName.MaxRenderbufferSize) V2i s ) member x.MaxComputeWorkGroupSize = - getOrQuery &maxComputeWorkGroupSize (fun _ -> + getOrQuery "max compute work group size" &maxComputeWorkGroupSize (fun _ -> let mutable res = V3i.Zero GL.GetInteger(GetIndexedPName.MaxComputeWorkGroupSize, 0, &res.X) GL.GetInteger(GetIndexedPName.MaxComputeWorkGroupSize, 1, &res.Y) @@ -309,15 +343,40 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this ) member x.MaxComputeWorkGroupInvocations = - getOrQuery &maxComputeWorkGroupInvocations (fun _ -> + getOrQuery "max compute work group invocations" &maxComputeWorkGroupInvocations (fun _ -> GL.GetInteger(GetPName.MaxComputeWorkGroupInvocations) ) member x.NumProgramBinaryFormats = - getOrQuery &numProgramBinaryFormats (fun _ -> + getOrQuery "number of program binary formats" &numProgramBinaryFormats (fun _ -> GL.GetInteger(GetPName.NumProgramBinaryFormats) ) + member x.MaxSamples = + getOrQuery "max samples" &maxSamples (fun _ -> + GL.GetInteger(GetPName.MaxSamples) + ) + + member x.MaxColorTextureSamples = + getOrQuery "max color texture samples" &maxColorTextureSamples (fun _ -> + GL.GetInteger(GetPName.MaxColorTextureSamples) + ) + + member x.MaxIntegerSamples = + getOrQuery "max integer samples" &maxIntegerSamples (fun _ -> + GL.GetInteger(GetPName.MaxIntegerSamples) + ) + + member x.MaxDepthTextureSamples = + getOrQuery "max depth texture samples" &maxDepthTextureSamples (fun _ -> + GL.GetInteger(GetPName.MaxDepthTextureSamples) + ) + + member x.MaxFramebufferSamples = + getOrQuery "max framebuffer samples" &maxFramebufferSamples (fun _ -> + GL.GetInteger(unbox 0x9318) + ) + member internal x.ImportMemoryBlock(external : ExternalMemoryBlock) = sharedMemoryManager.Import external @@ -358,7 +417,7 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this new RenderingLockDisposable(ValueSome handle, ValueNone) /// - /// makes one of the underlying context current on the calling thread + /// Makes one of the underlying context current on the calling thread /// and returns a disposable for releasing it again /// member x.ResourceLock : ResourceLockDisposable = @@ -380,7 +439,7 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this let handle = match bag.TryTake() with | (true, handle) -> handle - | _ -> failwith "could not dequeue resource-context" + | _ -> failf "could not dequeue resource-context" // make the obtained handle current handle.MakeCurrent() @@ -390,12 +449,43 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this // no release: put resource context back in bag and reset current to None new ResourceLockDisposable(ValueSome handle, bag, bagCount) + /// + /// Tries to make one of the underlying context current on the calling thread + /// and returns a disposable for releasing it again. If the context was already disposed, + /// the Success member of the returned disposable returns false. + /// + member x.TryResourceLock : ResourceLockDisposableOptional = + try new ResourceLockDisposableOptional(x.ResourceLock, true) + with :? ObjectDisposedException -> ResourceLockDisposableOptional.Invalid /// /// Returns the number of samples supported by the given target and format. /// member internal x.GetFormatSamples(target : ImageTarget, format : TextureFormat) = formatSampleCounts.GetOrCreate((target, format), fun _ -> + let estimate() = + let maxSamples = + [ + x.MaxSamples + + if format.IsColorRenderable then + x.MaxColorTextureSamples + + if format.IsIntegerFormat then + x.MaxIntegerSamples + + if format.HasDepth || format.HasStencil then + x.MaxDepthTextureSamples + ] + |> List.min + |> max 1 + + Report.Line(3, $"[GL] Internal format queries not supported, assuming up to {maxSamples} are supported (target = {target}, format = {format})") + + [1; 2; 4; 8; 16; 32; 64] + |> List.filter ((>=) maxSamples) + |> Set.ofList + if GL.ARB_internalformat_query then let format = TextureFormat.toSizedInternalFormat format @@ -406,12 +496,13 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this let buffer = GL.Dispatch.GetInternalformat(target, format, InternalFormatParameter.Samples, count) GL.Check "could not query sample counts" - Set.ofArray buffer + buffer + |> Set.ofArray + |> Set.add 1 else - Set.empty + estimate() else - Log.warn "[GL] Internal format queries not supported, assuming all sample counts are supported (target = %A, format = %A)" target format - Set.ofList [1; 2; 4; 8; 16; 32; 64] + estimate() ) /// @@ -437,14 +528,16 @@ type Context(runtime : IRuntime, createContext : unit -> ContextHandle) as this /// releases all resources created by the context /// member x.Dispose() = - try - shaderCache.Dispose() - - for i in 0..resourceContextCount-1 do - let s = resourceContexts.[i] - ContextHandle.delete s - with _ -> - () + if Interlocked.Exchange(&isDisposed, 1) = 0 then + try + shaderCache.Dispose() + + for c in resourceContexts do + ContextHandle.delete c + + parentContext |> Option.iter ContextHandle.delete + with exn -> + Log.error "[GL] Failed to dispose context: %A" exn interface IDisposable with member x.Dispose() = x.Dispose() diff --git a/src/Aardvark.Rendering.GL/Core/ContextHandles.fs b/src/Aardvark.Rendering.GL/Core/ContextHandles.fs index 9437f7d0f..e2419562e 100644 --- a/src/Aardvark.Rendering.GL/Core/ContextHandles.fs +++ b/src/Aardvark.Rendering.GL/Core/ContextHandles.fs @@ -62,19 +62,20 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) = let l = obj() let onDisposed = Event() + let mutable isDisposed = false let mutable debugOutput = None let mutable onMakeCurrent : ConcurrentHashSet unit> = null let mutable driverInfo = None static member Current - with get() = + with get() = let curr = current.Value match current.Value with - | ValueSome ctx when ctx.IsCurrent -> curr - | _ -> ValueNone + | ValueSome ctx when ctx.IsCurrent -> curr + | _ -> ValueNone and private set v = current.Value <- v - + [] static member ContextError = contextError.Publish @@ -85,16 +86,17 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) = (handle |> unbox).GetAddress(name) member x.OnMakeCurrent(f : unit -> unit) = + if isDisposed then failwith "Failed to register OnMakeCurrent callback, the context is already disposed!" Interlocked.CompareExchange(&onMakeCurrent, ConcurrentHashSet(), null) |> ignore onMakeCurrent.Add f |> ignore member x.Lock = l member x.WindowInfo = window - + member x.Handle = handle - member x.Driver = + member x.Driver = match driverInfo with | None -> let v = Driver.readInfo() @@ -102,32 +104,37 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) = v | Some v -> v + member x.IsDisposed = isDisposed + member x.IsCurrent = handle.IsCurrent member x.MakeCurrent() = Monitor.Enter l + if isDisposed then + failf' (fun msg -> ObjectDisposedException(null, msg)) "cannot use disposed ContextHandle" + match ContextHandle.Current with - | ValueSome handle -> handle.ReleaseCurrent() - | _ -> () - + | ValueSome handle -> handle.ReleaseCurrent() + | _ -> () + let mutable retry = true while retry do try - handle.MakeCurrent(window) // wglMakeCurrent + handle.MakeCurrent(window) // wglMakeCurrent retry <- false - with - | :? OpenTK.Graphics.GraphicsContextException as ex -> - Log.line "context error triggered" - let args = ContextErrorEventArgs(ex.Message) - contextError.Trigger(x, args) - retry <- args.Retry - if retry then - Log.line "application requested retry" - Thread.Sleep 100 - else - reraise() + with + | :? OpenTK.Graphics.GraphicsContextException as ex -> + Log.line "context error triggered" + let args = ContextErrorEventArgs(ex.Message) + contextError.Trigger(x, args) + retry <- args.Retry + if retry then + Log.line "application requested retry" + Thread.Sleep 100 + else + reraise() ContextHandle.Current <- ValueSome x @@ -148,23 +155,23 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) = member x.Use (action : unit -> 'a) = match ContextHandle.Current with - | ValueSome h -> - if h = x then - action() - else - try - h.ReleaseCurrent() - x.MakeCurrent() - action() - finally - x.ReleaseCurrent() - h.MakeCurrent() - | ValueNone -> + | ValueSome h -> + if h = x then + action() + else try + h.ReleaseCurrent() x.MakeCurrent() action() finally x.ReleaseCurrent() + h.MakeCurrent() + | ValueNone -> + try + x.MakeCurrent() + action() + finally + x.ReleaseCurrent() /// Sets default API states and initializes the debug output if required. member x.Initialize (debug : IDebugConfig, [] setDefaultStates : bool) = @@ -201,8 +208,29 @@ type ContextHandle(handle : IGraphicsContext, window : IWindowInfo) = | _ -> [||] member x.Dispose() = - debugOutput |> Option.iter (fun dbg -> dbg.Dispose()) - onDisposed.Trigger() + let mutable lockTaken = false + + try + Monitor.TryEnter(l, TimeSpan.FromSeconds 1.0, &lockTaken) + + if lockTaken then + if not isDisposed then + + // release potentially pending UnsharedObjects + let actions = Interlocked.Exchange(&onMakeCurrent, null) + if actions <> null then + x.Use(fun () -> + for a in actions do + a() + ) + + isDisposed <- true + debugOutput |> Option.iter (fun dbg -> dbg.Dispose()) + onDisposed.Trigger() + else + Log.warn "[GL] ContextHandle.Dispose() timed out" + finally + if lockTaken then Monitor.Exit l interface IDisposable with member x.Dispose() = x.Dispose() @@ -279,15 +307,23 @@ module ContextHandleOpenTK = () /// - /// creates a new context using the default configuration + /// Creates a new context using the default configuration. + /// The given context is used as parent for sharing. If parent is None, OpenTK chooses a context to use as parent. /// - let create (debug : IDebugConfig) = + let createWithParent (debug : IDebugConfig) (parent : ContextHandle option) = let window, context = let prev = ContextHandle.Current let mode = Graphics.GraphicsMode(ColorFormat(Config.BitsPerPixel), Config.DepthBits, Config.StencilBits, 1, ColorFormat.Empty, Config.Buffers, false) let window = new NativeWindow(16, 16, "background", GameWindowFlags.Default, mode, DisplayDevice.Default) - let context = new GraphicsContext(GraphicsMode.Default, window.WindowInfo, Config.MajorVersion, Config.MinorVersion, Config.ContextFlags); + + let context = + match parent with + | Some p -> + new GraphicsContext(GraphicsMode.Default, window.WindowInfo, p.Handle, Config.MajorVersion, Config.MinorVersion, Config.ContextFlags); + | _ -> + new GraphicsContext(GraphicsMode.Default, window.WindowInfo, Config.MajorVersion, Config.MinorVersion, Config.ContextFlags); + context.MakeCurrent(window.WindowInfo) let ctx = context |> unbox ctx.LoadAll() @@ -310,4 +346,10 @@ module ContextHandleOpenTK = handle.OnDisposed.Add dispose handle.Initialize(debug, setDefaultStates = false) - handle \ No newline at end of file + handle + + /// + /// Creates a new context using the default configuration. + /// + let create (debug : IDebugConfig) = + createWithParent debug None \ 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 5b366087d..2812e386f 100644 --- a/src/Aardvark.Rendering.GL/Core/ShaderCache.fs +++ b/src/Aardvark.Rendering.GL/Core/ShaderCache.fs @@ -5,6 +5,8 @@ open Aardvark.Rendering open System open System.Collections.Concurrent +open System.Collections.Generic +open System.Runtime.CompilerServices [] module internal ShaderCacheKeys = @@ -45,6 +47,7 @@ type internal ShaderCacheEntry(surface : IBackendSurface, destroy : unit -> unit type internal ShaderCache() = let codeCache = ConcurrentDictionary>() let effectCache = ConcurrentDictionary>() + let dynamicCache = ConditionalWeakTable() static let box (destroy : 'T -> unit) (value : Error<'T>) : Error = match value with @@ -62,6 +65,19 @@ type internal ShaderCache() = 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.GetOrAdd(key: obj, signature: IFramebufferSignature, create: unit -> Error<'T>) : Error<'T> = + lock dynamicCache (fun _ -> + let perLayout = + match dynamicCache.TryGetValue key with + | (true, d) -> FSharp.Core.Operators.unbox d + | _ -> + let d = Dictionary>() + dynamicCache.Add(key, d) + d + + perLayout.GetCreate(signature.Layout, ignore >> create) + ) + member x.Entries = [ codeCache.Values; effectCache.Values ] |> Seq.concat diff --git a/src/Aardvark.Rendering.GL/Core/Utilities/Common.fs b/src/Aardvark.Rendering.GL/Core/Utilities/Common.fs index ee795ba4e..f3c9db1d0 100644 --- a/src/Aardvark.Rendering.GL/Core/Utilities/Common.fs +++ b/src/Aardvark.Rendering.GL/Core/Utilities/Common.fs @@ -9,7 +9,7 @@ open Aardvark.Rendering [] module private ErrorUtilities = - let inline failf fmt = + let inline failf' (exn : string -> #exn) fmt = Printf.kprintf (fun str -> let str = if String.IsNullOrEmpty str then "An error occurred" @@ -17,9 +17,12 @@ module private ErrorUtilities = let msg = $"[GL] {str}" Report.Error msg - failwith msg + raise <| exn msg ) fmt + let inline failf fmt = + failf' Exception fmt + [] module TypeSizeExtensions = type Type with diff --git a/src/Aardvark.Rendering.GL/Management/PreparedRenderObject.fs b/src/Aardvark.Rendering.GL/Management/PreparedRenderObject.fs index 5d4d3cda7..e9650a466 100644 --- a/src/Aardvark.Rendering.GL/Management/PreparedRenderObject.fs +++ b/src/Aardvark.Rendering.GL/Management/PreparedRenderObject.fs @@ -18,14 +18,34 @@ open System.Threading // NOTE: Hacky solution for concurrency issues. // This lock is used for all OpenGL render tasks, basically preventing any concurrency. // The Vulkan backend has finer grained control over resource ownership. +// When acquiring this lock an OpenGL context should be current, otherwise deadlocks may occur when trying to acquire a resource context later! module GlobalResourceLock = let lockObj = obj() + [] + type Disposable(lockTaken : bool) = + member x.Dispose() = if lockTaken then Monitor.Exit lockObj + interface IDisposable with + member x.Dispose() = x.Dispose() + let using (action : unit -> 'T) = if RuntimeConfig.AllowConcurrentResourceAccess then action() else lock lockObj action + let inline lock() : Disposable = + let mutable lockTaken = false + + try + if not RuntimeConfig.AllowConcurrentResourceAccess then + Monitor.Enter(lockObj, &lockTaken) + + new Disposable(lockTaken) + + with _ -> + if lockTaken then Monitor.Exit(lockObj) + reraise() + type TextureBindingSlot = | ArrayBinding of IResource | SingleBinding of IResource * IResource @@ -823,26 +843,18 @@ type PreparedCommand(ctx : Context, renderPass : RenderPass, renderObject : Rend for r in x.Resources do r.Update(token, rt) member x.Dispose() = - GlobalResourceLock.using (fun _ -> - if Interlocked.Decrement(&refCount) = 0 then - lock x (fun () -> - let token = try Some ctx.ResourceLock with :? ObjectDisposedException -> None - - match token with - | Some token -> - try - cleanup |> List.iter (fun f -> f()) - x.Release() - cleanup <- [] - resourceStats <- None - resources <- None - finally - token.Dispose() - | None -> - //OpenGL died - () - ) - ) + if Interlocked.Decrement(&refCount) = 0 then + lock x (fun () -> + use contextLock = ctx.TryResourceLock + + if contextLock.Success then + use __ = GlobalResourceLock.lock() + cleanup |> List.iter (fun f -> f()) + x.Release() + cleanup <- [] + resourceStats <- None + resources <- None + ) interface IRenderObject with member x.AttributeScope = Ag.Scope.Root @@ -870,27 +882,28 @@ type PreparedObjectInfo = } member x.Dispose() = - GlobalResourceLock.using (fun _ -> - if x.oIsActive.IsDisposed then failwith "double free" - x.oBeginMode.Dispose() + use __ = x.oContext.ResourceLock + use __ = GlobalResourceLock.lock() - for b in x.oAttributeBuffers do - b.Dispose() + if x.oIsActive.IsDisposed then failwith "double free" + x.oBeginMode.Dispose() - match x.oIndexBinding with - | Some b -> b.Dispose() - | _ -> () + for b in x.oAttributeBuffers do + b.Dispose() - x.oIsActive.Dispose() + match x.oIndexBinding with + | Some b -> b.Dispose() + | _ -> () - match x.oIndirectBuffer with - | Some i -> i.Dispose() - | None -> x.oDrawCallInfos.Dispose() + x.oIsActive.Dispose() - x.oVertexInputBinding.Dispose() + match x.oIndirectBuffer with + | Some i -> i.Dispose() + | None -> x.oDrawCallInfos.Dispose() - x.oActivation.Dispose() - ) + x.oVertexInputBinding.Dispose() + + x.oActivation.Dispose() member x.Resources = seq { diff --git a/src/Aardvark.Rendering.GL/Management/ResourceCache.fs b/src/Aardvark.Rendering.GL/Management/ResourceCache.fs index 1efcb5b6b..e57f5429a 100644 --- a/src/Aardvark.Rendering.GL/Management/ResourceCache.fs +++ b/src/Aardvark.Rendering.GL/Management/ResourceCache.fs @@ -153,7 +153,7 @@ type internal Resource<'Handle, 'View when 'View : unmanaged>(kind : ResourceKin member x.Pointer = pointer and internal ResourceCache<'Handle, 'View when 'View : unmanaged>(parent : Option>, renderTaskLock : Option) = - let store = ConcurrentDictionary, Resource<'Handle, 'View>>() + let store = ConcurrentDictionary, Lazy>>() static let handleNonPrimitive = not typeof<'Handle>.IsPrimitive && not typeof<'Handle>.IsEnum @@ -184,16 +184,18 @@ and internal ResourceCache<'Handle, 'View when 'View : unmanaged>(parent : Optio member x.TryGet(key : list) = match store.TryGetValue(key) with - | (true, v) -> Some v + | (true, v) -> Some v.Value | _ -> None member x.GetOrCreateLocal(key : list, create : unit -> Resource<'Handle, 'View>) = let resource = store.GetOrAdd(key, fun _ -> - let res = create() - res.OnDispose.Add(fun () -> store.TryRemove key |> ignore) - res - ) + lazy ( + let res = create() + res.OnDispose.Add(fun () -> store.TryRemove key |> ignore) + res + ) + ).Value resource.AddRef() resource :> IResource<_,_> @@ -330,5 +332,5 @@ and internal ResourceCache<'Handle, 'View when 'View : unmanaged>(parent : Optio let remaining = store |> Seq.map (fun (KeyValue(_,r)) -> r) |> Seq.toArray for r in remaining do Log.warn "leaking resource: %A" r - r.ForceDispose() + r.Value.ForceDispose() store.Clear() \ No newline at end of file diff --git a/src/Aardvark.Rendering.GL/Management/ResourceManager.fs b/src/Aardvark.Rendering.GL/Management/ResourceManager.fs index bfc92acf6..352994092 100644 --- a/src/Aardvark.Rendering.GL/Management/ResourceManager.fs +++ b/src/Aardvark.Rendering.GL/Management/ResourceManager.fs @@ -203,7 +203,7 @@ type ResourceManager private (parent : Option, ctx : Context, r let textureArrayCache = UnaryCache, _>( - fun _arr -> ConcurrentDictionary[]>() + fun _arr -> ConcurrentDictionary[]>>() ) let staticSamplerStateCache = ConcurrentDictionary>() @@ -413,18 +413,20 @@ type ResourceManager private (parent : Option, ctx : Context, r let properties = samplerType.Properties innerCache.GetOrAdd((slotCount, properties), fun _ -> - Array.init slotCount (fun i -> - let arr = - textureArray |> AVal.map (fun t -> - if i < t.Length then - t.[i] - else - nullTexture - ) - - x.CreateTexture(arr, properties) + lazy ( + Array.init slotCount (fun i -> + let arr = + textureArray |> AVal.map (fun t -> + if i < t.Length then + t.[i] + else + nullTexture + ) + + x.CreateTexture(arr, properties) + ) ) - ) + ).Value member x.CreateSampler (sam : aval) = samplerCache.GetOrCreate(sam, fun () -> { diff --git a/src/Aardvark.Rendering.GL/Resources/FramebufferSignature.fs b/src/Aardvark.Rendering.GL/Resources/FramebufferSignature.fs index f6f97b332..4b84bdc30 100644 --- a/src/Aardvark.Rendering.GL/Resources/FramebufferSignature.fs +++ b/src/Aardvark.Rendering.GL/Resources/FramebufferSignature.fs @@ -46,28 +46,42 @@ module internal FramebufferSignatureContextExtensions = perLayerUniforms : Set) = use __ = x.ResourceLock - let maxSamples = - let counts = [ - if not colorAttachments.IsEmpty then - GL.GetInteger(GetPName.MaxColorTextureSamples) - - if colorAttachments |> Map.exists (fun _ att -> att.Format.IsIntegerFormat) then - GL.GetInteger(GetPName.MaxIntegerSamples) - - if depthStencilAttachment.IsSome then - GL.GetInteger(GetPName.MaxDepthTextureSamples) - ] - - if counts.IsEmpty then GL.GetInteger(GetPName.MaxFramebufferSamples) - else List.min counts - - GL.Check "could not query maximum samples for framebuffer signature" - let samples = - if samples > maxSamples then - Log.warn "[GL] cannot create framebuffer signature with %d samples (using %d instead)" samples maxSamples - maxSamples + if samples = 1 then samples else - samples + let framebufferMaxSamples = max 1 x.MaxFramebufferSamples + + let get fmt = + let rb = x.GetFormatSamples(ImageTarget.Renderbuffer, fmt) + let tex = x.GetFormatSamples(ImageTarget.Texture2DMultisample, fmt) + Set.union rb tex + + let counts = + let all = + Set.ofList [1; 2; 4; 8; 16; 32; 64] + + let color = + colorAttachments + |> Seq.map (fun (KeyValue(_, att)) -> get att.Format) + |> List.ofSeq + + let depthStencil = + depthStencilAttachment + |> Option.map get + |> Option.toList + + all :: (color @ depthStencil) + |> Set.intersectMany + |> Set.filter ((>=) framebufferMaxSamples) + + if counts.Contains samples then samples + else + let fallback = + counts + |> Set.toList + |> List.minBy ((-) samples >> abs) + + Log.warn "[GL] Cannot create framebuffer signature with %d samples (using %d instead)" samples fallback + fallback new FramebufferSignature(x.Runtime, colorAttachments, depthStencilAttachment, samples, layers, perLayerUniforms) \ No newline at end of file diff --git a/src/Aardvark.Rendering.GL/Resources/Program.fs b/src/Aardvark.Rendering.GL/Resources/Program.fs index 2a5272be3..ec0b82b33 100644 --- a/src/Aardvark.Rendering.GL/Resources/Program.fs +++ b/src/Aardvark.Rendering.GL/Resources/Program.fs @@ -640,14 +640,28 @@ module ProgramExtensions = use ms = new MemoryStream(data) deserialize ms + module private GLSLShader = + open System.IO + + let pickle (shader : GLSLShader) = + use ms = new MemoryStream() + GLSLShader.serialize ms shader + ms.ToArray() + + let unpickle (data : byte[]) = + use ms = new MemoryStream(data) + GLSLShader.deserialize ms [] module private Binary = + type Context with + member inline x.SupportsBinaryCache = GL.ARB_get_program_binary && x.NumProgramBinaryFormats > 0 + module Program = let tryGetBinary (program : Program) = - if GL.ARB_get_program_binary && program.Context.NumProgramBinaryFormats > 0 then + if program.Context.SupportsBinaryCache then let length = GL.Dispatch.GetProgramBinaryLength program.Handle GL.Check "failed to get program binary length" @@ -707,12 +721,15 @@ module ProgramExtensions = module private FileCache = open System.IO + type Program with + member inline x.GLSLShader : GLSLShader = { code = x.Code; iface = x.Interface } + module private Pickling = let tryGetByteArray (program : Program) = program |> Program.tryGetBinary |> Option.map (fun (format, binary) -> ShaderCacheEntry.pickle { - shader = { code = program.Code; iface = program.Interface } + shader = program.GLSLShader hasTess = program.HasTessellation format = format binary = binary @@ -724,7 +741,7 @@ module ProgramExtensions = let entry = ShaderCacheEntry.unpickle data Program.ofShaderCacheEntry context fixBindings entry - let private tryGetCacheFile (context : Context) (key : CodeCacheKey) = + let private tryGetCacheFile (extension : string) (context : Context) (key : CodeCacheKey) = context.ShaderCachePath |> Option.map (fun prefix -> // NOTE: context.Diver represents information obtained by primary context // -> possible that resource context have been created differently @@ -746,34 +763,55 @@ module ProgramExtensions = } let hash = pickler.ComputeHash(key).Hash |> System.Guid - Path.combine [prefix; string hash + ".bin"] + Path.combine [prefix; string hash + "." + extension] ) let write (key : CodeCacheKey) (program : Program) = - tryGetCacheFile program.Context key + let extension, getData = + if program.Context.SupportsBinaryCache then + "bin", fun () -> Pickling.tryGetByteArray program + else + "glsl", fun () -> Some <| GLSLShader.pickle program.GLSLShader + + tryGetCacheFile extension program.Context key |> Option.iter (fun file -> try - let binary = Pickling.tryGetByteArray program + let binary = getData() binary |> Option.iter (File.writeAllBytesSafe file) with | exn -> Log.warn "[GL] Failed to write to shader program file cache '%s': %s" file exn.Message ) - let tryRead (context : Context) (fixBindings : bool) (key : CodeCacheKey) = - tryGetCacheFile context key - |> Option.bind (fun file -> - if File.Exists file then - try - let data = File.readAllBytes file - Some <| Pickling.ofByteArray context fixBindings data - with - | exn -> - Log.warn "[GL] Failed to read from shader program file cache '%s': %s" file exn.Message - None - else + let private tryRead (unpickle : byte[] -> 'T) (file : string) : 'T option = + if File.Exists file then + Report.BeginTimed(4, $"[GL] Reading shader program file cache '%s{file}") + + try + let result = unpickle <| File.readAllBytes file + Report.EndTimed(4, ": success") |> ignore + Some result + with + | exn -> + Report.EndTimed(4, ": failed") |> ignore + Log.warn "[GL] Failed to read from shader program file cache '%s': %s" file exn.Message None - ) + else + None + + let tryReadBinary (context : Context) (fixBindings : bool) (key : CodeCacheKey) = + if context.SupportsBinaryCache then + tryGetCacheFile "bin" context key + |> Option.bind (tryRead (Pickling.ofByteArray context fixBindings)) + else + None + + // GLSL only cache as fallback for platforms that do not support program binaries (e.g. MacOS) + let tryReadGLSL (context : Context) (key : CodeCacheKey) = + if context.SupportsBinaryCache then None + else + tryGetCacheFile "glsl" context key + |> Option.bind (tryRead GLSLShader.unpickle) [] module internal ShaderCacheExtensions = @@ -827,12 +865,16 @@ module ProgramExtensions = { id = id; layout = layout } x.ShaderCache.GetOrAdd(key, fun key -> - match key |> FileCache.tryRead x false with + match key |> FileCache.tryReadBinary x false with | Some program -> Success program | _ -> - let (code, iface) = codeAndInterface.Value + let (code, iface) = + match FileCache.tryReadGLSL x key with + | Some shader -> shader.code, shader.iface + | _ -> codeAndInterface.Value + match code |> ProgramCompiler.tryCompileCompute x iface with | Success program -> program |> FileCache.write key @@ -850,12 +892,15 @@ module ProgramExtensions = { id = id; layout = layout } x.ShaderCache.GetOrAdd(key, fun key -> - match key |> FileCache.tryRead x true with + match key |> FileCache.tryReadBinary x true with | Some program -> Success program | _ -> - let shader = shader.Value + let shader = + FileCache.tryReadGLSL x key + |> Option.defaultWith (fun _ -> shader.Value) + let outputs = shader.iface.outputs |> List.map (fun p -> p.paramName, p.paramLocation) |> Map.ofList match shader |> ProgramCompiler.tryCompile x outputs with @@ -898,33 +943,35 @@ module ProgramExtensions = | Error err -> Error err | Surface.FShade create -> - let (inputLayout,b) = create (signature.EffectConfig(Range1d(-1.0, 1.0), false)) + // Use surface reference as key rather than create, since equality is undefined behavior for F# functions + // See F# specification: 6.9.24 Values with Underspecified Object Identity and Type Identity + x.ShaderCache.GetOrAdd(surface, signature, fun _ -> + let (inputLayout, module_) = create (signature.EffectConfig(Range1d(-1.0, 1.0), false)) - let initial = AVal.force b - let layoutHash = inputLayout.ComputeHash() + let initial = AVal.force module_ + let layoutHash = inputLayout.ComputeHash() - let compile (m : Module) = - try - ModuleCompiler.compileGLSL x.FShadeBackend m - with exn -> - Log.error "%s" exn.Message - reraise() + let compile (m : Module) = + try + ModuleCompiler.compileGLSL x.FShadeBackend m + with exn -> + Log.error "%s" exn.Message + reraise() - let iface = match x.TryCompileProgram(initial.Hash + layoutHash, signature, lazy (compile initial)) with - | Success prog -> prog.Interface - | Error e -> - failf "shader compiler returned errors: %s" e - - let changeableProgram = - b |> AVal.map (fun m -> - match x.TryCompileProgram(m.Hash + layoutHash, signature, lazy (compile m)) with - | Success p -> p - | Error e -> - failf "shader compiler returned errors: %s" e - ) + | Success prog -> + let changeableProgram = + module_ |> AVal.map (fun m -> + match x.TryCompileProgram(m.Hash + layoutHash, signature, lazy (compile m)) with + | Success p -> p + | Error e -> + failf "shader compiler returned errors: %s" e + ) - Success (iface, changeableProgram) + Success (prog.Interface, changeableProgram) + + | Error e -> Error e + ) | Surface.None -> Error "[GL] empty shader" diff --git a/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs b/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs index 6a1f6e2e1..3ea275db3 100644 --- a/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs +++ b/src/Aardvark.Rendering.GL/Resources/Textures/Texture.fs @@ -195,10 +195,9 @@ module internal TextureUtilitiesAndExtensions = let fallback = counts |> Set.toList - |> List.sortBy ((-) samples >> abs) - |> List.head + |> List.minBy ((-) samples >> abs) - Log.warn "[GL] cannot create %A image with %d samples (using %d instead)" format samples fallback + Log.warn "[GL] Cannot create %A image with %d samples (using %d instead)" format samples fallback fallback else 1 diff --git a/src/Aardvark.Rendering.GL/Resources/Textures/TextureCopy.fs b/src/Aardvark.Rendering.GL/Resources/Textures/TextureCopy.fs index 441aec211..a303625c8 100644 --- a/src/Aardvark.Rendering.GL/Resources/Textures/TextureCopy.fs +++ b/src/Aardvark.Rendering.GL/Resources/Textures/TextureCopy.fs @@ -67,19 +67,27 @@ module internal TextureCopyUtilities = [] module private CopyDispatch = + open System.Collections.Concurrent type private Dispatcher() = - static member CopyNativeToNative<'T when 'T : unmanaged>(src : nativeint, srcInfo : Tensor4Info, dst : nativeint, dstInfo : Tensor4Info) = copyBytes<'T> src srcInfo dst dstInfo - module Method = + module CopyNativeToNative = + type Delegate = delegate of nativeint * Tensor4Info * nativeint * Tensor4Info -> unit let private flags = BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public - let copyNativeToNative = typeof.GetMethod("CopyNativeToNative", flags) + let private definition = typeof.GetMethod(nameof Dispatcher.CopyNativeToNative, flags) + let private delegates = ConcurrentDictionary() + + let getDelegate (t: Type) = + delegates.GetOrAdd(t, fun t -> + let mi = definition.MakeGenericMethod [| t |] + unbox <| Delegate.CreateDelegate(typeof, null, mi) + ) let copyBytesTyped (elementType : Type) (src : nativeint) (srcInfo : Tensor4Info) (dst : nativeint) (dstInfo : Tensor4Info) = - let mi = Method.copyNativeToNative.MakeGenericMethod [| elementType |] - mi.Invoke(null, [| src; srcInfo; dst; dstInfo |]) |> ignore + let del = CopyNativeToNative.getDelegate elementType + del.Invoke(src, srcInfo, dst, dstInfo) let copyBytesWithSize (elementSize : int) (src : nativeint) (srcInfo : Tensor4Info) (dst : nativeint) (dstInfo : Tensor4Info) = copyBytesTyped StructTypes.types.[elementSize] src srcInfo dst dstInfo diff --git a/src/Aardvark.Rendering.GL/Runtime/Compute.fs b/src/Aardvark.Rendering.GL/Runtime/Compute.fs index 7025ab52f..08ebe6b5a 100644 --- a/src/Aardvark.Rendering.GL/Runtime/Compute.fs +++ b/src/Aardvark.Rendering.GL/Runtime/Compute.fs @@ -615,15 +615,13 @@ module internal ComputeTaskInternals = let update (token : AdaptiveToken) (renderToken : RenderToken) (action : unit -> 'T) = use __ = renderToken.Use() + use __ = ctx.ResourceLock + use __ = GlobalResourceLock.lock() - GlobalResourceLock.using (fun _ -> - use __ = ctx.ResourceLock + resources.Update(token, renderToken) + compiler.Update(token) - resources.Update(token, renderToken) - compiler.Update(token) - - action() - ) + action() member x.Update(token : AdaptiveToken, renderToken : RenderToken) = x.EvaluateIfNeeded token () (fun token -> diff --git a/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs b/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs index c9ec0e4f5..6747cf500 100644 --- a/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs +++ b/src/Aardvark.Rendering.GL/Runtime/GeometryPool.fs @@ -903,8 +903,8 @@ module GeometryPoolData = if a % b = 0 then a / b else 1 + a / b - static let cullingCache = System.Collections.Concurrent.ConcurrentDictionary() - static let boundCache = System.Collections.Concurrent.ConcurrentDictionary() + static let cullingCache = System.Collections.Concurrent.ConcurrentDictionary>() + static let boundCache = System.Collections.Concurrent.ConcurrentDictionary>() let initialCapacity = Fun.NextPowerOfTwo initialCapacity let adjust (call : DrawCallInfo) = @@ -963,10 +963,12 @@ module GeometryPoolData = do if bounds then culling <- cullingCache.GetOrAdd(ctx, fun ctx -> - let cs = ComputeShader.ofFunction (V3i(1024, 1024, 1024)) CullingShader.culling - let shader = ctx.CompileKernel cs - shader - ) + lazy ( + let cs = ComputeShader.ofFunction (V3i(1024, 1024, 1024)) CullingShader.culling + let shader = ctx.CompileKernel cs + shader + ) + ).Value infoSlot <- culling.Interface.storageBuffers.["infos"].ssbBinding boundSlot <- culling.Interface.storageBuffers.["bounds"].ssbBinding @@ -980,24 +982,26 @@ module GeometryPoolData = countOffset <- nativeint countField.ufOffset boxShader <- boundCache.GetOrAdd(ctx, fun ctx -> - let effect = - FShade.Effect.compose [ - Effect.ofFunction CullingShader.renderBounds - Effect.ofFunction (DefaultSurfaces.constantColor C4f.Red) - ] - - let shader = - lazy ( - let cfg = signature.EffectConfig(Range1d(-1.0, 1.0), false) - effect - |> Effect.toModule cfg - |> ModuleCompiler.compileGLSL ctx.FShadeBackend - ) + lazy ( + let effect = + FShade.Effect.compose [ + Effect.ofFunction CullingShader.renderBounds + Effect.ofFunction (DefaultSurfaces.constantColor C4f.Red) + ] + + let shader = + lazy ( + let cfg = signature.EffectConfig(Range1d(-1.0, 1.0), false) + effect + |> Effect.toModule cfg + |> ModuleCompiler.compileGLSL ctx.FShadeBackend + ) - match ctx.TryCompileProgram(effect.Id, signature, shader) with - | Success v -> v - | Error e -> failwith e - ) + match ctx.TryCompileProgram(effect.Id, signature, shader) with + | Success v -> v + | Error e -> failwith e + ) + ).Value boxBoundSlot <- boxShader.Interface.storageBuffers |> Seq.pick (fun (KeyValue(a,b)) -> if a = "Bounds" then Some b.ssbBinding else None) boxViewProjSlot <- boxShader.Interface.storageBuffers |> Seq.pick (fun (KeyValue(a,b)) -> if a = "ViewProjs" then Some b.ssbBinding else None) diff --git a/src/Aardvark.Rendering.GL/Runtime/LodRenderer.fs b/src/Aardvark.Rendering.GL/Runtime/LodRenderer.fs index 800e7ffcb..4c96e4964 100644 --- a/src/Aardvark.Rendering.GL/Runtime/LodRenderer.fs +++ b/src/Aardvark.Rendering.GL/Runtime/LodRenderer.fs @@ -1816,10 +1816,16 @@ type LodRenderer(manager : ResourceManager, config : LodRendererConfig, roots : dataSize = ref 0L } - + let inline startThreadSafe (name: string) (run: unit -> unit) = + startThread (fun () -> + try + run() + with exn -> + Log.error "[LodRenderer] %s thread faulted: %A" name exn + ) let cameraPrediction = - startThread (fun () -> + startThreadSafe "Camera prediction" (fun () -> let mutable lastTime = time() let mutable lastReport = time() let timer = new MultimediaTimer.Trigger(5) @@ -1884,72 +1890,74 @@ type LodRenderer(manager : ResourceManager, config : LodRendererConfig, roots : //let rootDeltas = ref HDeltaSet.empty let puller = - startThread (fun () -> + startThreadSafe "Puller" (fun () -> let timer = new MultimediaTimer.Trigger(20) let pickTrees = config.pickTrees + let reg = shutdown.Token.Register (System.Action(MVar.put changesPending)) while not shutdown.IsCancellationRequested do timer.Wait() MVar.take changesPending - // atomic fetch - let readers, removed = - lock rootLock (fun () -> - let roots = roots - let removed = List * TaskTreeReader<_>>() - - let delta = HashMap.computeDelta lastRoots roots - lastRoots <- roots - - for k, op in delta do - match op with - | Remove -> - match HashMap.tryRemove k readers with - | Some (r, rs) -> - readers <- rs - removed.Add((Some k, r)) - | None -> - Log.error "[Lod] free of unknown: %A" (k.GetHashCode()) - | Set v -> - match HashMap.tryFind k readers with - | Some o -> - Log.error "[Lod] double add %A" k - removed.Add (None, o) - let r = TaskTreeReader(v) - //rootUniformCache.TryRemove k |> ignore - //rootTrafoCache.TryRemove k |> ignore - //transact (fun () -> rootIds.MarkOutdated()) - readers <- HashMap.add k r readers - - | None -> - let r = TaskTreeReader(v) - readers <- HashMap.add k r readers - readers, removed - ) - - for (root, r) in removed do - match root with - | Some root -> - match pickTrees with - | Some mm -> transact (fun () -> mm.Value <- mm.Value |> HashMap.remove root) - | None -> () - | None -> - () - r.Destroy(renderDelta) - - for (root,r) in readers do - r.Update(renderDelta) - match pickTrees with - | Some mm -> - let trafo = getRootTrafo root - let picky = r.State |> Option.bind (fun s -> SimplePickTree.ofTreeNode trafo s) - transact (fun () -> - match picky with - | Some picky -> mm.Value <- mm.Value |> HashMap.add root picky - | None -> mm.Value <- mm.Value |> HashMap.remove root + if not shutdown.IsCancellationRequested then + // atomic fetch + let readers, removed = + lock rootLock (fun () -> + let roots = roots + let removed = List * TaskTreeReader<_>>() + + let delta = HashMap.computeDelta lastRoots roots + lastRoots <- roots + + for k, op in delta do + match op with + | Remove -> + match HashMap.tryRemove k readers with + | Some (r, rs) -> + readers <- rs + removed.Add((Some k, r)) + | None -> + Log.error "[Lod] free of unknown: %A" (k.GetHashCode()) + | Set v -> + match HashMap.tryFind k readers with + | Some o -> + Log.error "[Lod] double add %A" k + removed.Add (None, o) + let r = TaskTreeReader(v) + //rootUniformCache.TryRemove k |> ignore + //rootTrafoCache.TryRemove k |> ignore + //transact (fun () -> rootIds.MarkOutdated()) + readers <- HashMap.add k r readers + + | None -> + let r = TaskTreeReader(v) + readers <- HashMap.add k r readers + readers, removed ) - | None -> - () + + for (root, r) in removed do + match root with + | Some root -> + match pickTrees with + | Some mm -> transact (fun () -> mm.Value <- mm.Value |> HashMap.remove root) + | None -> () + | None -> + () + r.Destroy(renderDelta) + + for (root,r) in readers do + r.Update(renderDelta) + match pickTrees with + | Some mm -> + let trafo = getRootTrafo root + let picky = r.State |> Option.bind (fun s -> SimplePickTree.ofTreeNode trafo s) + transact (fun () -> + match picky with + | Some picky -> mm.Value <- mm.Value |> HashMap.add root picky + | None -> mm.Value <- mm.Value |> HashMap.remove root + ) + | None -> + () for (root,r) in readers do @@ -1961,7 +1969,7 @@ type LodRenderer(manager : ResourceManager, config : LodRendererConfig, roots : ) let thread = - startThread (fun () -> + startThreadSafe "Update" (fun () -> let notConverged = MVar.create () //new ManualResetEventSlim(true) let cancel = System.Collections.Concurrent.ConcurrentDictionary() @@ -2032,126 +2040,126 @@ type LodRenderer(manager : ResourceManager, config : LodRendererConfig, roots : while not shutdown.IsCancellationRequested do timer.Wait() MVar.take notConverged - shutdown.Token.ThrowIfCancellationRequested() - //caller.EvaluateAlways AdaptiveToken.Top (fun token -> - let view = config.view.GetValue AdaptiveToken.Top - let proj = config.proj.GetValue AdaptiveToken.Top - let maxSplits = config.maxSplits.GetValue AdaptiveToken.Top + if not shutdown.IsCancellationRequested then + //caller.EvaluateAlways AdaptiveToken.Top (fun token -> + let view = config.view.GetValue AdaptiveToken.Top + let proj = config.proj.GetValue AdaptiveToken.Top + let maxSplits = config.maxSplits.GetValue AdaptiveToken.Top - deltas <- - let ops = reader.GetChanges AdaptiveToken.Top - HashSetDelta.combine deltas ops - - let toFree = - lock toFreeUniforms (fun () -> - let r = !toFreeUniforms - toFreeUniforms := HashSet.empty - r + deltas <- + let ops = reader.GetChanges AdaptiveToken.Top + HashSetDelta.combine deltas ops + + let toFree = + lock toFreeUniforms (fun () -> + let r = !toFreeUniforms + toFreeUniforms := HashSet.empty + r + ) + + toFree |> HashSet.iter ( fun node -> + freeRootId node + rootUniforms <- HashMap.remove node rootUniforms ) - - toFree |> HashSet.iter ( fun node -> - freeRootId node - rootUniforms <- HashMap.remove node rootUniforms - ) - if maxSplits > 0 then - let ops = - let d = deltas - deltas <- HashSetDelta.empty - d - - //if ops.Count > 0 then - // lock rootDeltas (fun () -> - // rootDeltas := HDeltaSet.combine !rootDeltas ops - // ) - // MVar.put changesPending () - - //if ops.Count > 0 then - // for o in ops do - // match o with - // | Add(_,v) -> - // Log.warn "add %A" v.root - // | Rem(_,v) -> - // Log.warn "rem %A" v.root - - let roots = - if ops.Count > 0 then - lock rootLock (fun () -> - for o in ops do - match o with - | Add(_,i) -> - let r = i.root - match HashMap.tryFind r roots with - | Some o -> - Log.error "[Lod] add of existing root %A" i.root - | None -> - let u = i.uniforms - rootUniforms <- HashMap.add r u rootUniforms - let rid = getRootId r - let load ct n = load ct rid n (fun _ _ r -> r) - roots <- HashMap.add r (TaskTree(load, rid)) roots - - | Rem(_,i) -> - let r = i.root - match HashMap.tryRemove r roots with - | Some (_, rest) -> + if maxSplits > 0 then + let ops = + let d = deltas + deltas <- HashSetDelta.empty + d + + //if ops.Count > 0 then + // lock rootDeltas (fun () -> + // rootDeltas := HDeltaSet.combine !rootDeltas ops + // ) + // MVar.put changesPending () + + //if ops.Count > 0 then + // for o in ops do + // match o with + // | Add(_,v) -> + // Log.warn "add %A" v.root + // | Rem(_,v) -> + // Log.warn "rem %A" v.root + + let roots = + if ops.Count > 0 then + lock rootLock (fun () -> + for o in ops do + match o with + | Add(_,i) -> + let r = i.root + match HashMap.tryFind r roots with + | Some o -> + Log.error "[Lod] add of existing root %A" i.root + | None -> + let u = i.uniforms + rootUniforms <- HashMap.add r u rootUniforms + let rid = getRootId r + let load ct n = load ct rid n (fun _ _ r -> r) + roots <- HashMap.add r (TaskTree(load, rid)) roots + + | Rem(_,i) -> + let r = i.root + match HashMap.tryRemove r roots with + | Some (_, rest) -> - roots <- rest - | None -> - Log.error "[Lod] remove of nonexisting root %A" i.root - - MVar.put changesPending () - roots - ) - else - lock rootLock (fun () -> roots) + roots <- rest + | None -> + Log.error "[Lod] remove of nonexisting root %A" i.root + + MVar.put changesPending () + roots + ) + else + lock rootLock (fun () -> roots) - let modelView (r : ILodTreeNode) = - let t = getRootTrafo r - subs.GetOrCreate(t, fun t -> t.AddMarkingCallback (MVar.put notConverged)) |> ignore - let m = t.GetValue() - m * view + let modelView (r : ILodTreeNode) = + let t = getRootTrafo r + subs.GetOrCreate(t, fun t -> t.AddMarkingCallback (MVar.put notConverged)) |> ignore + let m = t.GetValue() + m * view - let budget = config.budget.GetValue() - let splitfactor = config.splitfactor.GetValue() + let budget = config.budget.GetValue() + let splitfactor = config.splitfactor.GetValue() - let start = time() - let maxQ = - if budget < 0L then 1.0 - else fst (TreeHelpers.getMaxQuality budget splitfactor (Seq.map fst roots) modelView proj) + let start = time() + let maxQ = + if budget < 0L then 1.0 + else fst (TreeHelpers.getMaxQuality budget splitfactor (Seq.map fst roots) modelView proj) - let dt = time() - start - maxQualityTime.Insert(dt.TotalMilliseconds) |> ignore + let dt = time() - start + maxQualityTime.Insert(dt.TotalMilliseconds) |> ignore - let collapseIfNotSplit = maxQ < 1.0 - let start = time() - let queue = List() - for (k,v) in roots do - v.BuildQueue(state, collapseIfNotSplit, k, maxQ, splitfactor, modelView, proj, queue) + let collapseIfNotSplit = maxQ < 1.0 + let start = time() + let queue = List() + for (k,v) in roots do + v.BuildQueue(state, collapseIfNotSplit, k, maxQ, splitfactor, modelView, proj, queue) - let q, fin = TaskTree<_>.ProcessQueue(state, queue, maxQ, splitfactor, modelView, proj, maxSplits) - lastQ <- q - let dt = time() - start - expandTime.Insert dt.TotalMilliseconds |> ignore - - transact (fun () -> - config.stats.Value <- - { - quality = lastQ - maxQuality = maxQ - totalPrimitives = !state.dataSize - totalNodes = !state.totalNodes - usedMemory = pool.UsedMemory + inner.UsedMemory - allocatedMemory = pool.TotalMemory + inner.TotalMemory - renderTime = inner.AverageRenderTime - } - ) - if not fin then MVar.put notConverged () + let q, fin = TaskTree<_>.ProcessQueue(state, queue, maxQ, splitfactor, modelView, proj, maxSplits) + lastQ <- q + let dt = time() - start + expandTime.Insert dt.TotalMilliseconds |> ignore + + transact (fun () -> + config.stats.Value <- + { + quality = lastQ + maxQuality = maxQ + totalPrimitives = !state.dataSize + totalNodes = !state.totalNodes + usedMemory = pool.UsedMemory + inner.UsedMemory + allocatedMemory = pool.TotalMemory + inner.TotalMemory + renderTime = inner.AverageRenderTime + } + ) + if not fin then MVar.put notConverged () finally @@ -2173,7 +2181,7 @@ type LodRenderer(manager : ResourceManager, config : LodRendererConfig, roots : override x.Release() = shutdown.Cancel() - cameraPrediction.Join() + cameraPrediction.Join(40) |> ignore thread.Join() puller.Join() inner.Dispose() diff --git a/src/Aardvark.Rendering.GL/Runtime/RenderTasks.fs b/src/Aardvark.Rendering.GL/Runtime/RenderTasks.fs index 8680f8393..ae2a5e619 100644 --- a/src/Aardvark.Rendering.GL/Runtime/RenderTasks.fs +++ b/src/Aardvark.Rendering.GL/Runtime/RenderTasks.fs @@ -120,50 +120,49 @@ module RenderTasks = manager.Dispose() override x.PerformUpdate(token, renderToken) = - GlobalResourceLock.using (fun _ -> - use __ = ctx.ResourceLock + use __ = ctx.ResourceLock + use __ = GlobalResourceLock.lock() - x.ProcessDeltas(token, renderToken) - x.UpdateResources(token, renderToken) + x.ProcessDeltas(token, renderToken) + x.UpdateResources(token, renderToken) - renderTaskLock.Run (fun () -> - x.Update(token, renderToken) - ) + renderTaskLock.Run (fun () -> + x.Update(token, renderToken) ) override x.Perform(token : AdaptiveToken, renderToken : RenderToken, desc : OutputDescription) = - GlobalResourceLock.using (fun _ -> - let fbo = desc.framebuffer // TODO: fix outputdesc - signature |> FramebufferSignature.validateCompability fbo + use __ = ctx.ResourceLock + use __ = GlobalResourceLock.lock() - use __ = ctx.ResourceLock - GL.Check "[RenderTask.Run] Entry" + GL.Check "[RenderTask.Run] Entry" - if currentContext.Value <> ContextHandle.Current.Value then - let intCtx = ContextHandle.Current.Value.Handle |> unbox - NativePtr.write contextHandle intCtx.Context.Handle - transact (fun () -> currentContext.Value <- ContextHandle.Current.Value) + let fbo = desc.framebuffer // TODO: fix outputdesc + signature |> FramebufferSignature.validateCompability fbo - let fbo = - match fbo with - | :? Framebuffer as fbo -> fbo - | _ -> failwithf "unsupported framebuffer: %A" fbo + if currentContext.Value <> ContextHandle.Current.Value then + let intCtx = ContextHandle.Current.Value.Handle |> unbox + NativePtr.write contextHandle intCtx.Context.Handle + transact (fun () -> currentContext.Value <- ContextHandle.Current.Value) - x.ProcessDeltas(token, renderToken) - x.UpdateResources(token, renderToken) + let fbo = + match fbo with + | :? Framebuffer as fbo -> fbo + | _ -> failwithf "unsupported framebuffer: %A" fbo - Framebuffer.draw signature fbo desc.viewport (fun _ -> - renderTaskLock.Run (fun () -> - beforeRender.Trigger() - NativePtr.write runtimeStats V2i.Zero + x.ProcessDeltas(token, renderToken) + x.UpdateResources(token, renderToken) - x.Perform(token, renderToken, fbo, desc) - GL.Check "[RenderTask.Run] Perform" + Framebuffer.draw signature fbo desc.viewport (fun _ -> + renderTaskLock.Run (fun () -> + beforeRender.Trigger() + NativePtr.write runtimeStats V2i.Zero - afterRender.Trigger() - let rt = NativePtr.read runtimeStats - renderToken.AddDrawCalls(rt.X, rt.Y) - ) + x.Perform(token, renderToken, fbo, desc) + GL.Check "[RenderTask.Run] Perform" + + afterRender.Trigger() + let rt = NativePtr.read runtimeStats + renderToken.AddDrawCalls(rt.X, rt.Y) ) GL.BindVertexArray 0 diff --git a/src/Aardvark.Rendering.Text/Font.fs b/src/Aardvark.Rendering.Text/Font.fs index 34731a137..d4e19f34d 100644 --- a/src/Aardvark.Rendering.Text/Font.fs +++ b/src/Aardvark.Rendering.Text/Font.fs @@ -508,7 +508,7 @@ module FontRenderingSettings = type ShapeCache(r : IRuntime) = - static let cache = ConcurrentDictionary() + static let cache = ConcurrentDictionary>() let types = Map.ofList [ @@ -520,9 +520,9 @@ type ShapeCache(r : IRuntime) = let ranges = ConcurrentDictionary() - let surfaceCache = ConcurrentDictionary() - let boundarySurfaceCache = ConcurrentDictionary() - let billboardSurfaceCache = ConcurrentDictionary() + let surfaceCache = ConcurrentDictionary>() + let boundarySurfaceCache = ConcurrentDictionary>() + let billboardSurfaceCache = ConcurrentDictionary>() let pathShader = @@ -574,41 +574,47 @@ type ShapeCache(r : IRuntime) = ] let surface (s : IFramebufferSignature) = - surfaceCache.GetOrAdd(s, fun s -> - r.PrepareEffect( - s, [ - Path.Shader.pathVertex |> toEffect - pathShader - ] + surfaceCache.GetOrAdd(s, fun s -> + lazy ( + r.PrepareEffect( + s, [ + Path.Shader.pathVertex |> toEffect + pathShader + ] + ) ) - ) + ).Value let boundarySurface (s : IFramebufferSignature) = boundarySurfaceCache.GetOrAdd(s, fun s -> - r.PrepareEffect( - s, [ - Path.Shader.boundaryVertex |> toEffect - Path.Shader.boundary |> toEffect - ] + lazy ( + r.PrepareEffect( + s, [ + Path.Shader.boundaryVertex |> toEffect + Path.Shader.boundary |> toEffect + ] + ) ) - ) + ).Value let billboardSurface (s : IFramebufferSignature) = billboardSurfaceCache.GetOrAdd(s, fun s -> - r.PrepareEffect( - s, [ - Path.Shader.pathVertexBillboard |> toEffect - Path.Shader.boundary |> toEffect - ] + lazy ( + r.PrepareEffect( + s, [ + Path.Shader.pathVertexBillboard |> toEffect + Path.Shader.boundary |> toEffect + ] + ) ) - ) + ).Value do r.OnDispose.Add(fun () -> - for x in boundarySurfaceCache.Values do r.DeleteSurface x - for x in surfaceCache.Values do r.DeleteSurface x - for x in billboardSurfaceCache.Values do r.DeleteSurface x + for x in boundarySurfaceCache.Values do r.DeleteSurface x.Value + for x in surfaceCache.Values do r.DeleteSurface x.Value + for x in billboardSurfaceCache.Values do r.DeleteSurface x.Value pool.Dispose() ranges.Clear() cache.Clear() @@ -629,8 +635,8 @@ type ShapeCache(r : IRuntime) = static member GetOrCreateCache(r : IRuntime) = cache.GetOrAdd(r, fun r -> - new ShapeCache(r) - ) + lazy (new ShapeCache(r)) + ).Value member x.Effect = effect member x.InstancedEffect = instancedEffect diff --git a/src/Aardvark.Rendering.Vulkan/Core/Device.fs b/src/Aardvark.Rendering.Vulkan/Core/Device.fs index 42045ebbe..f9f89ac4d 100644 --- a/src/Aardvark.Rendering.Vulkan/Core/Device.fs +++ b/src/Aardvark.Rendering.Vulkan/Core/Device.fs @@ -268,7 +268,8 @@ type Device internal(dev : PhysicalDevice, wantedExtensions : list) as t // TODO: Do we really want to enable all available features? // Does this have real performance implications? use pNext = - DeviceFeatures.toNativeChain dev.Features + dev.GetFeatures (fun e -> extensions |> Array.contains e) + |> DeviceFeatures.toNativeChain |> if isGroup then VkStructChain.add groupInfo else id let! pInfo = diff --git a/src/Aardvark.Rendering.Vulkan/Core/Platform.fs b/src/Aardvark.Rendering.Vulkan/Core/Platform.fs index 8f9cebfd2..a7c9405c2 100644 --- a/src/Aardvark.Rendering.Vulkan/Core/Platform.fs +++ b/src/Aardvark.Rendering.Vulkan/Core/Platform.fs @@ -38,6 +38,8 @@ module Instance = let ShaderSubgroupVote = EXTShaderSubgroupVote.Name let ShaderSubgroupBallot = EXTShaderSubgroupBallot.Name + let ConservativeRasterization = EXTConservativeRasterization.Name + let Debug = [ EXTDebugReport.Name EXTDebugUtils.Name @@ -485,32 +487,36 @@ and PhysicalDevice internal(instance : Instance, handle : VkPhysicalDevice) = let hasExtension (name : string) = globalExtensions |> Array.exists (fun e -> e.name = name) - let features = + let queryFeatures = let inline readOrEmpty (ptr : nativeptr< ^a>) = if NativePtr.isNull ptr then ((^a) : (static member Empty : ^a) ()) else !!ptr - let f, pm, ycbcr, s16, vp, sdp, idx, rtp, acc, rq, bda = - use chain = new VkStructChain() - let pMem = chain.Add() - let pYcbcr = chain.Add() - let p16bit = chain.Add() - let pVarPtrs = chain.Add() - let pDrawParams = chain.Add() - let pIdx = if hasExtension EXTDescriptorIndexing.Name then chain.Add() else NativePtr.zero - let pRTP = if hasExtension KHRRayTracingPipeline.Name then chain.Add() else NativePtr.zero - let pAcc = if hasExtension KHRAccelerationStructure.Name then chain.Add() else NativePtr.zero - let pRQ = if hasExtension KHRRayQuery.Name then chain.Add() else NativePtr.zero - let pDevAddr = if hasExtension KHRBufferDeviceAddress.Name then chain.Add() else NativePtr.zero - let pFeatures = chain.Add() - - VkRaw.vkGetPhysicalDeviceFeatures2(handle, VkStructChain.toNativePtr chain) - (!!pFeatures).features, !!pMem, !!pYcbcr, !!p16bit, !!pVarPtrs, !!pDrawParams, readOrEmpty pIdx, - readOrEmpty pRTP, readOrEmpty pAcc, readOrEmpty pRQ, readOrEmpty pDevAddr - - DeviceFeatures.create pm ycbcr s16 vp sdp idx rtp acc rq bda f + fun (hasExtension: string -> bool) -> + let f, pm, ycbcr, s16, vp, sdp, idx, rtp, acc, rq, bda = + use chain = new VkStructChain() + let pMem = chain.Add() + let pYcbcr = chain.Add() + let p16bit = chain.Add() + let pVarPtrs = chain.Add() + let pDrawParams = chain.Add() + let pIdx = if hasExtension EXTDescriptorIndexing.Name then chain.Add() else NativePtr.zero + let pRTP = if hasExtension KHRRayTracingPipeline.Name then chain.Add() else NativePtr.zero + let pAcc = if hasExtension KHRAccelerationStructure.Name then chain.Add() else NativePtr.zero + let pRQ = if hasExtension KHRRayQuery.Name then chain.Add() else NativePtr.zero + let pDevAddr = if hasExtension KHRBufferDeviceAddress.Name then chain.Add() else NativePtr.zero + let pFeatures = chain.Add() + + VkRaw.vkGetPhysicalDeviceFeatures2(handle, VkStructChain.toNativePtr chain) + (!!pFeatures).features, !!pMem, !!pYcbcr, !!p16bit, !!pVarPtrs, !!pDrawParams, readOrEmpty pIdx, + readOrEmpty pRTP, readOrEmpty pAcc, readOrEmpty pRQ, readOrEmpty pDevAddr + + DeviceFeatures.create pm ycbcr s16 vp sdp idx rtp acc rq bda f + + let features = + queryFeatures hasExtension let properties, raytracingProperties = use chain = new VkStructChain() @@ -747,6 +753,8 @@ and PhysicalDevice internal(instance : Instance, handle : VkPhysicalDevice) = member x.Features : DeviceFeatures = features member x.Limits : DeviceLimits = limits + member internal x.GetFeatures(hasExtension: string -> bool) = queryFeatures hasExtension + abstract member DeviceMask : uint32 default x.DeviceMask = deviceMask diff --git a/src/Aardvark.Rendering.Vulkan/Management/ResourceManager.fs b/src/Aardvark.Rendering.Vulkan/Management/ResourceManager.fs index 83bd8e3e9..c62a20bf1 100644 --- a/src/Aardvark.Rendering.Vulkan/Management/ResourceManager.fs +++ b/src/Aardvark.Rendering.Vulkan/Management/ResourceManager.fs @@ -966,7 +966,8 @@ module Resources = inherit AbstractPointerResourceWithEquality(owner, key) override x.Free(info : VkPipelineRasterizationStateCreateInfo) = - Marshal.FreeHGlobal info.pNext + if info.pNext <> 0n then + Marshal.FreeHGlobal info.pNext override x.Compute(user, token, renderToken) = let depthClamp = depthClamp.GetValue(user, token, renderToken) @@ -977,18 +978,23 @@ module Resources = let conservativeRaster = conservativeRaster.GetValue(user, token, renderToken) let state = RasterizerState.create conservativeRaster depthClamp bias cull front fill - let conservativeRaster = - VkPipelineRasterizationConservativeStateCreateInfoEXT( - VkPipelineRasterizationConservativeStateCreateFlagsEXT.None, - (if conservativeRaster then VkConservativeRasterizationModeEXT.Overestimate else VkConservativeRasterizationModeEXT.Disabled), - 0.0f - ) + let pConservativeRaster = + if conservativeRaster then + let info = + VkPipelineRasterizationConservativeStateCreateInfoEXT( + VkPipelineRasterizationConservativeStateCreateFlagsEXT.None, + VkConservativeRasterizationModeEXT.Overestimate, + 0.0f + ) - let pConservativeRaster = NativePtr.alloc 1 - conservativeRaster |> NativePtr.write pConservativeRaster + let ptr = NativePtr.alloc 1 + info |> NativePtr.write ptr + NativePtr.toNativeInt ptr + else + 0n VkPipelineRasterizationStateCreateInfo( - NativePtr.toNativeInt pConservativeRaster, + pConservativeRaster, VkPipelineRasterizationStateCreateFlags.None, (if state.depthClampEnable then 1u else 0u), (if state.rasterizerDiscardEnable then 1u else 0u), @@ -1907,9 +1913,9 @@ type ResourceManager(device : Device) = program.PipelineLayout, resource - member private x.CreateDynamicShaderProgram(pass : RenderPass, compile : FShade.EffectConfig -> FShade.EffectInputLayout * aval) = + member private x.CreateDynamicShaderProgram(key : obj, pass : RenderPass, compile : FShade.EffectConfig -> FShade.EffectInputLayout * aval) = dynamicProgramCache.GetOrCreate( - [pass.Layout :> obj; compile :> obj], + [key; pass.Layout :> obj], fun cache key -> let effectConfig = pass.EffectConfig(FShadeConfig.depthRange, false) @@ -1928,7 +1934,9 @@ type ResourceManager(device : Device) = x.CreateShaderProgram(pass, effect, top) | Surface.FShade compile -> - let program = x.CreateDynamicShaderProgram(pass, compile) + // Use surface itself as key rather than compile function, since equality is undefined behavior for F# functions. + // See F# specification: 6.9.24 Values with Underspecified Object Identity and Type Identity + let program = x.CreateDynamicShaderProgram(data, pass, compile) program.Layout, program | Surface.Backend surface -> diff --git a/src/Aardvark.Rendering.Vulkan/Resources/Buffer.fs b/src/Aardvark.Rendering.Vulkan/Resources/Buffer.fs index 67e82f7b8..f41fddc67 100644 --- a/src/Aardvark.Rendering.Vulkan/Resources/Buffer.fs +++ b/src/Aardvark.Rendering.Vulkan/Resources/Buffer.fs @@ -345,23 +345,25 @@ module Buffer = new Buffer(device, handle, ptr, size, flags) - let private emptyBuffers = ConcurrentDictionary() + let private emptyBuffers = ConcurrentDictionary>() let empty (export : bool) (usage : VkBufferUsageFlags) (memory : DeviceHeap) = let key = (memory, export, usage) let buffer = emptyBuffers.GetOrAdd(key, fun (memory, export, usage) -> - let device = memory.Device - let buffer = memory |> createInternal true export 1UL usage 256L + lazy ( + let device = memory.Device + let buffer = memory |> createInternal true export 1UL usage 256L - device.OnDispose.Add (fun () -> - emptyBuffers.TryRemove key |> ignore - buffer.Dispose() - ) + device.OnDispose.Add (fun () -> + emptyBuffers.TryRemove key |> ignore + buffer.Dispose() + ) - buffer - ) + buffer + ) + ).Value buffer.AddReference() buffer diff --git a/src/Aardvark.Rendering.Vulkan/Runtime/CommandTask.fs b/src/Aardvark.Rendering.Vulkan/Runtime/CommandTask.fs index b61b12dc9..310eb6272 100644 --- a/src/Aardvark.Rendering.Vulkan/Runtime/CommandTask.fs +++ b/src/Aardvark.Rendering.Vulkan/Runtime/CommandTask.fs @@ -2237,16 +2237,17 @@ type CommandTask(manager : ResourceManager, renderPass : RenderPass, command : R updateResources token renderToken (fun resourcesChanged -> if viewportChanged || commandChanged || resourcesChanged || framebufferChanged then - let cause = - String.concat "; " [ - if commandChanged then yield "content" - if resourcesChanged then yield "resources" - if viewportChanged then yield "viewport" - if framebufferChanged then yield "framebuffer" - ] - |> sprintf "{ %s }" - + if device.DebugConfig.PrintRenderTaskRecompile then + let cause = + String.concat "; " [ + if commandChanged then yield "content" + if resourcesChanged then yield "resources" + if viewportChanged then yield "viewport" + if framebufferChanged then yield "framebuffer" + ] + |> sprintf "{ %s }" + Log.line "[Vulkan] recompile commands: %s" cause inner.Begin(renderPass, fbo, CommandBufferUsage.RenderPassContinue, true) diff --git a/src/Aardvark.Rendering.Vulkan/Runtime/Headless.fs b/src/Aardvark.Rendering.Vulkan/Runtime/Headless.fs index 14aa58953..31947f3dd 100644 --- a/src/Aardvark.Rendering.Vulkan/Runtime/Headless.fs +++ b/src/Aardvark.Rendering.Vulkan/Runtime/Headless.fs @@ -14,6 +14,7 @@ type HeadlessVulkanApplication(debug : IDebugConfig, instanceExtensions : list] +module private SymDictExtensions = + + type SymbolDict<'T> with + member inline x.CopyOrNull() = + if isNull x then null else x.Copy() + module private IndexHelpers = - let addOffset (offset : int) (index : Array) : Array = + let createIndex (indexType: Type) (count: int) = + let arr = Array.CreateInstance(indexType, count) + arr.Visit { new ArrayVisitor() with + member x.Run(data: 'T[]) = + let conv = PrimitiveValueConverter.converter + for i = 0 to count - 1 do + data.[i] <- conv i + arr + } + + let addOffset (startAt: int) (offset: int) (index: Array) : Array = if isNull index then null else - match index with - | :? array as x -> x |> Array.map ((+) offset) :> Array - | :? array as x -> x |> Array.map ((+) (int16 offset)) :> Array - | :? array as x -> x |> Array.map ((+) (uint32 offset)) :> Array - | :? array as x -> x |> Array.map ((+) (uint16 offset)) :> Array - | _ -> - raise <| ArgumentException($"Unsupported type {index.GetType()}.") + index.Visit { new ArrayVisitor() with + member x.Run(data: 'T[]) = + let ofUInt = PrimitiveValueConverter.converter + let toUInt = PrimitiveValueConverter.converter<'T, uint> + for i = startAt to data.Length - 1 do + data.[i] <- ofUInt (uint offset + toUInt data.[i]) + index + } let applyIndex (index : Array) (data : Array) = if isNull index || isNull data then @@ -152,46 +170,101 @@ module IndexedGeometryMode = type IndexedGeometry = class + /// Primitive topology of the geometry. val mutable public Mode : IndexedGeometryMode + + /// Index array (null for non-indexed geometry). val mutable public IndexArray : Array + + /// Per-vertex attributes (can be null). val mutable public IndexedAttributes : SymbolDict + + /// Single value attributes (can be null). val mutable public SingleAttributes : SymbolDict - member x.IsIndexed = not (isNull x.IndexArray) + /// Indicates whether the geometry is indexed. + member x.IsIndexed = + not (isNull x.IndexArray) + + /// Indicates whether the geometry has a non-zero face vertex count. + member inline x.IsEmpty = + x.FaceVertexCount = 0 + + /// Indicates whether the geometry is valid (i.e. it has a position attribute and all attribute arrays are of sufficient length) + member inline x.IsValid = + if isNull x.IndexedAttributes then false + else + match x.IndexedAttributes.TryGetValue DefaultSemantic.Positions with + | (true, positions) -> x.IndexedAttributes |> Seq.forall (fun attr -> attr.Value.Length >= positions.Length) + | _ -> false + /// Total number of vertices in the geometry. member x.VertexCount = if isNull x.IndexedAttributes then 0 else - match Seq.tryHead x.IndexedAttributes.Values with - | Some att -> att.Length - | _ -> 0 - + match x.IndexedAttributes.TryGetValue DefaultSemantic.Positions with + | (true, positions) -> positions.Length + | _ -> + match x.IndexedAttributes |> Seq.tryHead with + | Some (KeyValue(_, att)) -> att.Length + | _ -> 0 + + /// Effective number of vertices in the geometry (i.e. the index count if indexed and the vertex count if non-indexed). member x.FaceVertexCount = if isNull x.IndexArray then x.VertexCount else x.IndexArray.Length + /// Number of faces in the geometry. member x.FaceCount = IndexedGeometryMode.faceCount x.Mode x.FaceVertexCount + ///Creates a copy. + ///If true, the index and attribute arrays are reused instead of being copied. + member x.Clone(shallowCopy: bool) = + let indices = + if isNull x.IndexArray || shallowCopy then + x.IndexArray + else + x.IndexArray.Copy() + + let indexedAttributes = + if isNull x.IndexedAttributes then + null + else + let d = SymbolDict(initialCapacity = x.IndexedAttributes.Count) + for (KeyValue(sem, attr)) in x.IndexedAttributes do + d.[sem] <- if shallowCopy then attr else attr.Copy() + d + + let singleAttributes = + if isNull x.SingleAttributes then null + else x.SingleAttributes.Copy() + + IndexedGeometry(x.Mode, indices, indexedAttributes, singleAttributes) + + /// Creates a copy. The copy is shallow as the index and attribute arrays are reused instead of being copied. member x.Clone() = - IndexedGeometry( - x.Mode, - x.IndexArray, - (if isNull x.IndexedAttributes then null else x.IndexedAttributes.Copy()), - (if isNull x.SingleAttributes then null else x.SingleAttributes.Copy()) - ) + x.Clone(true) - member x.ToIndexed() = + /// Returns an indexed copy of the geometry. + /// If it is already indexed, it is returned unmodified. + member x.ToIndexed(indexType: Type) = if isNull x.IndexArray then - let res = x.Clone() - let count = x.FaceVertexCount - res.IndexArray <- Array.init count id - res + let copy = x.Clone() + copy.IndexArray <- IndexHelpers.createIndex indexType x.FaceVertexCount + copy else x + /// Returns an indexed copy of the geometry. + /// If it is already indexed, it is returned unmodified. + member x.ToIndexed() = + x.ToIndexed typeof + + /// Returns a non-indexed copy of the geometry. + /// If it is already non-indexed, it is returned unmodified. member x.ToNonIndexed() = if isNull x.IndexArray then x @@ -210,6 +283,8 @@ type IndexedGeometry = res + /// Returns a copy of the geometry with a non-stripped primitive topology. + /// If the topology is not line or triangle strips, the geometry is returned unmodified. member x.ToNonStripped() = match x.Mode with | IndexedGeometryMode.LineStrip -> @@ -227,43 +302,65 @@ type IndexedGeometry = | _ -> x + /// Returns a union of the geometry with another. + /// The geometries must have the same attributes and primitive topology. member x.Union(y : IndexedGeometry) = if x.Mode <> y.Mode then raise <| ArgumentException("IndexedGeometryMode must match.") let acceptedTopologies = [ - IndexedGeometryMode.PointList - IndexedGeometryMode.LineList - IndexedGeometryMode.TriangleList - IndexedGeometryMode.QuadList - ] + IndexedGeometryMode.PointList + IndexedGeometryMode.LineList + IndexedGeometryMode.TriangleList + IndexedGeometryMode.QuadList + ] if not <| List.contains x.Mode acceptedTopologies then raise <| ArgumentException($"IndexedGeometryMode must be one of {acceptedTopologies}.") if x.IsIndexed <> y.IsIndexed then - let a = if x.IsIndexed then x else y.ToIndexed() - let b = if y.IsIndexed then y else x.ToIndexed() + let a = if x.IsIndexed then x else x.ToIndexed <| y.IndexArray.GetType().GetElementType() + let b = if y.IsIndexed then y else y.ToIndexed <| x.IndexArray.GetType().GetElementType() a.Union b else let indices = - try - y.IndexArray - |> IndexHelpers.addOffset x.VertexCount - |> ArrayHelpers.concat x.IndexArray - with - | exn -> - raise <| ArgumentException($"Invalid indices: {exn.Message}") + if x.IsIndexed then + try + y.IndexArray + |> ArrayHelpers.concat x.IndexArray + |> IndexHelpers.addOffset x.IndexArray.Length x.VertexCount + with + | exn -> + raise <| ArgumentException($"Invalid indices: {exn.Message}") + else + null let singleAttributes = - if isNull x.SingleAttributes then y.SingleAttributes - elif isNull y.SingleAttributes then x.SingleAttributes - else SymDict.union [x.SingleAttributes; y.SingleAttributes] + if isNull x.SingleAttributes then y.SingleAttributes.CopyOrNull() + elif isNull y.SingleAttributes then x.SingleAttributes.CopyOrNull() + else + let r = SymDict.empty + + for (KeyValue(sem, a)) in x.SingleAttributes do + match y.SingleAttributes |> SymDict.tryFind sem with + | Some b when a <> b -> + raise <| ArgumentException($"Conflicting single value attribute {sem}.") + | _ -> + r.[sem] <- a + + for (KeyValue(sem, a)) in y.SingleAttributes do + match x.SingleAttributes |> SymDict.tryFind sem with + | Some b when a <> b -> + raise <| ArgumentException($"Conflicting single value attribute {sem}.") + | _ -> + r.[sem] <- a + + r let indexedAttributes = - if isNull x.IndexedAttributes then y.IndexedAttributes - elif isNull y.IndexedAttributes then x.IndexedAttributes + if isNull x.IndexedAttributes then y.IndexedAttributes.CopyOrNull() + elif isNull y.IndexedAttributes then x.IndexedAttributes.CopyOrNull() else let r = SymDict.empty @@ -308,18 +405,52 @@ type IndexedGeometry = [] module IndexedGeometry = - + + /// Returns the primitive topology of the given geometry. let inline mode (g : IndexedGeometry) = g.Mode + + /// Returns the index array of the given geometry (null if non-indexed). let inline indexArray (g : IndexedGeometry) = g.IndexArray + + /// Returns the per-vertex attributes of the given geometry (can be null). let inline indexedAttributes (g : IndexedGeometry) = g.IndexedAttributes + + /// Returns the single value attributes of the given geometry (can be null). let inline singleAttributes (g : IndexedGeometry) = g.SingleAttributes + /// Returns whether the given geometry has a non-zero face vertex count. + let inline isEmpty (g : IndexedGeometry) = g.IsEmpty + + /// Returns whether the given geometry is valid (i.e. it has a position attribute and all attribute arrays are of sufficient length) + let inline isValid (g : IndexedGeometry) = g.IsValid + + /// Returns whether the given geometry is indexed. let inline isIndexed (g : IndexedGeometry) = g.IsIndexed + + /// Returns the total number of vertices in the given geometry. let inline vertexCount (g : IndexedGeometry) = g.VertexCount + + /// Returns the effective number of vertices in the given geometry (i.e. the index count if indexed and the vertex count if non-indexed). let inline faceVertexCount (g : IndexedGeometry) = g.FaceVertexCount + + /// Returns the number of faces in the given geometry. let inline faceCount (g : IndexedGeometry) = g.FaceCount + + /// Returns a shallow copy of the given geometry (index and attribute arrays are reused). let inline clone (g : IndexedGeometry) = g.Clone() + + /// Returns an indexed copy of the given geometry. + /// If it is already indexed, it is returned unmodified. let inline toIndexed (g : IndexedGeometry) = g.ToIndexed() + + /// Returns a non-indexed copy of the geometry. + /// If it is already non-indexed, it is returned unmodified. let inline toNonIndexed (g : IndexedGeometry) = g.ToNonIndexed() + + /// Returns a copy of the geometry with a non-stripped primitive topology. + /// If the topology is not line or triangle strips, the geometry is returned unmodified. let inline toNonStripped (g : IndexedGeometry) = g.ToNonStripped() + + /// Returns a union of two geometries with another. + /// The geometries must have the same attributes and primitive topology. let inline union (a : IndexedGeometry) (b : IndexedGeometry) = a.Union b \ No newline at end of file diff --git a/src/Aardvark.Rendering/Resources/Adaptive/AdaptiveConverter.fs b/src/Aardvark.Rendering/Resources/Adaptive/AdaptiveConverter.fs index 0948b0f71..2673eb638 100644 --- a/src/Aardvark.Rendering/Resources/Adaptive/AdaptiveConverter.fs +++ b/src/Aardvark.Rendering/Resources/Adaptive/AdaptiveConverter.fs @@ -45,12 +45,14 @@ module AdaptivePrimitiveValueConverterExtensions = member x.ConvertUntyped(value : IAdaptiveValue) = x.Convert(value) member x.Convert(array : aval) = x.Convert(array) - let private staticFieldCache = Dict() - let private getStaticField (name : string) (t : Type) : 'T = - staticFieldCache.GetOrCreate((t, name), fun (t, name) -> - let p = t.GetProperty(name, BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public) - p.GetValue(null) - ) |> unbox<'T> + let private staticInstanceCache = Dict() + let private getStaticInstance (t : Type) : 'T = + lock staticInstanceCache (fun () -> + staticInstanceCache.GetOrCreate(t, fun t -> + let p = t.GetProperty("Instance", BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public) + p.GetValue(null) + ) |> unbox<'T> + ) let convertArray (inputElementType : Type) (array : aval) : aval<'T[]> = if inputElementType = typeof<'T> then @@ -58,7 +60,7 @@ module AdaptivePrimitiveValueConverterExtensions = else try let tconv = typedefof>.MakeGenericType [| inputElementType; typeof<'T> |] - let converter : IAdaptiveConverter<'T> = tconv |> getStaticField "Instance" + let converter : IAdaptiveConverter<'T> = tconv |> getStaticInstance converter.Convert(array) with | InvalidConversion exn -> raise exn @@ -70,7 +72,7 @@ module AdaptivePrimitiveValueConverterExtensions = else try let tconv = typedefof>.MakeGenericType [| inputType; outputType |] - let converter : IAdaptiveConverter = tconv |> getStaticField "Instance" + let converter : IAdaptiveConverter = tconv |> getStaticInstance converter.ConvertUntyped(value) with | InvalidConversion exn -> raise exn @@ -82,7 +84,7 @@ module AdaptivePrimitiveValueConverterExtensions = else try let tconv = typedefof>.MakeGenericType [| inputType; typeof<'T> |] - let converter : IAdaptiveConverter<'T> = tconv |> getStaticField "Instance" + let converter : IAdaptiveConverter<'T> = tconv |> getStaticInstance converter.Convert(value) with | InvalidConversion exn -> raise exn \ No newline at end of file diff --git a/src/Aardvark.Rendering/Resources/Buffers/SingleValueBuffer.fs b/src/Aardvark.Rendering/Resources/Buffers/SingleValueBuffer.fs index eead7aa8e..7b0b49db5 100644 --- a/src/Aardvark.Rendering/Resources/Buffers/SingleValueBuffer.fs +++ b/src/Aardvark.Rendering/Resources/Buffers/SingleValueBuffer.fs @@ -61,4 +61,34 @@ type SingleValueBuffer<'T when 'T : unmanaged>(value : aval<'T>) = member x.Accept(visitor) = value.Accept(visitor) member x.AllInputsProcessed(obj) = value.AllInputsProcessed(obj) member x.InputChanged(t, o) = value.InputChanged(t, o) - member x.Mark() = value.Mark() \ No newline at end of file + member x.Mark() = value.Mark() + +[] +module SingleValueBuffer = + + [] + module private GenericDispatch = + open System.Reflection + open System.Collections.Concurrent + + [] + type Dispatcher() = + static member ToSingleValueBuffer<'T when 'T : unmanaged>(value: obj) : ISingleValueBuffer = + SingleValueBuffer<'T>(unbox<'T> value) + + module ToSingleValueBuffer = + type Delegate = delegate of obj -> ISingleValueBuffer + let private flags = BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public + let private definition = typeof.GetMethod(nameof Dispatcher.ToSingleValueBuffer, flags) + let private delegates = ConcurrentDictionary() + + let getDelegate (t: Type) = + delegates.GetOrAdd(t, fun t -> + let mi = definition.MakeGenericMethod [| t |] + unbox <| Delegate.CreateDelegate(typeof, null, mi) + ) + + /// Creates a single value buffer from an untyped value. + let create (value: obj) = + let del = ToSingleValueBuffer.getDelegate <| value.GetType() + del.Invoke(value) \ No newline at end of file diff --git a/src/Aardvark.Rendering/Runtime/Compute/MutableInputBinding.fs b/src/Aardvark.Rendering/Runtime/Compute/MutableInputBinding.fs index e51e21fa8..741d2495c 100644 --- a/src/Aardvark.Rendering/Runtime/Compute/MutableInputBinding.fs +++ b/src/Aardvark.Rendering/Runtime/Compute/MutableInputBinding.fs @@ -6,6 +6,7 @@ open Aardvark.Base open System open System.Reflection open System.Collections.Generic +open System.Collections.Concurrent open System.Runtime.CompilerServices module private MutableComputeBindingInternals = @@ -18,23 +19,41 @@ module private MutableComputeBindingInternals = [] module private Dispatch = + let private flags = BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public type private Dispatcher() = - static member Create<'T>() = AVal.init Unchecked.defaultof<'T> - static member Change(cval : ChangeableValue<'T>, value : 'T) = cval.Value <- value - - module Method = - let private flags = BindingFlags.Static ||| BindingFlags.NonPublic ||| BindingFlags.Public - let create = typeof.GetMethod("Create", flags) - let change = typeof.GetMethod("Change", flags) - - let create (contentType : Type) = - let mi = Method.create.MakeGenericMethod [| contentType |] - mi.Invoke(null, [||]) |> unbox - - let change (value : obj) (cval : IAdaptiveValue) = - let mi = Method.change.MakeGenericMethod [| cval.ContentType |] - mi.Invoke(null, [| cval; value |]) |> ignore + static member Create<'T>() : IAdaptiveValue = AVal.init Unchecked.defaultof<'T> + static member Change<'T>(cval: IAdaptiveValue, value: obj) = (unbox> cval).Value <- unbox<'T> value + + module Create = + type Delegate = delegate of unit -> IAdaptiveValue + let private definition = typeof.GetMethod(nameof Dispatcher.Create, flags) + let private delegates = ConcurrentDictionary() + + let getDelegate (t: Type) = + delegates.GetOrAdd(t, fun t -> + let mi = definition.MakeGenericMethod [| t |] + unbox <| Delegate.CreateDelegate(typeof, null, mi) + ) + + module Change = + type Delegate = delegate of IAdaptiveValue * obj -> unit + let private definition = typeof.GetMethod(nameof Dispatcher.Change, flags) + let private delegates = ConcurrentDictionary() + + let getDelegate (t: Type) = + delegates.GetOrAdd(t, fun t -> + let mi = definition.MakeGenericMethod [| t |] + unbox <| Delegate.CreateDelegate(typeof, null, mi) + ) + + let create (contentType: Type) : IAdaptiveValue = + let del = Create.getDelegate contentType + del.Invoke() + + let change (value: obj) (cval: IAdaptiveValue) = + let del = Change.getDelegate cval.ContentType + del.Invoke(cval, value) // When an input is first set, the provided value might be of a specialized type. // The type of that value determines the content type of cval value that is created at that point. diff --git a/src/Aardvark.Rendering/Surfaces/Surface.fs b/src/Aardvark.Rendering/Surfaces/Surface.fs index d09b0396a..4f710465f 100644 --- a/src/Aardvark.Rendering/Surfaces/Surface.fs +++ b/src/Aardvark.Rendering/Surfaces/Surface.fs @@ -12,7 +12,7 @@ type IBackendSurface = inherit IDisposable abstract member Handle : obj -[] +[] type Surface = | FShadeSimple of Effect | FShade of (EffectConfig -> EffectInputLayout * aval) diff --git a/src/Aardvark.SceneGraph/Pools/ManagedTracePool.fs b/src/Aardvark.SceneGraph/Pools/ManagedTracePool.fs index a365d53df..60f003f12 100644 --- a/src/Aardvark.SceneGraph/Pools/ManagedTracePool.fs +++ b/src/Aardvark.SceneGraph/Pools/ManagedTracePool.fs @@ -772,7 +772,7 @@ and ManagedTracePool(runtime : IRuntime, signature : TraceObjectSignature, member x.GetVertexAttribute(semantic : Symbol) = match x.TryGetVertexAttribute semantic with | Some attr -> attr - | None _ -> failf "could not find vertex attribute '%A'" semantic + | None -> failf "could not find vertex attribute '%A'" semantic member x.TryGetFaceAttribute(semantic : Symbol) = faceAttributeBuffers |> Map.tryFind semantic @@ -781,7 +781,7 @@ and ManagedTracePool(runtime : IRuntime, signature : TraceObjectSignature, member x.GetFaceAttribute(semantic : Symbol) = match x.TryGetFaceAttribute semantic with | Some attr -> attr - | None _ -> failf "could not find face attribute '%A'" semantic + | None -> failf "could not find face attribute '%A'" semantic member x.TryGetGeometryAttribute(semantic : Symbol) = geometryAttributeBuffers |> Map.tryFind semantic @@ -790,7 +790,7 @@ and ManagedTracePool(runtime : IRuntime, signature : TraceObjectSignature, member x.GetGeometryAttribute(semantic : Symbol) = match x.TryGetGeometryAttribute semantic with | Some attr -> attr - | None _ -> failf "could not find geometry attribute '%A'" semantic + | None -> failf "could not find geometry attribute '%A'" semantic member x.TryGetInstanceAttribute(semantic : Symbol) = instanceAttributeBuffers |> Map.tryFind semantic @@ -799,7 +799,7 @@ and ManagedTracePool(runtime : IRuntime, signature : TraceObjectSignature, member x.GetInstanceAttribute(semantic : Symbol) = match x.TryGetInstanceAttribute semantic with | Some attr -> attr - | None _ -> failf "could not find instance attribute '%A'" semantic + | None -> failf "could not find instance attribute '%A'" semantic member x.Dispose() = lock x (fun _ -> diff --git a/src/Aardvark.SceneGraph/SgFSharp.fs b/src/Aardvark.SceneGraph/SgFSharp.fs index 1228a8105..f26ee1e88 100644 --- a/src/Aardvark.SceneGraph/SgFSharp.fs +++ b/src/Aardvark.SceneGraph/SgFSharp.fs @@ -39,30 +39,36 @@ module SgFSharp = module Caching = // Note: we need these caches because of the AVal.maps below - let bufferCache = ConditionalWeakTable() + let private cache = ConditionalWeakTable() - let bufferOfArray (m : aval<'a[]>) = - match bufferCache.TryGetValue m with - | (true, r) -> r + let private getOrCreate (create: 'T1 -> 'T2) (value: 'T1) : 'T2 = + match cache.TryGetValue value with + | (true, r) when r.GetType() = typeof<'T2> -> unbox<'T2> r | _ -> - let b = m |> AVal.map (fun a -> ArrayBuffer a :> IBuffer) - let r = BufferView(b, typeof<'a>) - bufferCache.Add(m, r) + let r = create value + cache.Add(value, r) r - let bufferOfTrafos (m : aval) = - match bufferCache.TryGetValue m with - | (true, r) -> r - | _ -> - let b = - m |> AVal.map (fun a -> - let a = a |> Array.map (Trafo.forward >> M44f) - ArrayBuffer a :> IBuffer - ) - - let r = BufferView(b, typeof) - bufferCache.Add(m, r) - r + let bufferOfArray (value: aval<'T[]>) : BufferView = + value |> getOrCreate (fun value -> + let b = value |> AVal.map (fun a -> ArrayBuffer a :> IBuffer) + BufferView(b, typeof<'T>) + ) + + let buffersOfTrafos (value: aval) : BufferView * BufferView = + value |> getOrCreate (fun value -> + let forward = value |> AVal.map (fun a -> + let a = a |> Array.map (Trafo.forward >> M44f) + ArrayBuffer a :> IBuffer + ) + + let backward = value |> AVal.map (fun a -> + let a = a |> Array.map (Trafo.backward >> M44f) + ArrayBuffer a :> IBuffer + ) + + BufferView(forward, typeof), BufferView(backward, typeof) + ) module Sg = open SgFSharpHelpers @@ -107,7 +113,7 @@ module SgFSharp = Sg.AdapterNode(o) :> ISg /// Combines the render objects in the given adaptive set. - let renderObjectSet (s : #aset) = + let renderObjectSet (s : #aset) = Sg.RenderObjectNode(s) :> ISg /// Applies the given activation function to the the given scene graph. @@ -790,80 +796,22 @@ module SgFSharp = let indirectDraw (mode : IndexedGeometryMode) (buffer : aval) = Sg.IndirectRenderNode(buffer, mode) :> ISg - /// Creates a draw call from the given indexed geometry. - let ofIndexedGeometry (g : IndexedGeometry) = - let attributes = - g.IndexedAttributes |> Seq.map (fun (KeyValue(k,v)) -> - let t = v.GetType().GetElementType() - let view = BufferView(~~(ArrayBuffer(v) :> IBuffer), t) - - k, view - ) |> Map.ofSeq - - - let index, faceVertexCount = - if g.IsIndexed then - g.IndexArray, g.IndexArray.Length - else - null, g.IndexedAttributes.[DefaultSemantic.Positions].Length - - let call = - DrawCallInfo( - FaceVertexCount = faceVertexCount, - FirstIndex = 0, - InstanceCount = 1, - FirstInstance = 0, - BaseVertex = 0 - ) - - let sg = Sg.VertexAttributeApplicator(attributes, Sg.RenderNode(call,g.Mode)) :> ISg - if not (isNull index) then - Sg.VertexIndexApplicator(BufferView.ofArray index, sg) :> ISg - else - sg - - /// Creates a draw call from the given indexed geometry and instance count. - let ofIndexedGeometryInstanced (g : IndexedGeometry) (instanceCount : int) = - let attributes = - g.IndexedAttributes |> Seq.map (fun (KeyValue(k,v)) -> - let t = v.GetType().GetElementType() - let view = BufferView(~~(ArrayBuffer(v) :> IBuffer), t) - - k, view - ) |> Map.ofSeq - - - let index, faceVertexCount = - if g.IsIndexed then - g.IndexArray, g.IndexArray.Length - else - null, g.IndexedAttributes.[DefaultSemantic.Positions].Length - - let call = - DrawCallInfo( - FaceVertexCount = faceVertexCount, - FirstIndex = 0, - InstanceCount = instanceCount, - FirstInstance = 0, - BaseVertex = 0 - ) - - let sg = Sg.VertexAttributeApplicator(attributes, Sg.RenderNode(call, g.Mode)) :> ISg - if not (isNull index) then - Sg.VertexIndexApplicator(BufferView.ofArray index, sg) :> ISg - else - sg - /// Creates a draw call from the given indexed geometry and an adpative instance count. let ofIndexedGeometryInstancedA (g : IndexedGeometry) (instanceCount : aval) = let attributes = - g.IndexedAttributes |> Seq.map (fun (KeyValue(k,v)) -> - let t = v.GetType().GetElementType() - let view = BufferView(~~(ArrayBuffer(v) :> IBuffer), t) + let indexed = + g.IndexedAttributes |> Seq.map (fun (KeyValue(sem, attr)) -> + sem, BufferView(attr) + ) |> Map.ofSeq - k, view - ) |> Map.ofSeq + let single = + if isNull g.SingleAttributes then Map.empty + else + g.SingleAttributes |> Seq.map (fun (KeyValue(sem, value)) -> + sem, BufferView(SingleValueBuffer.create value) + ) |> Map.ofSeq + Map.union indexed single let index, faceVertexCount = if g.IsIndexed then @@ -886,43 +834,28 @@ module SgFSharp = else sg - /// Creates a draw call, supplying the given transformations as per-instance attributes with - /// name DefaultSemantic.InstanceTrafo. - let instancedGeometry (trafos : aval) (g : IndexedGeometry) = - let vertexAttributes = - g.IndexedAttributes |> Seq.map (fun (KeyValue(k,v)) -> - let t = v.GetType().GetElementType() - let view = BufferView(~~(ArrayBuffer(v) :> IBuffer), t) - - k, view - ) |> Map.ofSeq - - let index, faceVertexCount = - if g.IsIndexed then - g.IndexArray, g.IndexArray.Length - else - null, g.IndexedAttributes.[DefaultSemantic.Positions].Length - - let call = trafos |> AVal.map (fun t -> - DrawCallInfo( - FaceVertexCount = faceVertexCount, - FirstIndex = 0, - InstanceCount = t.Length, - FirstInstance = 0, - BaseVertex = 0 - ) - ) + /// Creates a draw call from the given indexed geometry and instance count. + let ofIndexedGeometryInstanced (g : IndexedGeometry) (instanceCount : int) = + instanceCount |> AVal.constant |> ofIndexedGeometryInstancedA g - let sg = Sg.VertexAttributeApplicator(vertexAttributes, Sg.RenderNode(call, g.Mode)) :> ISg + /// Creates a draw call from the given indexed geometry. + let ofIndexedGeometry (g : IndexedGeometry) = + ofIndexedGeometryInstanced g 1 + /// Creates a draw call, supplying the given transformations as per-instance attributes + /// DefaultSemantic.InstanceTrafo and DefaultSemantic.InstanceTrafoInv. + let instancedGeometry (trafos : aval) (g : IndexedGeometry) = let sg = - if index <> null then - Sg.VertexIndexApplicator(BufferView.ofArray index, sg) :> ISg - else - sg + trafos + |> AVal.mapNonAdaptive Array.length + |> ofIndexedGeometryInstancedA g + + let forward, backward = Caching.buffersOfTrafos trafos - let view = Caching.bufferOfTrafos trafos - Sg.InstanceAttributeApplicator([DefaultSemantic.InstanceTrafo, view] |> Map.ofList, sg) :> ISg + Sg.InstanceAttributeApplicator([ + DefaultSemantic.InstanceTrafo, forward + DefaultSemantic.InstanceTrafoInv, backward + ] |> Map.ofList, sg) :> ISg // ================================================================================================================ // Bounding boxes diff --git a/src/Application/Aardvark.Application.OpenVR.GL/Application.fs b/src/Application/Aardvark.Application.OpenVR.GL/Application.fs index df4272fca..d6735608f 100644 --- a/src/Application/Aardvark.Application.OpenVR.GL/Application.fs +++ b/src/Application/Aardvark.Application.OpenVR.GL/Application.fs @@ -66,7 +66,8 @@ type OpenGlVRApplicationLayered(samples : int, debug : IDebugConfig, adjustSize let mutable cTex = Unchecked.defaultof let mutable fbo = Unchecked.defaultof let mutable info = Unchecked.defaultof - let mutable fTex = Unchecked.defaultof + let mutable fTexl = Unchecked.defaultof + let mutable fTexr = Unchecked.defaultof let start = System.DateTime.Now let sw = System.Diagnostics.Stopwatch.StartNew() @@ -93,7 +94,7 @@ type OpenGlVRApplicationLayered(samples : int, debug : IDebugConfig, adjustSize let caller = DummyObject() let version = AVal.init 0 - let tex = AVal.custom (fun _ -> fTex :> ITexture) + let tex = AVal.custom (fun _ -> fTexl :> ITexture) let keyboard = new EventKeyboard() let mouse = new EventMouse(false) @@ -150,7 +151,8 @@ type OpenGlVRApplicationLayered(samples : int, debug : IDebugConfig, adjustSize override x.Use(f : unit -> 'a) = Operators.using ctx.ResourceLock (fun _ -> f()) - override x.Handedness with get() = Trafo3d.FromBasis(V3d.IOO, -V3d.OOI, -V3d.OIO, V3d.Zero) + //override x.Handedness + // with get() = Trafo3d.FromBasis(V3d.IOO, -V3d.OOI, -V3d.OIO, V3d.Zero) override x.OnLoad(i : VrRenderInfo) = //renderCtx.MakeCurrent() @@ -161,7 +163,8 @@ type OpenGlVRApplicationLayered(samples : int, debug : IDebugConfig, adjustSize if loaded then ctx.Delete (unbox fbo) - ctx.Delete fTex + ctx.Delete fTexl + ctx.Delete fTexr ctx.Delete dTex ctx.Delete cTex else @@ -171,7 +174,8 @@ type OpenGlVRApplicationLayered(samples : int, debug : IDebugConfig, adjustSize let nTex = ctx.CreateTexture2DArray(info.framebufferSize, 2, 1, TextureFormat.Rgba8, samples) let nDepth = ctx.CreateTexture2DArray(info.framebufferSize, 2, 1, TextureFormat.Depth24Stencil8, samples) - let nfTex = ctx.CreateTexture2D(info.framebufferSize * V2i(2,1), 1, TextureFormat.Rgba8, 1) + let nfTexl = ctx.CreateTexture2D(info.framebufferSize, 1, TextureFormat.Rgba8, 1) + let nfTexr = ctx.CreateTexture2D(info.framebufferSize, 1, TextureFormat.Rgba8, 1) let nFbo = runtime.CreateFramebuffer( @@ -186,12 +190,13 @@ type OpenGlVRApplicationLayered(samples : int, debug : IDebugConfig, adjustSize dTex <- nDepth cTex <- nTex - fTex <- nfTex + fTexl <- nfTexl + fTexr <- nfTexr fbo <- nFbo - let lTex = VrTexture.OpenGL(fTex.Handle, Box2d(V2d(0.0, 1.0), V2d(0.5, 0.0))) - let rTex = VrTexture.OpenGL(fTex.Handle, Box2d(V2d(0.5, 1.0), V2d(1.0, 0.0))) + let lTex = VrTexture.OpenGL(fTexl.Handle) + let rTex = VrTexture.OpenGL(fTexr.Handle) loaded <- true @@ -213,8 +218,8 @@ type OpenGlVRApplicationLayered(samples : int, debug : IDebugConfig, adjustSize GL.Sync() if samples > 1 then - runtime.ResolveMultisamples(cTex.[TextureAspect.Color, 0, 0], fTex, V2i.Zero, V2i.Zero, cTex.Size.XY) - runtime.ResolveMultisamples(cTex.[TextureAspect.Color, 0, 1], fTex, V2i.Zero, V2i(cTex.Size.X, 0), cTex.Size.XY) + runtime.ResolveMultisamples(cTex.[TextureAspect.Color, 0, 0], fTexl, V2i.Zero, V2i.Zero, cTex.Size.XY) + runtime.ResolveMultisamples(cTex.[TextureAspect.Color, 0, 1], fTexr, V2i.Zero, V2i.Zero, cTex.Size.XY) else failwith "not implemented" //runtime.Copy(cTex.[TextureAspect.Color, 0, *], fTex.[TextureAspect.Color, 0, *]) @@ -236,7 +241,8 @@ type OpenGlVRApplicationLayered(samples : int, debug : IDebugConfig, adjustSize ctx.Delete (unbox fbo) ctx.Delete dTex ctx.Delete cTex - ctx.Delete fTex + ctx.Delete fTexl + ctx.Delete fTexr app.Dispose() diff --git a/src/Application/Aardvark.Application.Slim.GL/Application.fs b/src/Application/Aardvark.Application.Slim.GL/Application.fs index 9d7e68fd3..e86e9bcac 100644 --- a/src/Application/Aardvark.Application.Slim.GL/Application.fs +++ b/src/Application/Aardvark.Application.Slim.GL/Application.fs @@ -104,23 +104,12 @@ module private OpenGL = [] type MyGraphicsContext(glfw : Glfw, win : nativeptr) as this = - //[] - //static val mutable private CurrentContext : OpenTK.ContextHandle - let mutable win = win static let addContext = typeof.GetMethod("AddContext", BindingFlags.NonPublic ||| BindingFlags.Static) static let remContext = typeof.GetMethod("RemoveContext", BindingFlags.NonPublic ||| BindingFlags.Static) let handle = ContextHandle(NativePtr.toNativeInt win) - static let mutable inited = false - do - if not inited then - inited <- true - let get = GraphicsContext.GetCurrentContextDelegate(fun () -> ContextHandle(NativePtr.toNativeInt (glfw.GetCurrentContext()))) - let t = typeof - let f = t.GetField("GetCurrentContext", BindingFlags.NonPublic ||| BindingFlags.Static) - f.SetValue(null, get) do addContext.Invoke(null, [| this :> obj |]) |> ignore @@ -134,12 +123,14 @@ module private OpenGL = let m = t.GetMethod("LoadEntryPoints", BindingFlags.NonPublic ||| BindingFlags.Instance) let gl = OpenTK.Graphics.OpenGL.GL() m.Invoke(gl, null) |> ignore - + + member x.Dispose() = + remContext.Invoke(null, [| x :> obj |]) |> ignore + win <- NativePtr.zero + interface IGraphicsContext with - member x.Dispose(): unit = - remContext.Invoke(null, [| x :> obj |]) |> ignore - win <- NativePtr.zero - () + member x.Dispose() = + x.Dispose() member x.ErrorChecking with get () = false @@ -222,8 +213,6 @@ module private OpenGL = size } - let mutable private lastWindow = None - let getFramebufferSamples() = if GL.getVersion() >= System.Version(4, 5) then GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0) @@ -246,7 +235,6 @@ module private OpenGL = let old = glfw.GetCurrentContext() glfw.MakeContextCurrent(NativePtr.zero) - if Option.isNone lastWindow then lastWindow <- Some win let ctx = new MyGraphicsContext(glfw, win) @@ -272,15 +260,18 @@ module private OpenGL = if old <> NativePtr.zero then glfw.MakeContextCurrent old - - let signature = - runtime.CreateFramebufferSignature([ - DefaultSemantic.Colors, TextureFormat.Rgba8 - DefaultSemantic.DepthStencil, TextureFormat.Depth24Stencil8 - ], samples) let handle = new Aardvark.Rendering.GL.ContextHandle(ctx, info) - do handle.Initialize(runtime.DebugConfig, setDefaultStates = true) + + let signature = + handle.Use (fun _ -> + handle.Initialize(runtime.DebugConfig, setDefaultStates = true) + + runtime.CreateFramebufferSignature([ + DefaultSemantic.Colors, TextureFormat.Rgba8 + DefaultSemantic.DepthStencil, TextureFormat.Depth24Stencil8 + ], samples) + ) { new IWindowSurface with override x.Signature = signature @@ -288,17 +279,39 @@ module private OpenGL = handle :> obj override this.CreateSwapchain(size: V2i) = createSwapchain runtime signature handle glfw win size - override this.Dispose() = - () + override this.Dispose() = + signature.Dispose() + ctx.Dispose() } - let interop (debug : DebugConfig) = + let getInterop (debug : DebugConfig) = let disableErrorChecks = debug.ErrorFlagCheck = ErrorFlagCheck.Disabled + let getCurrentContext = typeof.GetField("GetCurrentContext", BindingFlags.NonPublic ||| BindingFlags.Static) + let mutable getCurrentContextDelegate = null + + let install (glfw: Glfw) = + getCurrentContextDelegate <- + GraphicsContext.GetCurrentContextDelegate( + fun () -> ContextHandle(NativePtr.toNativeInt <| glfw.GetCurrentContext()) + ) + + if getCurrentContext.GetValue(null) <> null then + Log.warn "[GLFW] Overriding OpenTK GetCurrentContext" + + getCurrentContext.SetValue(null, getCurrentContextDelegate) + + let cleanup() = + if not <| isNull getCurrentContextDelegate then + if getCurrentContext.GetValue(null) = getCurrentContextDelegate then + getCurrentContext.SetValue(null, null) + getCurrentContextDelegate <- null + { new IWindowInterop with override __.Boot(glfw) = initVersion glfw + install glfw override __.CreateSurface(runtime : IRuntime, cfg: WindowConfig, glfw: Glfw, win: nativeptr) = createSurface (runtime :?> _) cfg glfw win @@ -329,18 +342,26 @@ module private OpenGL = glfw.WindowHint(WindowHintBool.ScaleToMonitor, true) glfw.WindowHint(WindowHintInt.Samples, if cfg.samples = 1 then 0 else cfg.samples) + + interface IDisposable with + member x.Dispose() = cleanup() } type OpenGlApplication private (runtime : Runtime, shaderCachePath : Option, hideCocoaMenuBar : bool) as this = - inherit Application(runtime, OpenGL.interop runtime.DebugConfig, hideCocoaMenuBar) + inherit Application(runtime, OpenGL.getInterop runtime.DebugConfig, hideCocoaMenuBar) - let createContext() = - let w = this.Instance.CreateWindow WindowConfig.Default - let h = w.Surface.Handle :?> Aardvark.Rendering.GL.ContextHandle - this.Instance.RemoveExistingWindow w - h + // Note: We ignore the passed parent since we determine the parent context in the CreateWindow method. + // This is always the first created context and should therefore match the passed one anyway. + let createContext (_parent : Aardvark.Rendering.GL.ContextHandle option) = + this.Instance.Invoke (fun _ -> + let w = this.Instance.CreateWindow WindowConfig.Default + let h = w.Surface.Handle :?> Aardvark.Rendering.GL.ContextHandle + this.Instance.RemoveExistingWindow w + h.OnDisposed.Add w.Dispose + h + ) - let ctx = new Context(runtime, fun () -> this.Instance.Invoke createContext) + let ctx = new Context(runtime, createContext) do ctx.ShaderCachePath <- shaderCachePath runtime.Initialize(ctx) @@ -370,9 +391,7 @@ type OpenGlApplication private (runtime : Runtime, shaderCachePath : Option] module MissingGlfwFunctions = - open System.Runtime.InteropServices + open System.Text type private GetWindowContentScaleDel = delegate of nativeptr * byref * byref -> unit @@ -270,6 +269,19 @@ module MissingGlfwFunctions = System.Text.Encoding.UTF8.GetString(l.ToArray()) + member x.GetErrorMessage() = + let err, ptr = x.GetError() + let mutable msg = String.Empty + + if err <> ErrorCode.NoError && not <| NativePtr.isNull ptr then + let mutable len = 0 + while NativePtr.get ptr len <> 0uy do + inc &len + + msg <- Encoding.UTF8.GetString(ptr, len) + + err, msg + type WindowEvent = | Resize | Run of action : (unit -> unit) @@ -553,6 +565,7 @@ type IWindowSurface = abstract CreateSwapchain : V2i -> ISwapchain abstract Handle : obj +// TODO: Inherit IDisposable (see Instance.Dispose()) type IWindowInterop = abstract Boot : Glfw -> unit abstract CreateSurface : IRuntime * WindowConfig * Glfw * nativeptr -> IWindowSurface @@ -574,7 +587,13 @@ type Instance(runtime : Aardvark.Rendering.IRuntime, interop : IWindowInterop, h interop.Boot glfw - let mutable lastWindow : nativeptr option = None + // For GL we need to specify a parent window to enable context sharing + // Simply use the one that was created first, assuming it is still valid and unused. + // If GL.RuntimeConfig.RobustContextSharing is false, this will be a resource context which + // may not always be available. If the flag is true, this will be a hidden context that + // is guaranteed to be available. + let mutable parentWindow : nativeptr option = None + let queue = System.Collections.Concurrent.ConcurrentQueue unit>() let existingWindows = System.Collections.Concurrent.ConcurrentHashSet() @@ -665,7 +684,7 @@ type Instance(runtime : Aardvark.Rendering.IRuntime, interop : IWindowInterop, h glfw.MakeContextCurrent(NativePtr.zero) let parent = - lastWindow |> Option.defaultValue NativePtr.zero + parentWindow |> Option.defaultValue NativePtr.zero glfw.DefaultWindowHints() @@ -683,9 +702,18 @@ type Instance(runtime : Aardvark.Rendering.IRuntime, interop : IWindowInterop, h let win = glfw.CreateWindow(cfg.width, cfg.height, cfg.title, NativePtr.zero, parent) if win = NativePtr.zero then - failwithf "GLFW could not create window: %A" (fst <| glfw.GetError()) + let err, msg = glfw.GetErrorMessage() + + let description = + if String.IsNullOrWhiteSpace msg then "Could not create window" + else msg - lastWindow <- Some win + let msg = $"[GLFW] {description} (error: {err})" + Report.Error msg + failwith msg + + if Option.isNone parentWindow then + parentWindow <- Some win let surface = interop.CreateSurface(runtime, cfg, glfw, win) @@ -725,6 +753,10 @@ type Instance(runtime : Aardvark.Rendering.IRuntime, interop : IWindowInterop, h if v then wait <- false member x.Dispose() = + match interop with + | :? IDisposable as d -> d.Dispose() + | _ -> () + glfw.Terminate() interface IDisposable with @@ -1269,14 +1301,14 @@ and Window(instance : Instance, win : nativeptr, title : string, e swapchain |> Option.iter Disposable.dispose swapchain <- None - surface.Dispose() - renderTask.Dispose() renderTask <- RenderTask.empty gpuQuery |> Option.iter Disposable.dispose gpuQuery <- None + surface.Dispose() + glfw.HideWindow(win) glfw.DestroyWindow(win) ) diff --git a/src/Application/Aardvark.Application.Utilities/Window.fs b/src/Application/Aardvark.Application.Utilities/Window.fs index 5dc7b1239..f2a8e444c 100644 --- a/src/Application/Aardvark.Application.Utilities/Window.fs +++ b/src/Application/Aardvark.Application.Utilities/Window.fs @@ -607,66 +607,43 @@ module Utilities = res let private createOpenVR (cfg : RenderConfig) = - match cfg.backend with - | Backend.Vulkan -> - let app = new VulkanVRApplicationLayered(cfg.samples, cfg.debug) - let hmdLocation = app.Hmd.MotionState.Pose |> AVal.map (fun t -> t.Forward.C3.XYZ) - - - let stencilTest = - { StencilMode.None with - Comparison = ComparisonFunction.Equal } - - { new SimpleRenderWindow(app, app.Info.viewTrafos, app.Info.projTrafos) with - - override x.WrapSg(win, sg) = - sg - |> Sg.stencilMode' stencilTest - |> Sg.uniform "ViewTrafo" app.Info.viewTrafos - |> Sg.uniform "ProjTrafo" app.Info.projTrafos - |> Sg.uniform "CameraLocation" hmdLocation - |> Sg.uniform "LightLocation" hmdLocation - - override x.Compile(win, sg) = - sg - |> Sg.stencilMode' stencilTest - |> Sg.uniform "ViewTrafo" app.Info.viewTrafos - |> Sg.uniform "ProjTrafo" app.Info.projTrafos - |> Sg.uniform "CameraLocation" hmdLocation - |> Sg.uniform "LightLocation" hmdLocation - |> Sg.compile app.Runtime app.FramebufferSignature - } :> ISimpleRenderWindow + let app = + match cfg.backend with + | Backend.Vulkan -> + let app = new VulkanVRApplicationLayered(cfg.samples, cfg.debug) + app :> VrRenderer + | Backend.GL -> + let app = new OpenGlVRApplicationLayered(cfg.samples, cfg.debug) + app :> VrRenderer + + let hmdLocation = app.Hmd.MotionState.Pose |> AVal.map (fun t -> t.Forward.C3.XYZ) + + let stencilTest = + { StencilMode.None with + Comparison = ComparisonFunction.Equal } + + + let app1 = unbox app + { new SimpleRenderWindow(app1, app.Info.viewTrafos, app.Info.projTrafos) with + + override x.WrapSg(win, sg) = + sg + |> Sg.stencilMode' stencilTest + |> Sg.uniform "ViewTrafo" app.Info.viewTrafos + |> Sg.uniform "ProjTrafo" app.Info.projTrafos + |> Sg.uniform "CameraLocation" hmdLocation + |> Sg.uniform "LightLocation" hmdLocation + + override x.Compile(win, sg) = + sg + |> Sg.stencilMode' stencilTest + |> Sg.uniform "ViewTrafo" app.Info.viewTrafos + |> Sg.uniform "ProjTrafo" app.Info.projTrafos + |> Sg.uniform "CameraLocation" hmdLocation + |> Sg.uniform "LightLocation" hmdLocation + |> Sg.compile app1.Runtime app1.FramebufferSignature + } :> ISimpleRenderWindow - | Backend.GL -> - let app = new OpenGlVRApplicationLayered(cfg.samples, cfg.debug) - - let hmdLocation = app.Hmd.MotionState.Pose |> AVal.map (fun t -> t.Forward.C3.XYZ) - - - let stencilTest = - { StencilMode.None with - Comparison = ComparisonFunction.Equal } - - { new SimpleRenderWindow(app, app.Info.viewTrafos, app.Info.projTrafos) with - - override x.WrapSg(win, sg) = - sg - |> Sg.stencilMode' stencilTest - |> Sg.uniform "ViewTrafo" app.Info.viewTrafos - |> Sg.uniform "ProjTrafo" app.Info.projTrafos - |> Sg.uniform "CameraLocation" hmdLocation - |> Sg.uniform "LightLocation" hmdLocation - - override x.Compile(win, sg) = - sg - |> Sg.stencilMode' stencilTest - |> Sg.uniform "ViewTrafo" app.Info.viewTrafos - |> Sg.uniform "ProjTrafo" app.Info.projTrafos - |> Sg.uniform "CameraLocation" hmdLocation - |> Sg.uniform "LightLocation" hmdLocation - |> Sg.compile app.Runtime app.FramebufferSignature - } :> ISimpleRenderWindow - let createWindow (cfg : RenderConfig) = diff --git a/src/Application/Aardvark.Application.WPF.GL/Application.fs b/src/Application/Aardvark.Application.WPF.GL/Application.fs index 0a371c346..bc05865e6 100644 --- a/src/Application/Aardvark.Application.WPF.GL/Application.fs +++ b/src/Application/Aardvark.Application.WPF.GL/Application.fs @@ -12,7 +12,7 @@ type OpenGlApplication(forceNvidia : bool, debug : IDebugConfig, shaderCachePath OpenTK.Toolkit.Init(new OpenTK.ToolkitOptions(Backend=OpenTK.PlatformBackend.PreferNative)) |> ignore let runtime = new Runtime(debug) - let ctx = new Context(runtime, fun () -> ContextHandleOpenTK.create debug) + let ctx = new Context(runtime, ContextHandleOpenTK.createWithParent debug) do ctx.ShaderCachePath <- shaderCachePath runtime.Initialize(ctx) @@ -31,7 +31,6 @@ type OpenGlApplication(forceNvidia : bool, debug : IDebugConfig, shaderCachePath member x.Runtime = runtime member x.Dispose() = - ctx.Dispose() runtime.Dispose() member x.Initialize(ctrl : IRenderControl, samples : int) = diff --git a/src/Application/Aardvark.Application.WPF.GL/SharingRenderControl.fs b/src/Application/Aardvark.Application.WPF.GL/SharingRenderControl.fs index 58a6c9fa1..53d8ca2a9 100644 --- a/src/Application/Aardvark.Application.WPF.GL/SharingRenderControl.fs +++ b/src/Application/Aardvark.Application.WPF.GL/SharingRenderControl.fs @@ -14,6 +14,9 @@ open Aardvark.Application open System.Windows.Threading open System.Security open System.Threading +open Microsoft.FSharp.NativeInterop + +#nowarn "9" [] module private DXSharingHelpers = @@ -173,40 +176,63 @@ module WGLDXExtensions = let worked = wglDXUnlockObjectsNV.Invoke(hDevice, objects.Length, pObjects) if not worked then fail "could not unlock objects" + [] module WGLDXContextExtensions = - type ShareContext private(ctx : Context, d3d : SharpDX.Direct3D9.Direct3DEx, device : SharpDX.Direct3D9.DeviceEx, shareDevice : WglDxShareDevice) = + let inline spanny<'a when 'a : unmanaged> (ptr : nativeptr<'a>) = + System.Span<'a>(NativePtr.toVoidPtr ptr, 1) + + open Silk.NET.Direct3D9 + type ShareContext private(ctx : Context, d3d : nativeptr, device : nativeptr, shareDevice : WglDxShareDevice) = static member Create(ctx : Context) = use __ = ctx.ResourceLock - let d3d = new SharpDX.Direct3D9.Direct3DEx() - let device = + + let d3d = D3D9.GetApi() + let mutable d3d9exptr = Unchecked.defaultof<_> + let _ = d3d.Direct3DCreate9Ex(32u, &d3d9exptr) + let d3d = d3d9exptr //System.Span(NativePtr.toVoidPtr d3d9exptr, 1) + + let pDevice = let hndl = GetDesktopWindow() - let parameters = - SharpDX.Direct3D9.PresentParameters( - BackBufferWidth = 10, - BackBufferHeight = 10, - BackBufferFormat = SharpDX.Direct3D9.Format.A8R8G8B8, - BackBufferCount = 0, - MultiSampleType = SharpDX.Direct3D9.MultisampleType.None, - MultiSampleQuality = 0, - SwapEffect = SharpDX.Direct3D9.SwapEffect.Discard, - Windowed = SharpDX.Mathematics.Interop.RawBool(true), - DeviceWindowHandle = hndl, - PresentationInterval = SharpDX.Direct3D9.PresentInterval.Default + let mutable parameters = + PresentParameters( + backBufferWidth = Nullable 10u, + backBufferHeight = Nullable 10u , + backBufferFormat = Nullable Format.FmtA8R8G8B8, + backBufferCount = Nullable 0u, + multiSampleType = Nullable MultisampleType.MultisampleNone, + multiSampleQuality = Nullable 0u, + swapEffect = Nullable Swapeffect.SwapeffectDiscard, + windowed = Nullable 1,// SharpDX.Mathematics.Interop.RawBool(true), + //deviceWindowHandle = hndl, + hDeviceWindow = Nullable hndl, + presentationInterval = Nullable 0u//PresentInterval.Default ) + //SharpDX.Direct3D9.CreateFlags.FpuPreserve ||| + //SharpDX.Direct3D9.CreateFlags.HardwareVertexProcessing ||| + //SharpDX.Direct3D9.CreateFlags.Multithreaded + let createFlags = 0x4u ||| 0x2u ||| 0x40u + let mutable dev = Unchecked.defaultof<_> + printfn "before" + let result = + spanny(d3d).[0].CreateDeviceEx( + 0u, + Devtype.DevtypeHal, + 0n, + createFlags, + ¶meters, + NativePtr.zero, + &dev + ) + if result <> 0 then + raise <| Marshal.GetExceptionForHR(result) + printfn "after " + dev + let shareDevice = WGL.OpenDevice(NativePtr.toNativeInt pDevice) - new SharpDX.Direct3D9.DeviceEx( - d3d, 0, - SharpDX.Direct3D9.DeviceType.Hardware, 0n, - SharpDX.Direct3D9.CreateFlags.FpuPreserve ||| SharpDX.Direct3D9.CreateFlags.HardwareVertexProcessing ||| - SharpDX.Direct3D9.CreateFlags.Multithreaded, - parameters - ) - let shareDevice = WGL.OpenDevice(device.NativePointer) - - ShareContext(ctx, d3d, device, shareDevice) + ShareContext(ctx, d3d, pDevice, shareDevice) @@ -218,7 +244,7 @@ module WGLDXContextExtensions = let private shareContexts = System.Runtime.CompilerServices.ConditionalWeakTable() - type D3DRenderbuffer(ctx : ShareContext, resolveBuffer : int, renderBuffer : int, size : V2i, fmt : TextureFormat, samples : int, dxSurface : SharpDX.Direct3D9.Surface, shareHandle : WglDxShareHandle) = + type D3DRenderbuffer(ctx : ShareContext, resolveBuffer : int, renderBuffer : int, size : V2i, fmt : TextureFormat, samples : int, dxSurface : nativeptr, shareHandle : WglDxShareHandle) = inherit Aardvark.Rendering.GL.Renderbuffer(ctx.Context, renderBuffer, size, fmt, samples, 0L) let mutable shareHandle = shareHandle @@ -293,7 +319,7 @@ module WGLDXContextExtensions = use __ = ctx.Context.ResourceLock WGL.UnregisterObject(ctx.ShareDevice, shareHandle) shareHandle <- WglDxShareHandle.Null - dxSurface.Dispose() + spanny(dxSurface).[0].Release() |> ignore GL.DeleteRenderbuffer(resolveBuffer) GL.DeleteRenderbuffer(renderBuffer) x.Handle <- 0 @@ -303,8 +329,8 @@ module WGLDXContextExtensions = let private dxFormat = LookupTable.lookupTable [ - TextureFormat.Rgba8, SharpDX.Direct3D9.Format.A8R8G8B8 - TextureFormat.Depth24Stencil8, SharpDX.Direct3D9.Format.D24S8 + TextureFormat.Rgba8, Format.FmtA8R8G8B8 + TextureFormat.Depth24Stencil8, Format.FmtD24S8 ] type Context with @@ -324,14 +350,15 @@ module WGLDXContextExtensions = let dxFormat = dxFormat format let ctx = x.ShareContext - let mutable wddmHandle = 0n - let surface = - SharpDX.Direct3D9.Surface.CreateRenderTarget( - ctx.Device, - size.X, size.Y, + let mutable wddmHandle = Unchecked.defaultof<_> + let mutable surface = Unchecked.defaultof<_> + let _ = + spanny(ctx.Device).[0].CreateRenderTarget( + uint32 size.X, uint32 size.Y, dxFormat, - SharpDX.Direct3D9.MultisampleType.None, 0, - true, + MultisampleType.MultisampleNone, 0u, + 1, + &surface, &wddmHandle ) @@ -359,8 +386,8 @@ module WGLDXContextExtensions = - WGL.SetResourceShareHandle(surface.NativePointer, wddmHandle) - let shareHandle = WGL.RegisterObject(ctx.ShareDevice, surface.NativePointer, resolveBuffer, All.Renderbuffer, WglDXAccess.WriteDiscard) + WGL.SetResourceShareHandle(NativePtr.toNativeInt surface, NativePtr.toNativeInt (NativePtr.ofVoidPtr wddmHandle)) + let shareHandle = WGL.RegisterObject(ctx.ShareDevice, NativePtr.toNativeInt surface, resolveBuffer, All.Renderbuffer, WglDXAccess.WriteDiscard) //let shareHandle = WglDxShareHandle.Null // let mutable ssamples = 0 @@ -526,7 +553,7 @@ type OpenGlSharingRenderControl(runtime : Runtime, samples : int) as this = use __ = ctx.RenderingLock(handle) img.Lock() c.Lock() - img.SetBackBuffer(Interop.D3DResourceType.IDirect3DSurface9, c.Surface.NativePointer) + img.SetBackBuffer(Interop.D3DResourceType.IDirect3DSurface9, NativePtr.toNativeInt c.Surface) c.Unlock() img.AddDirtyRect(Int32Rect(0,0,img.PixelWidth, img.PixelHeight)) diff --git a/src/Application/Aardvark.Application.WPF.GL/paket.references b/src/Application/Aardvark.Application.WPF.GL/paket.references index ab399674c..4046bfe31 100644 --- a/src/Application/Aardvark.Application.WPF.GL/paket.references +++ b/src/Application/Aardvark.Application.WPF.GL/paket.references @@ -2,7 +2,7 @@ Aardvark.Base.Essentials Aardvark.Base.Incremental FShade.Core Aardvark.Base.Runtime -SharpDX.Direct3D9 +Silk.NET.Direct3D9 FSharp.Core FShade.GLSL Unofficial.OpenTK diff --git a/src/Application/Aardvark.Application.WinForms.GL/Application.fs b/src/Application/Aardvark.Application.WinForms.GL/Application.fs index 11abcf1eb..1e0219cef 100644 --- a/src/Application/Aardvark.Application.WinForms.GL/Application.fs +++ b/src/Application/Aardvark.Application.WinForms.GL/Application.fs @@ -15,7 +15,7 @@ type OpenGlApplication(forceNvidia : bool, debug : IDebugConfig, shaderCachePath with e -> Report.Warn("Could not set UnhandledExceptionMode.") let runtime = new Runtime(debug) - let ctx = new Context(runtime, fun () -> ContextHandleOpenTK.create debug) + let ctx = new Context(runtime, ContextHandleOpenTK.createWithParent debug) do ctx.ShaderCachePath <- shaderCachePath runtime.Initialize(ctx) @@ -35,7 +35,6 @@ type OpenGlApplication(forceNvidia : bool, debug : IDebugConfig, shaderCachePath member x.Dispose() = // first dispose runtime in order to properly dispose resources.. runtime.Dispose() - ctx.Dispose() member x.Initialize(ctrl : IRenderControl, samples : int) = match ctrl with diff --git a/src/Application/Aardvark.Application.WinForms.Vulkan/Application.fs b/src/Application/Aardvark.Application.WinForms.Vulkan/Application.fs index 09c7aa625..56b1cef85 100644 --- a/src/Application/Aardvark.Application.WinForms.Vulkan/Application.fs +++ b/src/Application/Aardvark.Application.WinForms.Vulkan/Application.fs @@ -102,6 +102,7 @@ type VulkanApplication(debug : IDebugConfig, chooseDevice : list yield Instance.Extensions.ShaderSubgroupVote yield Instance.Extensions.ShaderSubgroupBallot yield Instance.Extensions.GetPhysicalDeviceProperties2 + yield Instance.Extensions.ConservativeRasterization yield! Instance.Extensions.Raytracing yield! Instance.Extensions.Sharing diff --git a/src/Examples (netcore)/01 - Hello Wpf/01 - Hello Wpf.csproj b/src/Examples (netcore)/01 - Hello Wpf/01 - Hello Wpf.csproj index 4db3015c0..e97b2a726 100644 --- a/src/Examples (netcore)/01 - Hello Wpf/01 - Hello Wpf.csproj +++ b/src/Examples (netcore)/01 - Hello Wpf/01 - Hello Wpf.csproj @@ -1,7 +1,7 @@  - WinExe + Exe net6.0-windows _01___Hello_Wpf enable diff --git a/src/Examples (netcore)/01 - Hello Wpf/MainWindow.xaml.cs b/src/Examples (netcore)/01 - Hello Wpf/MainWindow.xaml.cs index 3cb62468e..792c25e7f 100644 --- a/src/Examples (netcore)/01 - Hello Wpf/MainWindow.xaml.cs +++ b/src/Examples (netcore)/01 - Hello Wpf/MainWindow.xaml.cs @@ -23,6 +23,7 @@ using Aardvark.Rendering.CSharp; using Aardvark.Rendering; using Effects = Aardvark.Rendering.Effects; +using Aardvark.Application.WPF; namespace _01___Hello_Wpf { @@ -35,6 +36,8 @@ public MainWindow() { Aardvark.Base.Aardvark.Init(); // initialize aardvark base modules + Config.useSharingControl = true; + var app = new Aardvark.Application.WPF.OpenGlApplication(true); InitializeComponent(); app.Initialize(renderControl, 1); @@ -52,11 +55,12 @@ public MainWindow() var currentAngle = 0.0; var angle = renderControl.Time.Map(t => { - if (checkBox.IsChecked!.Value) - { - return currentAngle += 0.001; - } - else return currentAngle; + return currentAngle += 0.001; + //if (checkBox.IsChecked!.Value) + //{ + // return currentAngle += 0.001; + //} + //else return currentAngle; }); var rotatingTrafo = angle.Map(Trafo3d.RotationZ); diff --git a/src/Examples (netcore)/33 - Opc/33 - Opc.fsproj b/src/Examples (netcore)/33 - Opc/33 - Opc.fsproj index 39ef16347..906991876 100644 --- a/src/Examples (netcore)/33 - Opc/33 - Opc.fsproj +++ b/src/Examples (netcore)/33 - Opc/33 - Opc.fsproj @@ -13,6 +13,7 @@ ..\..\..\bin\Release + diff --git a/src/Examples (netcore)/33 - Opc/Program.fs b/src/Examples (netcore)/33 - Opc/Program.fs index 5023707d0..07bfa68fd 100644 --- a/src/Examples (netcore)/33 - Opc/Program.fs +++ b/src/Examples (netcore)/33 - Opc/Program.fs @@ -181,8 +181,11 @@ type PatchLodTree(globalCenter : V3d, opc : OpcPaths, root : option(Col.Format.RGBA) - with _ -> DefaultTextures.checkerboardPix + try + PixImage.Load(path).ToPixImage(Col.Format.RGBA) + with e -> + Log.error "[Opc] %s" e.Message + DefaultTextures.checkerboardPix let tex = { new INativeTexture with @@ -475,6 +478,7 @@ let main argv = |> Sg.uniform "ProjTrafo" proj |> Sg.uniform "MipMaps" mipMaps |> Sg.uniform "Anisotropic" anisotropic + |> Sg.diffuseTexture' nullTexture |> Sg.viewTrafo views |> Sg.projTrafo proj diff --git a/src/Examples (netcore)/33 - Opc/paket.references b/src/Examples (netcore)/33 - Opc/paket.references index a35625263..180c93249 100644 --- a/src/Examples (netcore)/33 - Opc/paket.references +++ b/src/Examples (netcore)/33 - Opc/paket.references @@ -1,15 +1,2 @@ -Aardvark.Base.Essentials -Aardvark.Base.Incremental -Aardvark.Base.Runtime -AssimpNet FShade.Core -FShade.GLSL -FSharp.Core -Unofficial.Typography -Unofficial.LibTessDotNet -Unofficial.OpenTK - -Unofficial.OpenVR -FShade.Debug -SixLabors.ImageSharp -Aardvark.Build \ No newline at end of file +Aardvark.PixImage.Pfim \ No newline at end of file diff --git a/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj b/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj index d8c58255a..8a4af38d4 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj +++ b/src/Tests/Aardvark.Rendering.Tests/Aardvark.Rendering.Tests.fsproj @@ -62,6 +62,7 @@ + @@ -71,9 +72,11 @@ + + diff --git a/src/Tests/Aardvark.Rendering.Tests/Program.fs b/src/Tests/Aardvark.Rendering.Tests/Program.fs index bbf20024a..2caf65d8a 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Program.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Program.fs @@ -33,6 +33,7 @@ let main argv = Rendering.IntegerAttachments.tests Rendering.Samplers.tests Rendering.Uniforms.tests + Rendering.Surfaces.tests ] let computeTests = @@ -57,7 +58,9 @@ let main argv = ``SceneGraph Tests``.tests ``CompactSet Tests``.tests ``AdaptiveResource Tests``.tests + ``ContextCreation Tests``.tests ``Camera Tests``.tests + ``IndexedGeometry Tests``.tests ] let allTests = diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Application.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Application.fs index 445f5f905..320d4f969 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Tests/Application.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Application.fs @@ -9,10 +9,24 @@ open OpenTK.Graphics.OpenGL4 open System open System.Reflection -type TestApplication(runtime : IRuntime, disposable : IDisposable) = - do runtime.ShaderCachePath <- None +[] +type Framework = + | GLFW + | OpenTK - member x.Runtime = runtime +[] +type TestBackend = + | GL of Framework + | Vulkan + +type ITestApplication = + inherit IDisposable + abstract member Runtime : IRuntime + +type TestApplication(inner : ITestApplication, disposable : IDisposable) = + do inner.Runtime.ShaderCachePath <- None + + member x.Runtime = inner.Runtime member x.Dispose() = disposable.Dispose() interface IDisposable with @@ -24,18 +38,40 @@ module TestApplication = open OpenTK open Aardvark.Rendering.GL - let create (debug : IDebugConfig) = + let private createOpenTK (debug : IDebugConfig) = + let toolkit = Toolkit.Init(ToolkitOptions(Backend = PlatformBackend.PreferNative)) + + let runtime = new Runtime(debug) + let ctx = new Context(runtime, ContextHandleOpenTK.createWithParent runtime.DebugConfig) + + runtime.Initialize(ctx) + + { new ITestApplication with + member x.Runtime = runtime + member x.Dispose() = + runtime.Dispose() + toolkit.Dispose() } + + let private createGLFW (debug : IDebugConfig) = + let app = new Slim.OpenGlApplication(debug) + + { new ITestApplication with + member x.Runtime = app.Runtime + member x.Dispose() = app.Dispose() } + + let create (framework : Framework) (debug : IDebugConfig) = Config.MajorVersion <- 4 Config.MinorVersion <- 6 RuntimeConfig.UseNewRenderTask <- true + RuntimeConfig.RobustContextSharing <- true RuntimeConfig.PreferHostSideTextureCompression <- true - let toolkit = Toolkit.Init(ToolkitOptions(Backend = PlatformBackend.PreferNative)) - - let runtime = new Runtime(debug) - let ctx = new Context(runtime, fun () -> ContextHandleOpenTK.create runtime.DebugConfig) + let app = + match framework with + | Framework.GLFW -> createGLFW debug + | Framework.OpenTK -> createOpenTK debug - runtime.Initialize(ctx) + let ctx = (unbox app.Runtime).Context let checkForErrors() = use __ = ctx.ResourceLock @@ -56,13 +92,12 @@ module TestApplication = failwithf "OpenGL debug output reported errors: %s" errors new TestApplication( - runtime, + app, { new IDisposable with member x.Dispose() = checkForErrors() - runtime.Dispose() + app.Dispose() checkForDebugErrors() - toolkit.Dispose() } ) @@ -71,7 +106,14 @@ module TestApplication = let create (debug : IDebugConfig) = CustomDeviceChooser.Register Seq.head - let app = new HeadlessVulkanApplication(debug) + + let headless = new HeadlessVulkanApplication(debug) + + let app = + { new ITestApplication with + member x.Runtime = headless.Runtime + member x.Dispose() = headless.Dispose() } + let onExit = { new IDisposable with member x.Dispose() = @@ -79,7 +121,7 @@ module TestApplication = let failed, errors = let br = Environment.NewLine + Environment.NewLine - let msgs = app.Instance.DebugSummary.ErrorMessages + let msgs = headless.Instance.DebugSummary.ErrorMessages msgs.Length > 0, msgs |> String.concat br |> (+) br @@ -89,26 +131,27 @@ module TestApplication = } new TestApplication( - app.Runtime, onExit + app, onExit ) let mutable private aardvarkInitialized = false - let create' (debug : IDebugConfig) (backend : Backend) = + let create' (debug : IDebugConfig) (backend : TestBackend) = if not aardvarkInitialized then IntrospectionProperties.CustomEntryAssembly <- Assembly.GetAssembly(typeof) Aardvark.Init() aardvarkInitialized <- true match backend with - | Backend.GL -> GL.create debug - | Backend.Vulkan -> Vulkan.create debug + | TestBackend.GL f -> GL.create f debug + | TestBackend.Vulkan -> Vulkan.create debug - let create (backend : Backend) = + let create (backend : TestBackend) = let config : IDebugConfig = - if backend = Backend.Vulkan then + if backend = TestBackend.Vulkan then + // Disable GPU-AV (producing sporadic false positives with Vulkan SDK 1.3.280) { Vulkan.DebugConfig.Normal with - ValidationLayer = Some Vulkan.ValidationLayerConfig.Full } + ValidationLayer = Some { Vulkan.ValidationLayerConfig.Full with ShaderBasedValidation = Vulkan.ShaderValidation.Disabled } } else { GL.DebugConfig.Normal with DebugRenderTasks = true @@ -117,6 +160,5 @@ module TestApplication = backend |> create' config let createUse f backend = - let app = create backend + use app = create backend f app.Runtime - app.Dispose() diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Common.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Common.fs index 9aae94c11..0241f21c3 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Tests/Common.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Common.fs @@ -11,15 +11,16 @@ open System.Reflection [] module ``Unit Test Utilities`` = - let private backendName = function - | Backend.GL -> "GL" - | Backend.Vulkan -> "Vulkan" - let testBackend (backend : Backend) (name : string) (tests : List Test>) = - let name = sprintf "[%s] %s" (backendName backend) name + let name = sprintf "[%s] %s" (string backend) name testList name (tests |> List.map (fun t -> t backend)) let prepareCases (backend : Backend) (name : string) (cases : List unit)>) = + let backend = + match backend with + | Backend.GL -> TestBackend.GL Framework.OpenTK + | Backend.Vulkan -> TestBackend.Vulkan + cases |> List.map (fun (name, test) -> testCase name (fun () -> TestApplication.createUse test backend) |> testSequenced diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Other/ContextCreation.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/ContextCreation.fs new file mode 100644 index 000000000..426e8a3f6 --- /dev/null +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/ContextCreation.fs @@ -0,0 +1,155 @@ +namespace Aardvark.Rendering.Tests + +open Aardvark.Base +open Aardvark.Rendering +open Aardvark.Rendering.GL + +open System.Threading +open FSharp.Data.Adaptive +open Expecto +open FShade +open OpenTK.Graphics.OpenGL4 + +module ``ContextCreation Tests`` = + + module Cases = + + module private TestTexture = + + let private textureData : uint8[] = + Array.init 16 uint8 + + let create() = + let t = GL.GenTexture() + GL.Check "failed to create texture" + + GL.BindTexture(TextureTarget.Texture1D, t) + GL.Check "failed to bind texture" + + GL.TexImage1D(TextureTarget.Texture1D, 0, PixelInternalFormat.R8ui, textureData.Length, 0, PixelFormat.RedInteger, PixelType.UnsignedByte, textureData) + GL.Check "failed to allocate texture" + + t + + let getAndCheck (texture : int) = + GL.BindTexture(TextureTarget.Texture1D, texture) + GL.Check "failed to bind texture" + + let result = Array.zeroCreate textureData.Length + GL.GetTexImage(TextureTarget.Texture1D, 0, PixelFormat.RedInteger, PixelType.UnsignedByte, result) + GL.Check "failed to get texture image" + + if result <> textureData then + failwithf "Data retrieved from texture is wrong: %A" result + + module private TextureSharing = + + let check (ctx : Context) (handle : ContextHandle) = + let texture = + use __ = ctx.ResourceLock + TestTexture.create() + + use __ = ctx.RenderingLock handle + TestTexture.getAndCheck texture + + GL.DeleteTexture texture + GL.Check "failed to delete texture" + + let createAfterDispose (runtime : IRuntime) = + let ctx = (unbox runtime).Context + + ctx.CreateContext().Dispose() + use h = ctx.CreateContext() + TextureSharing.check ctx h + + let createAfterMakeCurrent (runtime : IRuntime) = + let ctx = (unbox runtime).Context + + use p = ctx.CreateContext() + + let mutable stop = false + use event = new ManualResetEventSlim(false) + + let thread = + startThread (fun _ -> + use __ = ctx.RenderingLock p + + event.Set() + + while not stop do + Thread.Sleep 10 + ) + + try + event.Wait() + use h = ctx.CreateContext() + TextureSharing.check ctx h + + finally + stop <- true + thread.Join() + + let createAfterMakeCurrentAll (runtime : IRuntime) = + let ctx = (unbox runtime).Context + + let t = + use __ = ctx.ResourceLock + TestTexture.create() + + let count = RuntimeConfig.NumberOfResourceContexts + let mutable stop = false + use semaphore = new SemaphoreSlim(0, count) + + let threads = + Array.init count (fun _ -> + startThread (fun _ -> + use __ = ctx.ResourceLock + + semaphore.Release() |> ignore + + while not stop do + Thread.Sleep 10 + ) + ) + + try + for _ = 1 to count do + semaphore.Wait() + + use h = ctx.CreateContext() + use __ = ctx.RenderingLock h + + TestTexture.getAndCheck t + + GL.DeleteTexture t + GL.Check "failed to delete texture" + + finally + stop <- true + for t in threads do + t.Join() + + let private testsWithFramework (framework : Framework) = + [ + // Tests context sharing and demonstrates that using the + // last context as parent is not a good strategy. When sharing the + // parent context must exist (duh!) and not be current on another thread. + "Create after dispose", Cases.createAfterDispose + "Create after make current", Cases.createAfterMakeCurrent + + // Worst case scenario in which all contexts are currently used. + "Create after make current all", Cases.createAfterMakeCurrentAll + ] + |> List.map (fun (name, test) -> + let name = $"[{framework}] {name}" + + testCase name (fun () -> TestApplication.createUse test (TestBackend.GL framework)) + |> testSequenced + ) + + [] + let tests = + testList "Context.Creation" [ + yield! testsWithFramework Framework.GLFW + yield! testsWithFramework Framework.OpenTK + ] \ No newline at end of file diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs new file mode 100644 index 000000000..ff5f0e3ce --- /dev/null +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/IndexedGeometryTests.fs @@ -0,0 +1,101 @@ +namespace Aardvark.Rendering.Tests + +open Aardvark.Base +open Aardvark.Rendering +open Aardvark.SceneGraph +open Expecto + +module ``IndexedGeometry Tests`` = + + module Clone = + + let clone (shallow: bool) = + let name = if shallow then "shallow" else "deep" + + test $"Clone ({name})" { + let g = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + g.SingleAttributes <- SymDict.empty + let g_pos = g.IndexedAttributes.[DefaultSemantic.Positions] + let g_idx = g.IndexArray + + let gc = if shallow then g.Clone() else g.Clone(shallowCopy = false) + let gc_pos = gc.IndexedAttributes.[DefaultSemantic.Positions] + let gc_idx = gc.IndexArray + + Expect.isFalse (obj.ReferenceEquals(g.SingleAttributes, gc.SingleAttributes)) "Single attributes not copied" + Expect.isFalse (obj.ReferenceEquals(g.IndexedAttributes, gc.IndexedAttributes)) "Indexed attributes not copied" + + let expect = if shallow then Expect.isTrue else Expect.isFalse + expect (obj.ReferenceEquals(g_pos, gc_pos)) "Attribute array" + expect (obj.ReferenceEquals(g_idx, gc_idx)) "Index array" + + Expect.isTrue gc.IsValid "Invalid" + } + + module Union = + + let unionIndexed = + test "Union (indexed)" { + let a = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + a.IndexArray <- a.IndexArray |> unbox |> Array.map int16 + let b = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + b.IndexArray <- b.IndexArray |> unbox |> Array.map int16 + let c = IndexedGeometry.union a b + + let expected = + Array.concat [ + a.IndexArray |> unbox + b.IndexArray |> unbox |> Array.map ((+) (int16 a.VertexCount)) + ] + + Expect.equal (unbox c.IndexArray) expected "Unexpected indices" + Expect.equal c.FaceVertexCount (a.FaceVertexCount + b.FaceVertexCount) "Unexpected face vertex count" + Expect.isTrue c.IsValid "Invalid" + } + + let unionNonIndexed = + test "Union (non-indexed)" { + let a = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toNonIndexed + let b = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toNonIndexed + let c = IndexedGeometry.union a b + + Expect.isNull c.IndexArray "Unexpected index array" + Expect.equal c.FaceVertexCount (a.FaceVertexCount + b.FaceVertexCount) "Unexpected face vertex count" + Expect.isTrue c.IsValid "Invalid" + } + + let inline private unionNonIndexedAndIndexed (name: string) (mapIndex: int32 -> 'T) = + test $"Union (non-indexed & {name}-indexed)" { + let a = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toNonIndexed + let b = IndexedGeometryPrimitives.Box.solidBox Box3d.Unit C4b.Black |> IndexedGeometry.toIndexed + b.IndexArray <- b.IndexArray |> unbox |> Array.map mapIndex + let c = IndexedGeometry.union a b + + let expected = + Array.concat [ + Array.init a.FaceVertexCount (id >> mapIndex) + b.IndexArray |> unbox<'T[]> |> Array.map ((+) (mapIndex a.VertexCount)) + ] + + Expect.equal (unbox<'T[]> c.IndexArray) expected "Unexpected indices" + Expect.isTrue c.IsValid "Invalid" + } + + let unionNonIndexedAndInt16 = unionNonIndexedAndIndexed "int16" int16 + let unionNonIndexedAndInt32 = unionNonIndexedAndIndexed "int32" int32 + + [] + let tests = + testList "IndexedGeometry" [ + testList "Clone" [ + Clone.clone true + Clone.clone false + ] + + testList "Union" [ + Union.unionIndexed + Union.unionNonIndexed + Union.unionNonIndexedAndInt16 + Union.unionNonIndexedAndInt32 + ] + ] \ No newline at end of file diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Other/SgTest.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/SgTest.fs index 9878223c4..ea419f003 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Tests/Other/SgTest.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Other/SgTest.fs @@ -70,7 +70,7 @@ module ``SceneGraph Tests`` = let private testOnActivation (countAfterPrepare : int) (countAfterDispose : int) (sgWithActivator : (ISg -> ISg) -> ISg) = - use app = TestApplication.create Backend.Vulkan + use app = TestApplication.create TestBackend.Vulkan let runtime = app.Runtime use signature = @@ -119,7 +119,7 @@ module ``SceneGraph Tests`` = let delayModifySurface = test "Sg.Delay modify surface" { - use app = TestApplication.create Backend.Vulkan + use app = TestApplication.create TestBackend.Vulkan let runtime = app.Runtime use signature = diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/RenderingTests.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/RenderingTests.fs index 0153520af..bff511515 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/RenderingTests.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/RenderingTests.fs @@ -13,6 +13,7 @@ module ``Rendering Tests`` = IntegerAttachments.tests Samplers.tests Uniforms.tests + Surfaces.tests ] [] diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Surfaces.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Surfaces.fs new file mode 100644 index 000000000..cacdf301e --- /dev/null +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Rendering/Surfaces.fs @@ -0,0 +1,62 @@ +namespace Aardvark.Rendering.Tests.Rendering + +open Aardvark.Rendering +open Aardvark.Rendering.Tests +open Aardvark.Application +open FSharp.Data.Adaptive +open Expecto +open FShade + +module Surfaces = + + module Cases = + + let private glDynamicShaderCaching (signature: IFramebufferSignature) (surface: Surface) (runtime: GL.Runtime) = + use __ = runtime.Context.ResourceLock + let _, p1 = runtime.ResourceManager.CreateSurface(signature, surface, IndexedGeometryMode.TriangleList) + let _, p2 = runtime.ResourceManager.CreateSurface(signature, surface, IndexedGeometryMode.TriangleList) + Expect.isTrue (obj.ReferenceEquals(p1, p2)) "Not reference equal" + + let private vkDynamicShaderCaching (pass: Vulkan.RenderPass) (surface: Surface) (runtime: Vulkan.Runtime) = + let _, p1 = runtime.ResourceManager.CreateShaderProgram(pass, surface, IndexedGeometryMode.TriangleList) + let _, p2 = runtime.ResourceManager.CreateShaderProgram(pass, surface, IndexedGeometryMode.TriangleList) + + p1.Acquire() + p2.Acquire() + + try + Expect.isTrue (obj.ReferenceEquals(p1, p2)) "Not reference equal" + finally + p1.Release() + p2.Release() + + let dynamicShaderCaching (runtime: IRuntime) = + use signature = + runtime.CreateFramebufferSignature([ + DefaultSemantic.Colors, TextureFormat.Rgba8 + ]) + + let surface = + let effect = + Effect.compose [ + Effects.Trafo.Effect + Effects.VertexColor.Effect + ] + + let compile (cfg: EffectConfig) = + let module_ = Effect.toModule cfg effect + let layout = EffectInputLayout.ofModule module_ + layout, (AVal.init module_) :> aval<_> + + Surface.FShade compile + + match runtime, signature with + | :? GL.Runtime as r, _ -> glDynamicShaderCaching signature surface r + | :? Vulkan.Runtime as r, (:? Vulkan.RenderPass as p) -> vkDynamicShaderCaching p surface r + | _ -> failwith "Unknown backend" + + let tests (backend : Backend) = + [ + "Dynamic shader caching", Cases.dynamicShaderCaching + ] + |> prepareCases backend "Surfaces" \ No newline at end of file diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Compression.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Compression.fs index 59559827f..6577394ae 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Compression.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Compression.fs @@ -12,7 +12,6 @@ module TextureCompression = open BenchmarkDotNet.Attributes open OpenTK.Graphics.OpenGL4 - open Aardvark.Application type OnTheFlyCompression() = let mutable app = Unchecked.defaultof @@ -28,7 +27,7 @@ module TextureCompression = [] member x.Setup() = - app <- TestApplication.create' DebugLevel.None Backend.GL + app <- TestApplication.create' DebugLevel.None (TestBackend.GL Framework.OpenTK) let size = V2i x.Size image <- PixImage(Col.Format.RGBA, size) diff --git a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs index 10861e09f..d3b4bb862 100644 --- a/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs +++ b/src/Tests/Aardvark.Rendering.Tests/Tests/Texture/Upload.fs @@ -767,8 +767,8 @@ module TextureUpload = do! diffuseTexture } - use randomTexture = runtime.CreateTexture2D(V2i(8, 8), TextureFormat.Rgba8ui) - randomTexture.Upload(PixImage.random8ui <| V2i(8, 8)) + use randomTexture = runtime.CreateTexture2D(V2i(8, 8), TextureFormat.Rgba8i) + randomTexture.Upload(PixImage.random8i <| V2i(8, 8)) runtime |> renderQuadWithNullTexture shader randomTexture