]> O.S.I.I.S - jp/vke.net.git/commitdiff
split vk.net and cvkl, cvkl renamed in vke.net
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Tue, 10 Sep 2019 01:15:01 +0000 (03:15 +0200)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Tue, 10 Sep 2019 01:15:01 +0000 (03:15 +0200)
196 files changed:
.gitignore [new file with mode: 0644]
.travis.yml [new file with mode: 0644]
Directory.Build.props [new file with mode: 0644]
LICENSE.md [new file with mode: 0644]
README.md [new file with mode: 0644]
SpirVTasks/CompileGLSLTask.cs [new file with mode: 0644]
SpirVTasks/README.md [new file with mode: 0644]
SpirVTasks/SpirVTasks.csproj [new file with mode: 0644]
SpirVTasks/SpirVTasks.targets [new file with mode: 0644]
SpirVTasks/spirv.xml [new file with mode: 0644]
addons/Directory.Build.props [new file with mode: 0644]
addons/DistanceFieldFont/BMChar.cs [new file with mode: 0644]
addons/DistanceFieldFont/BMFont.cs [new file with mode: 0644]
addons/DistanceFieldFont/DistanceFieldFont.csproj [new file with mode: 0644]
addons/VkvgPipeline/VkvgPipeline.cs [new file with mode: 0644]
addons/VkvgPipeline/VkvgPipeline.csproj [new file with mode: 0644]
addons/gltfLoader/PbrModel.cs [new file with mode: 0644]
addons/gltfLoader/PbrModelSeparateTextures.cs [new file with mode: 0644]
addons/gltfLoader/PbrModelTexArray.cs [new file with mode: 0644]
addons/gltfLoader/glTFLoader.cs [new file with mode: 0644]
addons/gltfLoader/gltfLoader.csproj [new file with mode: 0644]
clean.sh [new file with mode: 0755]
netfx.props [new file with mode: 0644]
samples/Directory.Build.props [new file with mode: 0644]
samples/DistanceFieldFontTest/DistanceFieldFontTest.csproj [new file with mode: 0644]
samples/DistanceFieldFontTest/Program.cs [new file with mode: 0644]
samples/DistanceFieldFontTest/shaders/main.frag [new file with mode: 0644]
samples/DistanceFieldFontTest/shaders/main.vert [new file with mode: 0644]
samples/Model/Model.csproj [new file with mode: 0644]
samples/Model/data/DamagedHelmet.bin [new file with mode: 0644]
samples/Model/data/DamagedHelmet.gltf [new file with mode: 0644]
samples/Model/main.cs [new file with mode: 0644]
samples/Model/shaders/model.frag [new file with mode: 0644]
samples/Model/shaders/model.vert [new file with mode: 0644]
samples/Textured/Textured.csproj [new file with mode: 0644]
samples/Textured/main.cs [new file with mode: 0644]
samples/Textured/shaders/main.frag [new file with mode: 0644]
samples/Textured/shaders/main.vert [new file with mode: 0644]
samples/TexturedCube/TexturedCube.csproj [new file with mode: 0644]
samples/TexturedCube/main.cs [new file with mode: 0644]
samples/TexturedCube/shaders/FullScreenQuad.vert [new file with mode: 0644]
samples/TexturedCube/shaders/main.frag [new file with mode: 0644]
samples/TexturedCube/shaders/main.vert [new file with mode: 0644]
samples/TexturedCube/shaders/simpletexture.frag [new file with mode: 0644]
samples/TexturedCube/shaders/skybox.frag [new file with mode: 0644]
samples/TexturedCube/shaders/skybox.vert [new file with mode: 0644]
samples/Triangle/Triangle.csproj [new file with mode: 0644]
samples/Triangle/main.cs [new file with mode: 0644]
samples/Triangle/shaders/main.frag [new file with mode: 0644]
samples/Triangle/shaders/main.vert [new file with mode: 0644]
samples/common/CrowWin.cs [new file with mode: 0644]
samples/common/shaders/preamble.inc [new file with mode: 0644]
samples/compute/compute.csproj [new file with mode: 0644]
samples/compute/delaunay.cs [new file with mode: 0644]
samples/compute/main.cs [new file with mode: 0644]
samples/compute/shaders/FullScreenQuad.vert [new file with mode: 0644]
samples/compute/shaders/compute.comp [new file with mode: 0644]
samples/compute/shaders/computeTest.comp [new file with mode: 0644]
samples/compute/shaders/delaunay.comp [new file with mode: 0644]
samples/compute/shaders/init.comp [new file with mode: 0644]
samples/compute/shaders/mandelbrot.comp [new file with mode: 0644]
samples/compute/shaders/normalize.comp [new file with mode: 0644]
samples/compute/shaders/simpletexture.frag [new file with mode: 0644]
samples/compute/shaders/test.comp [new file with mode: 0644]
samples/compute/shaders/triangle2.frag [new file with mode: 0644]
samples/compute/shaders/triangle2.vert [new file with mode: 0644]
samples/compute/test.cs [new file with mode: 0644]
samples/compute/test2.cs [new file with mode: 0644]
samples/deferred/DebuDrawPipeline.cs [new file with mode: 0644]
samples/deferred/DeferredPbrRenderer.cs [new file with mode: 0644]
samples/deferred/EnvironmentPipeline.cs [new file with mode: 0644]
samples/deferred/deferred.csproj [new file with mode: 0644]
samples/deferred/main-crow.cs [new file with mode: 0644]
samples/deferred/main.cs [new file with mode: 0644]
samples/deferred/mainShadow.cs [new file with mode: 0644]
samples/deferred/mainWithDebugDrawer.cs [new file with mode: 0644]
samples/deferred/modelWithVkvgStats.cs [new file with mode: 0644]
samples/deferred/shaders/FullScreenQuad.vert [new file with mode: 0644]
samples/deferred/shaders/GBuffPbr.frag [new file with mode: 0644]
samples/deferred/shaders/GBuffPbr.vert [new file with mode: 0644]
samples/deferred/shaders/GBuffPbrTexArray.frag [new file with mode: 0644]
samples/deferred/shaders/bloom.comp [new file with mode: 0644]
samples/deferred/shaders/bloom.frag [new file with mode: 0644]
samples/deferred/shaders/compose.frag [new file with mode: 0644]
samples/deferred/shaders/compose_with_shadows.frag [new file with mode: 0644]
samples/deferred/shaders/debug.frag [new file with mode: 0644]
samples/deferred/shaders/debug.vert [new file with mode: 0644]
samples/deferred/shaders/emissive.frag [new file with mode: 0644]
samples/deferred/shaders/filtercube.vert [new file with mode: 0644]
samples/deferred/shaders/genbrdflut.frag [new file with mode: 0644]
samples/deferred/shaders/genbrdflut.vert [new file with mode: 0644]
samples/deferred/shaders/irradiancecube.frag [new file with mode: 0644]
samples/deferred/shaders/prefilterenvmap.frag [new file with mode: 0644]
samples/deferred/shaders/shadow.geom [new file with mode: 0644]
samples/deferred/shaders/shadow.vert [new file with mode: 0644]
samples/deferred/shaders/show_gbuff.frag [new file with mode: 0644]
samples/deferred/shaders/simpletexture.frag [new file with mode: 0644]
samples/deferred/shaders/skybox.frag [new file with mode: 0644]
samples/deferred/shaders/skybox.vert [new file with mode: 0644]
samples/deferred/shaders/tone_mapping.frag [new file with mode: 0644]
samples/deferred/shadowMapRenderer.cs [new file with mode: 0644]
samples/deferred/ui/debug.crow [new file with mode: 0644]
samples/deferred/ui/deferred.style [new file with mode: 0644]
samples/deferred/ui/main.crow [new file with mode: 0644]
samples/deferred/ui/materials.crow [new file with mode: 0644]
samples/deferred/ui/menu.crow [new file with mode: 0644]
samples/deferred/ui/sceneItem.crow [new file with mode: 0644]
samples/deferred/ui/scenes.crow [new file with mode: 0644]
samples/deferred/ui/testImage.crow [new file with mode: 0644]
samples/pbr/EnvironmentPipeline.cs [new file with mode: 0644]
samples/pbr/PbrModel.cs [new file with mode: 0644]
samples/pbr/PbrPipeline.cs [new file with mode: 0644]
samples/pbr/main.cs [new file with mode: 0644]
samples/pbr/pbr.csproj [new file with mode: 0644]
samples/pbr/shaders/FullScreenQuad.vert [new file with mode: 0644]
samples/pbr/shaders/filtercube.vert [new file with mode: 0644]
samples/pbr/shaders/genbrdflut.frag [new file with mode: 0644]
samples/pbr/shaders/genbrdflut.vert [new file with mode: 0644]
samples/pbr/shaders/irradiancecube.frag [new file with mode: 0644]
samples/pbr/shaders/pbr.vert [new file with mode: 0644]
samples/pbr/shaders/pbr_khr.frag [new file with mode: 0644]
samples/pbr/shaders/prefilterenvmap.frag [new file with mode: 0644]
samples/pbr/shaders/simpletexture.frag [new file with mode: 0644]
samples/pbr/shaders/skybox.frag [new file with mode: 0644]
samples/pbr/shaders/skybox.vert [new file with mode: 0644]
samples/pbr/ui/fps.crow [new file with mode: 0755]
samples/tests/Program.cs [new file with mode: 0644]
samples/tests/shaders/main.vert [new file with mode: 0644]
samples/tests/tests.csproj [new file with mode: 0644]
vke.net.sln [new file with mode: 0644]
vke/netfx.props [new symlink]
vke/shaders/FullScreenQuad.vert [new file with mode: 0644]
vke/shaders/simpletexture.frag [new file with mode: 0644]
vke/src/Camera.cs [new file with mode: 0644]
vke/src/ExtensionMethods.cs [new file with mode: 0644]
vke/src/FixedUtf8String.cs [new file with mode: 0644]
vke/src/MarshaledObject.cs [new file with mode: 0644]
vke/src/MemoryPool.cs [new file with mode: 0644]
vke/src/ResourceManager.cs [new file with mode: 0644]
vke/src/ShaderInfo.cs [new file with mode: 0644]
vke/src/SpecializationConstant.cs [new file with mode: 0644]
vke/src/StbImage.cs [new file with mode: 0644]
vke/src/Utils.cs [new file with mode: 0644]
vke/src/Version.cs [new file with mode: 0644]
vke/src/VertexAttribute.cs [new file with mode: 0644]
vke/src/VkFormatSize.cs [new file with mode: 0644]
vke/src/VkWindow.cs [new file with mode: 0644]
vke/src/base/Activable.cs [new file with mode: 0644]
vke/src/base/Buffer.cs [new file with mode: 0644]
vke/src/base/CommandBuffer.cs [new file with mode: 0644]
vke/src/base/CommandPool.cs [new file with mode: 0644]
vke/src/base/ComputePipeline.cs [new file with mode: 0644]
vke/src/base/DebuDrawPipeline.cs [new file with mode: 0644]
vke/src/base/DebugReport.cs [new file with mode: 0644]
vke/src/base/DebugUtilsMessenger.cs [new file with mode: 0644]
vke/src/base/DescriptorPool.cs [new file with mode: 0644]
vke/src/base/DescriptorSet.cs [new file with mode: 0644]
vke/src/base/DescriptorSetLayout.cs [new file with mode: 0644]
vke/src/base/DescriptorSetWrites.cs [new file with mode: 0644]
vke/src/base/Device.cs [new file with mode: 0644]
vke/src/base/FrameBuffer.cs [new file with mode: 0644]
vke/src/base/GPUBuffer.cs [new file with mode: 0644]
vke/src/base/GraphicPipeline.cs [new file with mode: 0644]
vke/src/base/GraphicPipelineConfig.cs [new file with mode: 0644]
vke/src/base/HostBuffer.cs [new file with mode: 0644]
vke/src/base/Image.cs [new file with mode: 0644]
vke/src/base/Instance.cs [new file with mode: 0644]
vke/src/base/PhysicalDevice.cs [new file with mode: 0644]
vke/src/base/Pipeline.cs [new file with mode: 0644]
vke/src/base/PipelineCache.cs [new file with mode: 0644]
vke/src/base/PipelineLayout.cs [new file with mode: 0644]
vke/src/base/QueryPool.cs [new file with mode: 0644]
vke/src/base/Queue.cs [new file with mode: 0644]
vke/src/base/RenderPass.cs [new file with mode: 0644]
vke/src/base/Resource.cs [new file with mode: 0644]
vke/src/base/SubPass.cs [new file with mode: 0644]
vke/src/base/SwapChain.cs [new file with mode: 0644]
vke/src/gl.cs [new file with mode: 0644]
vke/src/glfw/CodePoint.cs [new file with mode: 0644]
vke/src/glfw/Delegates.cs [new file with mode: 0644]
vke/src/glfw/ErrorCode.cs [new file with mode: 0644]
vke/src/glfw/Glfw3.cs [new file with mode: 0644]
vke/src/glfw/InputAction.cs [new file with mode: 0644]
vke/src/glfw/Key.cs [new file with mode: 0644]
vke/src/glfw/Modifier.cs [new file with mode: 0644]
vke/src/glfw/MonitorEvent.cs [new file with mode: 0644]
vke/src/glfw/MonitorHandle.cs [new file with mode: 0644]
vke/src/glfw/MouseButton.cs [new file with mode: 0644]
vke/src/glfw/NativeString.cs [new file with mode: 0644]
vke/src/glfw/VideoMode.cs [new file with mode: 0644]
vke/src/glfw/VideoModePointer.cs [new file with mode: 0644]
vke/src/glfw/WindowAttribute.cs [new file with mode: 0644]
vke/src/ktx.cs [new file with mode: 0644]
vke/src/model/BoundingBox.cs [new file with mode: 0644]
vke/src/model/Model.cs [new file with mode: 0644]
vke/vke.csproj [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..1aece0d
--- /dev/null
@@ -0,0 +1,7 @@
+.vs
+build/
+packages/
+bin/
+obj/
+*.user
+*.userprefs
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..610673e
--- /dev/null
@@ -0,0 +1,12 @@
+language: csharp
+dist: xenial
+dotnet: 2.1.502
+mono: latest
+
+script:
+    - nuget restore 
+    - msbuild 
+    - dotnet restore
+    - dotnet build 
+    
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644 (file)
index 0000000..449260d
--- /dev/null
@@ -0,0 +1,14 @@
+<Project>
+    <PropertyGroup>
+        <RootDirectory>$(MSBuildThisFileDirectory)</RootDirectory>
+        <Deterministic>true</Deterministic>
+        
+        <TargetFrameworks>net471;netstandard2.0</TargetFrameworks>
+        
+               <RepositoryUrl>https://github.com/jpbruyere/vke.net</RepositoryUrl>
+               <License>MIT</License>
+           <Authors>Jean-Philippe Bruyère</Authors>
+           
+           <LangVersion>7.2</LangVersion>
+    </PropertyGroup>
+</Project>
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644 (file)
index 0000000..2c2a0a1
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) [2016] [Jean-Philippe Bruyère]
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..3e59709
--- /dev/null
+++ b/README.md
@@ -0,0 +1,96 @@
+<h1 align="center">
+vke.net 
+  <br>  
+<p align="center">
+  <a href="https://www.nuget.org/packages/CVKL"><img src="https://buildstats.info/nuget/CVKL"></a>
+  <a href="https://www.paypal.me/GrandTetraSoftware">
+    <img src="https://img.shields.io/badge/Donate-PayPal-green.svg">
+  </a>
+</p>
+</h1>
+
+<p align="center">
+  <a href="https://github.com/jpbruyere/vk.net/blob/master/samples/pbr/screenshot.png">
+    <kbd><img src="https://raw.githubusercontent.com/jpbruyere/vk.net/master/samples/pbr/screenshot.png" height="300"></kbd>
+  </a>
+   <br>adaptation of the gltf PBR sample from Sacha Willems</br>
+</p>
+**Vulkan Engine for .net** is composed of high level classes encapsulating vulkan objects with `IDispose` model and **reference counting**. [GLFW](https://www.glfw.org/)  handles the windowing system.
+
+### Requirements
+- [GLFW](https://www.glfw.org/)
+- [libstb](https://github.com/nothings/stb), on debian install **libstb-dev**.
+- [Vulkan Sdk](https://www.lunarg.com/vulkan-sdk/), **glslc** has to be in the path.
+- optionaly for ui, you will need [vkvg](https://github.com/jpbruyere/vkvg).
+
+### Features
+
+- physicaly based rendering, direct and deferred
+- glTF 2.0
+- ktx image loading.
+- Memory pools
+
+### VkWindow class
+
+To create a new vulkan application, derrive your application from `VkWindow`. Validation and
+debug reports may be activated with the static Fields of the `Instance` class.
+
+```csharp
+class Program : VkWindow {
+       static void Main (string[] args) {
+               Instance.VALIDATION = true;
+               
+               using (Program vke = new Program ()) {
+                       vke.Run ();
+               }
+       }
+}
+```
+
+### Enabling features
+
+Override the `configureEnabledFeatures` method of `VkWindow` to enable features.
+```csharp
+protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) {
+       enabled_features.samplerAnisotropy = available_features.samplerAnisotropy;
+}
+```
+### Creating queues
+
+To create queues, override the `createQueues` method of `VkWindow`. This function is called before the logical device creation and will take care of physically available queues, creating duplicates if count exceed availability. The `base` method will create a default presentable queue.
+
+```csharp
+protected override void createQueues () {
+       base.createQueues ();
+       transferQ = new Queue (dev, VkQueueFlags.Transfer);
+}
+```
+### Rendering
+
+The constructor of the `VkWIndow` will finish the vulkan initialisation, so that you may create pipelines, buffers, and so on in your constructor.
+
+VkWindow will provide the default swapchain, but it's up to you to create the frame buffers. For the triangle example, create them in the `OnResize` override.
+```csharp
+Framebuffer[] frameBuffers;
+
+protected override void OnResize () {
+       if (frameBuffers != null)
+               for (int i = 0; i < swapChain.ImageCount; ++i)
+                       frameBuffers[i]?.Dispose ();
+       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+       for (int i = 0; i < swapChain.ImageCount; ++i) 
+               frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height,
+                       (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                               swapChain.images[i],
+                               null
+                       } : new Image[] {
+                               null,
+                               null,
+                               swapChain.images[i]
+                       });
+
+       buildCommandBuffers ();
+}
+```
+
diff --git a/SpirVTasks/CompileGLSLTask.cs b/SpirVTasks/CompileGLSLTask.cs
new file mode 100644 (file)
index 0000000..cac29fc
--- /dev/null
@@ -0,0 +1,203 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using Microsoft.Build.Framework;
+
+namespace SpirVTasks {
+
+       public class IncludeFileNotFound : FileNotFoundException {
+               public string SourceFile;
+               public int SourceLine;
+
+               public IncludeFileNotFound(string srcFileName, int srcLine, string includeFileName) :
+                       base ("include file not found", includeFileName) {
+
+                       SourceFile = srcFileName;
+                       SourceLine = srcLine;
+               }
+       }
+
+       public class CompileGLSLTask : Microsoft.Build.Utilities.Task {
+
+               [Required]
+               public ITaskItem SourceFile {
+                       get;
+                       set;
+               }
+               [Required]
+               public ITaskItem TempDirectory {
+                       get;
+                       set;
+               }
+               public ITaskItem AdditionalIncludeDirectories {
+                       get;
+                       set;
+               }
+               public ITaskItem SpirVCompilerPath {
+                       get;
+                       set;
+               }
+               [Required]
+               [Output]
+               public ITaskItem DestinationFile {
+                       get;
+                       set;
+               }
+
+               volatile bool success;
+
+               bool tryFindInclude (string include, out string incFile) {
+                       if (!string.IsNullOrEmpty (AdditionalIncludeDirectories?.ItemSpec)) {
+                               foreach (string incDir in AdditionalIncludeDirectories.ItemSpec.Split (';', ',', '|')) {
+                                       incFile = Path.Combine (incDir, include);
+                                       if (File.Exists (incFile))
+                                               return true;
+                               }
+                       }
+                       incFile = "";
+                       return false;
+               }
+
+               void build_source (string src, StreamWriter temp) {
+                       using (StreamReader sr = new StreamReader (File.OpenRead (src))) {
+                               int srcLine = 0;
+                               while (!sr.EndOfStream) {
+                                       string line = sr.ReadLine ();
+                                       if (line.Trim ().StartsWith ("#include", StringComparison.Ordinal)) {
+                                               string include = line.Split ('"', '<', '>')[1];
+                                               string incFile = Path.Combine (Path.GetDirectoryName (src), include);
+                                               if (!File.Exists (incFile)) {
+                                                       if (!tryFindInclude(include, out incFile))
+                                                               throw new IncludeFileNotFound (src, srcLine, include);
+                                               }
+                                               build_source (incFile, temp);
+                                       } else
+                                               temp.WriteLine (line);
+                                       srcLine++;
+                               }
+                       }
+               }
+
+               bool tryFindGlslcExecutable (out string glslcPath) {
+                       if (!string.IsNullOrEmpty (SpirVCompilerPath?.ItemSpec)) {
+                               glslcPath = SpirVCompilerPath.ItemSpec;
+                               if (!File.Exists (glslcPath))
+                                       return false;
+                       }
+
+                       string glslcExec = "glslc";
+                       if (Environment.OSVersion.Platform.ToString ().StartsWith ("Win", StringComparison.Ordinal))
+                               glslcExec = glslcExec + "exe";
+
+                       glslcPath = Path.Combine (Environment.GetEnvironmentVariable ("VULKAN_SDK"), "bin");
+                       glslcPath = Path.Combine (glslcPath, glslcExec);
+                       if (File.Exists (glslcPath))
+                               return true;
+
+                       string envStrPathes = Environment.GetEnvironmentVariable ("PATH");
+                       if (!string.IsNullOrEmpty (envStrPathes)) {
+                               foreach (string path in envStrPathes.Split (';')) {
+                                       glslcPath = Path.Combine (path, glslcExec);
+                                       if (File.Exists (glslcPath))
+                                               return true;
+                               }
+                       }
+                       return false;           
+               }
+
+
+               public override bool Execute () {
+
+                       success = true;
+
+                       if (!tryFindGlslcExecutable(out string glslcPath)) {
+                               BuildErrorEventArgs err = new BuildErrorEventArgs ("execute", "VK001", BuildEngine.ProjectFileOfTaskNode, 0, 0, 0, 0, $"glslc command not found: {glslcPath}", "Set 'VULKAN_SDK' environment variable", "SpirVTasks");
+                               BuildEngine.LogErrorEvent (err);
+                               return false;
+                       }
+
+                       string tempFile = Path.Combine (TempDirectory.ItemSpec, SourceFile.ItemSpec);
+                       if (File.Exists (tempFile))
+                               File.Delete (tempFile);
+                       try {
+                               Directory.CreateDirectory (Path.GetDirectoryName (tempFile));
+                               using (StreamWriter sw = new StreamWriter (File.OpenWrite(tempFile))) {
+                                       string src = SourceFile.ItemSpec;
+                                       build_source (SourceFile.ItemSpec, sw);
+                               }
+                       } catch (IncludeFileNotFound ex) {
+                               BuildErrorEventArgs err = new BuildErrorEventArgs ("include", "VK002", ex.SourceFile, ex.SourceLine, 0, 0, 0, $"include file not found: {ex.FileName}", "", "SpirVTasks");
+                               BuildEngine.LogErrorEvent (err);
+                               return false;
+                       }catch (Exception ex) {
+                               BuildErrorEventArgs err = new BuildErrorEventArgs ("include", "VK000", SourceFile.ItemSpec, 0, 0, 0, 0, ex.ToString(), "", "SpirVTasks");
+                               BuildEngine.LogErrorEvent (err);
+                               return false;
+                       }
+
+                       Directory.CreateDirectory (Path.GetDirectoryName (DestinationFile.ItemSpec));
+
+                       Process glslc = new Process();
+                       //glslc.StartInfo.StandardOutputEncoding = System.Text.Encoding.ASCII;
+                       //glslc.StartInfo.StandardErrorEncoding = System.Text.Encoding.ASCII;
+                       glslc.StartInfo.UseShellExecute = false;
+                       glslc.StartInfo.RedirectStandardOutput = true;
+                       glslc.StartInfo.RedirectStandardError = true;
+                       glslc.StartInfo.FileName = glslcPath;
+                       glslc.StartInfo.Arguments = $"{tempFile} -o {DestinationFile.ItemSpec}";
+                       glslc.StartInfo.CreateNoWindow = true;
+
+                       glslc.EnableRaisingEvents = true;
+                       glslc.OutputDataReceived += Glslc_OutputDataReceived;
+                       glslc.ErrorDataReceived += Glslc_ErrorDataReceived;
+
+                       glslc.Start ();
+
+                       glslc.BeginErrorReadLine ();
+                       glslc.BeginOutputReadLine ();
+
+                       DestinationFile.SetMetadata ("LogicalName", $"FromCS.{SourceFile.ItemSpec.Replace (Path.DirectorySeparatorChar, '.')}");
+
+                       glslc.WaitForExit ();
+
+                       return success;
+               }
+
+               void Glslc_ErrorDataReceived (object sender, DataReceivedEventArgs e) {
+                       if (e.Data == null)
+                               return;
+
+                       if (string.Equals (e.Data, "(0)", StringComparison.Ordinal))
+                               return;
+
+                       string[] tmp = e.Data.Split (':');
+
+                       Log.LogMessage (MessageImportance.High, $"glslc: {e.Data}");
+
+                       if (tmp.Length == 5) {
+                               string srcFile = SourceFile.ItemSpec;
+                               int line = Math.Max (0, int.Parse (tmp[1]) - 1);
+
+                               BuildErrorEventArgs err = new BuildErrorEventArgs ("compile", tmp[2], srcFile, line, 0, 0, 0, $"{tmp[3]} {tmp[4]}", "no help", "SpirVTasks");
+                               BuildEngine.LogErrorEvent (err);
+                               success = false;
+                       } else {
+                               Log.LogMessage (MessageImportance.High, $"{e.Data}");
+                       }
+               }
+
+
+               void Glslc_OutputDataReceived (object sender, DataReceivedEventArgs e) {
+                       if (e.Data == null)
+                               return;
+                       if (string.Equals (e.Data, "(0)", StringComparison.Ordinal))
+                               return;
+
+                       Log.LogMessage (MessageImportance.High, $"data:{e.Data}");
+
+                       BuildWarningEventArgs taskEvent = new BuildWarningEventArgs ("glslc", "0", BuildEngine.ProjectFileOfTaskNode, 0, 0, 0, 0, $"{e.Data}", "no help", "SpirVTasks");
+                       BuildEngine.LogWarningEvent (taskEvent);
+               }
+
+       }
+}
diff --git a/SpirVTasks/README.md b/SpirVTasks/README.md
new file mode 100644 (file)
index 0000000..226e438
--- /dev/null
@@ -0,0 +1,62 @@
+<h1 align="center">
+  SpirVTasks MSBuild add-on
+  <br>  
+<p align="center">
+  <a href="https://www.nuget.org/packages/SpirVTasks">
+    <img src="https://buildstats.info/nuget/SpirVTasks">
+  </a>
+  <a href="https://www.paypal.me/GrandTetraSoftware">
+    <img src="https://img.shields.io/badge/Donate-PayPal-green.svg">
+  </a>  
+</p>
+</h1>
+
+**SpirVTasks** package add **SpirV** compilation support to msbuild project. Error and warning
+are routed to the **IDE**.
+
+
+#### Usage
+```xml
+<ItemGroup>    
+  <GLSLShader Include="shaders\*.frag;shaders\*.vert;shaders\*.comp;shaders\*.geom" />
+</ItemGroup> 
+```
+Resulting `.spv` files are embedded with resource ID = **ProjectName.file.ext.spv**. You can override the default resource id by adding a custom LogicalName.
+```xml
+<ItemGroup>    
+  <GLSLShader Include="shaders\skybox.vert">
+         <LogicalName>NewName.vert.spv</LogicalName>
+  </GLSLShader>
+</ItemGroup> 
+```
+**VULKAN_SDK**/bin then **PATH** are searched for the **glslc** executable. You can also use **`SpirVglslcPath`** property.
+```xml
+<PropertyGroup>
+  <SpirVglslcPath>bin\glslc.exe</SpirVglslcPath>
+</PropertyGroup>
+```
+
+
+#### Include in glsl
+```glsl
+#include <preamble.inc>
+
+layout (location = 0) in vec3 inColor;
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+    outFragColor = vec4(inColor, 1.0);
+}
+```
+
+Included files are searched from the location of the current parsed file, then in the **`<SpirVAdditionalIncludeDirectories>`** directories if present.
+
+```xml
+<PropertyGroup>
+  <SpirVAdditionalIncludeDirectories>$(MSBuildThisFileDirectory)common;testdir;../anotherdir</SpirVAdditionalIncludeDirectories>
+</PropertyGroup>
+```
+
+#### todo
+- Error source file and line with included files.
diff --git a/SpirVTasks/SpirVTasks.csproj b/SpirVTasks/SpirVTasks.csproj
new file mode 100644 (file)
index 0000000..314ddc7
--- /dev/null
@@ -0,0 +1,39 @@
+<Project Sdk="Microsoft.NET.Sdk">
+    <PropertyGroup>
+        <TargetFrameworks>net471;netstandard2.0</TargetFrameworks>
+        <AssemblyVersion>0.1.9</AssemblyVersion>
+        <Description>MSBuild addon to compile and embed spirV shaders</Description>
+
+        <PackageId>SpirVTasks</PackageId>
+        <PackageTags>vulkan msbuild spirv glsl addons</PackageTags>    
+        <PackageVersion>$(AssemblyVersion)-beta</PackageVersion>
+        <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
+        <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
+        <PackageProjectUrl>https://github.com/jpbruyere/vk.net/blob/master/SpirVTasks/README.md</PackageProjectUrl>
+        <License>MIT</License>
+        <Authors>Jean-Philippe Bruyère</Authors>        
+        <OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
+        
+        <IncludeDefaultNoneItems>false</IncludeDefaultNoneItems>               
+               <EnableDefaultContentItems>false</EnableDefaultContentItems>
+               <EnableDefaultNoneItems>false</EnableDefaultNoneItems>
+               <EnableDefaultCompileItems>true</EnableDefaultCompileItems>
+        
+               <AssemblyName>SpirVTasks_$(TargetFramework)</AssemblyName>
+    </PropertyGroup>
+    <ItemGroup>        
+        <PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.0.461" />
+    </ItemGroup>
+    <ItemGroup>
+      <Content Include="SpirVTasks.targets">
+        <Pack>true</Pack>
+        <IncludeInPackage>true</IncludeInPackage>
+        <PackagePath>build\</PackagePath>
+      </Content>
+      <Content Include="spirv.xml">        
+        <Pack>true</Pack>
+        <IncludeInPackage>true</IncludeInPackage>
+        <PackagePath>build</PackagePath>
+      </Content>
+    </ItemGroup>
+</Project>
diff --git a/SpirVTasks/SpirVTasks.targets b/SpirVTasks/SpirVTasks.targets
new file mode 100644 (file)
index 0000000..6825c47
--- /dev/null
@@ -0,0 +1,30 @@
+<Project>
+  <UsingTask AssemblyFile="$(MSBuildThisFileDirectory)..\lib\$(TargetFramework)\SpirVTasks_$(TargetFramework).dll" TaskName="SpirVTasks.CompileGLSLTask" />
+  <ItemGroup>
+    <PropertyPageSchema Include="$(MSBuildThisFileDirectory)spirv.xml" />
+    <AvailableItemName Include="GLSLShader">
+      <Targets>DXC</Targets>
+    </AvailableItemName>
+  </ItemGroup>
+  <Target Name="CompileShaders" BeforeTargets="BeforeBuild" Condition="'@(GLSLShader)'!=''" Outputs="@(CompiledShaders)">                              
+       <CompileGLSLTask SourceFile="%(GLSLShader.Identity)"
+                                        AdditionalIncludeDirectories="$(SpirVAdditionalIncludeDirectories)"
+                                        TempDirectory="$(IntermediateOutputPath)"
+                                        DestinationFile="$(IntermediateOutputPath)%(RelativeDir)%(Filename)%(Extension).spv" />                
+    <CreateItem  Condition=" '%(GLSLShader.LogicalName)'!='' "
+        Include="$(IntermediateOutputPath)%(RelativeDir)%(Filename)%(Extension).spv"                   
+               AdditionalMetadata="LogicalName=%(GLSLShader.LogicalName)">
+               <Output                         
+                       TaskParameter="Include"                 
+                       ItemName="EmbeddedResource"/>       
+    </CreateItem>
+    <CreateItem Condition=" '%(GLSLShader.LogicalName)'=='' "
+        Include="$(IntermediateOutputPath)%(RelativeDir)%(Filename)%(Extension).spv"                   
+               AdditionalMetadata="LogicalName=$(AssemblyName).%(GLSLShader.Filename)%(Extension).spv">
+               <Output                         
+                       TaskParameter="Include"                 
+                       ItemName="EmbeddedResource"/>       
+    </CreateItem>              
+       <Message Importance="High" Text="%(GLSLShader.Identity) -> $(AssemblyName).%(Filename)%(Extension).spv (Embedded Resource)" />
+  </Target>
+</Project>
diff --git a/SpirVTasks/spirv.xml b/SpirVTasks/spirv.xml
new file mode 100644 (file)
index 0000000..9635ad6
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ProjectSchemaDefinitions xmlns="http://schemas.microsoft.com/build/2009/properties">
+  <ItemType Name="GLSLShader" DisplayName="GLSL Shader" />
+  <ContentType Name="GLSLShader" ItemType="GLSLShader" DisplayName="GLSL Shader" />
+  <FileExtension Name=".comp" ContentType="GLSLShader" />
+  <FileExtension Name=".geom" ContentType="GLSLShader" />
+  <FileExtension Name=".frag" ContentType="GLSLShader" />
+  <FileExtension Name=".vert" ContentType="GLSLShader" />
+</ProjectSchemaDefinitions>
diff --git a/addons/Directory.Build.props b/addons/Directory.Build.props
new file mode 100644 (file)
index 0000000..0e3fb55
--- /dev/null
@@ -0,0 +1,45 @@
+<Project>
+       <PropertyGroup>
+               <RootDirectory>$(MSBuildThisFileDirectory)../</RootDirectory>
+               <Deterministic>true</Deterministic>
+
+               <TargetFrameworks>net471;netstandard2.0</TargetFrameworks>
+
+               <RepositoryUrl>https://github.com/jpbruyere/vke.net</RepositoryUrl>
+               <License>MIT</License>
+               <Authors>Jean-Philippe Bruyère</Authors>
+
+               <OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>              
+               <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+               <IncludeDefaultNoneItems>false</IncludeDefaultNoneItems>
+               
+               <LangVersion>7.2</LangVersion>
+               
+               <SpirVAdditionalIncludeDirectories>$(MSBuildThisFileDirectory)common\shaders</SpirVAdditionalIncludeDirectories>
+
+       </PropertyGroup>
+    
+       <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">    
+               <DefineConstants>TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;_WITH_SHADOWS;WITH_VKVG</DefineConstants>    
+       </PropertyGroup>
+       <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+               <DefineConstants>NETSTANDARD;NETSTANDARD2_0;WITH_SHADOWS;_WITH_VKVG</DefineConstants>    
+       </PropertyGroup>  
+       
+       <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">              
+               <ProjectReference Include="$(RootDirectory)vke\vke.csproj" />
+       </ItemGroup>    
+       <ItemGroup >            
+               <PackageReference Include="vke" Version="0.1.7-beta" Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "/>
+       </ItemGroup>    
+       
+       <ItemGroup>
+               <PackageReference Include="SpirVTasks" Version="0.1.9-beta" />        
+               <PackageReference Include="Vulkan" Version="0.1.4" />
+       </ItemGroup>    
+
+       <ItemGroup>
+               <GLSLShader Include="shaders\**\*.frag;shaders\**\*.vert;shaders\**\*.comp;shaders\**\*.geom" />
+       </ItemGroup>
+       
+</Project>
diff --git a/addons/DistanceFieldFont/BMChar.cs b/addons/DistanceFieldFont/BMChar.cs
new file mode 100644 (file)
index 0000000..0667fc3
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+namespace CVKL.DistanceFieldFont {
+       public struct BMChar {
+               public enum Channel {
+                       Blue = 0x01,
+                       Green = 0x02,
+                       Red = 0x04,
+                       Alpha = 0x08,
+                       All = 0x0f
+               }
+               public uint id;
+               /// <summary>
+               /// The left position of the character image in the texture.
+               /// </summary>
+               public uint x;
+               public uint y;
+               /// <summary>
+               /// The width of the character image in the texture.
+               /// </summary>
+               public uint width;
+               /// <summary>
+               /// The height of the character image in the texture.
+               /// </summary>
+               public uint height;
+               /// <summary>
+               /// How much the current position should be offset when copying the image from the texture to the screen.
+               /// </summary>
+               public int xoffset;
+               /// <summary>
+               /// How much the current position should be offset when copying the image from the texture to the screen.
+               /// </summary>
+               public int yoffset;
+               /// <summary>
+               /// How much the current position should be advanced after drawing the character.
+               /// </summary>
+               public int xadvance;
+               /// <summary>
+               /// The texture page where the character image is found.
+               /// </summary>
+               public uint page;
+               /// <summary>
+               /// The texture channel where the character image is found (1 = blue, 2 = green, 4 = red, 8 = alpha, 15 = all channels).         
+               /// </summary>
+               public Channel channel;
+       }
+}
diff --git a/addons/DistanceFieldFont/BMFont.cs b/addons/DistanceFieldFont/BMFont.cs
new file mode 100644 (file)
index 0000000..e277517
--- /dev/null
@@ -0,0 +1,207 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using VK;
+
+namespace CVKL.DistanceFieldFont {
+       /// <summary>
+       /// BMF font. (http://www.angelcode.com/products/bmfont/doc/file_format.html)
+       /// </summary>
+       public class BMFont {
+               /// <summary>
+               /// distance in pixels between each line of text.
+               /// </summary>
+               public readonly ushort lineHeight;
+               /// <summary>
+               /// The number of pixels from the absolute top of the line to the base of the characters.
+               /// </summary>
+               public readonly ushort @base;
+               public readonly ushort width, height;
+
+               public readonly Dictionary<int, BMChar> CharMap = new Dictionary<int, BMChar> ();
+               public readonly Dictionary<int, string> PageImagePathes = new Dictionary<int, string> ();
+
+               readonly string fntFilePath;
+
+               public BMFont (string path) {
+                       fntFilePath = path;
+                       object thisFont = this;
+
+
+                       using (BMFontStreamReader bmf = new BMFontStreamReader (path)) {
+                               while (!bmf.EndOfStream) {
+
+                                       string w = bmf.ReadWord ();
+                                       if (string.IsNullOrEmpty (w))
+                                               continue;
+
+                                       if (string.Equals (w, "info", StringComparison.OrdinalIgnoreCase) ||
+                                               string.Equals (w, "common", StringComparison.OrdinalIgnoreCase))
+                                               bmf.ReadDatas (ref thisFont);
+                                       else if (string.Equals (w, "page", StringComparison.OrdinalIgnoreCase)) {
+                                               int p = bmf.ReadPageDatas (out string pageImg);
+                                               PageImagePathes.Add (p, pageImg);
+                                       } else if (string.Equals (w, "chars", StringComparison.OrdinalIgnoreCase))
+                                               bmf.ReadLine ();//skip char count
+                                       else if (string.Equals (w, "char", StringComparison.OrdinalIgnoreCase)) {
+                                               BMChar c = bmf.NextBMChar;
+                                               CharMap.Add ((int)c.id, c);
+                                       } else if (string.Equals (w, "kernings", StringComparison.OrdinalIgnoreCase))
+                                               bmf.ReadLine ();//skip
+                                       else if (string.Equals (w, "kerning", StringComparison.OrdinalIgnoreCase))
+                                               bmf.ReadLine ();//skip
+                               }
+                       }
+               }
+
+               public Image GetPageTexture (int page, Queue staggingQ, CommandPool cmdPool,
+                       VkMemoryPropertyFlags imgProp = VkMemoryPropertyFlags.DeviceLocal, bool genMipMaps = true, VkImageTiling tiling = VkImageTiling.Optimal) {
+
+                       string path = Path.Combine (Path.GetDirectoryName (fntFilePath), PageImagePathes[page]);
+
+                       if (path.EndsWith ("ktx", StringComparison.OrdinalIgnoreCase))
+                               return KTX.KTX.Load (staggingQ, cmdPool, path,
+                                       VkImageUsageFlags.Sampled, imgProp, genMipMaps, tiling);
+                       return Image.Load (staggingQ.Dev, staggingQ, cmdPool, path, VkFormat.R8g8b8a8Unorm, imgProp, tiling, genMipMaps);
+               }
+
+       }
+
+
+
+       internal class BMFontStreamReader : StreamReader {
+               public BMFontStreamReader (string bmPath) : base (bmPath) { }
+
+               public char PeekedChar => (char)Peek ();
+               public char ReadedChar => (char)Read ();
+               public bool EndOfLine => PeekedChar == '\n';
+
+               public string ReadWord () {
+                       string word = "";
+                       SkipWhiteSpacesAndLineBreak ();
+                       while (!EndOfStream) {
+                               if (!Char.IsLetterOrDigit (PeekedChar))
+                                       break;
+                               word += ReadedChar;
+                       }
+                       return word;
+               }
+               public void SkipWhiteSpaces () {
+                       while (!EndOfStream) {
+                               if (!char.IsWhiteSpace (PeekedChar) || PeekedChar == '\n')
+                                       break;
+                               Read ();
+                       }
+               }
+               public void SkipWhiteSpacesAndLineBreak () {
+                       while (!EndOfStream) {
+                               if (!char.IsWhiteSpace (PeekedChar))
+                                       break;
+                               Read ();
+                       }
+               }
+
+               public string NextValue {
+                       get {
+                               SkipWhiteSpaces ();
+                               if (ReadedChar != '=')
+                                       throw new Exception ("expecting '='");
+                               SkipWhiteSpaces ();
+                               if (PeekedChar == '"') {
+                                       Read ();
+                                       return ReadUntil ('"');
+                               }
+                               return ReadUntilSpace ();
+                       }
+               }
+               public string ReadUntil (char limit) {
+                       string word = "";
+                       while (!EndOfStream) {
+                               char c = ReadedChar;
+                               if (c == limit)
+                                       break;
+                               word += c;
+                       }
+                       return word;
+               }
+               public string ReadUntilSpace () {
+                       string word = "";
+                       while (!EndOfStream) {
+                               if (char.IsWhiteSpace (PeekedChar))
+                                       break;
+                               word += ReadedChar;
+                       }
+                       return word;
+               }
+
+               public void ReadField (ref object obj, string fieldName) {
+                       FieldInfo fi = obj.GetType ().GetField (fieldName);
+                       if (fi == null) {
+                               System.Diagnostics.Debug.WriteLine ($"BMFont property not handled: {fieldName}");
+                               string tmp = NextValue;
+                               return;
+                       }
+                       if (fi.FieldType == typeof (ushort)) {
+                               fi.SetValue (obj, ushort.Parse (NextValue));
+                               return;
+                       }
+                       if (fi.FieldType == typeof (short)) {
+                               fi.SetValue (obj, short.Parse (NextValue));
+                               return;
+                       }
+                       if (fi.FieldType == typeof (int)) {
+                               fi.SetValue (obj, int.Parse (NextValue));
+                               return;
+                       }
+                       if (fi.FieldType == typeof (uint)) {
+                               fi.SetValue (obj, uint.Parse (NextValue));
+                               return;
+                       }
+                       if (fi.FieldType == typeof (string)) {
+                               fi.SetValue (obj, NextValue);
+                               return;
+                       }
+               }
+               public void ReadDatas (ref object obj) {
+                       while (!EndOfLine && !EndOfStream) {
+                               string field = ReadWord ();
+                               ReadField (ref obj, field);
+                       }
+
+               }
+               public int ReadPageDatas (out string pageImgPath) {
+                       int idx = 0;
+                       pageImgPath = "";
+
+                       while (!EndOfLine && !EndOfStream) {
+                               string field = ReadWord ();
+                               if (string.Equals (field, "id", StringComparison.OrdinalIgnoreCase))
+                                       idx = int.Parse (NextValue);
+                               else if (string.Equals (field, "file", StringComparison.OrdinalIgnoreCase))
+                                       pageImgPath = NextValue;
+                               else
+                                       Console.WriteLine ($"BMFont page property not handled: {field} = {NextValue}");
+                       }
+                       return idx;
+               }
+               public BMChar NextBMChar {
+                       get {
+                               object c = default (BMChar);
+
+                               while (!EndOfLine && !EndOfStream) {
+                                       string field = ReadWord ();
+                                       ReadField (ref c, field);
+                                       SkipWhiteSpaces ();//trailing spaces
+                               }
+
+                               return (BMChar)c;
+                       }
+
+               }
+       }
+
+}
diff --git a/addons/DistanceFieldFont/DistanceFieldFont.csproj b/addons/DistanceFieldFont/DistanceFieldFont.csproj
new file mode 100644 (file)
index 0000000..a131f27
--- /dev/null
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <Import Project="$(RootDirectory)netfx.props" />
+  <PropertyGroup>
+    <AssemblyName>CVKL.DistanceFieldFont</AssemblyName>
+    <PackageId>CVKL.DistanceFieldFont</PackageId>
+    <AssemblyVersion>0.1.0</AssemblyVersion>
+    <Description>CVKL signed distance field font addons, BMFont file format</Description>
+    <PackageTags>C# vulkan CVKL gltf</PackageTags>    
+    <PackageVersion>$(AssemblyVersion)-beta</PackageVersion>
+    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
+    <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
+    <PackageProjectUrl>https://github.com/jpbruyere/vk.net/blob/master/README.md</PackageProjectUrl>
+    <License>MIT</License>
+    <PackageReleaseNotes></PackageReleaseNotes>
+    <SynchReleaseVersion>false</SynchReleaseVersion>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>    
+  </PropertyGroup>
+
+</Project>
diff --git a/addons/VkvgPipeline/VkvgPipeline.cs b/addons/VkvgPipeline/VkvgPipeline.cs
new file mode 100644 (file)
index 0000000..1d4ce02
--- /dev/null
@@ -0,0 +1,135 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Numerics;
+using CVKL;
+using VK;
+
+namespace VkvgPipeline {
+
+       public class VkvgPipeline : GraphicPipeline {
+               vkvg.Device vkvgDev;
+               vkvg.Surface vkvgSurf;
+               Image uiImage;
+
+               public vkvg.Context CreateContext () => new vkvg.Context (vkvgSurf);
+
+               public VkvgPipeline (Instance instance, Device dev, Queue queue, GraphicPipeline pipeline, PipelineCache pipelineCache = null)
+               : base (pipeline.RenderPass, pipelineCache, "vkvg pipeline") {
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, this.RenderPass.Samples, false);
+                       cfg.RenderPass = RenderPass;
+                       cfg.Layout = pipeline.Layout;
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#vke.FullScreenQuad.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#vke.simpletexture.frag.spv");
+                       cfg.multisampleState.rasterizationSamples = Samples;
+
+                       cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true);
+
+                       layout = cfg.Layout;
+
+                       init (cfg);
+
+                       vkvgDev = new vkvg.Device (instance.Handle, dev.phy.Handle, dev.VkDev.Handle, queue.qFamIndex,
+                               vkvg.SampleCount.Sample_4, queue.index);                                
+               }
+
+               void initUISurface (int width, int height) {
+                       uiImage?.Dispose ();
+                       vkvgSurf?.Dispose ();
+                       vkvgSurf = new vkvg.Surface (vkvgDev, width, height);
+                       uiImage = new Image (Dev, new VkImage ((ulong)vkvgSurf.VkImage.ToInt64 ()), VkFormat.B8g8r8a8Unorm,
+                               VkImageUsageFlags.ColorAttachment, (uint)vkvgSurf.Width, (uint)vkvgSurf.Height);
+                       uiImage.CreateView (VkImageViewType.ImageView2D, VkImageAspectFlags.Color);
+                       uiImage.CreateSampler (VkFilter.Nearest, VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder);
+                       uiImage.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+               }
+
+
+               public void Resize (int width, int height, DescriptorSetWrites dsUpdate) {
+                       initUISurface (width, height);
+                       dsUpdate.Write (Dev, uiImage.Descriptor);
+               }
+               public void RecordDraw (CommandBuffer cmd) {
+                       Bind (cmd);
+
+                       uiImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ColorAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal,
+                               VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader);
+
+                       cmd.Draw (3, 1, 0, 0);
+
+                       uiImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.ColorAttachmentOptimal,
+                               VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.BottomOfPipe);
+               }
+               public void DrawResources (vkvg.Context ctx, int width, int height) {
+                       ResourceManager rm = Dev.resourceManager;
+
+                       int margin = 5, memPoolHeight = 15;
+                       int drawingWidth = width - 4 * margin;
+                       int drawingHeight = (memPoolHeight + margin) * (rm.memoryPools.Length) + margin;
+                       int y = height - drawingHeight - margin;
+                       int x = 2 * margin;
+                       ctx.LineWidth = 1;
+                       ctx.SetSource (0.1, 0.1, 0.1, 0.8);
+                       ctx.Rectangle (0.5 + margin, 0.5 + y, width - 2 * margin, drawingHeight);
+                       ctx.FillPreserve ();
+                       ctx.Flush ();
+                       ctx.SetSource (0.8, 0.8, 0.8);
+                       ctx.Stroke ();
+
+                       foreach (MemoryPool mp in rm.memoryPools) {
+                               float byteWidth = (float)drawingWidth / mp.Size;
+
+                               y += margin;
+                               ctx.Rectangle (x, y, drawingWidth, memPoolHeight);
+                               ctx.SetSource (0.3, 0.3, 0.3, 0.4);
+                               ctx.Fill ();
+
+                               if (mp.Last == null)
+                                       return;
+
+                               Resource r = mp.Last;
+                               do {
+                                       float w = Math.Max (1f, byteWidth * r.AllocatedDeviceMemorySize);
+
+                                       Vector3 c = new Vector3 (0);
+                                       Image img = r as Image;
+                                       if (img != null) {
+                                               c.Z = 1f;
+                                               if (img.CreateInfo.usage.HasFlag (VkImageUsageFlags.InputAttachment))
+                                                       c.Y += 0.3f;
+                                               if (img.CreateInfo.usage.HasFlag (VkImageUsageFlags.ColorAttachment))
+                                                       c.Y += 0.1f;
+                                               if (img.CreateInfo.usage.HasFlag (VkImageUsageFlags.Sampled))
+                                                       c.X += 0.3f;
+                                       } else {
+                                               CVKL.Buffer buff = r as CVKL.Buffer;
+                                               c.X = 1f;
+                                               if (buff.Infos.usage.HasFlag (VkBufferUsageFlags.IndexBuffer))
+                                                       c.Y += 0.2f;
+                                               if (buff.Infos.usage.HasFlag (VkBufferUsageFlags.VertexBuffer))
+                                                       c.Y += 0.4f;
+                                               if (buff.Infos.usage.HasFlag (VkBufferUsageFlags.UniformBuffer))
+                                                       c.Z += 0.3f;
+                                       }
+                                       ctx.SetSource (c.X, c.Y, c.Z, 0.5);
+                                       ctx.Rectangle (0.5f + x + byteWidth * r.poolOffset, 0.5f + y, w, memPoolHeight);
+                                       ctx.FillPreserve ();
+                                       ctx.SetSource (0.01f, 0.01f, 0.01f);
+                                       ctx.Stroke ();
+                                       r = r.next;
+                               } while (r != mp.Last && r != null);
+                               y += memPoolHeight;
+                       }
+               }
+               protected override void Dispose (bool disposing) {
+                       uiImage?.Dispose ();
+                       vkvgSurf?.Dispose ();
+                       vkvgDev.Dispose ();
+
+                       base.Dispose (disposing);
+               }
+       }
+
+}
diff --git a/addons/VkvgPipeline/VkvgPipeline.csproj b/addons/VkvgPipeline/VkvgPipeline.csproj
new file mode 100644 (file)
index 0000000..3d07598
--- /dev/null
@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <Import Project="$(RootDirectory)netfx.props" />
+  <PropertyGroup>
+    <AssemblyName>CVKL.vkvgPipeline</AssemblyName>
+    <PackageId>CVKL.vkvgPipeline</PackageId>
+    <AssemblyVersion>0.1.0</AssemblyVersion>
+    <Description>CVKL vkvg addons, vectorial drawing with cairo like api.</Description>
+    <PackageTags>C# vulkan CVKL gltf</PackageTags>    
+    <PackageVersion>$(AssemblyVersion)-beta</PackageVersion>
+    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
+    <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
+    <PackageProjectUrl>https://github.com/jpbruyere/vk.net/blob/master/README.md</PackageProjectUrl>
+    <License>MIT</License>
+    <PackageReleaseNotes></PackageReleaseNotes>
+    <SynchReleaseVersion>false</SynchReleaseVersion>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>    
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="vkvg.net" Version="0.2.2-beta" />
+  </ItemGroup>
+</Project>
diff --git a/addons/gltfLoader/PbrModel.cs b/addons/gltfLoader/PbrModel.cs
new file mode 100644 (file)
index 0000000..396c42f
--- /dev/null
@@ -0,0 +1,124 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Diagnostics;
+using System.Numerics;
+using System.Runtime.InteropServices;
+
+using VK;
+using System.Collections.Generic;
+
+namespace CVKL.glTF {
+       //TODO:stride in buffer views?
+       public abstract class PbrModel : Model {        
+               //public new struct Vertex {
+               //      [VertexAttribute (VertexAttributeType.Position, VkFormat.R32g32b32Sfloat)]
+               //      public Vector3 pos;
+               //      [VertexAttribute (VertexAttributeType.Normal, VkFormat.R32g32b32Sfloat)]
+               //      public Vector3 normal;
+               //      [VertexAttribute (VertexAttributeType.UVs, VkFormat.R32g32Sfloat)]
+               //      public Vector2 uv0;
+               //      [VertexAttribute (VertexAttributeType.UVs, VkFormat.R32g32Sfloat)]
+               //      public Vector2 uv1;
+               //      public override string ToString () {
+               //              return pos.ToString () + ";" + normal.ToString () + ";" + uv0.ToString () + ";" + uv1.ToString ();
+               //      }
+               //};
+
+               protected DescriptorPool descriptorPool;
+               public GPUBuffer vbo;
+               public GPUBuffer ibo;
+               public Buffer materialUBO;
+
+               protected PbrModel () { }
+               public PbrModel (Queue transferQ, string path) {
+                       dev = transferQ.Dev;
+                       using (CommandPool cmdPool = new CommandPool (dev, transferQ.index)) {
+                               using (glTFLoader ctx = new glTFLoader (path, transferQ, cmdPool)) {
+                                       loadSolids (ctx);
+                               }
+                       }
+               }
+
+               protected void loadSolids (glTFLoader ctx) {
+                       loadSolids<Vertex> (ctx);
+               }
+
+               protected void loadSolids<VX> (glTFLoader ctx) {
+                       ulong vertexCount, indexCount;
+
+                       ctx.GetVertexCount (out vertexCount, out indexCount, out IndexBufferType);
+
+                       ulong vertSize = vertexCount * (ulong)Marshal.SizeOf<VX> ();
+                       ulong idxSize = indexCount * (IndexBufferType == VkIndexType.Uint16 ? 2ul : 4ul);
+                       ulong size = vertSize + idxSize;
+
+                       vbo = new GPUBuffer (dev, VkBufferUsageFlags.VertexBuffer | VkBufferUsageFlags.TransferDst, vertSize);
+                       ibo = new GPUBuffer (dev, VkBufferUsageFlags.IndexBuffer | VkBufferUsageFlags.TransferDst, idxSize);
+
+                       vbo.SetName ("vbo gltf");
+                       ibo.SetName ("ibo gltf");
+
+                       Meshes = new List<Mesh> (ctx.LoadMeshes<VX> (IndexBufferType, vbo, 0, ibo, 0));
+                       Scenes = new List<Scene> (ctx.LoadScenes (out defaultSceneIndex));
+               }
+
+               /// <summary> bind vertex and index buffers </summary>
+               public virtual void Bind (CommandBuffer cmd) {
+                       cmd.BindVertexBuffer (vbo);
+                       cmd.BindIndexBuffer (ibo, IndexBufferType);
+               }
+
+               //TODO:destset for binding must be variable
+               //TODO: ADD REFAULT MAT IF NO MAT DEFINED
+               public abstract void RenderNode (CommandBuffer cmd, PipelineLayout pipelineLayout, Node node, Matrix4x4 currentTransform, bool shadowPass = false);
+
+               public void DrawAll (CommandBuffer cmd, PipelineLayout pipelineLayout, bool shadowPass = false) {
+                       foreach (Scene sc in Scenes) {
+                               foreach (Node node in sc.Root.Children)
+                                       RenderNode (cmd, pipelineLayout, node, sc.Root.localMatrix, shadowPass);                                
+                       }
+               }
+        public void Draw(CommandBuffer cmd, PipelineLayout pipelineLayout, Scene scene, bool shadowPass = false)
+        {
+            if (scene.Root == null)
+                return;
+            RenderNode(cmd, pipelineLayout, scene.Root, Matrix4x4.Identity, shadowPass);
+        }
+
+        public void Draw (CommandBuffer cmd, PipelineLayout pipelineLayout, Buffer instanceBuf, bool shadowPass = false, params InstancedCmd[] instances) {
+            cmd.BindVertexBuffer(instanceBuf, 1);
+            uint firstInstance = 0;
+            for (int i = 0; i < instances.Length; i++)
+            {
+                foreach (Primitive p in Meshes[instances[i].meshIdx].Primitives)
+                {
+                    if (!shadowPass)
+                        cmd.PushConstant(pipelineLayout, VkShaderStageFlags.Fragment, (int)p.material, (uint)Marshal.SizeOf<Matrix4x4>());
+                    cmd.DrawIndexed(p.indexCount, instances[i].count, p.indexBase, p.vertexBase, firstInstance);
+                }
+                firstInstance += instances[i].count;
+            }
+        }
+
+        #region IDisposable Support
+        protected bool isDisposed;
+
+               protected virtual void Dispose (bool disposing) {
+                       if (!isDisposed) {
+                               if (disposing) {
+                                       ibo?.Dispose ();
+                                       vbo?.Dispose ();
+                                       materialUBO?.Dispose ();
+                                       descriptorPool?.Dispose ();
+                               } else
+                                       Debug.WriteLine ("model was not disposed");
+                               isDisposed = true;
+                       }
+               }
+               public void Dispose () {
+                       Dispose (true);
+               }
+               #endregion
+       }
+}
diff --git a/addons/gltfLoader/PbrModelSeparateTextures.cs b/addons/gltfLoader/PbrModelSeparateTextures.cs
new file mode 100644 (file)
index 0000000..c1a6ee9
--- /dev/null
@@ -0,0 +1,162 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Diagnostics;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL.glTF {
+       /// <summary>
+       /// Indexed pbr model whith one descriptorSet per material with separate textures attachments
+       /// </summary>
+       public class PbrModelSeparatedTextures : PbrModel {
+               /// <summary>
+               /// Pbr data structure suitable for push constant or ubo, containing
+               /// availablility of attached textures and the coef of pbr inputs
+               /// </summary>
+               public struct Material {
+                       public Vector4 baseColorFactor;
+                       public Vector4 emissiveFactor;
+                       public Vector4 diffuseFactor;
+                       public Vector4 specularFactor;
+                       public float workflow;
+                       public AttachmentType TexCoord0;
+                       public AttachmentType TexCoord1;
+                       public float metallicFactor;
+                       public float roughnessFactor;
+                       public float alphaMask;
+                       public float alphaMaskCutoff;
+#pragma warning disable 169
+                       readonly int pad0;//see std420
+#pragma warning restore 169
+               }
+
+               protected Image[] textures;
+               public Material[] materials;
+               /// <summary>
+               /// one descriptor per material containing textures
+               /// </summary>
+               protected DescriptorSet[] descriptorSets;
+
+               protected PbrModelSeparatedTextures () { }
+               public PbrModelSeparatedTextures (Queue transferQ, string path, DescriptorSetLayout layout, params AttachmentType[] attachments) {
+                       dev = transferQ.Dev;
+                       using (CommandPool cmdPool = new CommandPool (dev, transferQ.index)) {
+                               using (glTFLoader ctx = new glTFLoader (path, transferQ, cmdPool)) {
+                                       loadSolids (ctx);
+
+                                       textures = ctx.LoadImages ();
+                                       loadMaterials (ctx, layout, attachments);
+
+                                       materialUBO = new HostBuffer<Material> (dev, VkBufferUsageFlags.UniformBuffer, materials);
+
+                               }
+                       }
+               }
+
+               void loadMaterials (glTFLoader ctx, DescriptorSetLayout layout, params AttachmentType[] attachments) {
+                       glTFLoader.Material[] mats = ctx.LoadMaterial ();
+                       materials = new Material[mats.Length];
+                       descriptorSets = new DescriptorSet[mats.Length];
+
+                       if (attachments.Length == 0)
+                               throw new InvalidOperationException ("At least one attachment is required for Model.WriteMaterialDescriptor");
+
+                       descriptorPool = new DescriptorPool (dev, (uint)materials.Length,
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, (uint)(attachments.Length * materials.Length))
+                       );
+                       descriptorPool.SetName ("descPool gltfTextures");
+
+                       for (int i = 0; i < mats.Length; i++) {
+                               materials[i] = new Material {
+                                       workflow = (float)mats[i].workflow,
+                                       baseColorFactor = mats[i].baseColorFactor,
+                                       emissiveFactor = mats[i].emissiveFactor,
+                                       metallicFactor = mats[i].metallicFactor,
+                                       roughnessFactor = mats[i].roughnessFactor,
+                                       TexCoord0 = mats[i].availableAttachments,
+                                       TexCoord1 = mats[i].availableAttachments1,
+                                       alphaMask = 0f,
+                                       alphaMaskCutoff = 0.0f,
+                                       diffuseFactor = new Vector4 (0),
+                                       specularFactor = new Vector4 (0)
+                               };
+
+                               descriptorSets[i] = descriptorPool.Allocate (layout);
+                               descriptorSets[i].Handle.SetDebugMarkerName (dev, "descSet " + mats[i].Name);
+
+                               VkDescriptorSetLayoutBinding dslb =
+                                       new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler);
+
+                               using (DescriptorSetWrites2 uboUpdate = new DescriptorSetWrites2 (dev)) {
+                                       for (uint a = 0; a < attachments.Length; a++) {
+                                               dslb.binding = a;
+                                               switch (attachments[a]) {
+                                                       case AttachmentType.None:
+                                                               break;
+                                                       case AttachmentType.Color:
+                                                               if (mats[i].availableAttachments.HasFlag (AttachmentType.Color))
+                                                                       uboUpdate.AddWriteInfo (descriptorSets[i], dslb, textures[(int)mats[i].baseColorTexture].Descriptor);
+                                                               break;
+                                                       case AttachmentType.Normal:
+                                                               if (mats[i].availableAttachments.HasFlag (AttachmentType.Normal))
+                                                                       uboUpdate.AddWriteInfo (descriptorSets[i], dslb, textures[(int)mats[i].normalTexture].Descriptor);
+                                                               break;
+                                                       case AttachmentType.AmbientOcclusion:
+                                                               if (mats[i].availableAttachments.HasFlag (AttachmentType.AmbientOcclusion))
+                                                                       uboUpdate.AddWriteInfo (descriptorSets[i], dslb, textures[(int)mats[i].occlusionTexture].Descriptor);
+                                                               break;
+                                                       case AttachmentType.PhysicalProps:
+                                                               if (mats[i].availableAttachments.HasFlag (AttachmentType.PhysicalProps))
+                                                                       uboUpdate.AddWriteInfo (descriptorSets[i], dslb, textures[(int)mats[i].metallicRoughnessTexture].Descriptor);
+                                                               break;
+                                                       case AttachmentType.Metal:
+                                                               break;
+                                                       case AttachmentType.Roughness:
+                                                               break;
+                                                       case AttachmentType.Emissive:
+                                                               if (mats[i].availableAttachments.HasFlag (AttachmentType.Emissive))
+                                                                       uboUpdate.AddWriteInfo (descriptorSets[i], dslb, textures[(int)mats[i].emissiveTexture].Descriptor);
+                                                               break;
+                                               }
+                                       }
+                                       uboUpdate.Update ();
+                               }
+                       }
+               }
+
+               public override void RenderNode (CommandBuffer cmd, PipelineLayout pipelineLayout, Node node, Matrix4x4 currentTransform, bool shadowPass = false) {
+                       Matrix4x4 localMat = node.localMatrix * currentTransform;
+                       VkShaderStageFlags matStage = shadowPass ? VkShaderStageFlags.Geometry : VkShaderStageFlags.Vertex;
+                       cmd.PushConstant (pipelineLayout, matStage, localMat);
+
+                       if (node.Mesh != null) {
+                               foreach (Primitive p in node.Mesh.Primitives) {
+                                       if (!shadowPass) {
+                                               cmd.PushConstant (pipelineLayout, VkShaderStageFlags.Fragment, (int)p.material, (uint)Marshal.SizeOf<Matrix4x4> ());
+                                               if (descriptorSets[p.material] != null)
+                                                       cmd.BindDescriptorSet (pipelineLayout, descriptorSets[p.material], 2);
+                                       }
+                                       cmd.DrawIndexed (p.indexCount, 1, p.indexBase, p.vertexBase, 0);
+                               }
+                       }
+                       if (node.Children == null)
+                               return;
+                       foreach (Node child in node.Children)
+                               RenderNode (cmd, pipelineLayout, child, localMat, shadowPass);
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (!isDisposed) {
+                               if (disposing) {
+                                       foreach (Image txt in textures) 
+                                               txt.Dispose ();
+                               } else
+                                       Debug.WriteLine ("model was not disposed");
+                       }
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/addons/gltfLoader/PbrModelTexArray.cs b/addons/gltfLoader/PbrModelTexArray.cs
new file mode 100644 (file)
index 0000000..5943774
--- /dev/null
@@ -0,0 +1,139 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Diagnostics;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL.glTF {
+       /// <summary>
+       /// Indexed pbr model whith one descriptorSet per material with separate textures attachments
+       /// </summary>
+       public class PbrModelTexArray : PbrModel {
+               public static uint TEXTURE_DIM = 512;
+               public new struct Vertex {
+                       [VertexAttribute (VertexAttributeType.Position, VkFormat.R32g32b32Sfloat)]
+                       public Vector3 pos;
+                       [VertexAttribute (VertexAttributeType.Normal, VkFormat.R32g32b32Sfloat)]
+                       public Vector3 normal;
+                       [VertexAttribute (VertexAttributeType.UVs, VkFormat.R32g32Sfloat)]
+                       public Vector2 uv0;
+                       [VertexAttribute (VertexAttributeType.UVs, VkFormat.R32g32Sfloat)]
+                       public Vector2 uv1;
+                       public override string ToString () {
+                               return pos.ToString () + ";" + normal.ToString () + ";" + uv0.ToString () + ";" + uv1.ToString ();
+                       }
+               };
+
+               /// <summary>
+               /// Material structure for ubo containing texture indices in tex array
+               /// </summary>
+               public struct Material {
+                       public Vector4 baseColorFactor;
+                       public Vector4 emissiveFactor;
+                       public Vector4 diffuseFactor;
+                       public Vector4 specularFactor;
+
+                       public float workflow;
+                       public AttachmentType TexCoord0;
+                       public AttachmentType TexCoord1;
+                       public int baseColorTextureSet;
+
+                       public int phyDescTex;
+                       public int normalTex;
+                       public int aoTex;
+                       public int emissiveTex;
+
+                       public float metallicFactor;
+                       public float roughnessFactor;
+                       public float alphaMask;
+                       public float alphaMaskCutoff;
+               }
+
+               public Image texArray;
+               public Material[] materials;
+
+               public PbrModelTexArray (Queue transferQ, string path) {
+                       dev = transferQ.Dev;
+                       using (CommandPool cmdPool = new CommandPool (dev, transferQ.index)) {
+                               using (glTFLoader ctx = new glTFLoader (path, transferQ, cmdPool)) {
+                                       loadSolids<Vertex> (ctx);
+
+                                       if (ctx.ImageCount > 0) {
+                                               texArray = new Image (dev, Image.DefaultTextureFormat, VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferDst | VkImageUsageFlags.TransferSrc,
+                                                       VkMemoryPropertyFlags.DeviceLocal, TEXTURE_DIM, TEXTURE_DIM, VkImageType.Image2D,
+                                                       VkSampleCountFlags.SampleCount1, VkImageTiling.Optimal, Image.ComputeMipLevels (TEXTURE_DIM), ctx.ImageCount);
+
+                                               ctx.BuildTexArray (ref texArray, 0);
+
+                                               texArray.CreateView (VkImageViewType.ImageView2DArray, VkImageAspectFlags.Color, texArray.CreateInfo.arrayLayers);
+                                               texArray.CreateSampler ();
+                                               texArray.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                                               texArray.SetName ("model texArray");
+                                       }
+
+                                       loadMaterials (ctx);
+                                       materialUBO = new HostBuffer<Material> (dev, VkBufferUsageFlags.UniformBuffer, materials);
+                               }
+                       }
+               }
+
+               void loadMaterials (glTFLoader ctx) {
+                       glTFLoader.Material[] mats = ctx.LoadMaterial ();
+                       materials = new Material[mats.Length];
+
+                       for (int i = 0; i < mats.Length; i++) {
+                               materials[i] = new Material {
+                                       workflow = (float)mats[i].workflow,
+                                       baseColorFactor = mats[i].baseColorFactor,
+                                       emissiveFactor = mats[i].emissiveFactor,
+                                       metallicFactor = mats[i].metallicFactor,
+                                       roughnessFactor = mats[i].roughnessFactor,
+
+                                       baseColorTextureSet = mats[i].baseColorTexture,
+                                       phyDescTex = mats[i].metallicRoughnessTexture,
+                                       normalTex = mats[i].normalTexture,
+                                       aoTex = mats[i].occlusionTexture,
+                                       emissiveTex = mats[i].emissiveTexture,
+
+                                       TexCoord0 = mats[i].availableAttachments,
+                                       TexCoord1 = mats[i].availableAttachments1,
+
+                                       alphaMask = 0f,
+                                       alphaMaskCutoff = 0.0f,
+                                       diffuseFactor = new Vector4 (0),
+                                       specularFactor = new Vector4 (0)
+                               };
+                       }
+               }
+
+               public override void RenderNode (CommandBuffer cmd, PipelineLayout pipelineLayout, Node node, Matrix4x4 currentTransform, bool shadowPass = false) {
+                       Matrix4x4 localMat = node.localMatrix * currentTransform;
+
+                       cmd.PushConstant (pipelineLayout, VkShaderStageFlags.Vertex, localMat);
+
+                       if (node.Mesh != null) {
+                               foreach (Primitive p in node.Mesh.Primitives) {
+                                       if (!shadowPass) 
+                                               cmd.PushConstant (pipelineLayout, VkShaderStageFlags.Fragment, (int)p.material, (uint)Marshal.SizeOf<Matrix4x4> ());
+                                       cmd.DrawIndexed (p.indexCount, 1, p.indexBase, p.vertexBase, 0);
+                               }
+                       }
+                       if (node.Children == null)
+                               return;
+                       foreach (Node child in node.Children)
+                               RenderNode (cmd, pipelineLayout, child, localMat, shadowPass);
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (!isDisposed) {
+                               if (disposing) {
+                                       texArray?.Dispose ();                                           
+                               } else
+                                       Debug.WriteLine ("model was not disposed");
+                       }
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/addons/gltfLoader/glTFLoader.cs b/addons/gltfLoader/glTFLoader.cs
new file mode 100644 (file)
index 0000000..06e8ad6
--- /dev/null
@@ -0,0 +1,635 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Diagnostics;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using glTFLoader;
+using GL = glTFLoader.Schema;
+
+using VK;
+using System.Collections.Generic;
+using System.IO;
+
+
+
+namespace CVKL.glTF {
+       using static VK.Utils;
+       using static CVKL.Model;
+
+       /// <summary>
+       /// Loading context with I as the vertex index type (uint16,uint32)
+       /// </summary>
+       public class glTFLoader : IDisposable {
+               /// <summary>
+               /// Material class used by the gltfLoader to fetch values.
+               /// </summary>
+               public class Material {
+                       public enum Workflow { PhysicalyBaseRendering = 1, SpecularGlossinnes };
+                       public string Name;
+                       public Workflow workflow;
+                       public Int32 baseColorTexture;
+                       public Int32 metallicRoughnessTexture;
+                       public Int32 normalTexture;
+                       public Int32 occlusionTexture;
+                       public Int32 emissiveTexture;
+
+                       public Vector4 baseColorFactor;
+                       public Vector4 emissiveFactor;
+                       public AttachmentType availableAttachments;
+                       public AttachmentType availableAttachments1;
+
+                       public AlphaMode alphaMode;
+                       public float alphaCutoff;
+                       public float metallicFactor;
+                       public float roughnessFactor;
+
+                       public bool metallicRoughness = true;
+                       public bool specularGlossiness = false;
+
+                       public Material (Int32 _baseColorTexture = -1, Int32 _metallicRoughnessTexture = -1,
+                               Int32 _normalTexture = -1, Int32 _occlusionTexture = -1) {
+                               workflow = Workflow.PhysicalyBaseRendering;
+                               baseColorTexture = _baseColorTexture;
+                               metallicRoughnessTexture = _metallicRoughnessTexture;
+                               normalTexture = _normalTexture;
+                               occlusionTexture = _occlusionTexture;
+                               emissiveTexture = -1;
+
+                               alphaMode = AlphaMode.Opaque;
+                               alphaCutoff = 1f;
+                               metallicFactor = 1f;
+                               roughnessFactor = 1;
+                               baseColorFactor = new Vector4 (1);
+                               emissiveFactor = new Vector4 (1);
+
+                               metallicRoughness = true;
+                               specularGlossiness = false;
+
+                       }
+               }
+
+               public Queue transferQ;
+               public CommandPool cmdPool;
+               Device dev => transferQ.Dev;
+
+               public GL.Gltf gltf;
+               public string baseDirectory;
+
+               public byte[][] loadedBuffers;
+               public GCHandle[] bufferHandles;
+
+               List<Mesh> meshes;
+               string path;
+
+               public glTFLoader (string path, Queue _transferQ, CommandPool _cmdPool) {
+                       this.path = path;
+                       transferQ = _transferQ;
+                       cmdPool = _cmdPool;
+                       baseDirectory = System.IO.Path.GetDirectoryName (path);
+                       gltf = Interface.LoadModel (path); ;
+                       loadedBuffers = new byte[gltf.Buffers.Length][];
+                       bufferHandles = new GCHandle[gltf.Buffers.Length];
+               }
+
+               static byte[] loadDataUri (GL.Image img) {
+                       int idxComa = img.Uri.IndexOf (",", 5, StringComparison.Ordinal);
+                       return Convert.FromBase64String (img.Uri.Substring (idxComa + 1));
+               }
+               static byte[] loadDataUri (GL.Buffer buff) {
+                       int idxComa = buff.Uri.IndexOf (",", 5, StringComparison.Ordinal);
+                       return Convert.FromBase64String (buff.Uri.Substring (idxComa + 1));
+               }
+
+               void EnsureBufferIsLoaded (int bufferIdx) {
+                       if (loadedBuffers[bufferIdx] == null) {
+                               //load full buffer
+                               string uri = gltf.Buffers[bufferIdx].Uri;
+                               if (string.IsNullOrEmpty(uri))//glb
+                                       loadedBuffers[bufferIdx] = gltf.LoadBinaryBuffer (bufferIdx, path);
+                               else if (uri.StartsWith ("data", StringComparison.Ordinal))
+                                       loadedBuffers[bufferIdx] = loadDataUri (gltf.Buffers[bufferIdx]);//TODO:check this func=>System.Buffers.Text.Base64.EncodeToUtf8InPlace
+                               else
+                                       loadedBuffers[bufferIdx] = File.ReadAllBytes (Path.Combine (baseDirectory, gltf.Buffers[bufferIdx].Uri));
+                               bufferHandles[bufferIdx] = GCHandle.Alloc (loadedBuffers[bufferIdx], GCHandleType.Pinned);
+                       }
+               }
+
+               public void GetVertexCount (out ulong vertexCount, out ulong indexCount, out VkIndexType largestIndexType) {
+                       vertexCount = 0;
+                       indexCount = 0;
+                       largestIndexType = VkIndexType.Uint16;
+                       //compute size of stagging buf
+                       foreach (GL.Mesh mesh in gltf.Meshes) {
+                               foreach (GL.MeshPrimitive p in mesh.Primitives) {
+                                       int accessorIdx;
+                                       if (p.Attributes.TryGetValue ("POSITION", out accessorIdx))
+                                               vertexCount += (ulong)gltf.Accessors[accessorIdx].Count;
+                                       if (p.Indices != null) {
+                                               indexCount += (ulong)gltf.Accessors[(int)p.Indices].Count;
+                                               if (gltf.Accessors[(int)p.Indices].ComponentType == GL.Accessor.ComponentTypeEnum.UNSIGNED_INT)
+                                                       largestIndexType = VkIndexType.Uint32;
+                                       }
+                               }
+                       }
+               }
+
+               public uint ImageCount => gltf.Images == null ? 0 : (uint)gltf.Images.Length;
+               
+
+               //TODO: some buffer data are reused between primitives, and I duplicate the datas
+               //buffers must be constructed without duplications
+               public Mesh[] LoadMeshes<TVertex> (VkIndexType indexType, Buffer vbo, ulong vboOffset, Buffer ibo, ulong iboOffset) {
+                       ulong vCount, iCount;
+                       VkIndexType idxType;
+                       GetVertexCount (out vCount, out iCount, out idxType);
+
+                       int vertexByteSize = Marshal.SizeOf<TVertex> ();
+                       ulong vertSize = vCount * (ulong)vertexByteSize;
+                       ulong idxSize = iCount * (indexType == VkIndexType.Uint16 ? 2ul : 4ul);
+                       ulong size = vertSize + idxSize;
+
+                       int vertexCount = 0, indexCount = 0;
+                       int autoNamedMesh = 1;
+
+                       meshes = new List<Mesh> ();
+
+                       using (HostBuffer stagging = new HostBuffer (dev, VkBufferUsageFlags.TransferSrc, size)) {
+                               stagging.Map ();
+
+                               unsafe {
+                                       byte* stagVertPtrInit = (byte*)stagging.MappedData.ToPointer ();
+                                       byte* stagIdxPtrInit = (byte*)(stagging.MappedData.ToPointer ()) + vertSize;
+                                       byte* stagVertPtr = stagVertPtrInit;
+                                       byte* stagIdxPtr = stagIdxPtrInit;
+
+                                       foreach (GL.Mesh mesh in gltf.Meshes) {
+
+                                               string meshName = mesh.Name;
+                                               if (string.IsNullOrEmpty (meshName)) {
+                                                       meshName = "mesh_" + autoNamedMesh.ToString ();
+                                                       autoNamedMesh++;
+                                               }
+                                               Mesh m = new Mesh { Name = meshName };
+
+                                               foreach (GL.MeshPrimitive p in mesh.Primitives) {
+                                                       GL.Accessor AccPos = null, AccNorm = null, AccUv = null, AccUv1 = null;
+
+                                                       int accessorIdx;
+                                                       if (p.Attributes.TryGetValue ("POSITION", out accessorIdx)) {
+                                                               AccPos = gltf.Accessors[accessorIdx];
+                                                               EnsureBufferIsLoaded (gltf.BufferViews[(int)AccPos.BufferView].Buffer);
+                                                       }
+                                                       if (p.Attributes.TryGetValue ("NORMAL", out accessorIdx)) {
+                                                               AccNorm = gltf.Accessors[accessorIdx];
+                                                               EnsureBufferIsLoaded (gltf.BufferViews[(int)AccNorm.BufferView].Buffer);
+                                                       }
+                                                       if (p.Attributes.TryGetValue ("TEXCOORD_0", out accessorIdx)) {
+                                                               AccUv = gltf.Accessors[accessorIdx];
+                                                               EnsureBufferIsLoaded (gltf.BufferViews[(int)AccUv.BufferView].Buffer);
+                                                       }
+                                                       if (p.Attributes.TryGetValue ("TEXCOORD_1", out accessorIdx)) {
+                                                               AccUv1 = gltf.Accessors[accessorIdx];
+                                                               EnsureBufferIsLoaded (gltf.BufferViews[(int)AccUv1.BufferView].Buffer);
+                                                       }
+
+                                                       Primitive prim = new Primitive {
+                                                               indexBase = (uint)indexCount,
+                                                               vertexBase = vertexCount,
+                                                               vertexCount = (uint)AccPos.Count,
+                                                               material = (uint)(p.Material ?? 0)
+                                                       };
+
+                                                       prim.bb.min.ImportFloatArray (AccPos.Min);
+                                                       prim.bb.max.ImportFloatArray (AccPos.Max);
+                                                       prim.bb.isValid = true;
+
+                                                       //Interleaving vertices
+                                                       byte * inPosPtr = null, inNormPtr = null, inUvPtr = null, inUv1Ptr = null;
+
+                                                       GL.BufferView bv = gltf.BufferViews[(int)AccPos.BufferView];
+                                                       inPosPtr = (byte*)bufferHandles[bv.Buffer].AddrOfPinnedObject ().ToPointer ();
+                                                       inPosPtr += AccPos.ByteOffset + bv.ByteOffset;
+
+                                                       if (AccNorm != null) {
+                                                               bv = gltf.BufferViews[(int)AccNorm.BufferView];
+                                                               inNormPtr = (byte*)bufferHandles[bv.Buffer].AddrOfPinnedObject ().ToPointer ();
+                                                               inNormPtr += AccNorm.ByteOffset + bv.ByteOffset;
+                                                       }
+                                                       if (AccUv != null) {
+                                                               bv = gltf.BufferViews[(int)AccUv.BufferView];
+                                                               inUvPtr = (byte*)bufferHandles[bv.Buffer].AddrOfPinnedObject ().ToPointer ();
+                                                               inUvPtr += AccUv.ByteOffset + bv.ByteOffset;
+                                                       }
+                                                       if (AccUv1 != null) {
+                                                               bv = gltf.BufferViews[(int)AccUv1.BufferView];
+                                                               inUv1Ptr = (byte*)bufferHandles[bv.Buffer].AddrOfPinnedObject ().ToPointer ();
+                                                               inUv1Ptr += AccUv1.ByteOffset + bv.ByteOffset;
+                                                       }
+
+
+                                                       for (int j = 0; j < prim.vertexCount; j++) {
+                                                               System.Buffer.MemoryCopy (inPosPtr, stagVertPtr, 12, 12);
+                                                               inPosPtr += 12;
+                                                               if (inNormPtr != null) {
+                                                                       System.Buffer.MemoryCopy (inNormPtr, stagVertPtr + 12, 12, 12);
+                                                                       inNormPtr += 12;
+                                                               }
+                                                               if (inUvPtr != null) {
+                                                                       System.Buffer.MemoryCopy (inUvPtr, stagVertPtr + 24, 8, 8);
+                                                                       inUvPtr += 8;
+                                                               }
+                                                               if (inUv1Ptr != null) {
+                                                                       System.Buffer.MemoryCopy (inUv1Ptr, stagVertPtr + 32, 8, 8);
+                                                                       inUv1Ptr += 8;
+                                                               }
+                                                               stagVertPtr += vertexByteSize;
+                                                       }
+
+                                                       //indices loading
+                                                       if (p.Indices != null) {
+                                                               GL.Accessor acc = gltf.Accessors[(int)p.Indices];
+                                                               bv = gltf.BufferViews[(int)acc.BufferView];
+
+                                                               byte* inIdxPtr = (byte*)bufferHandles[bv.Buffer].AddrOfPinnedObject ().ToPointer ();
+                                                               inIdxPtr += acc.ByteOffset + bv.ByteOffset;
+
+                                                               //TODO:double check this, I dont seems to increment stag pointer
+                                                               if (acc.ComponentType == GL.Accessor.ComponentTypeEnum.UNSIGNED_SHORT) {
+                                                                       if (indexType == VkIndexType.Uint16) {
+                                                                               System.Buffer.MemoryCopy (inIdxPtr, stagIdxPtr, (long)acc.Count * 2, (long)acc.Count * 2);
+                                                                               stagIdxPtr += (long)acc.Count * 2;
+                                                                       } else {
+                                                                               uint* usPtr = (uint*)stagIdxPtr;
+                                                                               ushort* inPtr = (ushort*)inIdxPtr;
+                                                                               for (int i = 0; i < acc.Count; i++) 
+                                                                                       usPtr[i] = inPtr[i];                                                                            
+                                                                               stagIdxPtr += (long)acc.Count * 4;
+                                                                       }
+                                                               } else if (acc.ComponentType == GL.Accessor.ComponentTypeEnum.UNSIGNED_INT) {
+                                                                       if (indexType == VkIndexType.Uint32) {
+                                                                               System.Buffer.MemoryCopy (inIdxPtr, stagIdxPtr, (long)acc.Count * 4, (long)acc.Count * 4);
+                                                                               stagIdxPtr += (long)acc.Count * 4;
+                                                                       } else {
+                                                                               ushort* usPtr = (ushort*)stagIdxPtr;
+                                                                               uint* inPtr = (uint*)inIdxPtr;
+                                                                               for (int i = 0; i < acc.Count; i++) 
+                                                                                       usPtr[i] = (ushort)inPtr[i];
+                                                                               stagIdxPtr += (long)acc.Count * 2;
+                                                                       }
+                                                               } else if (acc.ComponentType == GL.Accessor.ComponentTypeEnum.UNSIGNED_BYTE) {
+                                                                       //convert
+                                                                       if (indexType == VkIndexType.Uint16) {
+                                                                               ushort* usPtr = (ushort*)stagIdxPtr;
+                                                                               for (int i = 0; i < acc.Count; i++)
+                                                                                       usPtr[i] = (ushort)inIdxPtr[i];
+                                                                               stagIdxPtr += (long)acc.Count * 2;
+                                                                       } else {
+                                                                               uint* usPtr = (uint*)stagIdxPtr;
+                                                                               for (int i = 0; i < acc.Count; i++)
+                                                                                       usPtr[i] = (uint)inIdxPtr[i];
+                                                                               stagIdxPtr += (long)acc.Count * 4;
+                                                                       }
+                                                               } else
+                                                                       throw new NotImplementedException ();
+
+                                                               prim.indexCount = (uint)acc.Count;
+                                                               indexCount += acc.Count;
+                                                       }
+
+                                                       m.AddPrimitive (prim);
+
+                                                       vertexCount += AccPos.Count;
+                                               }
+                                               meshes.Add (m);
+                                       }
+                               }
+
+                               stagging.Unmap ();
+
+                               CommandBuffer cmd = cmdPool.AllocateCommandBuffer ();
+                               cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               stagging.CopyTo (cmd, vbo, vertSize, 0, vboOffset);
+                               if (iCount>0)
+                                       stagging.CopyTo (cmd, ibo, idxSize, vertSize, iboOffset);
+
+                               cmd.End ();
+
+                               transferQ.Submit (cmd);
+
+                               dev.WaitIdle ();
+                               cmd.Free ();
+
+                       }
+
+                       return meshes.ToArray ();
+               }
+
+               public Scene[] LoadScenes (out int defaultScene) {
+                       defaultScene = -1;
+                       if (gltf.Scene == null)
+                               return new Scene[] {};
+
+                       List<Scene> scenes = new List<Scene> ();
+                       defaultScene = (int)gltf.Scene;                 
+                       
+                       for (int i = 0; i < gltf.Scenes.Length; i++) {
+                               GL.Scene scene = gltf.Scenes[i];
+                               Debug.WriteLine ("Loading Scene {0}", scene.Name);
+
+                               scenes.Add (new Scene {
+                                       Name = scene.Name,
+                               });
+
+                               if (scene.Nodes.Length == 0)
+                                       continue;
+
+                               scenes[i].Root = new Node {
+                                       localMatrix = Matrix4x4.Identity,
+                                       Children = new List<Node> ()
+                               };
+
+                               foreach (int nodeIdx in scene.Nodes)
+                                       loadNode (scenes[i].Root, gltf.Nodes[nodeIdx]);
+                       }
+                       return scenes.ToArray ();
+               }
+
+               void loadNode (Node parentNode, GL.Node gltfNode) {
+                       Debug.WriteLine ("Loading node {0}", gltfNode.Name);
+
+                       Vector3 translation = new Vector3 ();
+                       Quaternion rotation = Quaternion.Identity;
+                       Vector3 scale = new Vector3 (1);
+                       Matrix4x4 localTransform = Matrix4x4.Identity;
+
+                       if (gltfNode.Matrix != null) {
+                               float[] M = gltfNode.Matrix;
+                               localTransform = new Matrix4x4 (
+                                       M[0], M[1], M[2], M[3],
+                                       M[4], M[5], M[6], M[7],
+                                       M[8], M[9],M[10],M[11],
+                                  M[12],M[13],M[14],M[15]);
+                       }
+
+                       if (gltfNode.Translation != null) 
+                               FromFloatArray (ref translation, gltfNode.Translation);
+                       if (gltfNode.Translation != null) 
+                               FromFloatArray (ref rotation, gltfNode.Rotation);                       
+                       if (gltfNode.Translation != null) 
+                               FromFloatArray (ref scale, gltfNode.Scale);
+
+                       localTransform *=
+                               Matrix4x4.CreateScale (scale) *
+                               Matrix4x4.CreateFromQuaternion (rotation) *
+                               Matrix4x4.CreateTranslation (translation);
+
+                       //localTransform = Matrix4x4.Identity;
+
+                       Node node = new Node {
+                               localMatrix = localTransform,
+                               Parent = parentNode,
+                Name = gltfNode.Name
+                       };
+                       parentNode.Children.Add (node);
+
+                       if (gltfNode.Children != null) {
+                               node.Children = new List<Node> ();
+                               for (int i = 0; i < gltfNode.Children.Length; i++)
+                                       loadNode (node, gltf.Nodes[gltfNode.Children[i]]);
+                       }
+
+                       if (gltfNode.Mesh != null)
+                               node.Mesh = meshes[(int)gltfNode.Mesh];
+               }
+
+               ///// <summary>
+               ///// build texture array
+               ///// </summary>
+               ///// <returns>The images.</returns>
+               ///// <param name="textureSize">Uniformized Texture size for all images</param>
+               public void BuildTexArray (ref Image texArray, uint firstImg = 0) {
+                       int texDim = (int)texArray.CreateInfo.extent.width;
+
+                       CommandBuffer cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+                       texArray.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, 
+                                               VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.Transfer);
+                       cmd.End ();
+                       transferQ.Submit (cmd);
+                       transferQ.WaitIdle ();
+                       cmd.Free ();
+
+                       VkImageBlit imageBlit = new VkImageBlit {
+                               srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1, 0),
+                               dstOffsets_1 = new VkOffset3D (texDim, texDim, 1)
+                       };
+
+                       for (int l = 0; l < gltf.Images.Length; l++) {
+                               GL.Image img = gltf.Images[l];
+                               Image vkimg = null;
+
+                               if (img.BufferView != null) {//load image from gltf buffer view
+                                       GL.BufferView bv = gltf.BufferViews[(int)img.BufferView];
+                                       EnsureBufferIsLoaded (bv.Buffer);
+                                       vkimg = Image.Load (dev, bufferHandles[bv.Buffer].AddrOfPinnedObject () + bv.ByteOffset, (ulong)bv.ByteLength, VkImageUsageFlags.TransferSrc);
+                               } else if (img.Uri.StartsWith ("data:", StringComparison.Ordinal)) {//load base64 encoded image
+                                       Debug.WriteLine ("loading embedded image {0} : {1}", img.Name, img.MimeType);
+                                       vkimg = Image.Load (dev, glTFLoader.loadDataUri (img), VkImageUsageFlags.TransferSrc);
+                               } else {
+                                       Debug.WriteLine ("loading image {0} : {1} : {2}", img.Name, img.MimeType, img.Uri);//load image from file path in uri
+                                       vkimg = Image.Load (dev, Path.Combine (baseDirectory, img.Uri), VkImageUsageFlags.TransferSrc);
+                               }
+
+                               imageBlit.srcOffsets_1 = new VkOffset3D ((int)vkimg.CreateInfo.extent.width, (int)vkimg.CreateInfo.extent.height, 1);
+                               imageBlit.dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1, 0, (uint)l + firstImg);
+
+                               cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               vkimg.SetLayout (cmd, VkImageAspectFlags.Color,
+                                               VkAccessFlags.HostWrite, VkAccessFlags.TransferRead,
+                                               VkImageLayout.Undefined, VkImageLayout.TransferSrcOptimal,
+                                               VkPipelineStageFlags.Host, VkPipelineStageFlags.Transfer);
+
+                               Vk.vkCmdBlitImage (cmd.Handle, vkimg.Handle, VkImageLayout.TransferSrcOptimal,
+                                       texArray.Handle, VkImageLayout.TransferDstOptimal, 1, ref imageBlit, VkFilter.Linear);
+
+                               cmd.End ();
+                               transferQ.Submit (cmd);
+                               transferQ.WaitIdle ();
+                               cmd.Free ();
+
+                               vkimg.Dispose ();
+                       }
+
+                       cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                       uint imgCount = (uint)gltf.Images.Length;
+                       VkImageSubresourceRange mipSubRange = new VkImageSubresourceRange (VkImageAspectFlags.Color, 0, 1, firstImg, imgCount);
+
+                       for (int i = 1; i < texArray.CreateInfo.mipLevels; i++) {
+                               imageBlit = new VkImageBlit {
+                                       srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, imgCount, (uint)i - 1, firstImg),
+                                       srcOffsets_1 = new VkOffset3D ((int)texDim >> (i - 1), (int)texDim >> (i - 1), 1),
+                                       dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, imgCount, (uint)i, firstImg),
+                                       dstOffsets_1 = new VkOffset3D ((int)texDim >> i, (int)texDim >> i, 1)
+                               };
+
+                               texArray.SetLayout (cmd,
+                                       VkAccessFlags.TransferWrite, VkAccessFlags.TransferRead,
+                                       VkImageLayout.TransferDstOptimal, VkImageLayout.TransferSrcOptimal, mipSubRange,
+                                       VkPipelineStageFlags.Transfer, VkPipelineStageFlags.Transfer);
+
+                               Vk.vkCmdBlitImage (cmd.Handle, texArray.Handle, VkImageLayout.TransferSrcOptimal,
+                                       texArray.Handle, VkImageLayout.TransferDstOptimal, 1, ref imageBlit, VkFilter.Linear);
+                               texArray.SetLayout (cmd, VkImageLayout.TransferSrcOptimal, VkImageLayout.ShaderReadOnlyOptimal, mipSubRange,
+                                       VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader);
+
+                               mipSubRange.baseMipLevel = (uint)i;
+                       }
+                       mipSubRange.baseMipLevel = texArray.CreateInfo.mipLevels - 1;
+                       texArray.SetLayout (cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, mipSubRange,
+                               VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader);
+
+                       cmd.End ();
+                       transferQ.Submit (cmd);
+                       transferQ.WaitIdle ();
+                       cmd.Free ();
+               }
+               /// <summary>
+               /// Load model images as separate texture in a c# array
+               /// </summary>
+               /// <returns>The images.</returns>
+               public Image[] LoadImages () {
+                       if (gltf.Images == null)
+                               return new Image[] {};
+
+                       List<Image>     textures = new List<Image> ();
+
+                       foreach (GL.Image img in gltf.Images) {
+                               Image vkimg = null;
+
+                               string imgName = img.Name;
+
+                               if (img.BufferView != null) {//load image from gltf buffer view
+                                       GL.BufferView bv = gltf.BufferViews[(int)img.BufferView];
+                                       EnsureBufferIsLoaded (bv.Buffer);
+                                       vkimg = Image.Load (dev, transferQ, cmdPool, bufferHandles[bv.Buffer].AddrOfPinnedObject () + bv.ByteOffset, (ulong)bv.ByteLength);
+                               } else if (img.Uri.StartsWith ("data:", StringComparison.Ordinal)) {//load base64 encoded image
+                                       Debug.WriteLine ("loading embedded image {0} : {1}", img.Name, img.MimeType);
+                                       vkimg = Image.Load (dev, transferQ, cmdPool, glTFLoader.loadDataUri (img));
+                               } else {
+                                       Debug.WriteLine ("loading image {0} : {1} : {2}", img.Name, img.MimeType, img.Uri);//load image from file path in uri
+                                       vkimg = Image.Load (dev, transferQ, cmdPool, Path.Combine (baseDirectory, img.Uri));
+                                       imgName += ";" + img.Uri;
+                               }
+
+                               vkimg.CreateView ();
+                               vkimg.CreateSampler ();
+                               vkimg.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                               vkimg.SetName (imgName);
+                               vkimg.Descriptor.imageView.SetDebugMarkerName (dev, "imgView " + imgName);
+                               vkimg.Descriptor.sampler.SetDebugMarkerName (dev, "sampler " + imgName);
+
+                               textures.Add (vkimg);
+                       }
+                       return textures.ToArray ();
+               }
+
+               public Material[] LoadMaterial () {
+                       if (gltf.Materials == null)
+                               return new Material[] {};
+
+                       List<Material>  materials = new List<Material> ();
+
+                       foreach (GL.Material mat in gltf.Materials) {
+                               Debug.WriteLine ("loading material: " + mat.Name);
+                               Material pbr = new Material ();
+                               pbr.Name = mat.Name;
+
+                               pbr.alphaCutoff = mat.AlphaCutoff;
+                               pbr.alphaMode = (AlphaMode)mat.AlphaMode;
+
+                               FromFloatArray (ref pbr.emissiveFactor, mat.EmissiveFactor);
+
+                               if (mat.EmissiveTexture != null) {
+                                       pbr.emissiveTexture = mat.EmissiveTexture.Index;
+                                       if (mat.EmissiveTexture.TexCoord == 1)
+                                               pbr.availableAttachments1 |= AttachmentType.Emissive;
+                                       else
+                                               pbr.availableAttachments |= AttachmentType.Emissive;
+                               }
+                               if (mat.NormalTexture != null) {
+                                       pbr.normalTexture = mat.NormalTexture.Index;
+                                       if (mat.NormalTexture.TexCoord == 1)
+                                               pbr.availableAttachments1 |= AttachmentType.Normal;
+                                       else
+                                               pbr.availableAttachments |= AttachmentType.Normal;
+                               }
+                               if (mat.OcclusionTexture != null) {
+                                       pbr.occlusionTexture = mat.OcclusionTexture.Index;
+                                       if (mat.OcclusionTexture.TexCoord == 1)
+                                               pbr.availableAttachments1 |= AttachmentType.AmbientOcclusion;
+                                       else
+                                               pbr.availableAttachments |= AttachmentType.AmbientOcclusion;
+                               }
+
+                               if (mat.PbrMetallicRoughness != null) {
+                                       if (mat.PbrMetallicRoughness.BaseColorTexture != null) {
+                                               pbr.baseColorTexture = mat.PbrMetallicRoughness.BaseColorTexture.Index;
+                                               if (mat.PbrMetallicRoughness.BaseColorTexture.TexCoord == 1)
+                                                       pbr.availableAttachments1 |= AttachmentType.Color;
+                                               else
+                                                       pbr.availableAttachments |= AttachmentType.Color;
+                                       }
+
+                                       FromFloatArray (ref pbr.baseColorFactor, mat.PbrMetallicRoughness.BaseColorFactor);
+
+                                       if (mat.PbrMetallicRoughness.MetallicRoughnessTexture != null) {
+                                               pbr.metallicRoughnessTexture = mat.PbrMetallicRoughness.MetallicRoughnessTexture.Index;
+                                               if (mat.PbrMetallicRoughness.MetallicRoughnessTexture.TexCoord == 1)
+                                                       pbr.availableAttachments1 |= AttachmentType.PhysicalProps;
+                                               else
+                                                       pbr.availableAttachments |= AttachmentType.PhysicalProps;
+                                       }
+                                       pbr.metallicFactor = mat.PbrMetallicRoughness.MetallicFactor;
+                                       pbr.roughnessFactor = mat.PbrMetallicRoughness.RoughnessFactor;
+
+                                       pbr.workflow = Material.Workflow.PhysicalyBaseRendering;
+                               }
+                               materials.Add (pbr);
+                       }
+                       return materials.ToArray ();
+               }
+
+
+               #region IDisposable Support
+               private bool isDisposed = false; // Pour détecter les appels redondants
+
+               protected virtual void Dispose (bool disposing) {
+                       if (!isDisposed) {
+                               if (disposing) {
+                                       // TODO: supprimer l'état managé (objets managés).
+                               }
+
+                               for (int i = 0; i < gltf.Buffers.Length; i++) {
+                                       if (bufferHandles[i].IsAllocated)
+                                               bufferHandles[i].Free ();
+                               }
+
+                               isDisposed = true;
+                       }
+               }
+
+               ~glTFLoader () {
+                       Dispose (false);
+               }
+               public void Dispose () {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+               #endregion
+       }
+}
\ No newline at end of file
diff --git a/addons/gltfLoader/gltfLoader.csproj b/addons/gltfLoader/gltfLoader.csproj
new file mode 100644 (file)
index 0000000..a439773
--- /dev/null
@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />
+  <PropertyGroup>
+    <AssemblyName>CVKL.gltfLoader</AssemblyName>
+    <PackageId>CVKL.gltfLoader</PackageId>
+    <AssemblyVersion>0.1.6</AssemblyVersion>
+    <Description>CVKL gltf addons</Description>
+    <PackageTags>C# vulkan CVKL gltf</PackageTags>    
+    <PackageVersion>$(AssemblyVersion)-beta</PackageVersion>
+    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
+    <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
+    <PackageProjectUrl>https://github.com/jpbruyere/vk.net/blob/master/README.md</PackageProjectUrl>
+    <License>MIT</License>
+    <PackageReleaseNotes></PackageReleaseNotes>
+    <SynchReleaseVersion>false</SynchReleaseVersion>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>    
+  </PropertyGroup>
+  <ItemGroup>    
+    <PackageReference Include="glTF2Loader" Version="1.1.3-alpha" />
+  </ItemGroup>
+</Project>
diff --git a/clean.sh b/clean.sh
new file mode 100755 (executable)
index 0000000..d39df8f
--- /dev/null
+++ b/clean.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+rm -fr build && find . -iname bin -o -iname obj | xargs rm -rf
diff --git a/netfx.props b/netfx.props
new file mode 100644 (file)
index 0000000..25fdbe0
--- /dev/null
@@ -0,0 +1,27 @@
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <!-- When compiling .NET SDK 2.0 projects targeting .NET 4.x on Mono using 'dotnet build' you -->
+    <!-- have to teach MSBuild where the Mono copy of the reference asssemblies is -->
+    <TargetIsMono Condition="$(TargetFramework.StartsWith('net4')) and '$(OS)' == 'Unix'">true</TargetIsMono>
+
+    <!-- Look in the standard install locations -->
+    <BaseFrameworkPathOverrideForMono Condition="'$(BaseFrameworkPathOverrideForMono)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/Library/Frameworks/Mono.framework/Versions/Current/lib/mono')">/Library/Frameworks/Mono.framework/Versions/Current/lib/mono</BaseFrameworkPathOverrideForMono>
+    <BaseFrameworkPathOverrideForMono Condition="'$(BaseFrameworkPathOverrideForMono)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/usr/lib/mono')">/usr/lib/mono</BaseFrameworkPathOverrideForMono>
+    <BaseFrameworkPathOverrideForMono Condition="'$(BaseFrameworkPathOverrideForMono)' == '' AND '$(TargetIsMono)' == 'true' AND EXISTS('/usr/local/lib/mono')">/usr/local/lib/mono</BaseFrameworkPathOverrideForMono>
+
+    <!-- If we found Mono reference assemblies, then use them -->
+    <FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net45'">$(BaseFrameworkPathOverrideForMono)/4.5-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net451'">$(BaseFrameworkPathOverrideForMono)/4.5.1-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net452'">$(BaseFrameworkPathOverrideForMono)/4.5.2-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net46'">$(BaseFrameworkPathOverrideForMono)/4.6-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net461'">$(BaseFrameworkPathOverrideForMono)/4.6.1-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net462'">$(BaseFrameworkPathOverrideForMono)/4.6.2-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net47'">$(BaseFrameworkPathOverrideForMono)/4.7-api</FrameworkPathOverride>
+    <FrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != '' AND '$(TargetFramework)' == 'net471'">$(BaseFrameworkPathOverrideForMono)/4.7.1-api</FrameworkPathOverride>
+    <EnableFrameworkPathOverride Condition="'$(BaseFrameworkPathOverrideForMono)' != ''">true</EnableFrameworkPathOverride>
+
+    <!-- Add the Facades directory.  Not sure how else to do this. Necessary at least for .NET 4.5 -->
+    <AssemblySearchPaths Condition="'$(BaseFrameworkPathOverrideForMono)' != ''">$(FrameworkPathOverride)/Facades;$(AssemblySearchPaths)</AssemblySearchPaths>
+  </PropertyGroup>
+</Project>
+
diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props
new file mode 100644 (file)
index 0000000..738ae0b
--- /dev/null
@@ -0,0 +1,44 @@
+<Project>
+       <PropertyGroup>
+               <RootDirectory>$(MSBuildThisFileDirectory)../</RootDirectory>
+               <Deterministic>true</Deterministic>
+
+               <TargetFrameworks>net471;netstandard2.0</TargetFrameworks>
+
+               <RepositoryUrl>https://github.com/jpbruyere/vke.net</RepositoryUrl>
+               <License>MIT</License>
+               <Authors>Jean-Philippe Bruyère</Authors>
+
+               <OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
+               <OutputType>Exe</OutputType>        
+               <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+               <IncludeDefaultNoneItems>false</IncludeDefaultNoneItems>
+
+               <SpirVAdditionalIncludeDirectories>$(MSBuildThisFileDirectory)common\shaders</SpirVAdditionalIncludeDirectories>
+
+       </PropertyGroup>
+    
+       <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">    
+               <DefineConstants>TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;_WITH_SHADOWS;WITH_VKVG</DefineConstants>    
+       </PropertyGroup>
+       <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+               <DefineConstants>NETSTANDARD;NETSTANDARD2_0;WITH_SHADOWS;_WITH_VKVG</DefineConstants>    
+       </PropertyGroup>
+       
+       <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">              
+               <ProjectReference Include="$(RootDirectory)vke\vke.csproj" />
+       </ItemGroup>    
+       <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">            
+               <PackageReference Include="vke" Version="0.1.7-beta" />
+       </ItemGroup>   
+       
+       <ItemGroup>
+               <PackageReference Include="SpirVTasks" Version="0.1.9-beta" />     
+               <PackageReference Include="Vulkan" Version="0.1.4" />        
+       </ItemGroup>    
+
+       <ItemGroup>    
+               <GLSLShader Include="shaders\**\*.frag;shaders\**\*.vert;shaders\**\*.comp;shaders\**\*.geom" />
+       </ItemGroup>     
+       
+</Project>
diff --git a/samples/DistanceFieldFontTest/DistanceFieldFontTest.csproj b/samples/DistanceFieldFontTest/DistanceFieldFontTest.csproj
new file mode 100644 (file)
index 0000000..c603527
--- /dev/null
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />
+  <ItemGroup>
+    <ProjectReference Include="..\..\addons\DistanceFieldFont\DistanceFieldFont.csproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="shaders\" />
+  </ItemGroup>
+</Project>
diff --git a/samples/DistanceFieldFontTest/Program.cs b/samples/DistanceFieldFontTest/Program.cs
new file mode 100644 (file)
index 0000000..1eea932
--- /dev/null
@@ -0,0 +1,300 @@
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Glfw;
+
+using VK;
+using CVKL;
+using CVKL.DistanceFieldFont;
+
+namespace DistanceFieldFontTest {
+
+
+
+       class Program : VkWindow {
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = false;
+#endif
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               float rotSpeed = 0.01f, zoomSpeed = 0.01f;
+               float rotX, rotY, rotZ = 0f, zoom = 1f;
+
+               struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 view;
+                       public Matrix4x4 model;
+               }
+
+               Matrices matrices;
+
+               HostBuffer uboMats;
+               GPUBuffer<float> vbo;
+               GPUBuffer<ushort> ibo;
+
+               DescriptorPool descriptorPool;
+               DescriptorSetLayout dsLayout;
+               DescriptorSet descriptorSet;
+
+               GraphicPipeline pipeline;
+               Framebuffer[] frameBuffers;
+
+               Image fontTexture;
+
+               struct Vertex {
+                       public Vector3 pos;
+                       public Vector2 uv;
+                       public Vertex (float x, float y, float z, float u, float v) {
+                               pos.X = x; pos.Y = y; pos.Z = z;
+                               uv.X = u; uv.Y = v;
+                       }
+               }
+
+               BMFont font;
+               Vector4 textColor = new Vector4 (1.0f, 1.0f, 0.0f, 1.0f);//alpha => 0:disabled 1:enabled
+               Vector4 outlineColor = new Vector4 (1.0f, 0.0f, 0.0f, 0.6f);//alpha => 0:disabled 1:enabled
+
+               Program () : base () {
+
+                       font = new BMFont (Utils.DataDirectory + "font.fnt");
+
+                       vbo = new GPUBuffer<float> (dev, VkBufferUsageFlags.VertexBuffer, 1024);
+                       ibo = new GPUBuffer<ushort> (dev, VkBufferUsageFlags.IndexBuffer, 2048);
+
+                       descriptorPool = new DescriptorPool (dev, 1,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       dsLayout = new DescriptorSetLayout (dev, 0,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex, VkDescriptorType.UniformBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount4, false);
+
+                       cfg.Layout = new PipelineLayout (dev, dsLayout);
+                       cfg.Layout.AddPushConstants (new VkPushConstantRange (VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf<Vector4> () * 2));
+
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, cfg.Samples);
+
+                       cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (
+                               true, VkBlendFactor.One, VkBlendFactor.OneMinusSrcAlpha, VkBlendOp.Add, VkBlendFactor.One, VkBlendFactor.Zero);
+
+                       cfg.AddVertexBinding (0, 5 * sizeof (float));
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#DistanceFieldFontTest.main.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#DistanceFieldFontTest.main.frag.spv");
+
+                       pipeline = new GraphicPipeline (cfg);
+
+                       uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, matrices);
+                       uboMats.Map ();//permanent map
+
+                       descriptorSet = descriptorPool.Allocate (dsLayout);
+
+                       fontTexture = font.GetPageTexture (0, presentQueue, cmdPool);
+                       fontTexture.CreateView ();
+                       fontTexture.CreateSampler ();
+                       fontTexture.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                       DescriptorSetWrites dsUpdate = new DescriptorSetWrites (descriptorSet, dsLayout);
+                       dsUpdate.Write (dev, uboMats.Descriptor, fontTexture.Descriptor);
+
+                       generateText ("Vulkan", out HostBuffer<Vertex> staggingVbo, out HostBuffer<ushort> staggingIbo);
+
+                       CommandBuffer cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                       staggingVbo.CopyTo (cmd, vbo);
+                       staggingIbo.CopyTo (cmd, ibo);
+
+                       presentQueue.EndSubmitAndWait (cmd);
+
+                       staggingVbo.Dispose ();
+                       staggingIbo.Dispose ();
+               }
+
+
+               // Creates a vertex buffer containing quads for the passed text
+               void generateText (string text, out HostBuffer<Vertex> svbo, out HostBuffer<ushort> sibo) {
+                       List<Vertex> vertices = new List<Vertex> ();
+                       List<ushort> indices = new List<ushort> ();
+                       ushort indexOffset = 0;
+
+                       float w = fontTexture.Width;
+
+                       float posx = 0.0f;
+                       float posy = 0.0f;
+
+                       for (int i = 0; i < text.Length; i++) {
+                               BMChar charInfo = font.CharMap[text[i]];
+
+
+                               if (charInfo.width == 0)
+                                       charInfo.width = 36;
+
+                               float charw = ((float)(charInfo.width) / 36.0f);
+                               float dimx = 1.0f * charw;
+                               float charh = ((float)(charInfo.height) / 36.0f);
+                               float dimy = 1.0f * charh;
+                               posy = 1.0f - charh;
+
+                               float us = charInfo.x / w;
+                               float ue = (charInfo.x + charInfo.width) / w;
+                               float ts = charInfo.y / w;
+                               float te = (charInfo.y + charInfo.height) / w;
+
+                               float xo = charInfo.xoffset / 36.0f;
+                               float yo = charInfo.yoffset / 36.0f;
+
+                               vertices.Add (new Vertex (posx + dimx + xo, posy + dimy, 0.0f, ue, te));
+                               vertices.Add (new Vertex (posx + xo, posy + dimy, 0.0f, us, te));
+                               vertices.Add (new Vertex (posx + xo, posy, 0.0f, us, ts));
+                               vertices.Add (new Vertex (posx + dimx + xo, posy, 0.0f, ue, ts));
+
+                               indices.AddRange (new ushort[] { indexOffset, (ushort)(indexOffset + 1), (ushort)(indexOffset + 2), (ushort)(indexOffset + 2), (ushort)(indexOffset + 3), indexOffset });
+                               indexOffset += 4;
+
+                               float advance = charInfo.xadvance / 36.0f;
+                               posx += advance;
+                       }
+
+                       Vertex[] vx = vertices.ToArray ();
+
+                       // Center
+                       for (int i = 0; i < vx.Length; i++) {
+                               vx[i].pos.X -= posx / 2.0f;
+                               vx[i].pos.Y -= 0.5f;
+                       }
+
+                       svbo = new HostBuffer<Vertex> (dev, VkBufferUsageFlags.TransferSrc, vx);
+                       sibo = new HostBuffer<ushort> (dev, VkBufferUsageFlags.TransferSrc, indices.ToArray());
+               }
+
+               bool rebuildBuffers;
+
+               public override void Update () {
+                       if (rebuildBuffers) {
+                               buildCommandBuffers ();
+                               rebuildBuffers = false;
+                       }
+               }
+
+               void buildCommandBuffers () {
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               cmds[i]?.Free ();
+                               cmds[i] = cmdPool.AllocateAndStart ();
+
+                               recordDraw (cmds[i], frameBuffers[i]);
+
+                               cmds[i].End ();
+                       }
+               }
+
+               void recordDraw (CommandBuffer cmd, Framebuffer fb) {
+                       pipeline.RenderPass.Begin (cmd, fb);
+
+                       cmd.SetViewport (fb.Width, fb.Height);
+                       cmd.SetScissor (fb.Width, fb.Height);
+
+                       cmd.BindPipeline (pipeline, descriptorSet);
+
+                       cmd.PushConstant (pipeline, textColor);
+                       cmd.PushConstant (pipeline, outlineColor, 0, 16);
+
+                       cmd.BindVertexBuffer (vbo, 0);
+                       cmd.BindIndexBuffer (ibo, VkIndexType.Uint16);
+                       cmd.DrawIndexed ((uint)ibo.ElementCount,1,0,0,0);
+
+                       pipeline.RenderPass.End (cmd);
+               }
+
+
+               public override void UpdateView () {
+                       matrices.projection = Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (60f), (float)swapChain.Width / (float)swapChain.Height, 0.1f, 256.0f);
+                       matrices.view = Matrix4x4.CreateTranslation (0, 0, -2.5f * zoom);
+                       matrices.model =
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotZ) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotY) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotX);
+
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+
+                       updateViewRequested = false;
+               }
+
+               protected override void onMouseMove (double xPos, double yPos) {
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               rotY -= rotSpeed * (float)diffX;
+                               rotX += rotSpeed * (float)diffY;
+                       } else if (MouseButton[1]) {
+                               zoom += zoomSpeed * (float)diffY;
+                       }
+
+                       updateViewRequested = true;
+               }
+
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.F2:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               outlineColor.W -= 0.01f;
+                                       else
+                                               outlineColor.W += 0.01f;
+                                       rebuildBuffers = true;
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       break;
+                       }                                       
+               }
+               protected override void OnResize () {
+
+                       updateViewRequested = true;
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                       } : new Image[] {
+                                               null,
+                                               swapChain.images[i]
+                                       });
+                       }
+
+                       buildCommandBuffers ();
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+                                       pipeline.Dispose ();
+                                       dsLayout.Dispose ();
+                                       for (int i = 0; i < swapChain.ImageCount; i++)
+                                               frameBuffers[i].Dispose ();
+                                       descriptorPool.Dispose ();
+                                       fontTexture?.Dispose ();
+                                       vbo.Dispose ();
+                                       ibo.Dispose ();
+                                       uboMats.Dispose ();
+                               }
+                       }
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/DistanceFieldFontTest/shaders/main.frag b/samples/DistanceFieldFontTest/shaders/main.frag
new file mode 100644 (file)
index 0000000..fa1f6c6
--- /dev/null
@@ -0,0 +1,32 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (binding = 1)   uniform sampler2D samplerColor;
+
+layout (push_constant) uniform PushConsts {
+       vec4 color;
+       vec4 outlineColor;
+};
+
+layout (location = 0) in vec2 inUV;
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+    float distance = texture(samplerColor, inUV).a;
+    float smoothWidth = fwidth(distance);      
+    float alpha = smoothstep(0.5 - smoothWidth, 0.5 + smoothWidth, distance);
+       vec3 rgb = color.rgb * vec3(alpha);
+                                                                        
+       if (outlineColor.a > 0.0) 
+       {
+               float w = 1.0 - outlineColor.a;
+               alpha = smoothstep(w - smoothWidth, w + smoothWidth, distance);
+        rgb += mix(vec3(alpha), outlineColor.rgb, alpha);
+    }
+                                                                                                                                        
+    outFragColor = vec4(rgb, alpha * color.a); 
+       
+}
\ No newline at end of file
diff --git a/samples/DistanceFieldFontTest/shaders/main.vert b/samples/DistanceFieldFontTest/shaders/main.vert
new file mode 100644 (file)
index 0000000..3b395bf
--- /dev/null
@@ -0,0 +1,28 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inColor;
+
+layout (binding = 0) uniform UBO 
+{
+       mat4 projectionMatrix;
+    mat4 viewMatrix;
+       mat4 modelMatrix;
+} ubo;
+
+layout (location = 0) out vec3 outColor;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+
+void main() 
+{
+       outColor = inColor;
+       gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0);
+}
diff --git a/samples/Model/Model.csproj b/samples/Model/Model.csproj
new file mode 100644 (file)
index 0000000..d6da2bb
--- /dev/null
@@ -0,0 +1,7 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />     
+  <ItemGroup>
+    <ProjectReference Include="..\..\addons\gltfLoader\gltfLoader.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/samples/Model/data/DamagedHelmet.bin b/samples/Model/data/DamagedHelmet.bin
new file mode 100644 (file)
index 0000000..662eacc
Binary files /dev/null and b/samples/Model/data/DamagedHelmet.bin differ
diff --git a/samples/Model/data/DamagedHelmet.gltf b/samples/Model/data/DamagedHelmet.gltf
new file mode 100644 (file)
index 0000000..d685d77
--- /dev/null
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:efe99dfac198094a30c71dc02a4d3421f0eef6bf335aeb695daa4d62134cd93f
+size 4537
diff --git a/samples/Model/main.cs b/samples/Model/main.cs
new file mode 100644 (file)
index 0000000..6a98629
--- /dev/null
@@ -0,0 +1,257 @@
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using CVKL;
+using CVKL.glTF;
+using VK;
+
+namespace ModelSample {
+       class Program : VkWindow {
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = false;
+#endif
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 view;
+                       public Matrix4x4 model;
+                       //public Vector4 lightPos;
+               }
+
+               public struct PushConstants {
+                       public Matrix4x4 matrix;
+               }
+
+               Matrices matrices;
+
+               HostBuffer uboMats;
+
+               DescriptorPool descriptorPool;
+               DescriptorSet dsMatrices, dsTextures;
+               DescriptorSetLayout descLayoutMatrix;
+               DescriptorSetLayout descLayoutTextures;
+
+               Framebuffer[] frameBuffers;
+
+               GraphicPipeline pipeline;
+
+               VkSampleCountFlags NUM_SAMPLES = VkSampleCountFlags.SampleCount1;
+
+               float rotSpeed = 0.01f, zoomSpeed = 0.01f;
+               float rotX, rotY, rotZ = 0f, zoom = 2f;
+
+               SimpleModel helmet;
+               CVKL.DebugUtils.Messenger dbgmsg;
+
+               Program () : base () {
+
+
+                       descriptorPool = new DescriptorPool (dev, 2,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 3));
+
+                       descLayoutMatrix = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex, VkDescriptorType.UniformBuffer));
+
+                       descLayoutTextures = new DescriptorSetLayout (dev, 
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       VkPushConstantRange pushConstantRange = new VkPushConstantRange { 
+                               stageFlags = VkShaderStageFlags.Vertex,
+                               size = (uint)Marshal.SizeOf<PushConstants>(),
+                               offset = 0
+                       };
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, NUM_SAMPLES);
+                       cfg.rasterizationState.cullMode = VkCullModeFlags.Back;
+                       if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1) {
+                               cfg.multisampleState.sampleShadingEnable = true;
+                               cfg.multisampleState.minSampleShading = 0.5f;
+                       }
+
+                       cfg.Layout = new PipelineLayout (dev, pushConstantRange, descLayoutMatrix, descLayoutTextures);
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), cfg.Samples);
+                       cfg.AddVertexBinding<Model.Vertex> (0);
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#Model.model.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#Model.model.frag.spv");
+
+                       pipeline = new GraphicPipeline (cfg);
+
+                       helmet = new SimpleModel (presentQueue, Utils.DataDirectory + "models/DamagedHelmet/glTF/DamagedHelmet.gltf");
+                       //helmet = new SimpleModel (presentQueue, Utils.DataDirectory + "models/Hubble.glb");
+
+                       //helmet = new SimpleModel (presentQueue, "/mnt/devel/vulkan/Lugdunum/resources/models/Box.gltf");
+
+                       dsMatrices = descriptorPool.Allocate (descLayoutMatrix);
+                       dsTextures = descriptorPool.Allocate (descLayoutTextures);
+
+                       uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, matrices);
+                       //matrices.lightPos = new Vector4 (0.0f, 0.0f, -2.0f, 1.0f);
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMatrices, descLayoutMatrix);
+                       uboUpdate.Write (dev, uboMats.Descriptor);
+
+                       DescriptorSetWrites texturesUpdate = new DescriptorSetWrites (dsTextures, descLayoutTextures);
+                       texturesUpdate.Write (dev,
+                               helmet.textures[0].Descriptor,
+                               helmet.textures[1].Descriptor,
+                               helmet.textures[2].Descriptor);
+
+                       uboMats.Map ();//permanent map
+               }
+
+               public override void UpdateView () {
+                       matrices.projection = Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (45f),
+                               (float)swapChain.Width / (float)swapChain.Height, 0.1f, 256.0f) * Camera.VKProjectionCorrection;
+                       matrices.view =
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotZ) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotY) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotX) *
+                               Matrix4x4.CreateTranslation (0, 0, -3f * zoom);
+                       matrices.model = Matrix4x4.Identity;
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+                       updateViewRequested = false;
+               }
+
+               protected override void onMouseMove (double xPos, double yPos) {
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               rotY -= rotSpeed * (float)diffX;
+                               rotX += rotSpeed * (float)diffY;
+                       } else if (MouseButton[1]) {
+                               zoom += zoomSpeed * (float)diffY;
+                       }
+                       updateViewRequested = true;
+               }
+               void buildCommandBuffers () {
+                       cmdPool.Reset (VkCommandPoolResetFlags.ReleaseResources);
+                       cmds = cmdPool.AllocateCommandBuffer (swapChain.ImageCount);
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               Framebuffer fb = frameBuffers[i];
+                               cmds[i].Start ();
+
+                               pipeline.RenderPass.Begin (cmds[i], fb);
+
+                               cmds[i].SetViewport (swapChain.Width, swapChain.Height);
+                               cmds[i].SetScissor (swapChain.Width, swapChain.Height);
+
+                               cmds[i].BindDescriptorSet (pipeline.Layout, dsMatrices);
+                               cmds[i].BindDescriptorSet (pipeline.Layout, dsTextures, 1);
+
+                               PushConstants pc = new PushConstants { matrix = Matrix4x4.Identity };
+                               cmds[i].PushConstant (pipeline.Layout, VkShaderStageFlags.Vertex, pc, (uint)Marshal.SizeOf<Matrix4x4> ());
+
+                               cmds[i].BindPipeline (pipeline);
+
+                               helmet.DrawAll (cmds[i], pipeline.Layout);
+
+                               pipeline.RenderPass.End (cmds[i]);
+
+                               cmds[i].End ();
+                       }
+               }
+
+               protected override void OnResize () {
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i)
+                               frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                               null
+                                       } : new Image[] {
+                                               null,
+                                               null,
+                                               swapChain.images[i]
+                                       });
+
+                       buildCommandBuffers ();
+               }
+
+               class SimpleModel : PbrModel {
+                       public new struct Vertex {
+                               [VertexAttribute (VertexAttributeType.Position, VkFormat.R32g32b32Sfloat)]
+                               public Vector3 pos;
+                               [VertexAttribute (VertexAttributeType.Normal, VkFormat.R32g32b32Sfloat)]
+                               public Vector3 normal;
+                               [VertexAttribute (VertexAttributeType.UVs, VkFormat.R32g32Sfloat)]
+                               public Vector2 uv;
+                               public override string ToString () {
+                                       return pos.ToString () + ";" + normal.ToString () + ";" + uv.ToString ();
+                               }
+                       };
+                       public Image[] textures;
+
+                       public SimpleModel (Queue transferQ, string path) {
+                               dev = transferQ.Dev;
+
+                               using (CommandPool cmdPool = new CommandPool (dev, transferQ.index)) {
+                                       using (CVKL.glTF.glTFLoader ctx = new CVKL.glTF.glTFLoader(path, transferQ, cmdPool)) {
+                                               loadSolids<Vertex> (ctx);
+                                               textures = ctx.LoadImages ();
+                                       }
+                               }
+                       }
+
+                       public void DrawAll (CommandBuffer cmd, PipelineLayout pipelineLayout) {
+                               //helmet.Meshes
+                               cmd.BindVertexBuffer (vbo);
+                               cmd.BindIndexBuffer (ibo, IndexBufferType);
+                               foreach (Mesh m in Meshes) {
+                                       foreach (var p in m.Primitives) {
+                                               cmd.DrawIndexed (p.indexCount,1,p.indexBase,p.vertexBase);
+                                       }
+                               }
+
+                               //foreach (Scene sc in Scenes) {
+                               //      foreach (Node node in sc.Root.Children)
+                               //              RenderNode (cmd, pipelineLayout, node, sc.Root.localMatrix, shadowPass);
+                               //}
+                       }
+
+                       public override void RenderNode (CommandBuffer cmd, PipelineLayout pipelineLayout, Node node, Matrix4x4 currentTransform, bool shadowPass = false) {
+                               throw new System.NotImplementedException ();
+                       }
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       helmet.vbo.Dispose ();
+                                       helmet.ibo.Dispose ();
+                                       foreach (var t in helmet.textures) 
+                                               t.Dispose ();
+
+                                       pipeline.Dispose ();
+                                       descLayoutMatrix.Dispose ();
+                                       descLayoutTextures.Dispose ();
+                                       for (int i = 0; i < swapChain.ImageCount; i++)
+                                               frameBuffers[i]?.Dispose ();
+                                       descriptorPool.Dispose ();
+                                       uboMats.Dispose ();
+                                       dbgmsg?.Dispose ();
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/Model/shaders/model.frag b/samples/Model/shaders/model.frag
new file mode 100644 (file)
index 0000000..7083a06
--- /dev/null
@@ -0,0 +1,84 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set = 1, binding = 0) uniform sampler2D samplerColor;
+layout (set = 1, binding = 1) uniform sampler2D samplerNormal;
+layout (set = 1, binding = 2) uniform sampler2D samplerOcclusion;
+
+layout (location = 0) in vec2 inUV;
+layout (location = 1) in vec3 inN;
+layout (location = 2) in vec3 inV;//ViewDir
+layout (location = 3) in vec3 inL;
+
+layout (location = 0) out vec4 outFragColor;
+
+
+
+// http://www.thetenthplanet.de/archives/1180
+mat3 cotangent_frame(vec3 N, vec3 p, vec2 uv)
+{
+    // get edge vectors of the pixel triangle
+    vec3 dp1 = dFdx( p );
+    vec3 dp2 = dFdy( p );
+    vec2 duv1 = dFdx( uv );
+    vec2 duv2 = dFdy( uv );
+    // solve the linear system
+    vec3 dp2perp = cross( dp2, N );
+    vec3 dp1perp = cross( N, dp1 );
+    vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
+    vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
+    // construct a scale-invariant frame 
+    float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );
+    return mat3( T * invmax, B * invmax, N );
+}
+
+vec3 perturb_normal( vec3 N, vec3 V, vec2 texcoord )
+{
+    // assume N, the interpolated vertex normal and 
+    // V, the view vector (vertex to eye)
+    vec3 map = texture(samplerNormal, texcoord).xyz;
+    map = map * 255./127. - 128./127.;
+    mat3 TBN = cotangent_frame(N, -V, texcoord);
+    return normalize(TBN * map);
+}
+
+
+void main() 
+{
+    vec4 color = texture(samplerColor, inUV);
+
+    vec3 N = normalize(inN);
+    vec3 L = normalize(inL);
+    vec3 V = normalize(inV);
+    vec3 R = reflect(-L, N);
+    vec3 diffuse = max(dot(N, L), 0.0) * vec3(0.9);
+    vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.75);
+    outFragColor = vec4(diffuse * color.rgb + specular, 1.0);       
+}
+/*void main() 
+{    
+    vec4 diff = texture(samplerColor, inUV);
+    
+    vec3 n = normalize(inN);
+    vec3 l = normalize(-light);
+    vec3 pn = perturb_normal(n, inV, inUV);    
+    
+    float lambert = max(0.0, dot(pn, l));
+    
+    //diff.rgb *= lambert * texture(samplerOcclusion, inUV).rgb;
+    vec3 spec = vec3(0);
+    vec3 amb = vec3(0.1);
+    if (lambert >= 0.0) {
+       vec3 rd = reflect(l, pn);
+       float s = dot(rd, normalize(inV));
+       spec = vec3(0.9,0.9,0.9)* //light.specular.xyz * material.specular.xyz *
+             pow(max(0.0, s), 10.5); //0.5 = mat shininess
+    }
+    //outFragColor = vec4(1.0);  // 
+    //outFragColor = vec4(diff.rgb , 1.0);  // 
+    outFragColor = vec4(diff.rgb + amb + spec , diff.a);  // 
+}*/
\ No newline at end of file
diff --git a/samples/Model/shaders/model.vert b/samples/Model/shaders/model.vert
new file mode 100644 (file)
index 0000000..c4dbfb8
--- /dev/null
@@ -0,0 +1,50 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inUV;
+
+layout (binding = 0) uniform UBO 
+{
+       mat4 projectionMatrix;
+    mat4 viewMatrix;
+       mat4 modelMatrix;
+} ubo;
+
+layout (location = 0) out vec2 outUV;
+layout (location = 1) out vec3 outN;
+layout (location = 2) out vec3 outV;//ViewDir
+layout (location = 3) out vec3 outL;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+layout(push_constant) uniform PushConsts {
+    mat4 model;
+} pc;
+
+vec3 light = vec3(2.0,2.0,-2.0);
+
+void main() 
+{
+    outUV = inUV;
+    
+    mat4 mod = ubo.modelMatrix;// * pc.model;
+    vec4 pos = mod * vec4(inPos.xyz, 1.0);
+    vec3 lPos = mat3(mod) * light;
+    
+    //outN = normalize(transpose(inverse(mat3(mod))) * inNormal);    
+    outN = mat3(mod)* inNormal;    
+    
+    //mat4 viewInv = inverse(ubo.viewMatrix);
+    
+    outV = -pos.xyz;//normalize(vec3(viewInv * vec4(0.0, 0.0, 0.0, 1.0) - pos));
+    outL = lPos - pos.xyz;
+    
+       gl_Position = ubo.projectionMatrix * ubo.viewMatrix * pos;    
+}
diff --git a/samples/Textured/Textured.csproj b/samples/Textured/Textured.csproj
new file mode 100644 (file)
index 0000000..1785b4a
--- /dev/null
@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />
+    
+  <ItemGroup>
+    <PackageReference Include="glTF2Loader" Version="1.1.3-alpha" />
+  </ItemGroup>        
+  
+</Project>
diff --git a/samples/Textured/main.cs b/samples/Textured/main.cs
new file mode 100644 (file)
index 0000000..6b64eed
--- /dev/null
@@ -0,0 +1,266 @@
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Glfw;
+using CVKL;
+using VK;
+
+namespace Textured {
+       class Program : VkWindow {
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = true;
+#endif
+
+                       foreach (string s in System.Reflection.Assembly.GetEntryAssembly ().GetManifestResourceNames ())
+                               Console.WriteLine (s);
+
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+               protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures features) {
+                       base.configureEnabledFeatures (available_features, ref features);
+                       features.textureCompressionBC = available_features.textureCompressionBC;
+                       features.textureCompressionASTC_LDR = available_features.textureCompressionASTC_LDR;
+               }
+
+               float rotSpeed = 0.01f, zoomSpeed = 0.01f;
+               float rotX, rotY, rotZ = 0f, zoom = 1f;
+
+               struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 view;
+                       public Matrix4x4 model;
+               }
+
+               Matrices matrices;
+
+               HostBuffer uboMats;
+               GPUBuffer<float> vbo;
+               GPUBuffer<ushort> ibo;
+
+               DescriptorPool descriptorPool;
+               DescriptorSetLayout dsLayout;
+               DescriptorSet descriptorSet;
+
+               GraphicPipeline pipeline;
+               Framebuffer[] frameBuffers;
+
+               Image texture;
+               Image nextTexture;
+
+               float[] vertices = {
+                        1.0f,  1.0f, 0.0f ,  1.0f, 0.0f,
+                       -1.0f,  1.0f, 0.0f ,  0.0f, 0.0f,
+                       -1.0f, -1.0f, 0.0f ,  0.0f, 1.0f,
+                        1.0f, -1.0f, 0.0f ,  1.0f, 1.0f,
+               };
+               ushort[] indices = { 0, 1, 2, 2, 0, 3 };
+               int currentImgIndex = 0;
+               string[] imgPathes = {
+                       "/mnt/devel/vulkan/VulkanExamples/data/models/voyager/voyager_rgba_unorm.ktx",
+                       "/mnt/devel/vulkan/vulkanExUpstream/data/models/voyager/voyager_astc_8x8_unorm.ktx",
+                       Utils.DataDirectory + "font.ktx",
+                       "/mnt/devel/vulkan/vulkanExUpstream/data/textures/trail_astc_8x8_unorm.ktx",
+                       Utils.DataDirectory + "textures/texturearray_rocks_bc3_unorm.ktx",
+                       Utils.DataDirectory + "textures/texture.jpg",
+                       Utils.DataDirectory + "textures/tex256.jpg",
+               };
+
+               Program () : base () {          
+                               
+                       loadTexture (imgPathes[currentImgIndex]);
+
+                       vbo = new GPUBuffer<float> (presentQueue, cmdPool, VkBufferUsageFlags.VertexBuffer, vertices);
+                       ibo = new GPUBuffer<ushort> (presentQueue, cmdPool, VkBufferUsageFlags.IndexBuffer, indices);
+
+                       descriptorPool = new DescriptorPool (dev, 1,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       dsLayout = new DescriptorSetLayout (dev, 0,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex, VkDescriptorType.UniformBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));
+                               
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount4);
+
+                       cfg.Layout = new PipelineLayout (dev, dsLayout);
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), cfg.Samples);
+
+                       cfg.AddVertexBinding (0, 5 * sizeof(float));
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#Textured.main.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#Textured.main.frag.spv");
+
+                       pipeline = new GraphicPipeline (cfg);
+
+
+                       uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, matrices);
+                       uboMats.Map ();//permanent map
+
+                       descriptorSet = descriptorPool.Allocate (dsLayout);
+
+                       updateTextureSet ();
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descriptorSet, dsLayout.Bindings[0]);
+                       uboUpdate.Write (dev, uboMats.Descriptor);
+               }
+
+               void buildCommandBuffers () {
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               cmds[i]?.Free ();
+                               cmds[i] = cmdPool.AllocateAndStart ();
+
+                               recordDraw (cmds[i], frameBuffers[i]);
+
+                               cmds[i].End ();                          
+                       }
+               }
+               void recordDraw (CommandBuffer cmd, Framebuffer fb) {
+                       pipeline.RenderPass.Begin (cmd, fb);
+
+                       cmd.SetViewport (fb.Width, fb.Height);
+                       cmd.SetScissor (fb.Width, fb.Height);
+                       cmd.BindDescriptorSet (pipeline.Layout, descriptorSet);
+
+                       pipeline.Bind (cmd);
+
+                       cmd.BindVertexBuffer (vbo, 0);
+                       cmd.BindIndexBuffer (ibo, VkIndexType.Uint16);
+                       cmd.DrawIndexed ((uint)indices.Length);
+
+                       pipeline.RenderPass.End (cmd);
+               }
+
+               VkMemoryPropertyFlags imgProp = VkMemoryPropertyFlags.DeviceLocal;
+               bool genMipMaps = true;
+               VkImageTiling tiling = VkImageTiling.Optimal;
+
+               //in the thread of the keyboard
+               void loadTexture (string path) {
+                       try {
+                               Console.WriteLine ($"Loading:{path}");
+                               if (path.EndsWith ("ktx", StringComparison.OrdinalIgnoreCase))
+                                       nextTexture = KTX.KTX.Load (presentQueue, cmdPool, path,
+                                               VkImageUsageFlags.Sampled, imgProp, genMipMaps, tiling);
+                               else
+                                       nextTexture = Image.Load (dev, presentQueue, cmdPool, path, VkFormat.R8g8b8a8Unorm, imgProp, tiling, genMipMaps);
+                               updateViewRequested = true;
+                       } catch (Exception ex) {
+                               Console.WriteLine (ex);
+                               nextTexture = null;
+                       }
+               }
+
+               //in the main vulkan thread
+               void updateTextureSet (){ 
+                       nextTexture.CreateView ();
+                       nextTexture.CreateSampler ();
+                       nextTexture.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descriptorSet, dsLayout.Bindings[1]);
+                       uboUpdate.Write (dev, nextTexture.Descriptor);
+
+                       texture?.Dispose ();
+                       texture = nextTexture;
+                       nextTexture = null;
+               }
+               void updateMatrices () { 
+                       matrices.projection = Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (60f), (float)swapChain.Width / (float)swapChain.Height, 0.1f, 256.0f);
+                       matrices.view = Matrix4x4.CreateTranslation (0, 0, -2.5f * zoom);
+                       matrices.model =
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotZ) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotY) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotX);
+
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+               }
+
+               public override void UpdateView () {
+                       if (nextTexture != null) {
+                               updateTextureSet ();
+                               buildCommandBuffers ();
+                       }else 
+                               updateMatrices ();
+
+                       updateViewRequested = false;
+               }
+
+               protected override void onMouseMove (double xPos, double yPos) {
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               rotY -= rotSpeed * (float)diffX;
+                               rotX += rotSpeed * (float)diffY;
+                       } else if (MouseButton[1]) {
+                               zoom += zoomSpeed * (float)diffY;
+                       }
+
+                       updateViewRequested = true;
+               }
+
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.Space:
+                                       currentImgIndex++;
+                                       if (currentImgIndex == imgPathes.Length)
+                                               currentImgIndex = 0;
+                                       loadTexture (imgPathes[currentImgIndex]);
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       break;
+                       }
+               }
+
+               protected override void OnResize () {
+
+                       updateMatrices ();
+
+                       if (frameBuffers!=null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                               null
+                                       } : new Image[] {
+                                               null,
+                                               null,
+                                               swapChain.images[i]
+                                       });     
+                       }
+
+                       buildCommandBuffers ();
+               }       
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+                                       pipeline.Dispose ();
+                                       dsLayout.Dispose ();
+                                       for (int i = 0; i < swapChain.ImageCount; i++)
+                                               frameBuffers[i].Dispose ();
+                                       descriptorPool.Dispose ();
+                                       texture?.Dispose ();
+                                       nextTexture?.Dispose ();
+                                       vbo.Dispose ();
+                                       ibo.Dispose ();
+                                       uboMats.Dispose ();
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/Textured/shaders/main.frag b/samples/Textured/shaders/main.frag
new file mode 100644 (file)
index 0000000..73dfd79
--- /dev/null
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (binding = 1) uniform sampler2D samplerColor;
+
+layout (location = 0) in vec2 inUV;
+
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+    outFragColor = texture(samplerColor, inUV);  
+}
\ No newline at end of file
diff --git a/samples/Textured/shaders/main.vert b/samples/Textured/shaders/main.vert
new file mode 100644 (file)
index 0000000..3b395bf
--- /dev/null
@@ -0,0 +1,28 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inColor;
+
+layout (binding = 0) uniform UBO 
+{
+       mat4 projectionMatrix;
+    mat4 viewMatrix;
+       mat4 modelMatrix;
+} ubo;
+
+layout (location = 0) out vec3 outColor;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+
+void main() 
+{
+       outColor = inColor;
+       gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0);
+}
diff --git a/samples/TexturedCube/TexturedCube.csproj b/samples/TexturedCube/TexturedCube.csproj
new file mode 100644 (file)
index 0000000..8235ef1
--- /dev/null
@@ -0,0 +1,7 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />     
+  <ItemGroup>
+    <ProjectReference Include="..\..\addons\VkvgPipeline\VkvgPipeline.csproj" />
+  </ItemGroup>
+</Project>
+
diff --git a/samples/TexturedCube/main.cs b/samples/TexturedCube/main.cs
new file mode 100644 (file)
index 0000000..21aef33
--- /dev/null
@@ -0,0 +1,330 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Glfw;
+using CVKL;
+using VK;
+using Buffer = CVKL.Buffer;
+
+namespace TextureCube {
+       class Program : VkWindow {
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = false;
+#endif
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               float rotSpeed = 0.01f, zoomSpeed = 0.01f;
+               float rotX, rotY, rotZ = 0f, zoom = 1f;
+
+               struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 view;
+               }
+
+               Matrices matrices;
+
+               HostBuffer                      uboMats;
+               GPUBuffer<float>        vbo;
+               DescriptorPool          descriptorPool;
+               DescriptorSetLayout dsLayout;
+               DescriptorSet           descriptorSet, dsVkvg;
+               GraphicPipeline         pipeline;
+               Framebuffer[]           frameBuffers;
+
+               Image texture;
+               Image nextTexture;
+
+               static float[] g_vertex_buffer_data = {
+                       -1.0f,-1.0f,-1.0f,    0.0f, 1.0f,  // -X side
+                       -1.0f,-1.0f, 1.0f,    1.0f, 1.0f,
+                       -1.0f, 1.0f, 1.0f,    1.0f, 0.0f,
+                       -1.0f, 1.0f, 1.0f,    1.0f, 0.0f,
+                       -1.0f, 1.0f,-1.0f,    0.0f, 0.0f,
+                       -1.0f,-1.0f,-1.0f,    0.0f, 1.0f,
+                                             
+                       -1.0f,-1.0f,-1.0f,    1.0f, 1.0f,  // -Z side
+                        1.0f, 1.0f,-1.0f,    0.0f, 0.0f,
+                        1.0f,-1.0f,-1.0f,    0.0f, 1.0f,
+                       -1.0f,-1.0f,-1.0f,    1.0f, 1.0f,
+                       -1.0f, 1.0f,-1.0f,    1.0f, 0.0f,
+                        1.0f, 1.0f,-1.0f,    0.0f, 0.0f,
+                                             
+                       -1.0f,-1.0f,-1.0f,    1.0f, 0.0f,  // -Y side
+                        1.0f,-1.0f,-1.0f,    1.0f, 1.0f,
+                        1.0f,-1.0f, 1.0f,    0.0f, 1.0f,
+                       -1.0f,-1.0f,-1.0f,    1.0f, 0.0f,
+                        1.0f,-1.0f, 1.0f,    0.0f, 1.0f,
+                       -1.0f,-1.0f, 1.0f,    0.0f, 0.0f,
+                                             
+                       -1.0f, 1.0f,-1.0f,    1.0f, 0.0f,  // +Y side
+                       -1.0f, 1.0f, 1.0f,    0.0f, 0.0f,
+                        1.0f, 1.0f, 1.0f,    0.0f, 1.0f,
+                       -1.0f, 1.0f,-1.0f,    1.0f, 0.0f,
+                        1.0f, 1.0f, 1.0f,    0.0f, 1.0f,
+                        1.0f, 1.0f,-1.0f,    1.0f, 1.0f,
+                                             
+                        1.0f, 1.0f,-1.0f,    1.0f, 0.0f,  // +X side
+                        1.0f, 1.0f, 1.0f,    0.0f, 0.0f,
+                        1.0f,-1.0f, 1.0f,    0.0f, 1.0f,
+                        1.0f,-1.0f, 1.0f,    0.0f, 1.0f,
+                        1.0f,-1.0f,-1.0f,    1.0f, 1.0f,
+                        1.0f, 1.0f,-1.0f,    1.0f, 0.0f,
+                                             
+                       -1.0f, 1.0f, 1.0f,    0.0f, 0.0f,  // +Z side
+                       -1.0f,-1.0f, 1.0f,    0.0f, 1.0f,
+                        1.0f, 1.0f, 1.0f,    1.0f, 0.0f,
+                       -1.0f,-1.0f, 1.0f,    0.0f, 1.0f,
+                        1.0f,-1.0f, 1.0f,    1.0f, 1.0f,
+                        1.0f, 1.0f, 1.0f,    1.0f, 0.0f,
+               };
+               int currentImgIndex = 4;
+               string[] imgPathes = {
+                       Utils.DataDirectory + "textures/uffizi_cube.ktx",
+                       Utils.DataDirectory + "textures/papermill.ktx",
+                       Utils.DataDirectory + "textures/cubemap_yokohama_bc3_unorm.ktx",
+                       Utils.DataDirectory + "textures/gcanyon_cube.ktx",
+                       Utils.DataDirectory + "textures/pisa_cube.ktx",
+               };
+
+               VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1;
+
+#if WITH_VKVG
+               VkvgPipeline.VkvgPipeline vkvgPipeline;
+
+               void vkvgDraw () {
+                       using (vkvg.Context ctx = vkvgPipeline.CreateContext()) {
+                               ctx.Operator = vkvg.Operator.Clear;
+                               ctx.Paint ();
+                               ctx.Operator = vkvg.Operator.Over;
+                               vkvgPipeline.DrawResources (ctx, (int)swapChain.Width, (int)swapChain.Height);
+                       }
+               }
+
+
+#endif
+
+
+
+               Program () : base () {
+                       vbo = new GPUBuffer<float> (presentQueue, cmdPool, VkBufferUsageFlags.VertexBuffer, g_vertex_buffer_data);
+
+                       descriptorPool = new DescriptorPool (dev, 2,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 2)
+                       );
+
+                       dsLayout = new DescriptorSetLayout (dev, 0,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex, VkDescriptorType.UniformBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples);
+
+                       cfg.Layout = new PipelineLayout (dev, dsLayout);
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), cfg.Samples);
+
+                       cfg.AddVertexBinding (0, 5 * sizeof (float));
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#TexturedCube.skybox.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#TexturedCube.skybox.frag.spv");
+
+                       pipeline = new GraphicPipeline (cfg);
+
+                       uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, matrices);
+                       uboMats.Map ();//permanent map
+
+                       descriptorSet = descriptorPool.Allocate (dsLayout);
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descriptorSet, dsLayout.Bindings[0]);
+                       uboUpdate.Write (dev, uboMats.Descriptor);
+
+                       loadTexture (imgPathes[currentImgIndex]);
+                       if (nextTexture != null)
+                               updateTextureSet ();
+
+
+#if WITH_VKVG
+                       dsVkvg = descriptorPool.Allocate (pipeline.Layout.DescriptorSetLayouts[0]);
+                       vkvgPipeline = new VkvgPipeline.VkvgPipeline (instance, dev, presentQueue, pipeline);
+               }
+
+               public override void Update () {
+                       vkvgDraw ();
+#endif
+               }
+
+               void buildCommandBuffers () {
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {                                                                
+                       cmds[i]?.Free ();
+                               cmds[i] = cmdPool.AllocateCommandBuffer ();
+                               cmds[i].Start ();
+
+                               recordDraw (cmds[i], frameBuffers[i]);
+
+                               cmds[i].End ();                          
+                       }
+               } 
+               void recordDraw (CommandBuffer cmd, Framebuffer fb) { 
+                       pipeline.RenderPass.Begin (cmd, fb);
+
+                       cmd.SetViewport (fb.Width, fb.Height);
+                       cmd.SetScissor (fb.Width, fb.Height);
+                       cmd.BindDescriptorSet (pipeline.Layout, descriptorSet);
+
+                       pipeline.Bind (cmd);
+
+                       cmd.BindVertexBuffer (vbo, 0);
+                       cmd.Draw (36);
+
+#if WITH_VKVG
+                       cmd.BindDescriptorSet (pipeline.Layout, dsVkvg);
+                       vkvgPipeline.RecordDraw (cmd);
+#endif
+
+                       pipeline.RenderPass.End (cmd);
+               }
+
+               //in the thread of the keyboard
+               void loadTexture (string path) {
+                       try {
+                               if (path.EndsWith ("ktx", StringComparison.OrdinalIgnoreCase))
+                                       nextTexture = KTX.KTX.Load (presentQueue, cmdPool, path,
+                                               VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal, true);
+                               else
+                                       nextTexture = Image.Load (dev, path);
+                               updateViewRequested = true;
+                       } catch (Exception ex) {
+                               Console.WriteLine (ex);
+                               nextTexture = null;
+                       }
+               }
+
+               //in the main vulkan thread
+               void updateTextureSet (){ 
+                       nextTexture.CreateView (VkImageViewType.Cube,VkImageAspectFlags.Color,6);
+                       nextTexture.CreateSampler ();
+
+                       nextTexture.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descriptorSet, dsLayout.Bindings[1]);                          
+                       uboUpdate.Write (dev, nextTexture.Descriptor);
+
+                       texture?.Dispose ();
+                       texture = nextTexture;
+                       nextTexture = null;
+               }
+               void updateMatrices () { 
+                       matrices.projection = Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (60f), (float)swapChain.Width / (float)swapChain.Height, 0.1f, 5.0f);
+                       matrices.view =
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotZ) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotY) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotX);
+
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+               }
+
+               protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) {
+                       base.configureEnabledFeatures (available_features, ref enabled_features);
+                       enabled_features.textureCompressionBC = available_features.textureCompressionBC;
+               }
+
+               public override void UpdateView () {
+                       if (nextTexture != null) {
+                               dev.WaitIdle ();
+                               updateTextureSet ();
+                               buildCommandBuffers ();
+                       }else 
+                               updateMatrices ();
+
+                       updateViewRequested = false;
+                       dev.WaitIdle ();
+               }
+
+               protected override void onMouseMove (double xPos, double yPos) {
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               rotY -= rotSpeed * (float)diffX;
+                               rotX += rotSpeed * (float)diffY;
+                       } else if (MouseButton[1]) {
+                               zoom += zoomSpeed * (float)diffY;
+                       }
+
+                       updateViewRequested = true;
+               }
+
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.Space:
+                                       currentImgIndex++;
+                                       if (currentImgIndex == imgPathes.Length)
+                                               currentImgIndex = 0;
+                                       loadTexture (imgPathes[currentImgIndex]);
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       break;
+                       }
+               }
+
+               protected override void OnResize () {
+                       dev.WaitIdle ();
+
+#if WITH_VKVG
+                       vkvgPipeline.Resize ((int)swapChain.Width, (int)swapChain.Height, new DescriptorSetWrites (dsVkvg, dsLayout.Bindings[1]));
+#endif
+
+                       updateMatrices ();
+
+                       if (frameBuffers!=null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                               null
+                                       } : new Image[] {
+                                               null,
+                                               null,
+                                               swapChain.images[i]
+                                       });     
+                       }
+
+                       buildCommandBuffers ();
+
+                       dev.WaitIdle ();
+               }
+                       
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+                                       pipeline.Dispose ();
+                                       for (int i = 0; i < swapChain.ImageCount; i++)
+                                               frameBuffers[i].Dispose ();
+                                       descriptorPool.Dispose ();
+                                       texture.Dispose ();
+                                       uboMats.Dispose ();
+                                       vbo.Dispose ();
+
+#if WITH_VKVG
+                                       vkvgPipeline.Dispose ();
+#endif
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/TexturedCube/shaders/FullScreenQuad.vert b/samples/TexturedCube/shaders/FullScreenQuad.vert
new file mode 100644 (file)
index 0000000..826720b
--- /dev/null
@@ -0,0 +1,10 @@
+#version 450
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+       outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+       gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
+       //gl_Position = vec4(outUV -1.0f, 0.0f, 1.0f);
+}
diff --git a/samples/TexturedCube/shaders/main.frag b/samples/TexturedCube/shaders/main.frag
new file mode 100644 (file)
index 0000000..73dfd79
--- /dev/null
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (binding = 1) uniform sampler2D samplerColor;
+
+layout (location = 0) in vec2 inUV;
+
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+    outFragColor = texture(samplerColor, inUV);  
+}
\ No newline at end of file
diff --git a/samples/TexturedCube/shaders/main.vert b/samples/TexturedCube/shaders/main.vert
new file mode 100644 (file)
index 0000000..3b395bf
--- /dev/null
@@ -0,0 +1,28 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inColor;
+
+layout (binding = 0) uniform UBO 
+{
+       mat4 projectionMatrix;
+    mat4 viewMatrix;
+       mat4 modelMatrix;
+} ubo;
+
+layout (location = 0) out vec3 outColor;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+
+void main() 
+{
+       outColor = inColor;
+       gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0);
+}
diff --git a/samples/TexturedCube/shaders/simpletexture.frag b/samples/TexturedCube/shaders/simpletexture.frag
new file mode 100644 (file)
index 0000000..cee63cb
--- /dev/null
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set = 0, binding = 1) uniform sampler2D samplerColor;
+
+layout (location = 0) in       vec2 inUV;
+
+layout (location = 0) out      vec4 outFragColor;
+
+void main()
+{
+       outFragColor = texture(samplerColor, inUV);
+}
diff --git a/samples/TexturedCube/shaders/skybox.frag b/samples/TexturedCube/shaders/skybox.frag
new file mode 100644 (file)
index 0000000..bdd272d
--- /dev/null
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (binding = 1) uniform samplerCube skybox;
+
+layout (location = 0) in vec3 TexCoords;
+
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+    outFragColor = textureLod(skybox, TexCoords,0);
+}
diff --git a/samples/TexturedCube/shaders/skybox.vert b/samples/TexturedCube/shaders/skybox.vert
new file mode 100644 (file)
index 0000000..2d6a6b6
--- /dev/null
@@ -0,0 +1,26 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec2 inUV;
+
+layout (location = 0) out vec3 outUVW;
+
+layout (binding = 0) uniform UBO 
+{
+    mat4 projection;
+    mat4 view;    
+} ubo;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+void main()
+{
+    outUVW = inPos;
+    gl_Position = ubo.projection * ubo.view * vec4(inPos, 1.0);
+}
diff --git a/samples/Triangle/Triangle.csproj b/samples/Triangle/Triangle.csproj
new file mode 100644 (file)
index 0000000..dbb4bc8
--- /dev/null
@@ -0,0 +1,6 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />
+  <PropertyGroup>
+    <IncludeDefaultNoneItems>false</IncludeDefaultNoneItems>
+  </PropertyGroup>    
+</Project>
diff --git a/samples/Triangle/main.cs b/samples/Triangle/main.cs
new file mode 100644 (file)
index 0000000..75c20ed
--- /dev/null
@@ -0,0 +1,184 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Numerics;
+using System.Runtime.InteropServices;
+using CVKL;
+using VK;
+
+namespace Triangle {
+       class Program : VkWindow {
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = false;
+#endif
+
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               float rotSpeed = 0.01f, zoomSpeed = 0.01f;
+               float rotX, rotY, rotZ = 0f, zoom = 1f;
+
+               struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 view;
+                       public Matrix4x4 model;
+               }
+               struct Vertex {
+                       Vector3 position;
+                       Vector3 color;
+
+                       public Vertex (float x, float y, float z, float r, float g, float b) {
+                               position = new Vector3 (x, y, z);
+                               color = new Vector3 (r, g, b);
+                       }
+               }
+
+               Matrices matrices;
+
+               HostBuffer ibo;
+               HostBuffer vbo;
+               HostBuffer uboMats;
+
+               DescriptorPool descriptorPool;
+               DescriptorSetLayout dsLayout;
+               DescriptorSet descriptorSet;
+
+               Framebuffer[] frameBuffers;
+               GraphicPipeline pipeline;
+
+               Vertex[] vertices = {
+                       new Vertex (-1.0f, -1.0f, 0.0f ,  1.0f, 0.0f, 0.0f),
+                       new Vertex ( 1.0f, -1.0f, 0.0f ,  0.0f, 1.0f, 0.0f),
+                       new Vertex ( 0.0f,  1.0f, 0.0f ,  0.0f, 0.0f, 1.0f),
+               };
+               ushort[] indices = new ushort[] { 0, 1, 2 };
+
+               Program () : base () {
+                       vbo = new HostBuffer<Vertex> (dev, VkBufferUsageFlags.VertexBuffer, vertices);
+                       ibo = new HostBuffer<ushort> (dev, VkBufferUsageFlags.IndexBuffer, indices);
+                       uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, matrices);
+
+                       descriptorPool = new DescriptorPool (dev, 1, new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer));
+                       dsLayout = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer));
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1);
+
+                       cfg.Layout = new PipelineLayout (dev, dsLayout);
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), cfg.Samples);
+                       cfg.AddVertexBinding<Vertex> (0);
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#Triangle.main.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#Triangle.main.frag.spv");
+
+                       pipeline = new GraphicPipeline (cfg);
+
+                       //note that descriptor set is allocated after the pipeline creation that use this layout, layout is activated
+                       //automaticaly on pipeline creation, and will be disposed automatically when no longuer in use.
+                       descriptorSet = descriptorPool.Allocate (dsLayout);
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descriptorSet, dsLayout);
+                       uboUpdate.Write (dev, uboMats.Descriptor);
+
+                       uboMats.Map ();
+               }
+
+               public override void UpdateView () {
+                       matrices.projection = Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (45f),
+                               (float)swapChain.Width / (float)swapChain.Height, 0.1f, 256.0f) * Camera.VKProjectionCorrection;
+                       matrices.view = 
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotZ) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotY) *
+                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotX) *
+                               Matrix4x4.CreateTranslation (0, 0, -3f * zoom);
+                       matrices.model = Matrix4x4.Identity;
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+                       updateViewRequested = false;
+               }
+
+               protected override void onMouseMove (double xPos, double yPos) {
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               rotY -= rotSpeed * (float)diffX;
+                               rotX += rotSpeed * (float)diffY;
+                       } else if (MouseButton[1]) {
+                               zoom += zoomSpeed * (float)diffY;
+                       }
+                       updateViewRequested = true;
+               }
+               void buildCommandBuffers() {
+                       cmdPool.Reset (VkCommandPoolResetFlags.ReleaseResources);
+                       cmds = cmdPool.AllocateCommandBuffer (swapChain.ImageCount);
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               Framebuffer fb = frameBuffers[i];
+                               cmds[i].Start ();
+
+                               pipeline.RenderPass.Begin (cmds[i], fb);
+
+                               cmds[i].SetViewport (swapChain.Width, swapChain.Height);
+                               cmds[i].SetScissor (swapChain.Width, swapChain.Height);
+
+                               cmds[i].BindDescriptorSet (pipeline.Layout, descriptorSet);
+
+                               cmds[i].BindPipeline (pipeline);
+
+                               cmds[i].BindVertexBuffer (vbo);
+                               cmds[i].BindIndexBuffer (ibo, VkIndexType.Uint16);
+                               cmds[i].DrawIndexed ((uint)indices.Length);
+
+                               pipeline.RenderPass.End (cmds[i]);
+
+                               cmds[i].End ();
+                       }
+               }
+
+               protected override void OnResize () {
+                       dev.WaitIdle ();
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) 
+                               frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                               null
+                                       } : new Image[] {
+                                               null,
+                                               null,
+                                               swapChain.images[i]
+                                       });
+
+                       buildCommandBuffers ();
+
+                       dev.WaitIdle ();
+               }
+
+               protected override void Dispose (bool disposing) {              
+                       dev.WaitIdle ();
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       pipeline.Dispose ();
+                                       for (int i = 0; i < swapChain.ImageCount; i++)
+                                               frameBuffers[i].Dispose ();
+                                       descriptorPool.Dispose ();
+                                       vbo.Dispose ();
+                                       ibo.Dispose ();
+                                       uboMats.Dispose ();
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/Triangle/shaders/main.frag b/samples/Triangle/shaders/main.frag
new file mode 100644 (file)
index 0000000..5ff09f5
--- /dev/null
@@ -0,0 +1,9 @@
+#include <preamble.inc>
+
+layout (location = 0) in vec3 inColor;
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+    outFragColor = vec4(inColor, 1.0);
+}
\ No newline at end of file
diff --git a/samples/Triangle/shaders/main.vert b/samples/Triangle/shaders/main.vert
new file mode 100644 (file)
index 0000000..542d015
--- /dev/null
@@ -0,0 +1,30 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inColor;
+
+layout (binding = 0) uniform UBO 
+{
+       mat4 projectionMatrix;
+    mat4 viewMatrix;
+       mat4 modelMatrix;
+} ubo;
+
+layout (location = 0) out vec3 outColor;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+
+void main() 
+{
+       outColor = inColor;
+       gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0);
+       //gl_Position.y = -gl_Position.y;
+       //gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0;
+}
diff --git a/samples/common/CrowWin.cs b/samples/common/CrowWin.cs
new file mode 100644 (file)
index 0000000..4eb8730
--- /dev/null
@@ -0,0 +1,254 @@
+using System;
+using Glfw;
+using VK;
+using System.Threading;
+
+namespace Crow {
+       public class CrowWin : CVKL.VkWindow, IValueChange {
+               #region IValueChange implementation
+               public event EventHandler<ValueChangeEventArgs> ValueChanged;
+               public virtual void NotifyValueChanged (string MemberName, object _value) {
+                       if (ValueChanged != null)
+                               ValueChanged.Invoke (this, new ValueChangeEventArgs (MemberName, _value));
+               }
+               #endregion
+
+               CVKL.DescriptorPool descriptorPool;
+               CVKL.DescriptorSetLayout descLayout;
+               CVKL.DescriptorSet dsCrow;
+
+               CVKL.GraphicPipeline uiPipeline;
+               CVKL.Framebuffer[] uiFrameBuffers;
+
+               protected Interface crow;
+               protected vkvg.Device vkvgDev;
+               protected CVKL.Image uiImage;
+               protected bool isRunning;
+
+               protected CrowWin (string name = "CrowWin", uint _width = 1024, uint _height = 768, bool vSync = false) :
+                       base (name, _width, _height, vSync) {
+
+                       Thread crowThread = new Thread (crow_thread_func);
+                       crowThread.IsBackground = true;
+                       crowThread.Start ();
+
+                       while (crow == null)
+                               Thread.Sleep (2);
+
+                       initUISurface ();
+
+                       initUIPipeline ();
+               }
+
+               protected override void render () {
+                       int idx = swapChain.GetNextImage ();
+
+                       if (idx < 0) {
+                               OnResize ();
+                               return;
+                       }
+
+                       lock (crow.RenderMutex) {
+                               presentQueue.Submit (cmds[idx], swapChain.presentComplete, drawComplete[idx]);
+                               presentQueue.Present (swapChain, drawComplete[idx]);
+                               presentQueue.WaitIdle ();
+                       }
+                       Thread.Sleep (1);
+               }
+
+               void initUIPipeline (VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1) {
+                       descriptorPool = new CVKL.DescriptorPool (dev, 1, new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler));
+                       descLayout = new CVKL.DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       CVKL.GraphicPipelineConfig cfg = CVKL.GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples, false);
+                       cfg.Layout = new CVKL.PipelineLayout (dev, descLayout);
+                       cfg.RenderPass = new CVKL.RenderPass (dev, swapChain.ColorFormat, samples);
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.FullScreenQuad.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.simpletexture.frag.spv");
+
+                       cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true);
+
+                       uiPipeline = new CVKL.GraphicPipeline (cfg);
+
+                       dsCrow = descriptorPool.Allocate (descLayout);
+               }
+               void initUISurface () {
+                       lock (crow.UpdateMutex) {
+                               uiImage?.Dispose ();
+                               uiImage = new CVKL.Image (dev, new VkImage ((ulong)crow.surf.VkImage.ToInt64 ()), VkFormat.B8g8r8a8Unorm,
+                                       VkImageUsageFlags.Sampled, swapChain.Width, swapChain.Height);
+                               uiImage.SetName ("uiImage");
+                               uiImage.CreateView (VkImageViewType.ImageView2D, VkImageAspectFlags.Color);
+                               uiImage.CreateSampler (VkFilter.Nearest, VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder);
+                               uiImage.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                       }
+               }
+
+               void crow_thread_func () {
+                       vkvgDev = new vkvg.Device (instance.Handle, phy.Handle, dev.Handle, presentQueue.qFamIndex,
+                               vkvg.SampleCount.Sample_4, presentQueue.index);
+
+                       crow = new Interface (vkvgDev, (int)swapChain.Width, (int)swapChain.Height);
+
+                       isRunning = true;       
+                       while (isRunning) {
+                               crow.Update ();
+                               Thread.Sleep (2);
+                       }
+
+                       dev.WaitIdle ();
+                       crow.Dispose ();
+                       vkvgDev.Dispose ();
+                       crow = null;
+               }
+
+               protected void loadWindow (string path, object dataSource = null) {
+                       try {
+                               Widget w = crow.FindByName (path);
+                               if (w != null) {
+                                       crow.PutOnTop (w);
+                                       return;
+                               }
+                               w = crow.Load (path);
+                               w.Name = path;
+                               w.DataSource = dataSource;
+
+                       } catch (Exception ex) {
+                               System.Diagnostics.Debug.WriteLine (ex.ToString ());
+                       }
+               }
+               protected virtual void recordDraw (CVKL.CommandBuffer cmd, int imageIndex) { }
+
+               void buildCommandBuffers () {
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               cmds[i]?.Free ();
+                               cmds[i] = cmdPool.AllocateAndStart ();
+
+                               CVKL.CommandBuffer cmd = cmds[i];
+
+                               recordDraw (cmd, i);
+
+                               uiPipeline.RenderPass.Begin (cmd, uiFrameBuffers[i]);
+
+                               uiPipeline.Bind (cmd);
+                               cmd.BindDescriptorSet (uiPipeline.Layout, dsCrow);
+
+                               uiImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ColorAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal,
+                                       VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader);
+
+                               cmd.Draw (3, 1, 0, 0);
+
+                               uiImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.ColorAttachmentOptimal,
+                                       VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.BottomOfPipe);
+                                       
+                               uiPipeline.RenderPass.End (cmd);
+
+                               cmds[i].End ();
+                       }
+               }
+
+               /// <summary>
+               /// rebuild command buffers if needed
+               /// </summary>
+               public override void Update () {
+                       if (rebuildBuffers) {
+                               buildCommandBuffers ();
+                               rebuildBuffers = false;
+                       }
+               }
+
+               protected override void OnResize () {
+                       dev.WaitIdle ();
+
+                       crow.ProcessResize (new Rectangle (0, 0, (int)swapChain.Width, (int)swapChain.Height));
+
+                       initUISurface ();
+
+                       CVKL.DescriptorSetWrites uboUpdate = new CVKL.DescriptorSetWrites (dsCrow, descLayout);
+                       uboUpdate.Write (dev, uiImage.Descriptor);
+
+                       if (uiFrameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       uiFrameBuffers[i]?.Dispose ();
+
+                       uiFrameBuffers = new CVKL.Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               uiFrameBuffers[i] = new CVKL.Framebuffer (uiPipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (uiPipeline.Samples == VkSampleCountFlags.SampleCount1) ? new CVKL.Image[] {
+                                               swapChain.images[i],
+                                       } : new CVKL.Image[] {
+                                               null,
+                                               swapChain.images[i]
+                                       });
+                               uiFrameBuffers[i].SetName ("ui FB " + i);
+                       }
+
+                       buildCommandBuffers ();
+                       dev.WaitIdle ();
+               }
+
+               #region Mouse and keyboard
+               protected override void onScroll (double xOffset, double yOffset) {
+                       if (KeyModifiers.HasFlag (Modifier.Shift))
+                               crow.ProcessMouseWheelChanged ((float)xOffset);
+                       else
+                               crow.ProcessMouseWheelChanged ((float)yOffset);
+               }
+               protected override void onMouseMove (double xPos, double yPos) {
+                       if (crow.ProcessMouseMove ((int)xPos, (int)yPos))
+                               return;
+                       base.onMouseMove (xPos, yPos);
+               }
+               protected override void onMouseButtonDown (Glfw.MouseButton button) {
+                       if (crow.ProcessMouseButtonDown ((MouseButton)button))
+                               return;
+                       base.onMouseButtonDown (button);
+               }
+               protected override void onMouseButtonUp (Glfw.MouseButton button) {
+                       if (crow.ProcessMouseButtonUp ((MouseButton)button))
+                               return;
+                       base.onMouseButtonUp (button);
+               }
+               protected override void onKeyDown (Glfw.Key key, int scanCode, Modifier modifiers) {
+                       if (crow.ProcessKeyDown ((Key)key))
+                               return;
+                       base.onKeyDown (key, scanCode, modifiers);
+               }
+               protected override void onKeyUp (Glfw.Key key, int scanCode, Modifier modifiers) {
+                       if (crow.ProcessKeyUp ((Key)key))
+                               return;
+               }
+               protected override void onChar (CodePoint cp) {
+                       if (crow.ProcessKeyPress (cp.ToChar ()))
+                               return;
+               }
+               #endregion
+
+               #region dispose
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+                                       isRunning = false;
+
+                                       for (int i = 0; i < swapChain.ImageCount; ++i)
+                                               uiFrameBuffers[i]?.Dispose ();
+
+                                       uiPipeline.Dispose ();
+                                       descLayout.Dispose ();
+                                       descriptorPool.Dispose ();
+
+                                       uiImage?.Dispose ();
+                                       while (crow != null)
+                                               Thread.Sleep (1);
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+               #endregion
+       }
+}
diff --git a/samples/common/shaders/preamble.inc b/samples/common/shaders/preamble.inc
new file mode 100644 (file)
index 0000000..da434f8
--- /dev/null
@@ -0,0 +1,4 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
diff --git a/samples/compute/compute.csproj b/samples/compute/compute.csproj
new file mode 100644 (file)
index 0000000..e1734ff
--- /dev/null
@@ -0,0 +1,10 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />
+  <PropertyGroup>    
+    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
+  </PropertyGroup>
+              
+  <ItemGroup>    
+    <Compile Include="delaunay.cs" /> 
+  </ItemGroup>     
+</Project>
\ No newline at end of file
diff --git a/samples/compute/delaunay.cs b/samples/compute/delaunay.cs
new file mode 100644 (file)
index 0000000..a01cd85
--- /dev/null
@@ -0,0 +1,329 @@
+using System;
+using Glfw;
+using VK;
+using CVKL;
+
+namespace delaunay {
+       class Program : VkWindow {
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = false;
+#endif
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               Framebuffer[] frameBuffers;
+               GraphicPipeline grPipeline;
+
+               Image imgResult;
+
+               Queue computeQ, transferQ;
+
+               GPUBuffer inBuff, outBuff;
+               HostBuffer<float> stagingDataBuff;
+               DescriptorPool dsPool;
+               DescriptorSetLayout dslCompute, dslImage;
+               DescriptorSet dsetPing, dsetPong, dsImage;
+
+               ComputePipeline plCompute, plNormalize;
+
+               DebugReport dbgReport;
+
+               const uint imgDim = 256;
+               uint zoom = 2;
+               int invocationCount = 8;
+
+               uint data_size => imgDim * imgDim * 4;
+
+               float[] datas;
+
+               uint seedCount;
+
+               void addSeed (uint x, uint y) {
+                       uint ptr = (y * imgDim + x) * 4;
+                       datas[ptr] = ++seedCount;//seedId
+                       datas[ptr + 1] = x;
+                       datas[ptr + 2] = y;
+                       datas[ptr + 3] = 1;
+
+               }
+
+
+               public Program () : base () {
+                       if (Instance.DEBUG_UTILS)
+                               dbgReport = new DebugReport (instance,
+                                       VkDebugReportFlagsEXT.ErrorEXT
+                                       | VkDebugReportFlagsEXT.DebugEXT
+                                       | VkDebugReportFlagsEXT.WarningEXT
+                                       | VkDebugReportFlagsEXT.PerformanceWarningEXT
+                               
+                               );
+                       imgResult = new Image (dev, VkFormat.R32g32b32a32Sfloat, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal,
+                               imgDim, imgDim);
+                       imgResult.CreateView ();
+                       imgResult.CreateSampler (VkFilter.Nearest, VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder);
+                       imgResult.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                       datas = new float[data_size];
+
+                       addSeed (imgDim / 2 - 1, imgDim / 2 - 1);
+
+
+                       stagingDataBuff = new HostBuffer<float> (dev, VkBufferUsageFlags.TransferSrc, datas);
+                       stagingDataBuff.Map ();
+
+                       inBuff = new GPUBuffer<float> (dev, VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferSrc | VkBufferUsageFlags.TransferDst, (int)data_size);
+                       outBuff = new GPUBuffer<float> (dev, VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferSrc, (int)data_size);
+
+                       dsPool = new DescriptorPool (dev, 3,
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorPoolSize (VkDescriptorType.StorageBuffer, 4));
+                       dslImage = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+                       dslCompute = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer)
+                       );
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1);
+
+                       cfg.Layout = new PipelineLayout (dev, dslImage);
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), VkSampleCountFlags.SampleCount1);
+                       cfg.RenderPass.ClearValues[0] = new VkClearValue { color = new VkClearColorValue (0.0f, 0.1f, 0.0f) };
+
+                       cfg.ResetShadersAndVerticesInfos ();
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#compute.FullScreenQuad.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#compute.simpletexture.frag.spv");
+
+                       cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true);
+
+                       grPipeline = new GraphicPipeline (cfg);
+
+                       plCompute = new ComputePipeline (
+                               new PipelineLayout (dev, new VkPushConstantRange (VkShaderStageFlags.Compute, 2 * sizeof (int)), dslCompute),
+                               "#compute.computeTest.comp.spv");
+                       plNormalize = new ComputePipeline (
+                               plCompute.Layout,
+                               "#compute.normalize.comp.spv");
+
+                       dsImage = dsPool.Allocate (dslImage);
+                       dsetPing = dsPool.Allocate (dslCompute);
+                       dsetPong = dsPool.Allocate (dslCompute);
+
+                       DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dsetPing, dslCompute);
+                       dsUpdate.Write (dev, inBuff.Descriptor, outBuff.Descriptor);
+                       dsUpdate.Write (dev, dsetPong, outBuff.Descriptor, inBuff.Descriptor);
+                       dsUpdate = new DescriptorSetWrites (dsImage, dslImage);
+                       dsUpdate.Write (dev, imgResult.Descriptor);
+
+                       UpdateFrequency = 5;
+               }
+
+               protected override void createQueues () {
+                       computeQ = new Queue (dev, VkQueueFlags.Compute);
+                       transferQ = new Queue (dev, VkQueueFlags.Transfer);
+
+                       base.createQueues ();
+               }
+
+               protected override void OnResize () {
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (grPipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (grPipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                               null
+                                       } : new Image[] {
+                                               null,
+                                               null,
+                                               swapChain.images[i]
+                                       });
+
+                               cmds[i] = cmdPool.AllocateCommandBuffer ();
+                               cmds[i].Start ();
+
+                               imgResult.SetLayout (cmds[i], VkImageAspectFlags.Color,
+                                       VkImageLayout.Undefined, VkImageLayout.ShaderReadOnlyOptimal,
+                                       VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.FragmentShader);
+
+                               grPipeline.RenderPass.Begin (cmds[i], frameBuffers[i]);
+
+                               int xPad = (int)swapChain.Width / 2 - (int)imgDim * (int)zoom / 2;
+                               int yPad = (int)swapChain.Height / 2- (int)imgDim * (int)zoom / 2;
+
+                               cmds[i].SetViewport (imgDim * zoom, imgDim * zoom, xPad, yPad);
+                               cmds[i].SetScissor (imgDim * zoom, imgDim * zoom, Math.Max (0, xPad), Math.Max (0, yPad));
+
+                               cmds[i].BindDescriptorSet (grPipeline.Layout, dsImage);
+                               cmds[i].BindPipeline (grPipeline);
+                               cmds[i].Draw (3, 1, 0, 0);
+
+                               grPipeline.RenderPass.End (cmds[i]);
+
+                               cmds[i].End ();
+                       }
+               }
+               bool pong;
+
+               public override void Update () {
+                       initGpuBuffers ();
+
+                       using (CommandPool cmdPoolCompute = new CommandPool (dev, computeQ.qFamIndex)) {
+
+                               CommandBuffer cmd = cmdPoolCompute.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               pong = false;
+                               uint stepSize = imgDim / 2;
+
+                               plCompute.Bind (cmd);
+                               cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, imgDim, sizeof(int));
+
+                               int pass = 0;
+                               while (stepSize > 0 && pass < invocationCount) {
+                                       cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, stepSize);
+
+                                       if (pong)
+                                               plCompute.BindDescriptorSet (cmd, dsetPong);
+                                       else
+                                               plCompute.BindDescriptorSet (cmd, dsetPing);
+
+                                       cmd.Dispatch (imgDim, imgDim);
+
+                                       VkMemoryBarrier memBar = VkMemoryBarrier.New ();
+                                       memBar.srcAccessMask = VkAccessFlags.ShaderWrite;
+                                       memBar.dstAccessMask = VkAccessFlags.ShaderRead;
+                                       Vk.vkCmdPipelineBarrier (cmd.Handle, VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.ComputeShader, VkDependencyFlags.ByRegion,
+                                               1, ref memBar, 0, IntPtr.Zero, 0, IntPtr.Zero);
+
+                                       pong = !pong;
+                                       stepSize /= 2;
+                                       pass++;
+                               }
+
+                               plNormalize.Bind (cmd);
+                               if (pong)
+                                       plNormalize.BindDescriptorSet (cmd, dsetPong);
+                               else
+                                       plNormalize.BindDescriptorSet (cmd, dsetPing);
+                               cmd.Dispatch (imgDim, imgDim);
+                               pong = !pong;
+
+                               cmd.End ();
+
+                               computeQ.Submit (cmd);
+                               computeQ.WaitIdle ();
+                       }
+
+                       printResults ();
+               }
+
+               protected override void onMouseButtonDown (MouseButton button) {
+                       int xPad = (int)swapChain.Width / 2 - (int)imgDim * (int)zoom / 2;
+                       int yPad = (int)swapChain.Height / 2 - (int)imgDim * (int)zoom / 2;
+
+                       int localX = (int)((lastMouseX - xPad) / zoom);
+                       int localY = (int)((lastMouseY - yPad) / zoom);
+
+                       if (localX < 0 || localY < 0 || localX >= imgDim || localY >= imgDim)
+                               base.onMouseButtonDown (button);
+                       else {
+                               addSeed ((uint)localX, (uint)localY);
+                               stagingDataBuff.Update (datas);
+                       }
+               }
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.Delete:
+                                       datas = new float[data_size];
+                                       stagingDataBuff.Update (datas);
+                                       seedCount = 0;
+                                       break;
+                               case Key.KeypadAdd:
+                                       invocationCount++;
+                                       break;
+                               case Key.KeypadSubtract:
+                                       if (invocationCount>0)
+                                               invocationCount--;
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       break;
+                       }
+                       Console.WriteLine ($"break after {invocationCount} step");
+               }
+
+               void printResults () {
+                       dev.WaitIdle ();
+                       using (CommandPool cmdPoolTransfer = new CommandPool (dev, transferQ.qFamIndex)) {
+
+                               CommandBuffer cmd = cmdPoolTransfer.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               imgResult.SetLayout (cmd, VkImageAspectFlags.Color,
+                                       VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.TransferDstOptimal,
+                                       VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.Transfer);
+
+                               if (pong)
+                                       outBuff.CopyTo (cmd, imgResult, VkImageLayout.ShaderReadOnlyOptimal);
+                               else
+                                       inBuff.CopyTo (cmd, imgResult, VkImageLayout.ShaderReadOnlyOptimal);
+
+                               cmd.End ();
+
+                               transferQ.Submit (cmd);
+                               transferQ.WaitIdle ();
+                       }
+               }
+
+               void initGpuBuffers () {
+                       using (CommandPool staggingCmdPool = new CommandPool (dev, transferQ.qFamIndex)) {
+                               CommandBuffer cmd = staggingCmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               stagingDataBuff.CopyTo (cmd, inBuff);
+
+                               transferQ.EndSubmitAndWait (cmd);
+                       }
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+
+                                       for (int i = 0; i < swapChain.ImageCount; ++i)
+                                               frameBuffers[i]?.Dispose ();
+
+                                       grPipeline.Dispose ();
+                                       plCompute.Dispose ();
+                                       plNormalize.Dispose ();
+
+                                       dslCompute.Dispose ();
+                                       dslImage.Dispose ();
+
+                                       dsPool.Dispose ();
+
+                                       inBuff.Dispose ();
+                                       outBuff.Dispose ();
+                                       stagingDataBuff.Dispose ();
+
+                                       imgResult.Dispose ();
+
+                                       dbgReport?.Dispose ();
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+
+
+       }
+}
diff --git a/samples/compute/main.cs b/samples/compute/main.cs
new file mode 100644 (file)
index 0000000..02f7069
--- /dev/null
@@ -0,0 +1,133 @@
+using System;
+using System.Runtime.InteropServices;
+using CVKL;
+using VK;
+using System.Linq;
+
+namespace SimpleCompute {
+       class Program : IDisposable {   
+               VkPhysicalDeviceFeatures enabledFeatures = default (VkPhysicalDeviceFeatures);
+               string[] enabledExtensions = { Ext.D.VK_KHR_swapchain };
+
+               Instance instance;
+               PhysicalDevice phy;
+               Device dev;
+               Queue computeQ;
+
+               HostBuffer inBuff, outBuff;
+               DescriptorPool dsPool;
+               DescriptorSetLayout dsLayout;
+               DescriptorSet dset;
+
+               ComputePipeline plCompute;
+
+               DebugReport dbgReport;
+
+               const uint data_size = 256;
+               int[] datas;
+
+               public Program () {
+                       instance = new Instance ();
+
+#if DEBUG
+                       dbgReport = new DebugReport (instance,
+                               VkDebugReportFlagsEXT.ErrorEXT
+                               | VkDebugReportFlagsEXT.DebugEXT
+                               | VkDebugReportFlagsEXT.WarningEXT
+                               | VkDebugReportFlagsEXT.PerformanceWarningEXT
+                       
+                       );
+#endif
+
+                       phy = instance.GetAvailablePhysicalDevice ().FirstOrDefault ();
+                       dev = new Device (phy);
+                       computeQ = new Queue (dev, VkQueueFlags.Compute);
+                       dev.Activate (enabledFeatures, enabledExtensions);
+
+                       datas = new int[data_size];
+                       Random rnd = new Random ();
+                       for (uint i = 0; i < data_size; i++) {
+                               datas[i] = rnd.Next ();
+                       }
+
+                       inBuff = new HostBuffer<int> (dev, VkBufferUsageFlags.StorageBuffer, datas);
+                       outBuff = new HostBuffer<int> (dev, VkBufferUsageFlags.StorageBuffer, data_size);
+
+                       dsPool = new DescriptorPool (dev, 1, new VkDescriptorPoolSize (VkDescriptorType.StorageBuffer, 2));
+                       dsLayout = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer)
+                       );
+
+                       plCompute = new ComputePipeline (new PipelineLayout (dev, dsLayout), "shaders/compute.comp.spv" );
+
+                       dset = dsPool.Allocate (dsLayout);
+                       DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dset, dsLayout);
+                       dsUpdate.Write (dev, inBuff.Descriptor, outBuff.Descriptor);
+               }
+
+
+
+               public void Run () {
+                       using (CommandPool cmdPool = new CommandPool (dev, computeQ.qFamIndex)) {
+
+                               CommandBuffer cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               plCompute.Bind (cmd);
+                               plCompute.BindDescriptorSet (cmd, dset);
+
+                               cmd.Dispatch (data_size * sizeof (int));
+
+                               cmd.End ();
+
+                               computeQ.Submit (cmd);
+                               computeQ.WaitIdle ();
+                       }
+
+                       printResults ();
+               }
+
+               void printResults () {
+                       int[] results = new int[data_size];
+
+                       outBuff.Map ();
+                       Marshal.Copy (outBuff.MappedData, results, 0, results.Length);
+
+                       Console.ForegroundColor = ConsoleColor.DarkBlue;
+                       Console.Write ("IN :");
+                       for (int i = 0; i < data_size; i++) {
+                               Console.Write ($" {datas[i]} ");
+                       }
+                       Console.WriteLine ();
+                       Console.Write ("OUT:");
+                       for (int i = 0; i < data_size; i++) {
+                               Console.Write ($" {results[i]} ");
+                       }
+                       Console.WriteLine ();
+                       outBuff.Unmap ();
+               }
+
+               public void Dispose () {
+                       dev.WaitIdle ();
+
+                       plCompute.Dispose ();
+                       dsLayout.Dispose ();
+                       dsPool.Dispose ();
+
+                       inBuff.Dispose ();
+                       outBuff.Dispose ();
+
+                       dev.Dispose ();
+
+#if DEBUG
+                       dbgReport.Dispose ();
+#endif
+                       instance.Dispose ();
+               }
+
+               static void Main (string[] args) {
+                       using (Program vke = new Program ())
+                               vke.Run ();
+               }
+       }
+}
diff --git a/samples/compute/shaders/FullScreenQuad.vert b/samples/compute/shaders/FullScreenQuad.vert
new file mode 100644 (file)
index 0000000..826720b
--- /dev/null
@@ -0,0 +1,10 @@
+#version 450
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+       outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+       gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
+       //gl_Position = vec4(outUV -1.0f, 0.0f, 1.0f);
+}
diff --git a/samples/compute/shaders/compute.comp b/samples/compute/shaders/compute.comp
new file mode 100644 (file)
index 0000000..10cfddd
--- /dev/null
@@ -0,0 +1,23 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(binding = 0) buffer buffIn {
+    int dataIn[];
+};
+layout(binding = 1) buffer buffOut {
+    int dataOut[];
+};
+
+//layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+void main()
+{
+       uint i = gl_GlobalInvocationID.x;
+    int d = dataIn[i];
+    
+    for (int j=0; j<8; j++)
+        d += 1;
+    dataOut[i] = d;
+}
diff --git a/samples/compute/shaders/computeTest.comp b/samples/compute/shaders/computeTest.comp
new file mode 100644 (file)
index 0000000..f24bee3
--- /dev/null
@@ -0,0 +1,80 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(binding = 0) buffer buffIn {
+    vec4 dataIn[];
+};
+layout(binding = 1) buffer buffOut {
+    vec4 dataOut[];
+};
+
+layout(set = 1, binding = 0) buffer VBO {
+    vec2 vertices[];
+};
+layout(set = 1, binding = 1) buffer IBO {
+    uint indices[];
+};
+
+
+layout(push_constant) uniform PushConsts {
+    int iStepSize;
+    int imgDim;
+    int pointCount;
+};
+
+const ivec2 dirs[] = ivec2[8] (
+    ivec2( 1, 0),
+    ivec2( 1, 1),
+    ivec2( 0, 1),
+    ivec2(-1, 1),
+    ivec2(-1, 0),
+    ivec2(-1,-1),
+    ivec2( 0,-1),
+    ivec2( 1,-1)
+);
+
+layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+
+vec4 getPixel (ivec2 uv) {
+    return dataIn[uv.y * imgDim + uv.x];
+}
+void setPixel (ivec2 uv, vec4 pix) {
+    dataOut[uv.y * imgDim + uv.x] = pix;
+}
+
+void main()
+{
+       ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);    
+    vec4 d = getPixel (uv);
+    
+    ivec2 thisSeedPos;                      
+    if (d.r > 0.0)
+        thisSeedPos = ivec2(int(d.g), int(d.b));
+    else//no seed in current pixel
+        thisSeedPos = ivec2(-1);        
+    
+    for (int j=0; j<8; j++){
+        ivec2 otherUV = uv + iStepSize * dirs[j];
+        if (otherUV.x < 0 || otherUV.y < 0 || otherUV.x >= imgDim || otherUV.y >= imgDim)
+            continue;
+            
+        vec4 other = getPixel (otherUV);        
+        
+        if (other.r > 0.0) {//seed in other
+            ivec2 otherSeedPos = ivec2(int(other.g), int(other.b));
+            if (thisSeedPos.x < 0) {//replace current
+                d = other;
+                thisSeedPos = otherSeedPos;
+            }else if (distance (uv, thisSeedPos) > distance (uv, otherSeedPos)) {
+                d = other;
+                thisSeedPos = otherSeedPos;
+            }        
+        }
+    }
+        
+    setPixel (uv, d);
+}
diff --git a/samples/compute/shaders/delaunay.comp b/samples/compute/shaders/delaunay.comp
new file mode 100644 (file)
index 0000000..410dcee
--- /dev/null
@@ -0,0 +1,70 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(binding = 0) buffer buffIn {
+    vec4 dataIn[];
+};
+layout(binding = 1) buffer buffOut {
+    vec4 dataOut[];
+};
+
+layout(push_constant) uniform PushConsts {
+    int iStepSize;
+    int imgDim;
+};
+
+const ivec2 dirs[] = ivec2[8] (
+    ivec2( 1, 0),
+    ivec2( 1, 1),
+    ivec2( 0, 1),
+    ivec2(-1, 1),
+    ivec2(-1, 0),
+    ivec2(-1,-1),
+    ivec2( 0,-1),
+    ivec2( 1,-1)
+);
+
+layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+
+vec4 getPixel (ivec2 uv) {
+    return dataIn[uv.y * imgDim + uv.x];
+}
+void setPixel (ivec2 uv, vec4 pix) {
+    dataOut[uv.y * imgDim + uv.x] = pix;
+}
+
+void main()
+{
+    ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);    
+    vec4 d = getPixel (uv);
+    
+    ivec2 thisSeedPos;                      
+    if (d.r > 0.0)
+        thisSeedPos = ivec2(int(d.g), int(d.b));
+    else//no seed in current pixel
+        thisSeedPos = ivec2(-1);        
+    
+    for (int j=0; j<8; j++){
+        ivec2 otherUV = uv + iStepSize * dirs[j];
+        if (otherUV.x < 0 || otherUV.y < 0 || otherUV.x >= imgDim || otherUV.y >= imgDim)
+            continue;
+            
+        vec4 other = getPixel (otherUV);        
+        
+        if (other.r > 0.0) {//seed in other
+            ivec2 otherSeedPos = ivec2(int(other.g), int(other.b));
+            if (thisSeedPos.x < 0) {//replace current
+                d = other;
+                thisSeedPos = otherSeedPos;
+            }else if (distance (uv, thisSeedPos) > distance (uv, otherSeedPos)) {
+                d = other;
+                thisSeedPos = otherSeedPos;
+            }        
+        }
+    }
+        
+    setPixel (uv, d);
+}
diff --git a/samples/compute/shaders/init.comp b/samples/compute/shaders/init.comp
new file mode 100644 (file)
index 0000000..fc615ee
--- /dev/null
@@ -0,0 +1,36 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(binding = 0) buffer buffIn {
+    vec4 dataIn[];
+};
+layout(binding = 1) buffer buffOut {
+    vec4 dataOut[];
+};
+
+layout(set = 1, binding = 0) buffer VBO {
+    vec2 vertices[];
+};
+layout(set = 1, binding = 1) buffer IBO {
+    uint indices[];
+};
+
+layout(push_constant) uniform PushConsts {
+    int iStepSize;
+    int imgDim;
+    int pointCount;
+};
+
+
+layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+void main()
+{
+    vec2 v = vertices[gl_GlobalInvocationID.x];
+    ivec2 uv = ivec2(int(v.x), int(v.y));
+    dataIn[uv.y * imgDim + uv.x] = vec4 (gl_GlobalInvocationID.x + 1, uv.x, uv.y, 1.0);
+}
+
diff --git a/samples/compute/shaders/mandelbrot.comp b/samples/compute/shaders/mandelbrot.comp
new file mode 100644 (file)
index 0000000..a03f4a0
--- /dev/null
@@ -0,0 +1,56 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+
+#define WIDTH 3200
+#define HEIGHT 2400
+#define WORKGROUP_SIZE 32
+layout (local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1 ) in;
+
+struct Pixel{
+  vec4 value;
+};
+
+layout(std140, binding = 0) buffer buf
+{
+   Pixel imageData[];
+};
+
+void main() {
+
+  /*
+  In order to fit the work into workgroups, some unnecessary threads are launched.
+  We terminate those threads here. 
+  */
+  if(gl_GlobalInvocationID.x >= WIDTH || gl_GlobalInvocationID.y >= HEIGHT)
+    return;
+
+  float x = float(gl_GlobalInvocationID.x) / float(WIDTH);
+  float y = float(gl_GlobalInvocationID.y) / float(HEIGHT);
+
+  /*
+  What follows is code for rendering the mandelbrot set. 
+  */
+  vec2 uv = vec2(x,y);
+  float n = 0.0;
+  vec2 c = vec2(-.445, 0.0) +  (uv - 0.5)*(2.0+ 1.7*0.2  ), 
+  z = vec2(0.0);
+  const int M =128;
+  for (int i = 0; i<M; i++)
+  {
+    z = vec2(z.x*z.x - z.y*z.y, 2.*z.x*z.y) + c;
+    if (dot(z, z) > 2) break;
+    n++;
+  }
+          
+  // we use a simple cosine palette to determine color:
+  // http://iquilezles.org/www/articles/palettes/palettes.htm         
+  float t = float(n) / float(M);
+  vec3 d = vec3(0.3, 0.3 ,0.5);
+  vec3 e = vec3(-0.2, -0.3 ,-0.5);
+  vec3 f = vec3(2.1, 2.0, 3.0);
+  vec3 g = vec3(0.0, 0.1, 0.0);
+  vec4 color = vec4( d + e*cos( 6.28318*(f*t+g) ) ,1.0);
+          
+  // store the rendered mandelbrot set into a storage buffer:
+  imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x].value = color;
+}
\ No newline at end of file
diff --git a/samples/compute/shaders/normalize.comp b/samples/compute/shaders/normalize.comp
new file mode 100644 (file)
index 0000000..2bc6aa6
--- /dev/null
@@ -0,0 +1,73 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(binding = 0) buffer buffIn {
+    vec4 dataIn[];
+};
+layout(binding = 1) buffer buffOut {
+    vec4 dataOut[];
+};
+
+layout(set = 1, binding = 0) buffer VBO {
+    vec2 vertices[];
+};
+layout(set = 1, binding = 1) buffer IBO {
+    uint indices[];
+};
+
+layout(push_constant) uniform PushConsts {
+    int iStepSize;
+    int imgDim;
+    int pointCount;
+};
+
+layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+vec4 getPixel (ivec2 uv) {
+    return dataIn[uv.y * imgDim + uv.x];
+}
+void setPixel (ivec2 uv, vec4 pix) {
+    dataOut[uv.y * imgDim + uv.x] = pix;
+}
+
+const vec4[] colors = vec4[](
+    vec4(1,0,0,1),
+    vec4(0,1,0,1),
+    vec4(0,0,1,1),
+    vec4(1,0,1,1),
+    vec4(0,1,1,1),
+    vec4(1,1,0,1),
+    vec4(0.5,0,0,1),
+    vec4(0,0.5,0,1),
+    vec4(0,0,0.5,1),
+    vec4(0.5,0,0.5,1),
+    vec4(0,0.5,0.5,1),
+    vec4(0.5,0.5,0,1),
+    vec4(0,0.5,0.5,1),
+    vec4(0.1,0.9,0.2,1),
+    vec4(0.3,0.7,0.4,1),
+    vec4(0.5,0.5,0.6,1),
+    vec4(0.7,0.3,0.8,1),
+    vec4(0.9,0.1,0.0,1)
+    );
+
+void main()
+{
+    ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);    
+    vec4 d = getPixel (uv);
+    
+    if (d.r > 0.0) {
+        if (int(d.g) == uv.x && int(d.b) == uv.y)
+            d.a = 1.0;
+        else
+            d.a = 0.4;
+        
+        d.rgb = colors [int(d.r)].rgb;
+    }
+    
+    setPixel (uv, d);
+}
+
diff --git a/samples/compute/shaders/simpletexture.frag b/samples/compute/shaders/simpletexture.frag
new file mode 100644 (file)
index 0000000..a081876
--- /dev/null
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set = 0, binding = 0) uniform sampler2D samplerColor;
+
+layout (location = 0) in       vec2 inUV;
+
+layout (location = 0) out      vec4 outFragColor;
+
+void main()
+{    
+       outFragColor = texture(samplerColor, inUV);
+}
diff --git a/samples/compute/shaders/test.comp b/samples/compute/shaders/test.comp
new file mode 100644 (file)
index 0000000..8ab1ec7
--- /dev/null
@@ -0,0 +1,21 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(binding = 0) buffer readonly buff {
+    vec4 data[];
+};
+layout(binding = 1) buffer writeonly buffOut {
+    vec4 dataOut[];
+};
+layout(push_constant) uniform PushConsts {
+    int iStepSize;
+    int imgDim;
+};
+
+
+void main()
+{
+       ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);  
+    
+}
diff --git a/samples/compute/shaders/triangle2.frag b/samples/compute/shaders/triangle2.frag
new file mode 100644 (file)
index 0000000..3f905f7
--- /dev/null
@@ -0,0 +1,13 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+//layout (location = 0) in vec3 inColor;
+
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+  outFragColor = vec4(1.0, 1.0, 1.0, 1.0);
+}
\ No newline at end of file
diff --git a/samples/compute/shaders/triangle2.vert b/samples/compute/shaders/triangle2.vert
new file mode 100644 (file)
index 0000000..2922694
--- /dev/null
@@ -0,0 +1,24 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec2 inPos;
+
+layout(push_constant) uniform PushConsts {
+    int imgDim;
+    int xPad;
+    int yPad;
+    int zoom;
+};
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+
+void main() 
+{      
+       gl_Position = vec4(inPos.xy * vec2(2) / vec2(imgDim) - vec2(1), 0.0, 1.0);
+}
diff --git a/samples/compute/test.cs b/samples/compute/test.cs
new file mode 100644 (file)
index 0000000..80e66d4
--- /dev/null
@@ -0,0 +1,133 @@
+using System;
+using System.Runtime.InteropServices;
+using CVKL;
+using VK;
+using System.Linq;
+
+namespace test {
+       class Program : IDisposable {   
+               VkPhysicalDeviceFeatures enabledFeatures = default (VkPhysicalDeviceFeatures);
+               string[] enabledExtensions = { Ext.D.VK_KHR_swapchain };
+
+               static void Main (string[] args) {
+                       Instance.VALIDATION = true;
+                       //Instance.DEBUG_UTILS = true;
+
+                       using (Program vke = new Program ())
+                               vke.Run ();
+               }
+
+               Instance instance;
+               PhysicalDevice phy;
+               Device dev;
+               Queue computeQ;
+
+               HostBuffer inBuff, outBuff;
+               DescriptorPool dsPool;
+               DescriptorSetLayout dsLayoutCompute;
+               DescriptorSet dsetPing, dsetPong;
+
+               ComputePipeline plCompute;
+
+               DebugReport dbgReport;
+
+               const uint imgDim = 256;
+
+               uint data_size => imgDim * imgDim * 4;
+
+               float[] datas;
+
+               public Program () {
+                       instance = new Instance ();
+
+#if DEBUG
+                       /*dbgReport = new DebugReport (instance,
+                               VkDebugReportFlagsEXT.ErrorEXT
+                               | VkDebugReportFlagsEXT.DebugEXT
+                               | VkDebugReportFlagsEXT.WarningEXT
+                               | VkDebugReportFlagsEXT.PerformanceWarningEXT
+                       
+                       );*/
+#endif
+
+                       phy = instance.GetAvailablePhysicalDevice ().FirstOrDefault ();
+                       dev = new Device (phy);
+                       computeQ = new Queue (dev, VkQueueFlags.Compute);
+                       dev.Activate (enabledFeatures, enabledExtensions);
+
+                       datas = new float[data_size];
+                       Random rnd = new Random ();
+                       for (uint i = 0; i < data_size; i++) {
+                               datas[i] = (float)rnd.NextDouble ();
+                       }
+
+                       inBuff = new HostBuffer<float> (dev, VkBufferUsageFlags.StorageBuffer, datas);
+                       outBuff = new HostBuffer<float> (dev, VkBufferUsageFlags.StorageBuffer, data_size);
+
+                       dsPool = new DescriptorPool (dev, 2, new VkDescriptorPoolSize (VkDescriptorType.StorageBuffer, 4));
+                       dsLayoutCompute = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer)
+                       );
+                               
+                       plCompute = new ComputePipeline (
+                               new PipelineLayout (dev, new VkPushConstantRange (VkShaderStageFlags.Compute, sizeof (int)), dsLayoutCompute),
+                               "#compute.computeTest.comp.spv" );
+
+                       dsetPing = dsPool.Allocate (dsLayoutCompute);
+                       dsetPong = dsPool.Allocate (dsLayoutCompute);
+                       DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dsetPing, dsLayoutCompute);
+                       dsUpdate.Write (dev, inBuff.Descriptor, outBuff.Descriptor);
+
+                       dsUpdate.Write (dev, dsetPong, outBuff.Descriptor, inBuff.Descriptor);
+               }
+
+
+
+               public void Run () {
+                       using (CommandPool cmdPool = new CommandPool (dev, computeQ.qFamIndex)) {
+
+                               CommandBuffer cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               bool pong = false;
+
+                               plCompute.Bind (cmd);
+                               cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, imgDim);
+
+                               for (int i = 0; i < 4; i++) {
+                                       if (pong)
+                                               plCompute.BindDescriptorSet (cmd, dsetPong);
+                                       else
+                                               plCompute.BindDescriptorSet (cmd, dsetPing);
+
+                                       cmd.Dispatch (imgDim, imgDim);
+                               }
+
+                               cmd.End ();
+
+                               computeQ.Submit (cmd);
+                               computeQ.WaitIdle ();
+                       }
+
+               }
+
+
+               public void Dispose () {
+                       dev.WaitIdle ();
+
+                       plCompute.Dispose ();
+                       dsLayoutCompute.Dispose ();
+                       dsPool.Dispose ();
+
+                       inBuff.Dispose ();
+                       outBuff.Dispose ();
+
+                       dev.Dispose ();
+
+#if DEBUG
+                       dbgReport?.Dispose ();
+#endif
+                       instance.Dispose ();
+               }
+       }
+}
diff --git a/samples/compute/test2.cs b/samples/compute/test2.cs
new file mode 100644 (file)
index 0000000..689e7e8
--- /dev/null
@@ -0,0 +1,389 @@
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Glfw;
+using VK;
+using CVKL;
+
+namespace triangulation {
+       class Program : VkWindow {
+               static void Main (string[] args) {
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               Framebuffer[] frameBuffers;
+               GraphicPipeline grPipeline, trianglesPipeline;
+
+               Image imgResult;
+
+               Queue computeQ, transferQ;
+
+               GPUBuffer inBuff, outBuff;
+               HostBuffer<Vector2> staggingVBO;
+               DescriptorPool dsPool;
+               DescriptorSetLayout dslCompute, dslImage, dslVAO;
+               DescriptorSet dsPing, dsPong, dsImage, dsVAO;
+
+               ComputePipeline plCompute, plNormalize, plInit;
+
+               DebugReport dbgReport;
+
+
+               GPUBuffer<Vector2> vbo;
+               GPUBuffer<uint> ibo;
+
+               uint zoom = 2;
+
+               const int MAX_VERTICES = 128;
+               const uint IMG_DIM = 256;
+
+               int invocationCount = 8;
+
+               uint data_size => IMG_DIM * IMG_DIM * 4;
+
+               Vector2[] points = new Vector2[MAX_VERTICES];
+               uint pointCount;
+               bool clear = true;//if true, inBuff will be fill with zeros
+
+               bool pong;//ping-pong between buffers
+
+               void addPoint (uint x, uint y) {
+                       points[pointCount] = new Vector2 (x, y);
+                       pointCount++;
+                       staggingVBO.Update (points, pointCount * (ulong)Marshal.SizeOf<Vector2> ());
+
+
+               }
+               void clearPoints () {
+                       pointCount = 0;
+                       clear = true;
+               }
+
+               public Program () : base () {
+#if DEBUG
+                       dbgReport = new DebugReport (instance,
+                               VkDebugReportFlagsEXT.ErrorEXT
+                               | VkDebugReportFlagsEXT.DebugEXT
+                               | VkDebugReportFlagsEXT.WarningEXT
+                               | VkDebugReportFlagsEXT.PerformanceWarningEXT
+                       
+                       );
+#endif
+                       imgResult = new Image (dev, VkFormat.R32g32b32a32Sfloat, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal,
+                               IMG_DIM, IMG_DIM);
+                       imgResult.CreateView ();
+                       imgResult.CreateSampler (VkFilter.Nearest, VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder);
+                       imgResult.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+
+                       staggingVBO = new HostBuffer<Vector2> (dev, VkBufferUsageFlags.TransferSrc, MAX_VERTICES);
+                       staggingVBO.Map ();
+
+                       vbo = new GPUBuffer<Vector2> (dev, VkBufferUsageFlags.VertexBuffer | VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferDst, MAX_VERTICES);
+                       ibo = new GPUBuffer<uint> (dev, VkBufferUsageFlags.IndexBuffer | VkBufferUsageFlags.StorageBuffer, MAX_VERTICES * 3);
+
+                       inBuff = new GPUBuffer<float> (dev, VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferSrc | VkBufferUsageFlags.TransferDst, (int)data_size);
+                       outBuff = new GPUBuffer<float> (dev, VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferSrc | VkBufferUsageFlags.TransferDst, (int)data_size);
+
+                       dsPool = new DescriptorPool (dev, 4,
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorPoolSize (VkDescriptorType.StorageBuffer, 6));
+                       dslImage = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+                       dslCompute = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer)
+                       );
+                       dslVAO = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer)
+                       );
+
+                       plInit = new ComputePipeline (
+                               new PipelineLayout (dev, new VkPushConstantRange (VkShaderStageFlags.Compute, 3 * sizeof (int)), dslCompute, dslVAO),
+                               "shaders/init.comp.spv");
+                       plCompute = new ComputePipeline (plInit.Layout, "shaders/computeTest.comp.spv");
+                       plNormalize = new ComputePipeline (plInit.Layout, "shaders/normalize.comp.spv");
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1);
+
+                       cfg.Layout = new PipelineLayout (dev, dslImage);
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), VkSampleCountFlags.SampleCount1);
+                       cfg.RenderPass.ClearValues[0] = new VkClearValue { color = new VkClearColorValue (0.1f, 0.1f, 0.1f) };
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/FullScreenQuad.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/simpletexture.frag.spv");
+
+                       cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true);
+
+                       grPipeline = new GraphicPipeline (cfg);
+
+                       cfg.ResetShadersAndVerticesInfos ();
+                       cfg.Layout = new PipelineLayout (dev, new VkPushConstantRange (VkShaderStageFlags.Vertex, 4 * sizeof (int)));
+                       cfg.inputAssemblyState.topology = VkPrimitiveTopology.LineStrip;
+                       cfg.AddVertexBinding<Vector2> (0);
+                       cfg.SetVertexAttributes (0, VkFormat.R32g32Sfloat);
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/triangle.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/triangle.frag.spv");
+
+                       trianglesPipeline = new GraphicPipeline (cfg);
+
+                       dsImage = dsPool.Allocate (dslImage);
+                       dsPing = dsPool.Allocate (dslCompute);
+                       dsPong = dsPool.Allocate (dslCompute);
+                       dsVAO = dsPool.Allocate (dslCompute);
+
+
+                       DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dsPing, dslCompute);
+                       dsUpdate.Write (dev, inBuff.Descriptor, outBuff.Descriptor);
+                       dsUpdate.Write (dev, dsPong, outBuff.Descriptor, inBuff.Descriptor);
+                       dsUpdate = new DescriptorSetWrites (dsImage, dslImage);
+                       dsUpdate.Write (dev, imgResult.Descriptor);
+                       dsUpdate = new DescriptorSetWrites (dsVAO, dslVAO);
+                       dsUpdate.Write (dev, vbo.Descriptor, ibo.Descriptor);
+
+                       UpdateFrequency = 5;
+
+                       addPoint (IMG_DIM / 2 - 1, IMG_DIM / 2 - 1);
+               }
+
+               protected override void createQueues () {
+                       computeQ = new Queue (dev, VkQueueFlags.Compute);
+                       transferQ = new Queue (dev, VkQueueFlags.Transfer);
+
+                       base.createQueues ();
+               }
+
+               protected override void OnResize () {
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (grPipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (grPipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                               null
+                                       } : new Image[] {
+                                               null,
+                                               null,
+                                               swapChain.images[i]
+                                       });
+
+                               cmds[i] = cmdPool.AllocateCommandBuffer ();
+                               cmds[i].Start ();
+
+                               imgResult.SetLayout (cmds[i], VkImageAspectFlags.Color,
+                                       VkImageLayout.Undefined, VkImageLayout.ShaderReadOnlyOptimal,
+                                       VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.FragmentShader);
+
+                               grPipeline.RenderPass.Begin (cmds[i], frameBuffers[i]);
+
+                               int xPad = (int)swapChain.Width / 2 - (int)IMG_DIM * (int)zoom / 2;
+                               int yPad = (int)swapChain.Height / 2- (int)IMG_DIM * (int)zoom / 2;
+
+                               cmds[i].SetViewport (IMG_DIM * zoom, IMG_DIM * zoom, xPad, yPad);
+                               cmds[i].SetScissor (IMG_DIM * zoom, IMG_DIM * zoom, Math.Max (0, xPad), Math.Max (0, yPad));
+
+                               cmds[i].BindDescriptorSet (grPipeline.Layout, dsImage);
+                               cmds[i].BindPipeline (grPipeline);
+                               cmds[i].Draw (3, 1, 0, 0);
+
+                               trianglesPipeline.Bind (cmds[i]);
+                               cmds[i].PushConstant (trianglesPipeline.Layout, VkShaderStageFlags.Vertex, IMG_DIM);
+                               cmds[i].PushConstant (trianglesPipeline.Layout, VkShaderStageFlags.Vertex, xPad, sizeof(int));
+                               cmds[i].PushConstant (trianglesPipeline.Layout, VkShaderStageFlags.Vertex, yPad, 2 * sizeof (int));
+                               cmds[i].PushConstant (trianglesPipeline.Layout, VkShaderStageFlags.Vertex, zoom, 3 * sizeof (int));
+
+                               cmds[i].BindVertexBuffer (vbo);
+                               cmds[i].BindIndexBuffer (ibo, VkIndexType.Uint32);
+                               cmds[i].DrawIndexed (pointCount*3);
+
+                               grPipeline.RenderPass.End (cmds[i]);
+
+                               cmds[i].End ();
+                       }
+               }
+
+               public override void Update () {
+                       initGpuBuffers ();
+
+                       using (CommandPool cmdPoolCompute = new CommandPool (dev, computeQ.qFamIndex)) {
+
+                               CommandBuffer cmd = cmdPoolCompute.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               plInit.BindDescriptorSet (cmd, dsVAO, 1);
+                               cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, IMG_DIM, sizeof (int));
+                               cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, pointCount, 2 * sizeof (int));
+
+                               if (!pong)
+                                       plInit.BindDescriptorSet (cmd, dsPong);
+                               else
+                                       plInit.BindDescriptorSet (cmd, dsPing);
+
+                               plInit.Bind (cmd);
+                               cmd.Dispatch (pointCount);
+
+                               VkMemoryBarrier memBar = VkMemoryBarrier.New ();
+                               memBar.srcAccessMask = VkAccessFlags.ShaderWrite;
+                               memBar.dstAccessMask = VkAccessFlags.ShaderRead;
+                               Vk.vkCmdPipelineBarrier (cmd.Handle, VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.ComputeShader, VkDependencyFlags.ByRegion,
+                                       1, ref memBar, 0, IntPtr.Zero, 0, IntPtr.Zero);
+
+                               pong = false;
+                               uint stepSize = IMG_DIM / 2;
+
+                               plCompute.Bind (cmd);
+
+
+                               int pass = 0;
+                               while (stepSize > 0 && pass < invocationCount) {
+                                       cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, stepSize);
+
+                                       if (pong)
+                                               plCompute.BindDescriptorSet (cmd, dsPong);
+                                       else
+                                               plCompute.BindDescriptorSet (cmd, dsPing);
+
+                                       cmd.Dispatch (IMG_DIM, IMG_DIM);
+
+                                       Vk.vkCmdPipelineBarrier (cmd.Handle, VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.ComputeShader, VkDependencyFlags.ByRegion,
+                                               1, ref memBar, 0, IntPtr.Zero, 0, IntPtr.Zero);
+
+                                       pong = !pong;
+                                       stepSize /= 2;
+                                       pass++;
+                               }
+
+                               plNormalize.Bind (cmd);
+                               if (pong)
+                                       plNormalize.BindDescriptorSet (cmd, dsPong);
+                               else
+                                       plNormalize.BindDescriptorSet (cmd, dsPing);
+                               cmd.Dispatch (IMG_DIM, IMG_DIM);
+                               pong = !pong;
+
+                               cmd.End ();
+
+                               computeQ.Submit (cmd);
+                               computeQ.WaitIdle ();
+                       }
+
+                       printResults ();
+               }
+
+               protected override void onMouseButtonDown (MouseButton button) {
+                       int xPad = (int)swapChain.Width / 2 - (int)IMG_DIM * (int)zoom / 2;
+                       int yPad = (int)swapChain.Height / 2 - (int)IMG_DIM * (int)zoom / 2;
+
+                       int localX = (int)((lastMouseX - xPad) / zoom);
+                       int localY = (int)((lastMouseY - yPad) / zoom);
+
+                       if (localX < 0 || localY < 0 || localX >= IMG_DIM || localY >= IMG_DIM)
+                               base.onMouseButtonDown (button);
+                       else {
+                               addPoint ((uint)localX, (uint)localY);
+                       }
+               }
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.Delete:
+                                       clearPoints ();
+                                       break;
+                               case Key.KeypadAdd:
+                                       invocationCount++;
+                                       break;
+                               case Key.KeypadSubtract:
+                                       if (invocationCount>0)
+                                               invocationCount--;
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       break;
+                       }
+                       Console.WriteLine ($"break after {invocationCount} step");
+               }
+
+               void printResults () {
+                       dev.WaitIdle ();
+                       using (CommandPool cmdPoolTransfer = new CommandPool (dev, transferQ.qFamIndex)) {
+
+                               CommandBuffer cmd = cmdPoolTransfer.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               imgResult.SetLayout (cmd, VkImageAspectFlags.Color,
+                                       VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.TransferDstOptimal,
+                                       VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.Transfer);
+
+                               if (pong)
+                                       outBuff.CopyTo (cmd, imgResult, VkImageLayout.ShaderReadOnlyOptimal);
+                               else
+                                       inBuff.CopyTo (cmd, imgResult, VkImageLayout.ShaderReadOnlyOptimal);
+
+                               cmd.End ();
+
+                               transferQ.Submit (cmd);
+                               transferQ.WaitIdle ();
+                       }
+               }
+
+               void initGpuBuffers () {
+                       using (CommandPool staggingCmdPool = new CommandPool (dev, transferQ.qFamIndex)) {
+                               CommandBuffer cmd = staggingCmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               if (clear) {
+                                       if (pong)
+                                               inBuff.Fill (cmd, 0);
+                                       else
+                                               outBuff.Fill (cmd, 0);
+                               }
+
+                               staggingVBO.CopyTo (cmd, vbo, pointCount * (ulong)Marshal.SizeOf<Vector2>());
+
+                               transferQ.EndSubmitAndWait (cmd);
+                       }
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+
+                                       for (int i = 0; i < swapChain.ImageCount; ++i)
+                                               frameBuffers[i]?.Dispose ();
+
+                                       grPipeline.Dispose ();
+                                       trianglesPipeline.Dispose ();
+
+                                       plInit.Dispose ();
+                                       plCompute.Dispose ();
+                                       plNormalize.Dispose ();
+
+                                       dslCompute.Dispose ();
+                                       dslImage.Dispose ();
+
+                                       dsPool.Dispose ();
+
+                                       inBuff.Dispose ();
+                                       outBuff.Dispose ();
+                                       staggingVBO.Dispose ();
+                                       vbo.Dispose ();
+                                       ibo.Dispose ();
+
+                                       imgResult.Dispose ();
+
+#if DEBUG
+                                       dbgReport.Dispose ();
+#endif
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+
+
+       }
+}
diff --git a/samples/deferred/DebuDrawPipeline.cs b/samples/deferred/DebuDrawPipeline.cs
new file mode 100644 (file)
index 0000000..fef2751
--- /dev/null
@@ -0,0 +1,78 @@
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL {
+       public class DebugDrawPipeline : GraphicPipeline {
+               public HostBuffer Vertices;
+               uint vertexCount;
+               uint vboLength = 100 * 6 * sizeof (float);
+
+               public DebugDrawPipeline (Device dev, DescriptorSetLayout dsLayout, VkFormat colorFormat, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1) :
+                       base (new RenderPass (dev, colorFormat), "Debug draw pipeline") {
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.LineList, samples);
+                       cfg.rasterizationState.lineWidth = 1.0f;
+                       cfg.RenderPass = RenderPass;
+                       cfg.Layout = new PipelineLayout(dev, dsLayout);
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf<Matrix4x4> () * 2)                     
+                       );
+                       cfg.AddVertexBinding (0, 6 * sizeof(float));
+                       cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat);
+
+                       cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/debug.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/debug.frag.spv");
+
+                       layout = cfg.Layout;
+
+                       init (cfg);
+
+                       Vertices = new HostBuffer (dev, VkBufferUsageFlags.VertexBuffer, vboLength);
+                       Vertices.Map ();
+               }
+
+               public void AddLine (Vector3 start, Vector3 end, float r, float g, float b) {
+                       float[] data = {
+                               start.X, start.Y, start.Z,
+                               r, g, b,
+                               end.X, end.Y, end.Z,
+                               r, g, b
+                       };
+                       Vertices.Update (data, 12 * sizeof (float), vertexCount * 6 * sizeof (float));
+                       vertexCount+=2;
+               }
+
+               public void RecordDraw (CommandBuffer cmd, Framebuffer fb, Camera camera) {
+                       RenderPass.Begin (cmd, fb);
+                       const int ratio = 8;
+                       cmd.SetViewport (fb.Width/ratio, fb.Height/ratio, (ratio-1) * (int)fb.Width / ratio, (ratio-1) * (int)fb.Height / ratio);
+                       //cmd.SetViewport (200, 200,100,100,-10,10);//, 4 * (int)fb.Width / 5, 4 * (int)fb.Height / 5);
+                       cmd.SetScissor (fb.Width / ratio, fb.Height / ratio, (ratio-1) * (int)fb.Width / ratio, (ratio-1) * (int)fb.Height / ratio);
+                       //cmd.SetScissor (200, 200,100,100);
+
+                       Matrix4x4 ortho = Matrix4x4.CreateOrthographic (4, 4.0f / camera.AspectRatio,-1,1);
+
+                       cmd.PushConstant (layout, VkShaderStageFlags.Vertex, ortho);
+
+                       Bind (cmd);
+
+                       cmd.BindVertexBuffer (Vertices);
+                       cmd.Draw (vertexCount);
+                       RenderPass.End (cmd);
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               Vertices.Unmap ();
+                               Vertices.Dispose ();
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+
+}
diff --git a/samples/deferred/DeferredPbrRenderer.cs b/samples/deferred/DeferredPbrRenderer.cs
new file mode 100644 (file)
index 0000000..3dc8520
--- /dev/null
@@ -0,0 +1,528 @@
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using CVKL;
+using CVKL.glTF;
+using VK;
+
+namespace deferred {
+       public class DeferredPbrRenderer : IDisposable {
+               Device dev;
+               Queue gQueue;
+               public static int MAX_MATERIAL_COUNT = 4;
+               public static VkSampleCountFlags NUM_SAMPLES = VkSampleCountFlags.SampleCount1;
+               public static VkFormat HDR_FORMAT = VkFormat.R16g16b16a16Sfloat;
+               public static VkFormat MRT_FORMAT = VkFormat.R32g32b32a32Sfloat;
+               public static bool TEXTURE_ARRAY;
+
+               public enum DebugView {
+                       none,
+                       color,
+                       normal,
+                       pos,
+                       occlusion,
+                       emissive,
+                       metallic,
+                       roughness,
+                       depth,
+                       prefill,
+                       irradiance,
+                       shadowMap
+               }
+               public DebugView currentDebugView = DebugView.none;
+               public int lightNumDebug = 0;
+               public int debugMip = 0;
+               public int debugFace = 0;
+
+               const float lightMoveSpeed = 0.1f;
+               public float exposure = 2.0f;
+               public float gamma = 1.2f;
+
+               public struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 model;
+                       public Matrix4x4 view;
+                       public Vector4 camPos;
+                       public float prefilteredCubeMipLevels;
+                       public float scaleIBLAmbient;
+               }
+               public struct Light {
+                       public Vector4 position;
+                       public Vector4 color;
+                       public Matrix4x4 mvp;
+               }
+
+               public Matrices matrices = new Matrices {
+                       scaleIBLAmbient = 0.5f,
+               };
+               public Light[] lights = {
+                       new Light {
+                               position = new Vector4(2.5f,5.5f,2,0f),
+                               color = new Vector4(1,0.8f,0.8f,1)
+                       },
+                       /*new Light {
+                               position = new Vector4(-2.5f,5.5f,2,0f),
+                               color = new Vector4(0.8f,0.8f,1,1)
+                       }*/
+               };
+
+               Framebuffer frameBuffer;
+               public Image gbColorRough, gbEmitMetal, gbN_AO, gbPos, hdrImgResolved, hdrImgMS;
+
+               DescriptorPool descriptorPool;
+               DescriptorSetLayout descLayoutMain, descLayoutTextures, descLayoutGBuff;
+               DescriptorSet dsMain, dsGBuff;
+
+               public PipelineCache pipelineCache;
+               Pipeline gBuffPipeline, composePipeline, debugPipeline;
+
+               public HostBuffer uboMatrices { get; private set; }
+               public HostBuffer<Light> uboLights { get; private set; }
+
+               RenderPass renderPass;
+
+               public PbrModel model { get; private set; }
+               public EnvironmentCube envCube;
+               public ShadowMapRenderer shadowMapRenderer;
+
+               public BoundingBox modelAABB;
+
+               public VkSemaphore DrawComplete;
+
+               const int SP_SKYBOX             = 0;
+               const int SP_MODELS             = 1;
+               const int SP_COMPOSE            = 2;
+               //const int SP_TONE_MAPPING     = 3;
+
+               string cubemapPath;
+
+               uint width, height;
+               public uint Width => width;
+               public uint Height => height;
+
+               public DeferredPbrRenderer (Queue gQueue, string cubemapPath, uint width, uint height, float nearPlane, float farPlane) {
+                       this.gQueue = gQueue;
+                       this.dev = gQueue.Dev;
+                       this.cubemapPath = cubemapPath;
+                       this.width = width;
+                       this.height = height;
+
+                       DrawComplete = dev.CreateSemaphore();
+
+                       pipelineCache = new PipelineCache (dev);
+
+                       descriptorPool = new DescriptorPool (dev, 5,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 3),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 6),
+                               new VkDescriptorPoolSize (VkDescriptorType.InputAttachment, 5),
+                               new VkDescriptorPoolSize (VkDescriptorType.StorageImage, 4)
+                       );                              
+
+                       uboMatrices = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, matrices, true);
+                       uboLights = new HostBuffer<Light> (dev, VkBufferUsageFlags.UniformBuffer, lights, true);
+
+#if WITH_SHADOWS
+                       shadowMapRenderer = new ShadowMapRenderer (gQueue, this);
+#endif
+
+                       init (nearPlane, farPlane);
+               }
+
+               void init_renderpass () {
+                       renderPass = new RenderPass (dev, NUM_SAMPLES);
+
+                       renderPass.AddAttachment (HDR_FORMAT, VkImageLayout.ColorAttachmentOptimal, VkSampleCountFlags.SampleCount1);//final outpout
+                       renderPass.AddAttachment (dev.GetSuitableDepthFormat (), VkImageLayout.DepthStencilAttachmentOptimal, NUM_SAMPLES);
+                       renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES, VkAttachmentLoadOp.Clear, VkAttachmentStoreOp.DontCare);//GBuff0 (color + roughness) and final color before resolve
+                       renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES, VkAttachmentLoadOp.Clear, VkAttachmentStoreOp.DontCare);//GBuff1 (emit + metal)
+                       renderPass.AddAttachment (MRT_FORMAT, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES, VkAttachmentLoadOp.Clear, VkAttachmentStoreOp.DontCare);//GBuff2 (normals + AO)
+                       renderPass.AddAttachment (MRT_FORMAT, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES, VkAttachmentLoadOp.Clear, VkAttachmentStoreOp.DontCare);//GBuff3 (Pos + depth)
+                       if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1)
+                               renderPass.AddAttachment (HDR_FORMAT, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES);//hdr color multisampled
+
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1)
+                                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+
+                       uint mainHdr = NUM_SAMPLES == VkSampleCountFlags.SampleCount1 ? 0u : 6u;
+
+                       SubPass[] subpass = { new SubPass (), new SubPass (), new SubPass ()};
+                       //skybox
+                       subpass[SP_SKYBOX].AddColorReference (mainHdr, VkImageLayout.ColorAttachmentOptimal);
+                       //models
+                       subpass[SP_MODELS].AddColorReference (new VkAttachmentReference (2, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (3, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (4, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (5, VkImageLayout.ColorAttachmentOptimal));
+                       subpass[SP_MODELS].SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal);
+                       subpass[SP_MODELS].AddPreservedReference (mainHdr);
+
+                       //compose
+                       subpass[SP_COMPOSE].AddColorReference (mainHdr, VkImageLayout.ColorAttachmentOptimal);
+                       subpass[SP_COMPOSE].AddInputReference (new VkAttachmentReference (2, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (3, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (4, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (5, VkImageLayout.ShaderReadOnlyOptimal));
+                       if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1)
+                               subpass[SP_COMPOSE].AddResolveReference (0, VkImageLayout.ColorAttachmentOptimal);
+                       //tone mapping
+                       //subpass[SP_TONE_MAPPING].AddColorReference ((NUM_SAMPLES == VkSampleCountFlags.SampleCount1) ? 0u : 2u, VkImageLayout.ColorAttachmentOptimal);
+                       //subpass[SP_TONE_MAPPING].AddInputReference (new VkAttachmentReference (6, VkImageLayout.ShaderReadOnlyOptimal));
+                       //if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1)
+                       //subpass[SP_TONE_MAPPING].AddResolveReference (0, VkImageLayout.ColorAttachmentOptimal);
+
+                       renderPass.AddSubpass (subpass);
+
+                       renderPass.AddDependency (Vk.SubpassExternal, SP_SKYBOX,
+                               VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput,
+                               VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite);
+                       renderPass.AddDependency (SP_SKYBOX, SP_MODELS,
+                               VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader,
+                               VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead);
+                       renderPass.AddDependency (SP_MODELS, SP_COMPOSE,
+                               VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader,
+                               VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead);
+                       //renderPass.AddDependency (SP_COMPOSE, Vk.SubpassExternal,
+                               //VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.Transfer,
+                               //VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.TransferRead);
+                       //renderPass.AddDependency (SP_COMPOSE, SP_COMPOSE,
+                               //VkPipelineStageFlags.Transfer, VkPipelineStageFlags.ComputeShader,
+                               //VkAccessFlags.TransferWrite, VkAccessFlags.ShaderRead);
+                       //renderPass.AddDependency (Vk.SubpassExternal, SP_TONE_MAPPING,
+                       //      VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.FragmentShader,
+                       //      VkAccessFlags.ShaderWrite, VkAccessFlags.ShaderRead);
+                       //renderPass.AddDependency (SP_SKYBOX, SP_TONE_MAPPING,
+                               //VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader,
+                               //VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead);
+                       renderPass.AddDependency (SP_COMPOSE, Vk.SubpassExternal,
+                               VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.Transfer,
+                               VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.TransferRead);
+                       //renderPass.AddDependency (SP_TONE_MAPPING, Vk.SubpassExternal,
+                                       //VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe,
+                                       //VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead);
+               }
+
+               void init (float nearPlane, float farPlane) {
+                       init_renderpass ();
+
+                       descLayoutMain = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),//matrices and params
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),//lights
+                               new VkDescriptorSetLayoutBinding (5, VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer));//materials
+#if WITH_SHADOWS
+                       descLayoutMain.Bindings.Add (new VkDescriptorSetLayoutBinding (6, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));
+#endif
+
+                       if (TEXTURE_ARRAY) {
+                               descLayoutMain.Bindings.Add (new VkDescriptorSetLayoutBinding (7, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));//texture array
+                               //descLayoutMain.Bindings.Add (new VkDescriptorSetLayoutBinding (8, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));//down sampled hdr
+                       } else { 
+                               descLayoutTextures = new DescriptorSetLayout (dev,
+                                       new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                                       new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                                       new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                                       new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                                       new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                               ); 
+                       }
+
+                       descLayoutGBuff = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),//color + roughness
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),//emit + metal
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),//normals + AO
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment));//Pos + depth
+
+
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, NUM_SAMPLES);
+                       cfg.rasterizationState.cullMode = VkCullModeFlags.Back;
+                       if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1) {
+                               cfg.multisampleState.sampleShadingEnable = true;
+                               cfg.multisampleState.minSampleShading = 0.5f;
+                       }
+                       cfg.Cache = pipelineCache;
+                       if (TEXTURE_ARRAY) 
+                               cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutGBuff);
+                        else 
+                               cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutGBuff, descLayoutTextures);
+
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf<Matrix4x4> ()),
+                               new VkPushConstantRange (VkShaderStageFlags.Fragment, sizeof (int), 64)
+                       );
+                       cfg.RenderPass = renderPass;
+                       cfg.SubpassIndex = SP_MODELS;
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       //cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+
+                       cfg.AddVertex<PbrModelTexArray.Vertex> ();
+
+                       using (SpecializationInfo constants = new SpecializationInfo (
+                                               new SpecializationConstant<float> (0, nearPlane),
+                                               new SpecializationConstant<float> (1, farPlane),
+                                               new SpecializationConstant<float> (2, MAX_MATERIAL_COUNT))) {
+
+                               cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.GBuffPbr.vert.spv");
+                               if (TEXTURE_ARRAY) 
+                                       cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.GBuffPbrTexArray.frag.spv", constants);
+                               else
+                                       cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.GBuffPbr.frag.spv", constants);
+
+                               gBuffPipeline = new GraphicPipeline (cfg);
+                       }
+                       cfg.rasterizationState.cullMode = VkCullModeFlags.Front;
+                       //COMPOSE PIPELINE
+                       cfg.blendAttachments.Clear ();
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.ResetShadersAndVerticesInfos ();
+                       cfg.SubpassIndex = SP_COMPOSE;
+                       cfg.Layout = gBuffPipeline.Layout;
+                       cfg.depthStencilState.depthTestEnable = false;
+                       cfg.depthStencilState.depthWriteEnable = false;
+                       using (SpecializationInfo constants = new SpecializationInfo (
+                               new SpecializationConstant<uint> (0, (uint)lights.Length))) {
+                               cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.FullScreenQuad.vert.spv");
+#if WITH_SHADOWS
+                               cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.compose_with_shadows.frag.spv", constants);
+#else
+                               cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.compose.frag.spv", constants);
+#endif
+                               composePipeline = new GraphicPipeline (cfg);
+                       }
+                       //DEBUG DRAW use subpass of compose
+                       cfg.shaders[1] = new ShaderInfo (VkShaderStageFlags.Fragment, "#deferred.show_gbuff.frag.spv");
+                       cfg.SubpassIndex = SP_COMPOSE;
+                       debugPipeline = new GraphicPipeline (cfg);
+                       ////TONE MAPPING
+                       //cfg.shaders[1] = new ShaderInfo (VkShaderStageFlags.Fragment, "#deferred.tone_mapping.frag.spv");
+                       //cfg.SubpassIndex = SP_TONE_MAPPING;
+                       //toneMappingPipeline = new GraphicPipeline (cfg);
+
+                       dsMain = descriptorPool.Allocate (descLayoutMain);
+                       dsGBuff = descriptorPool.Allocate (descLayoutGBuff);
+
+                       envCube = new EnvironmentCube (cubemapPath, dsMain, gBuffPipeline.Layout, gQueue, renderPass);
+
+                       matrices.prefilteredCubeMipLevels = envCube.prefilterCube.CreateInfo.mipLevels;
+
+                       DescriptorSetWrites dsMainWrite = new DescriptorSetWrites (dsMain, descLayoutMain.Bindings.GetRange (0, 5).ToArray ());
+                       dsMainWrite.Write (dev, 
+                               uboMatrices.Descriptor,
+                               envCube.irradianceCube.Descriptor,
+                               envCube.prefilterCube.Descriptor,
+                               envCube.lutBrdf.Descriptor,
+                               uboLights.Descriptor);
+
+#if WITH_SHADOWS
+                       dsMainWrite = new DescriptorSetWrites (dsMain, descLayoutMain.Bindings[6]);
+                       dsMainWrite.Write (dev, shadowMapRenderer.shadowMap.Descriptor);
+#endif
+               }
+
+
+               public void LoadModel (Queue transferQ, string path) {
+                       dev.WaitIdle ();
+                       model?.Dispose ();
+
+                       if (TEXTURE_ARRAY) {
+                               PbrModelTexArray mod = new PbrModelTexArray (transferQ, path);
+                               if (mod.texArray != null) {
+                                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMain, descLayoutMain.Bindings[5], descLayoutMain.Bindings[7]);
+                                       uboUpdate.Write (dev, mod.materialUBO.Descriptor, mod.texArray.Descriptor);
+                               }
+                               model = mod;
+                       } else {
+                               model = new PbrModelSeparatedTextures (transferQ, path,
+                                       descLayoutTextures,
+                                       AttachmentType.Color,
+                                       AttachmentType.PhysicalProps,
+                                       AttachmentType.Normal,
+                                       AttachmentType.AmbientOcclusion,
+                                       AttachmentType.Emissive);
+
+                               DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMain, descLayoutMain.Bindings[5]);
+                               uboUpdate.Write (dev, model.materialUBO.Descriptor);
+                       }
+
+
+                       modelAABB = model.DefaultScene.AABB;
+               }
+               public void buildCommandBuffers (CommandBuffer cmd) {
+
+
+                       renderPass.Begin (cmd, frameBuffer);
+
+                       cmd.SetViewport (frameBuffer.Width, frameBuffer.Height);
+                       cmd.SetScissor (frameBuffer.Width, frameBuffer.Height);
+
+                       cmd.BindDescriptorSet (gBuffPipeline.Layout, dsMain);
+
+                       envCube.RecordDraw (cmd);
+
+                       renderPass.BeginSubPass (cmd);
+
+                       if (model != null) {
+                               gBuffPipeline.Bind (cmd);
+                               model.Bind (cmd);
+                               model.DrawAll (cmd, gBuffPipeline.Layout);
+                       }
+
+                       renderPass.BeginSubPass (cmd);
+
+                       cmd.BindDescriptorSet (composePipeline.Layout, dsGBuff, 1);
+
+                       if (currentDebugView == DebugView.none)
+                               composePipeline.Bind (cmd);
+                       else {
+                               debugPipeline.Bind (cmd);
+                               uint debugValue = (uint)currentDebugView - 1;
+                               if (currentDebugView == DebugView.shadowMap)
+                                       debugValue += (uint)((lightNumDebug << 8));
+                               else
+                                       debugValue += (uint)((debugFace << 8) + (debugMip << 16));
+                               cmd.PushConstant (debugPipeline.Layout, VkShaderStageFlags.Fragment, debugValue, (uint)Marshal.SizeOf<Matrix4x4> ());
+                       }
+
+                       cmd.Draw (3, 1, 0, 0);
+
+                       //renderPass.BeginSubPass (cmd);
+                       //toneMappingPipeline.Bind (cmd);
+                       //cmd.Draw (3, 1, 0, 0);
+
+                       renderPass.End (cmd);
+               }
+
+               public void MoveLight (Vector4 dir) {
+                       lights[lightNumDebug].position += dir * lightMoveSpeed;
+#if WITH_SHADOWS
+                       shadowMapRenderer.updateShadowMap = true;
+#endif
+               }
+
+               #region update
+               public void UpdateView (Camera camera) {
+                       camera.AspectRatio = (float)width / height;
+
+                       matrices.projection = camera.Projection;
+                       matrices.view = camera.View;
+                       matrices.model = camera.Model;
+
+                       matrices.camPos = new Vector4 (
+                               -camera.Position.Z * (float)Math.Sin (camera.Rotation.Y) * (float)Math.Cos (camera.Rotation.X),
+                                camera.Position.Z * (float)Math.Sin (camera.Rotation.X),
+                                camera.Position.Z * (float)Math.Cos (camera.Rotation.Y) * (float)Math.Cos (camera.Rotation.X),
+                                0
+                       );
+
+                       uboMatrices.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+               }
+
+               #endregion
+
+
+               void createGBuff () {
+                       gbColorRough?.Dispose ();
+                       gbEmitMetal?.Dispose ();
+                       gbN_AO?.Dispose ();
+                       gbPos?.Dispose ();
+                       hdrImgResolved?.Dispose ();
+                       hdrImgMS?.Dispose ();
+
+
+                       hdrImgResolved = new Image (dev, HDR_FORMAT, VkImageUsageFlags.Sampled | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferSrc, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, VkSampleCountFlags.SampleCount1);
+                       gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES);
+                       gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES);
+                       gbN_AO = new Image (dev, MRT_FORMAT, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES);
+                       gbPos = new Image (dev, MRT_FORMAT, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES);
+                       if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1)
+                               hdrImgMS = new Image (dev, HDR_FORMAT, VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES);
+
+
+                       gbColorRough.CreateView (); gbColorRough.CreateSampler ();
+                       gbColorRough.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                       gbEmitMetal.CreateView (); gbEmitMetal.CreateSampler ();
+                       gbEmitMetal.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                       gbN_AO.CreateView (); gbN_AO.CreateSampler ();
+                       gbN_AO.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                       gbPos.CreateView (); gbPos.CreateSampler ();
+                       gbPos.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                       hdrImgResolved.CreateView (); hdrImgResolved.CreateSampler ();
+                       hdrImgResolved.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                       hdrImgMS?.CreateView ();
+
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff);
+                       uboUpdate.Write (dev, dsGBuff,  gbColorRough.Descriptor,
+                                                                               gbEmitMetal.Descriptor,
+                                                                               gbN_AO.Descriptor,
+                                                                               gbPos.Descriptor);
+
+                       gbColorRough.SetName ("GBuffColorRough");
+                       gbEmitMetal.SetName ("GBuffEmitMetal");
+                       gbN_AO.SetName ("GBuffN");
+                       gbPos.SetName ("GBuffPos");
+                       hdrImgResolved.SetName ("HDRimg resolved");
+                       hdrImgMS?.SetName ("HDRimg resolved");
+               }
+
+               public void Resize (uint width, uint height) {
+                       this.width = width;
+                       this.height = height;
+
+                       frameBuffer?.Dispose ();
+                       createGBuff ();
+
+                       frameBuffer = (NUM_SAMPLES == VkSampleCountFlags.SampleCount1) ?
+                               new Framebuffer (renderPass, width, height, new Image[] {
+                                       hdrImgResolved, null, gbColorRough, gbEmitMetal, gbN_AO, gbPos}) :
+                               new Framebuffer (renderPass, width, height, new Image[] {
+                                       hdrImgResolved, null, gbColorRough, gbEmitMetal, gbN_AO, gbPos, hdrImgMS});
+               }
+
+               public void Dispose () {
+                       dev.WaitIdle ();
+
+                       frameBuffer?.Dispose ();
+
+                       gbColorRough.Dispose ();
+                       gbEmitMetal.Dispose ();
+                       gbN_AO.Dispose ();
+                       gbPos.Dispose ();
+                       hdrImgMS?.Dispose ();
+                       hdrImgResolved.Dispose ();
+
+                       gBuffPipeline.Dispose ();
+                       composePipeline.Dispose ();
+                       //toneMappingPipeline.Dispose ();
+                       debugPipeline?.Dispose ();
+
+                       descLayoutMain.Dispose ();
+                       descLayoutTextures?.Dispose ();
+                       descLayoutGBuff.Dispose ();
+
+                       uboMatrices.Dispose ();
+                       uboLights.Dispose ();
+                       model.Dispose ();
+                       envCube.Dispose ();
+
+#if WITH_SHADOWS
+                       shadowMapRenderer.Dispose ();
+#endif
+
+                       descriptorPool.Dispose ();
+
+                       dev.DestroySemaphore (DrawComplete);
+               }
+       }
+}
diff --git a/samples/deferred/EnvironmentPipeline.cs b/samples/deferred/EnvironmentPipeline.cs
new file mode 100644 (file)
index 0000000..4e97a1c
--- /dev/null
@@ -0,0 +1,318 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL {
+       public class EnvironmentCube : GraphicPipeline {
+
+               GPUBuffer vboSkybox;
+
+               public Image cubemap { get; private set; }
+               public Image lutBrdf { get; private set; }
+               public Image irradianceCube { get; private set; }
+               public Image prefilterCube { get; set; }
+
+               public EnvironmentCube (string cubemapPath, DescriptorSet dsSkybox, PipelineLayout plLayout, Queue staggingQ, RenderPass renderPass, PipelineCache cache = null)
+               : base (renderPass, cache, "EnvCube pipeline") {
+
+                       using (CommandPool cmdPool = new CommandPool (staggingQ.Dev, staggingQ.index)) {
+
+                               vboSkybox = new GPUBuffer<float> (staggingQ, cmdPool, VkBufferUsageFlags.VertexBuffer, box_vertices);
+
+                               cubemap = KTX.KTX.Load (staggingQ, cmdPool, cubemapPath,
+                                       VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal, true);
+                               cubemap.CreateView (VkImageViewType.Cube, VkImageAspectFlags.Color);
+                               cubemap.CreateSampler (VkSamplerAddressMode.ClampToEdge);
+                               cubemap.SetName ("skybox Texture");
+                               cubemap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                               GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, renderPass.Samples, false);
+                               cfg.RenderPass = renderPass;
+                               cfg.Layout = plLayout;
+                               cfg.AddVertexBinding (0, 3 * sizeof (float));
+                               cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat);
+                               cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.skybox.vert.spv");
+                               cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.skybox.frag.spv");
+                               cfg.multisampleState.rasterizationSamples = Samples;
+
+                               layout = cfg.Layout;
+
+                               init (cfg);
+
+                               generateBRDFLUT (staggingQ, cmdPool);
+                               generateCubemaps (staggingQ, cmdPool);
+                       }
+
+               }
+
+               public void RecordDraw (CommandBuffer cmd) {
+                       Bind (cmd);
+                       cmd.BindVertexBuffer (vboSkybox);
+                       cmd.Draw (36);
+               }
+
+               #region skybox
+
+               static float[] box_vertices = {
+                        1.0f, 1.0f,-1.0f,  // +X side
+                        1.0f, 1.0f, 1.0f,
+                        1.0f,-1.0f, 1.0f,
+                        1.0f,-1.0f, 1.0f,
+                        1.0f,-1.0f,-1.0f,
+                        1.0f, 1.0f,-1.0f,
+
+                       -1.0f,-1.0f,-1.0f,      // +X side
+                       -1.0f,-1.0f, 1.0f,
+                       -1.0f, 1.0f, 1.0f,
+                       -1.0f, 1.0f, 1.0f,
+                       -1.0f, 1.0f,-1.0f,
+                       -1.0f,-1.0f,-1.0f,
+
+                       -1.0f, 1.0f,-1.0f,  // +Y side
+                       -1.0f, 1.0f, 1.0f,
+                        1.0f, 1.0f, 1.0f,
+                       -1.0f, 1.0f,-1.0f,
+                        1.0f, 1.0f, 1.0f,
+                        1.0f, 1.0f,-1.0f,
+
+                       -1.0f,-1.0f,-1.0f,  // -Y side
+                        1.0f,-1.0f,-1.0f,
+                        1.0f,-1.0f, 1.0f,
+                       -1.0f,-1.0f,-1.0f,
+                        1.0f,-1.0f, 1.0f,
+                       -1.0f,-1.0f, 1.0f,
+
+                       -1.0f, 1.0f, 1.0f,  // +Z side
+                       -1.0f,-1.0f, 1.0f,
+                        1.0f, 1.0f, 1.0f,
+                       -1.0f,-1.0f, 1.0f,
+                        1.0f,-1.0f, 1.0f,
+                        1.0f, 1.0f, 1.0f,
+
+                       -1.0f,-1.0f,-1.0f,  // -Z side
+                        1.0f, 1.0f,-1.0f,
+                        1.0f,-1.0f,-1.0f,
+                       -1.0f,-1.0f,-1.0f,
+                       -1.0f, 1.0f,-1.0f,
+                        1.0f, 1.0f,-1.0f,
+
+               };
+               #endregion
+
+               void generateBRDFLUT (Queue staggingQ, CommandPool cmdPool) {
+                       const VkFormat format = VkFormat.R16g16Sfloat;
+                       const int dim = 512;
+
+                       lutBrdf = new Image (Dev, format, VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.Sampled,
+                               VkMemoryPropertyFlags.DeviceLocal, dim, dim);
+                       lutBrdf.SetName ("lutBrdf");
+
+                       lutBrdf.CreateView ();
+                       lutBrdf.CreateSampler (VkSamplerAddressMode.ClampToEdge);
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false);
+
+                       cfg.Layout = new PipelineLayout (Dev, new DescriptorSetLayout (Dev));
+                       cfg.RenderPass = new RenderPass (Dev);
+                       cfg.RenderPass.AddAttachment (format, VkImageLayout.ShaderReadOnlyOptimal);
+                       cfg.RenderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0, 0, 0) });
+                       cfg.RenderPass.AddSubpass (new SubPass (VkImageLayout.ColorAttachmentOptimal));
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.genbrdflut.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.genbrdflut.frag.spv");
+
+                       using (GraphicPipeline pl = new GraphicPipeline (cfg)) {
+                               using (Framebuffer fb = new Framebuffer (cfg.RenderPass, dim, dim, lutBrdf)) {
+                                       CommandBuffer cmd = cmdPool.AllocateCommandBuffer ();
+                                       cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit);
+                                       pl.RenderPass.Begin (cmd, fb);
+                                       cmd.SetViewport (dim, dim);
+                                       cmd.SetScissor (dim, dim);
+                                       pl.Bind (cmd);
+                                       cmd.Draw (3, 1, 0, 0);
+                                       pl.RenderPass.End (cmd);
+                                       cmd.End ();
+
+                                       staggingQ.Submit (cmd);
+                                       staggingQ.WaitIdle ();
+
+                                       cmd.Free ();
+                               }
+                       }
+                       lutBrdf.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+               }
+
+               public enum CBTarget { IRRADIANCE = 0, PREFILTEREDENV = 1 };
+
+               public Image generateCubeMap (Queue staggingQ, CommandPool cmdPool, CBTarget target) {
+                       const float deltaPhi = (2.0f * (float)Math.PI) / 180.0f;
+                       const float deltaTheta = (0.5f * (float)Math.PI) / 64.0f;
+
+                       VkFormat format = VkFormat.R32g32b32a32Sfloat;
+                       uint dim = 64;
+
+                       if (target == CBTarget.PREFILTEREDENV) {
+                               format = VkFormat.R16g16b16a16Sfloat;
+                               dim = 512;
+                       }
+
+                       uint numMips = (uint)Math.Floor (Math.Log (dim, 2)) + 1;
+
+                       Image imgFbOffscreen = new Image (Dev, format, VkImageUsageFlags.TransferSrc | VkImageUsageFlags.ColorAttachment,
+                               VkMemoryPropertyFlags.DeviceLocal, dim, dim);
+                       imgFbOffscreen.SetName ("offscreenfb");
+                       imgFbOffscreen.CreateView ();
+
+                       Image cmap = new Image (Dev, format, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled,
+                               VkMemoryPropertyFlags.DeviceLocal, dim, dim, VkImageType.Image2D, VkSampleCountFlags.SampleCount1, VkImageTiling.Optimal,
+                               numMips, 6, 1, VkImageCreateFlags.CubeCompatible);
+                       if (target == CBTarget.PREFILTEREDENV)
+                               cmap.SetName ("prefilterenvmap");
+                       else
+                               cmap.SetName ("irradianceCube");
+                       cmap.CreateView (VkImageViewType.Cube, VkImageAspectFlags.Color, 6, 0);
+                       cmap.CreateSampler (VkSamplerAddressMode.ClampToEdge);
+
+                       DescriptorPool dsPool = new DescriptorPool (Dev, 2, new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler));
+
+                       DescriptorSetLayout dsLayout = new DescriptorSetLayout (Dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));
+
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false);
+                       cfg.Layout = new PipelineLayout (Dev, dsLayout);
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf<Matrix4x4> () + 8));
+
+                       cfg.RenderPass = new RenderPass (Dev);
+                       cfg.RenderPass.AddAttachment (format, VkImageLayout.ColorAttachmentOptimal);
+                       cfg.RenderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0, 0, 0) });
+                       cfg.RenderPass.AddSubpass (new SubPass (VkImageLayout.ColorAttachmentOptimal));
+
+                       cfg.AddVertexBinding (0, 3 * sizeof (float));
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.filtercube.vert.spv");
+                       if (target == CBTarget.PREFILTEREDENV)
+                               cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.prefilterenvmap.frag.spv");
+                       else
+                               cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.irradiancecube.frag.spv");
+
+                       Matrix4x4[] matrices = {
+                               // POSITIVE_X
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(90)),
+                               // NEGATIVE_X
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(-90)),
+                               // POSITIVE_Y
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(-90)),
+                               // NEGATIVE_Y
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(90)),
+                               // POSITIVE_Z
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)),
+                               // NEGATIVE_Z
+                               Matrix4x4.CreateRotationZ(Utils.DegreesToRadians(180))
+                       };
+
+                       VkImageSubresourceRange subRes = new VkImageSubresourceRange (VkImageAspectFlags.Color, 0, numMips, 0, 6);
+
+                       using (GraphicPipeline pl = new GraphicPipeline (cfg)) {
+
+                               DescriptorSet dset = dsPool.Allocate (dsLayout);
+                               DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dsLayout);
+                               dsUpdate.Write (Dev, dset, cubemap.Descriptor);
+                               Dev.WaitIdle ();
+
+                               using (Framebuffer fb = new Framebuffer (pl.RenderPass, dim, dim, imgFbOffscreen)) {
+                                       CommandBuffer cmd = cmdPool.AllocateCommandBuffer ();
+                                       cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                                       cmap.SetLayout (cmd, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subRes);
+
+                                       float roughness = 0;
+
+                                       cmd.SetScissor (dim, dim);
+                                       cmd.SetViewport ((float)(dim), (float)dim);
+
+                                       for (int m = 0; m < numMips; m++) {
+                                               roughness = (float)m / ((float)numMips - 1f);
+
+                                               for (int f = 0; f < 6; f++) {
+                                                       pl.RenderPass.Begin (cmd, fb);
+
+                                                       pl.Bind (cmd);
+
+                                                       float viewPortSize = (float)Math.Pow (0.5, m) * dim;
+                                                       cmd.SetViewport (viewPortSize, viewPortSize);
+                                                       cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment,
+                                                               matrices[f] * Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (90), 1f, 0.1f, 512f));
+                                                       if (target == CBTarget.IRRADIANCE) {
+                                                               cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaPhi, (uint)Marshal.SizeOf<Matrix4x4> ());
+                                                               cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaTheta, (uint)Marshal.SizeOf<Matrix4x4> () + 4);
+                                                       } else {
+                                                               cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, roughness, (uint)Marshal.SizeOf<Matrix4x4> ());
+                                                               cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 64u, (uint)Marshal.SizeOf<Matrix4x4> () + 4);
+                                                       }
+
+                                                       cmd.BindDescriptorSet (pl.Layout, dset);
+                                                       cmd.BindVertexBuffer (vboSkybox);
+                                                       cmd.Draw (36);
+
+                                                       pl.RenderPass.End (cmd);
+
+                                                       imgFbOffscreen.SetLayout (cmd, VkImageAspectFlags.Color,
+                                                               VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal);
+
+                                                       VkImageCopy region = new VkImageCopy ();
+                                                       region.srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1);
+                                                       region.dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1, (uint)m, (uint)f);
+                                                       region.extent = new VkExtent3D { width = (uint)viewPortSize, height = (uint)viewPortSize, depth = 1 };
+
+                                                       Vk.vkCmdCopyImage (cmd.Handle,
+                                                               imgFbOffscreen.Handle, VkImageLayout.TransferSrcOptimal,
+                                                               cmap.Handle, VkImageLayout.TransferDstOptimal,
+                                                               1, region.Pin ());
+                                                       region.Unpin ();
+
+                                                       imgFbOffscreen.SetLayout (cmd, VkImageAspectFlags.Color,
+                                                               VkImageLayout.TransferSrcOptimal, VkImageLayout.ColorAttachmentOptimal);
+
+                                               }
+                                       }
+
+                                       cmap.SetLayout (cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, subRes);
+
+                                       cmd.End ();
+
+                                       staggingQ.Submit (cmd);
+                                       staggingQ.WaitIdle ();
+
+                                       cmd.Free ();
+                               }
+                       }
+                       cmap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                       dsLayout.Dispose ();
+                       imgFbOffscreen.Dispose ();
+                       dsPool.Dispose ();
+
+                       return cmap;
+               }
+
+               void generateCubemaps (Queue staggingQ, CommandPool cmdPool) {
+                       irradianceCube = generateCubeMap (staggingQ, cmdPool, CBTarget.IRRADIANCE);
+                       prefilterCube = generateCubeMap (staggingQ, cmdPool, CBTarget.PREFILTEREDENV);
+               }
+
+               protected override void Dispose (bool disposing) {
+                       vboSkybox.Dispose ();
+                       cubemap.Dispose ();
+                       lutBrdf.Dispose ();
+                       irradianceCube.Dispose ();
+                       prefilterCube.Dispose ();
+
+                       base.Dispose (disposing);
+               }
+       }
+
+}
diff --git a/samples/deferred/deferred.csproj b/samples/deferred/deferred.csproj
new file mode 100644 (file)
index 0000000..89a5f38
--- /dev/null
@@ -0,0 +1,43 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />
+  <PropertyGroup>    
+    <EnableDefaultCompileItems>false</EnableDefaultCompileItems>        
+<!--    <EnableDefaultNoneItems>false</EnableDefaultNoneItems>     -->
+  </PropertyGroup>
+        
+    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">    
+        <DefineConstants>TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;MEMORY_POOLS;WITH_SHADOWS;_WITH_VKVG</DefineConstants>    
+    </PropertyGroup>
+    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugCrow|AnyCPU' ">    
+        <DefineConstants>TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;MEMORY_POOLS;WITH_SHADOWS;_WITH_VKVG</DefineConstants>    
+    </PropertyGroup>
+    
+  <ItemGroup>    
+    <Compile Include="EnvironmentPipeline.cs;DeferredPbrRenderer.cs;shadowMapRenderer.cs" />
+  </ItemGroup>     
+
+
+  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugCrow|AnyCPU' ">    
+    <Compile Include="../common/CrowWin.cs" />
+    <Compile Include="main-crow.cs" />
+    <EmbeddedResource Include="ui\**\*.*">
+      <LogicalName>deferred.%(Filename)%(Extension)</LogicalName>
+    </EmbeddedResource>                
+  </ItemGroup>  
+    
+  <ItemGroup Condition="$(Configuration) != 'DebugCrow' ">
+    <Compile Include="main.cs" />
+  </ItemGroup>    
+    
+    
+  <ItemGroup>
+    <PackageReference Include="Crow.VK" Version="0.9.0-beta" />
+  </ItemGroup>
+    
+  <ItemGroup>
+    <Folder Include="shaders\" />
+  </ItemGroup>    
+  <ItemGroup>
+    <ProjectReference Include="..\..\addons\gltfLoader\gltfLoader.csproj" />
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/samples/deferred/main-crow.cs b/samples/deferred/main-crow.cs
new file mode 100644 (file)
index 0000000..fe0fc4c
--- /dev/null
@@ -0,0 +1,404 @@
+using System;
+using System.Numerics;
+using Glfw;
+using VK;
+using CVKL;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace deferred {
+       class Program : Crow.CrowWin {
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = false;
+#endif
+                       DeferredPbrRenderer.TEXTURE_ARRAY = true;
+                       DeferredPbrRenderer.NUM_SAMPLES = VkSampleCountFlags.SampleCount1;
+                       DeferredPbrRenderer.HDR_FORMAT = VkFormat.R32g32b32a32Sfloat;
+                       DeferredPbrRenderer.MRT_FORMAT = VkFormat.R16g16b16a16Sfloat;
+
+                       PbrModelTexArray.TEXTURE_DIM = 1024;
+
+                       using (Program vke = new Program ()) {
+                               vke.Run ();                     
+                       }
+               }
+
+               #region crow ui
+               public Crow.Command CMDViewScenes, CMDViewEditor, CMDViewDebug, CMDViewMaterials;
+               void init_crow_commands () {
+                       CMDViewScenes = new Crow.Command (new Action (() => loadWindow ("#deferred.main.crow", this))) { Caption = "Lighting", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true };
+                       CMDViewEditor = new Crow.Command (new Action (() => loadWindow ("#deferred.scenes.crow", this))) { Caption = "Scenes", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true };
+                       CMDViewDebug = new Crow.Command (new Action (() => loadWindow ("#deferred.debug.crow", this))) { Caption = "Debug", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true };
+                       CMDViewMaterials = new Crow.Command (new Action (() => loadWindow ("#deferred.materials.crow", this))) { Caption = "Materials", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true };
+               }
+
+               public DeferredPbrRenderer.DebugView CurrentDebugView {
+                       get { return renderer.currentDebugView; }
+                       set {
+                               if (value == renderer.currentDebugView)
+                                       return;
+                               lock(crow.UpdateMutex)
+                                       renderer.currentDebugView = value;
+                               rebuildBuffers = true;
+                               NotifyValueChanged ("CurrentDebugView", renderer.currentDebugView);
+                       }
+               }
+
+               public float Gamma {
+                       get { return renderer.matrices.gamma; }
+                       set {
+                               if (value == renderer.matrices.gamma)
+                                       return;
+                               renderer.matrices.gamma = value;
+                               NotifyValueChanged ("Gamma", value);
+                               updateViewRequested = true;
+                       }
+               }
+               public float Exposure {
+                       get { return renderer.matrices.exposure; }
+                       set {
+                               if (value == renderer.matrices.exposure)
+                                       return;
+                               renderer.matrices.exposure = value;
+                               NotifyValueChanged ("Exposure", value);
+                               updateViewRequested = true;
+                       }
+               }
+               public float LightStrength {
+                       get { return renderer.lights[renderer.lightNumDebug].color.X; }
+                       set {
+                               if (value == renderer.lights[renderer.lightNumDebug].color.X)
+                                       return;
+                               renderer.lights[renderer.lightNumDebug].color = new Vector4(value);
+                               NotifyValueChanged ("LightStrength", value);
+                               renderer.uboLights.Update (renderer.lights);
+                       }
+               }
+               public List<DeferredPbrRenderer.Light> Lights => renderer.lights.ToList ();
+               public List<Model.Scene> Scenes => renderer.model.Scenes;
+               #endregion
+
+               public override string[] EnabledDeviceExtensions => new string[] {
+                       Ext.D.VK_KHR_swapchain,
+                       Ext.D.VK_EXT_debug_marker
+               };
+
+               protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures features) {
+                       base.configureEnabledFeatures (available_features, ref features);
+
+                       features.samplerAnisotropy = available_features.samplerAnisotropy;
+                       features.sampleRateShading = available_features.sampleRateShading;
+                       features.geometryShader = available_features.geometryShader;
+                       features.pipelineStatisticsQuery = true;
+
+                       if (available_features.textureCompressionETC2) {
+                               features.textureCompressionETC2 = true;
+                               Image.DefaultTextureFormat = VkFormat.Etc2R8g8b8a8UnormBlock;
+                       }else if (available_features.textureCompressionBC) {
+                               //features.textureCompressionBC = true;
+                               //Image.DefaultTextureFormat = VkFormat.Bc3UnormBlock;
+                       }
+
+
+               }
+
+               protected override void createQueues () {
+                       base.createQueues ();
+                       transferQ = new Queue (dev, VkQueueFlags.Transfer);
+               }
+
+               string[] cubemapPathes = {
+                       Utils.DataDirectory + "textures/papermill.ktx",
+                       Utils.DataDirectory + "textures/cubemap_yokohama_bc3_unorm.ktx",
+                       Utils.DataDirectory + "textures/gcanyon_cube.ktx",
+                       Utils.DataDirectory + "textures/pisa_cube.ktx",
+                       Utils.DataDirectory + "textures/uffizi_cube.ktx",
+               };
+               string[] modelPathes = {
+                               //"/mnt/devel/gts/vkChess.net/data/models/chess.glb",
+                               //"/home/jp/gltf/jaguar/scene.gltf",
+                               Utils.DataDirectory + "models/DamagedHelmet/glTF/DamagedHelmet.gltf",
+                               Utils.DataDirectory + "models/shadow.glb",
+                               Utils.DataDirectory + "models/Hubble.glb",
+                               Utils.DataDirectory + "models/MER_static.glb",
+                               Utils.DataDirectory + "models/ISS_stationary.glb",
+                       };
+
+               int curModelIndex = 0;
+               bool reloadModel;
+               bool rebuildBuffers;
+
+               Queue transferQ;
+               DeferredPbrRenderer renderer;
+               PipelineStatisticsQueryPool statPool;
+               TimestampQueryPool timestampQPool;
+               ulong[] results;
+
+               DebugReport dbgRepport;
+
+               Program () : base() {
+
+                       if (Instance.DEBUG_UTILS)
+                               dbgRepport = new DebugReport (instance,
+                                       VkDebugReportFlagsEXT.ErrorEXT
+                                       | VkDebugReportFlagsEXT.DebugEXT
+                                       | VkDebugReportFlagsEXT.WarningEXT
+                                       | VkDebugReportFlagsEXT.PerformanceWarningEXT
+                               );
+
+                       camera = new Camera (Utils.DegreesToRadians (45f), 1f, 0.1f, 16f);
+                       camera.SetPosition (0, 0, 2);
+
+                       renderer = new DeferredPbrRenderer (dev, swapChain, presentQueue, cubemapPathes[2], camera.NearPlane, camera.FarPlane);
+                       renderer.LoadModel (transferQ, modelPathes[curModelIndex]);
+                       camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth));
+
+                       statPool = new PipelineStatisticsQueryPool (dev,
+                               VkQueryPipelineStatisticFlags.InputAssemblyVertices |
+                               VkQueryPipelineStatisticFlags.InputAssemblyPrimitives |
+                               VkQueryPipelineStatisticFlags.ClippingInvocations |
+                               VkQueryPipelineStatisticFlags.ClippingPrimitives |
+                               VkQueryPipelineStatisticFlags.FragmentShaderInvocations);
+
+                       timestampQPool = new TimestampQueryPool (dev);
+
+                       init_crow_commands ();
+
+                       crow.Load ("#deferred.menu.crow").DataSource = this;
+
+               }
+
+               protected override void recordDraw (CommandBuffer cmd, int imageIndex) {
+                       statPool.Begin (cmd);
+                       renderer.buildCommandBuffers (cmd, imageIndex);
+                       statPool.End (cmd);
+               }
+
+               public override void UpdateView () {
+                       renderer.UpdateView (camera);
+                       updateViewRequested = false;
+#if WITH_SHADOWS
+                       if (renderer.shadowMapRenderer.updateShadowMap)
+                               renderer.shadowMapRenderer.update_shadow_map (cmdPool);
+#endif
+               }
+
+               int frameCount = 0;
+               public override void Update () {
+                       if (reloadModel) {
+                               renderer.LoadModel (transferQ, modelPathes[curModelIndex]);
+                               reloadModel = false;
+                               camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth));
+                               updateViewRequested = true;
+                               rebuildBuffers = true;
+#if WITH_SHADOWS
+                               renderer.shadowMapRenderer.updateShadowMap = true;
+#endif
+                       }
+
+                       base.Update ();
+
+                       if (++frameCount > 20) {
+                               NotifyValueChanged ("fps", fps);
+                               frameCount = 0;
+                       }
+
+                       results = statPool.GetResults ();
+               }
+               protected override void OnResize () {           
+                       renderer.Resize ();
+                       base.OnResize ();
+               }
+
+               #region Mouse and keyboard
+               protected override void onMouseMove (double xPos, double yPos) {
+                       if (crow.ProcessMouseMove ((int)xPos, (int)yPos))
+                               return;
+
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               camera.Rotate ((float)-diffX, (float)-diffY);
+                       } else if (MouseButton[1]) {
+                               camera.SetZoom ((float)diffY);
+                       } else
+                               return;
+
+                       updateViewRequested = true;
+               }
+               protected override void onMouseButtonDown (Glfw.MouseButton button) {
+                       if (crow.ProcessMouseButtonDown ((Crow.MouseButton)button))
+                               return;
+                       base.onMouseButtonDown (button);
+               }
+               protected override void onMouseButtonUp (Glfw.MouseButton button) {
+                       if (crow.ProcessMouseButtonUp ((Crow.MouseButton)button))
+                               return;
+                       base.onMouseButtonUp (button);
+               }
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       if (crow.ProcessKeyDown ((Crow.Key)key))
+                               return;
+                       switch (key) {
+                               case Key.F:
+                                       if (modifiers.HasFlag (Modifier.Shift)) {
+                                               renderer.debugFace--;
+                                               if (renderer.debugFace < 0)
+                                                       renderer.debugFace = 5;
+                                       } else {
+                                               renderer.debugFace++;
+                                               if (renderer.debugFace >= 5)
+                                                       renderer.debugFace = 0;
+                                       }
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.M:
+                                       if (modifiers.HasFlag (Modifier.Shift)) {
+                                               renderer.debugMip--;
+                                               if (renderer.debugMip < 0)
+                                                       renderer.debugMip = (int)renderer.envCube.prefilterCube.CreateInfo.mipLevels - 1;
+                                       } else {
+                                               renderer.debugMip++;
+                                               if (renderer.debugMip >= renderer.envCube.prefilterCube.CreateInfo.mipLevels)
+                                                       renderer.debugMip = 0;
+                                       }
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.L:
+                                       if (modifiers.HasFlag (Modifier.Shift)) {
+                                               renderer.lightNumDebug--;
+                                               if (renderer.lightNumDebug < 0)
+                                                       renderer.lightNumDebug = (int)renderer.lights.Length - 1;
+                                       } else {
+                                               renderer.lightNumDebug++;
+                                               if (renderer.lightNumDebug >= renderer.lights.Length)
+                                                       renderer.lightNumDebug = 0;
+                                       }
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.Keypad0:
+                               case Key.Keypad1:
+                               case Key.Keypad2:
+                               case Key.Keypad3:
+                               case Key.Keypad4:
+                               case Key.Keypad5:
+                               case Key.Keypad6:
+                               case Key.Keypad7:
+                               case Key.Keypad8:
+                               case Key.Keypad9:
+                                       renderer.currentDebugView = (DeferredPbrRenderer.DebugView)(int)key-320;
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.KeypadDivide:
+                                       renderer.currentDebugView = DeferredPbrRenderer.DebugView.irradiance;
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.S:
+                                       if (modifiers.HasFlag (Modifier.Control)) {
+                                               renderer.pipelineCache.Save ();
+                                               Console.WriteLine ($"Pipeline Cache saved.");
+                                       } else {
+                                               renderer.currentDebugView = DeferredPbrRenderer.DebugView.shadowMap;
+                                               rebuildBuffers = true; 
+                                       }
+                                       break;
+                               case Key.Up:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight(-Vector4.UnitZ);
+                                       else
+                                               camera.Move (0, 0, 1);
+                                       break;
+                               case Key.Down:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (Vector4.UnitZ);
+                                       else
+                                               camera.Move (0, 0, -1);
+                                       break;
+                               case Key.Left:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (-Vector4.UnitX);
+                                       else
+                                               camera.Move (1, 0, 0);
+                                       break;
+                               case Key.Right:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (Vector4.UnitX);
+                                       else
+                                               camera.Move (-1, 0, 0);
+                                       break;
+                               case Key.PageUp:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (Vector4.UnitY);
+                                       else
+                                               camera.Move (0, 1, 0);
+                                       break;
+                               case Key.PageDown:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (-Vector4.UnitY);
+                                       else
+                                               camera.Move (0, -1, 0);
+                                       break;
+                               case Key.F2:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.matrices.exposure -= 0.3f;
+                                       else
+                                               renderer.matrices.exposure += 0.3f;
+                                       break;
+                               case Key.F3:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.matrices.gamma -= 0.1f;
+                                       else
+                                               renderer.matrices.gamma += 0.1f;
+                                       break;
+                               case Key.F4:
+                                       if (camera.Type == Camera.CamType.FirstPerson)
+                                               camera.Type = Camera.CamType.LookAt;
+                                       else
+                                               camera.Type = Camera.CamType.FirstPerson;
+                                       Console.WriteLine ($"camera type = {camera.Type}");
+                                       break;
+                               case Key.KeypadAdd:
+                                       curModelIndex++;
+                                       if (curModelIndex >= modelPathes.Length)
+                                               curModelIndex = 0;
+                                       reloadModel = true;
+                                       break;
+                               case Key.KeypadSubtract:
+                                       curModelIndex--;
+                                       if (curModelIndex < 0)
+                                               curModelIndex = modelPathes.Length -1;
+                                       reloadModel = true;
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       return;
+                       }
+                       updateViewRequested = true;
+               }
+               protected override void onKeyUp (Key key, int scanCode, Modifier modifiers) {
+                       if (crow.ProcessKeyUp ((Crow.Key)key))
+                               return;
+               }
+               protected override void onChar (CodePoint cp) {
+                       if (crow.ProcessKeyPress (cp.ToChar ()))
+                               return;
+               }
+               #endregion
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       renderer.Dispose ();
+                                       statPool.Dispose ();
+                                       timestampQPool.Dispose ();
+                                       dbgRepport?.Dispose ();
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/deferred/main.cs b/samples/deferred/main.cs
new file mode 100644 (file)
index 0000000..f15132b
--- /dev/null
@@ -0,0 +1,549 @@
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using CVKL;
+using CVKL.glTF;
+using Glfw;
+using VK;
+
+namespace deferred {
+       class Deferred : VkWindow {
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = false;
+#endif
+                       SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Srgb;
+                       DeferredPbrRenderer.TEXTURE_ARRAY = true;
+                       DeferredPbrRenderer.NUM_SAMPLES = VkSampleCountFlags.SampleCount1;
+                       DeferredPbrRenderer.HDR_FORMAT = VkFormat.R16g16b16a16Sfloat;
+                       DeferredPbrRenderer.MRT_FORMAT = VkFormat.R32g32b32a32Sfloat;
+
+                       PbrModelTexArray.TEXTURE_DIM = 1024;
+
+                       using (Deferred vke = new Deferred ()) {
+                               vke.Run ();
+                       }
+               }
+
+               public override string[] EnabledDeviceExtensions => new string[] {
+                       Ext.D.VK_KHR_swapchain,
+                       Ext.D.VK_EXT_debug_marker
+               };
+
+               protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) {
+                       base.configureEnabledFeatures (available_features, ref enabled_features);
+
+                       enabled_features.samplerAnisotropy = available_features.samplerAnisotropy;
+                       enabled_features.sampleRateShading = available_features.sampleRateShading;
+                       enabled_features.geometryShader = available_features.geometryShader;
+
+                       enabled_features.textureCompressionBC = available_features.textureCompressionBC;
+               }
+
+               protected override void createQueues () {
+                       base.createQueues ();
+                       transferQ = new Queue (dev, VkQueueFlags.Transfer);
+                       computeQ = new Queue (dev, VkQueueFlags.Compute);
+               }
+               string[] cubemapPathes = {
+                       Utils.DataDirectory + "textures/papermill.ktx",
+                       Utils.DataDirectory + "textures/cubemap_yokohama_bc3_unorm.ktx",
+                       Utils.DataDirectory + "textures/gcanyon_cube.ktx",
+                       Utils.DataDirectory + "textures/pisa_cube.ktx",
+                       Utils.DataDirectory + "textures/uffizi_cube.ktx",
+               };
+               string[] modelPathes = {
+                               //"/mnt/devel/gts/vkChess.net/data/models/chess.glb",
+                               Utils.DataDirectory + "models/DamagedHelmet/glTF/DamagedHelmet.gltf",
+                               Utils.DataDirectory + "models/shadow.glb",
+                               Utils.DataDirectory + "models/Hubble.glb",
+                               Utils.DataDirectory + "models/MER_static.glb",
+                               Utils.DataDirectory + "models/ISS_stationary.glb",
+                       };
+
+               int curModelIndex = 0;
+               bool reloadModel;
+               bool rebuildBuffers;
+
+               Queue transferQ, computeQ;
+               DeferredPbrRenderer renderer;
+
+
+               GraphicPipeline plToneMap;
+               Framebuffer[] frameBuffers;
+               DescriptorPool descriptorPool;
+               DescriptorSet descriptorSet;
+
+
+
+               Deferred () : base("deferred") {                
+                       camera = new Camera (Utils.DegreesToRadians (45f), 1f, 0.1f, 16f);
+                       camera.SetPosition (0, 0, 2);
+
+                       //renderer = new DeferredPbrRenderer (presentQueue, cubemapPathes[2], swapChain.Width, swapChain.Height, camera.NearPlane, camera.FarPlane);
+                       renderer = new DeferredPbrRenderer (presentQueue, cubemapPathes[2], swapChain.Width, swapChain.Height, camera.NearPlane, camera.FarPlane);
+                       renderer.LoadModel (transferQ, modelPathes[curModelIndex]);
+                       camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth));
+
+                       init_final_pl ();
+               }
+
+               void init_final_pl() {
+                       descriptorPool = new DescriptorPool (dev, 3,
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 2),
+                               new VkDescriptorPoolSize (VkDescriptorType.StorageImage, 4)
+                       );
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, DeferredPbrRenderer.NUM_SAMPLES);
+
+                       cfg.Layout = new PipelineLayout (dev,
+                               new VkPushConstantRange (VkShaderStageFlags.Fragment, 2 * sizeof (float)),
+                               new DescriptorSetLayout (dev, 0,
+                                       new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                                       new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                                       ));
+
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, DeferredPbrRenderer.NUM_SAMPLES);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.FullScreenQuad.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.tone_mapping.frag.spv");
+
+                       plToneMap = new GraphicPipeline (cfg);
+
+                       descriptorSet = descriptorPool.Allocate (cfg.Layout.DescriptorSetLayouts[0]);
+
+                       init_blur ();
+               }
+
+               ComputePipeline plBlur;
+               DescriptorSetLayout dsLayoutBlur;
+               DescriptorSet dsetBlurPing, dsetBlurPong;
+               Image downSamp, downSamp2;
+               CommandPool computeCmdPool;
+
+               struct BlurPushCsts {
+                       public Vector2 texSize;
+                       public int dir;
+                       public float scale;
+                       public float strength;
+               };
+               BlurPushCsts pcBloom = new BlurPushCsts () { strength = 1.3f, scale = 0.4f };
+
+               void init_blur () {
+                       computeCmdPool = new CommandPool (computeQ);
+
+                       blurComplete = dev.CreateSemaphore ();
+
+                       dsLayoutBlur = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageImage),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageImage)
+                       );
+                       plBlur = new ComputePipeline (
+                               new PipelineLayout (dev, new VkPushConstantRange (VkShaderStageFlags.Compute, (uint)Marshal.SizeOf<BlurPushCsts> ()), dsLayoutBlur),
+                               "#deferred.bloom.comp.spv");
+
+                       dsetBlurPing = descriptorPool.Allocate (dsLayoutBlur);
+                       dsetBlurPong = descriptorPool.Allocate (dsLayoutBlur);
+               }
+
+               void buildBlurCmd (CommandBuffer cmd) {
+                       renderer.hdrImgResolved.SetLayout (cmd, VkImageAspectFlags.Color,
+                               VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.TransferRead,
+                               VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal,
+                               VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.Transfer);
+                       downSamp.SetLayout (cmd, VkImageAspectFlags.Color,
+                               VkAccessFlags.ShaderRead, VkAccessFlags.TransferWrite,
+                               VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.TransferDstOptimal,
+                               VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.Transfer);
+
+
+                       renderer.hdrImgResolved.BlitTo (cmd, downSamp);
+
+                       renderer.hdrImgResolved.SetLayout (cmd, VkImageAspectFlags.Color,
+                               VkImageLayout.TransferSrcOptimal, VkImageLayout.ShaderReadOnlyOptimal,
+                               VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader);
+
+                       downSamp2.SetLayout (cmd, VkImageAspectFlags.Color,
+                               0, VkAccessFlags.MemoryWrite,
+                               VkImageLayout.Undefined, VkImageLayout.General,
+                               VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.ComputeShader);
+
+                       downSamp.SetLayout (cmd, VkImageAspectFlags.Color,
+                               VkAccessFlags.TransferWrite, VkAccessFlags.MemoryRead,
+                               VkImageLayout.TransferDstOptimal, VkImageLayout.General,
+                               VkPipelineStageFlags.Transfer, VkPipelineStageFlags.ComputeShader);
+
+                       plBlur.Bind (cmd);
+
+                       pcBloom.dir = 0;
+                       /*
+                       plBlur.BindDescriptorSet (cmd, dsetBlurPing);
+                       cmd.PushConstant (plBlur.Layout, VkShaderStageFlags.Compute, pcBloom);
+                       cmd.Dispatch (downSamp.Width / 16, downSamp.Height / 16);
+
+                       cmd.SetMemoryBarrier (VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.ComputeShader,
+                                                                       VkAccessFlags.ShaderWrite, VkAccessFlags.ShaderRead);
+                               
+                       plBlur.BindDescriptorSet (cmd, dsetBlurPong);
+                       cmd.PushConstant (plBlur.Layout, VkShaderStageFlags.Compute, 1, (uint)Marshal.SizeOf<Vector2> ());
+                       cmd.Dispatch (downSamp.Width / 16, downSamp.Height / 16);
+
+                       downSamp.SetLayout (cmd, VkImageAspectFlags.Color,
+                               VkAccessFlags.MemoryWrite, VkAccessFlags.ShaderRead,
+                               VkImageLayout.General, VkImageLayout.ShaderReadOnlyOptimal,
+                               VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.FragmentShader);*/
+
+                       downSamp.SetLayout (cmd, VkImageAspectFlags.Color,
+                               VkAccessFlags.TransferWrite, VkAccessFlags.ShaderRead,
+                               VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal,
+                               VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader);
+                               
+                       cmd.End ();
+               }
+
+
+               CommandBuffer cmdPbr;
+               CommandBuffer cmdBlur;
+               VkSemaphore blurComplete;
+               const uint downSizing = 1;
+               float finalDebug = -1.0f;
+
+               void buildCommandBuffers () {
+                       cmdPbr?.Free ();
+                       cmdPbr = cmdPool.AllocateAndStart ();
+                       renderer.buildCommandBuffers (cmdPbr);
+                       cmdPbr.End ();
+
+                       cmdBlur?.Free ();
+                       cmdBlur = computeCmdPool.AllocateAndStart ();
+                       buildBlurCmd (cmdBlur);
+
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               cmds[i]?.Free ();
+                               cmds[i] = cmdPool.AllocateAndStart ();
+
+                               //renderer.hdrImgResolved.SetLayout (cmds[i], VkImageAspectFlags.Color,
+                                       //VkAccessFlags.TransferRead, VkAccessFlags.ShaderRead,
+                                       //VkImageLayout.TransferSrcOptimal, VkImageLayout.ShaderReadOnlyOptimal,
+                                       //VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader);
+
+                               plToneMap.RenderPass.Begin (cmds[i], frameBuffers[i]);
+
+                               cmds[i].SetViewport (frameBuffers[i].Width, frameBuffers[i].Height);
+                               cmds[i].SetScissor (frameBuffers[i].Width, frameBuffers[i].Height);
+
+                               plToneMap.Bind (cmds[i]);
+                               plToneMap.BindDescriptorSet (cmds[i], descriptorSet);
+
+                               cmds[i].PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 12, new float[] { renderer.exposure, renderer.gamma, finalDebug }, 0);
+
+                               cmds[i].Draw (3, 1, 0, 0);
+
+                               plToneMap.RenderPass.End (cmds[i]);
+
+                               renderer.hdrImgResolved.SetLayout (cmds[i], VkImageAspectFlags.Color,
+                                       VkAccessFlags.ShaderRead, VkAccessFlags.ColorAttachmentWrite,
+                                       VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.ColorAttachmentOptimal,
+                                       VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.ColorAttachmentOutput);
+
+                               cmds[i].End ();
+                       }
+               }
+
+               public override void UpdateView () {
+                       renderer.UpdateView (camera);
+                       updateViewRequested = false;
+#if WITH_SHADOWS
+                       if (renderer.shadowMapRenderer.updateShadowMap)
+                               renderer.shadowMapRenderer.update_shadow_map (cmdPool);
+#endif
+               }
+
+               public override void Update () {
+                       if (reloadModel) {
+                               renderer.LoadModel (transferQ, modelPathes[curModelIndex]);
+                               reloadModel = false;
+                               camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth));
+                               updateViewRequested = true;
+                               rebuildBuffers = true;
+#if WITH_SHADOWS
+                               renderer.shadowMapRenderer.updateShadowMap = true;
+#endif
+                       }
+
+                       if (rebuildBuffers) {
+                               buildCommandBuffers ();
+                               rebuildBuffers = false;
+                       }
+
+               }
+
+
+               protected override void render () {
+                       int idx = swapChain.GetNextImage ();
+                       if (idx < 0) {
+                               OnResize ();
+                               return;
+                       }
+
+                       if (cmds[idx] == null)
+                               return;
+
+                       presentQueue.Submit (cmdPbr, swapChain.presentComplete, renderer.DrawComplete);
+
+                       computeQ.Submit (cmdBlur, renderer.DrawComplete, blurComplete);
+
+                       presentQueue.Submit (cmds[idx], blurComplete, drawComplete[idx]);
+                       presentQueue.Present (swapChain, drawComplete[idx]);
+
+                       presentQueue.WaitIdle ();
+               }
+               protected override void OnResize () {
+                       dev.WaitIdle ();
+
+                       renderer.Resize (swapChain.Width, swapChain.Height);
+
+                       UpdateView ();
+
+                       downSamp?.Dispose ();
+                       downSamp2?.Dispose ();
+                       downSamp = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Storage | VkImageUsageFlags.Sampled,
+                               VkMemoryPropertyFlags.DeviceLocal, renderer.Width / downSizing, renderer.Height / downSizing, VkImageType.Image2D);
+                       downSamp2 = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.Storage,
+                               VkMemoryPropertyFlags.DeviceLocal, renderer.Width / downSizing, renderer.Height/ downSizing, VkImageType.Image2D);
+                       downSamp.CreateView (); downSamp.CreateSampler ();
+                       downSamp.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                       downSamp2.CreateView (); downSamp2.CreateSampler ();
+                       downSamp2.Descriptor.imageLayout = VkImageLayout.General;
+
+                       downSamp.SetName ("HDRimgDownScaled");
+                       downSamp2.SetName ("HDRimgDownScaled2");
+
+                       pcBloom.texSize.X = downSamp.Width;
+                       pcBloom.texSize.Y = downSamp.Height;
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (plToneMap.RenderPass, swapChain.Width, swapChain.Height,
+                                       (plToneMap.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                       } : new Image[] {
+                                               null,
+                                               swapChain.images[i]
+                                       });
+                       }
+
+                       DescriptorSetWrites dsUpdate = new DescriptorSetWrites (plToneMap.Layout.DescriptorSetLayouts[0]);
+                       dsUpdate.Write (dev, descriptorSet, renderer.hdrImgResolved.Descriptor, downSamp.Descriptor);
+
+                       dsUpdate = new DescriptorSetWrites (dsLayoutBlur);
+                       downSamp.Descriptor.imageLayout = VkImageLayout.General;
+                       dsUpdate.Write (dev, dsetBlurPong, downSamp2.Descriptor, downSamp.Descriptor);
+                       dsUpdate.Write (dev, dsetBlurPing, downSamp.Descriptor, downSamp2.Descriptor);
+
+                       buildCommandBuffers ();
+
+                       dev.WaitIdle ();
+               }
+
+               #region Mouse and keyboard
+               protected override void onScroll (double xOffset, double yOffset) {
+               }
+               protected override void onMouseMove (double xPos, double yPos) {
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               camera.Rotate ((float)-diffX, (float)-diffY);
+                       } else if (MouseButton[1]) {
+                               camera.SetZoom ((float)diffY);
+                       } else
+                               return;
+
+                       updateViewRequested = true;
+               }
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.F:
+                                       if (modifiers.HasFlag (Modifier.Shift)) {
+                                               renderer.debugFace--;
+                                               if (renderer.debugFace < 0)
+                                                       renderer.debugFace = 5;
+                                       } else {
+                                               renderer.debugFace++;
+                                               if (renderer.debugFace >= 5)
+                                                       renderer.debugFace = 0;
+                                       }
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.M:
+                                       if (modifiers.HasFlag (Modifier.Shift)) {
+                                               renderer.debugMip--;
+                                               if (renderer.debugMip < 0)
+                                                       renderer.debugMip = (int)renderer.envCube.prefilterCube.CreateInfo.mipLevels - 1;
+                                       } else {
+                                               renderer.debugMip++;
+                                               if (renderer.debugMip >= renderer.envCube.prefilterCube.CreateInfo.mipLevels)
+                                                       renderer.debugMip = 0;
+                                       }
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.L:
+                                       if (modifiers.HasFlag (Modifier.Shift)) {
+                                               renderer.lightNumDebug--;
+                                               if (renderer.lightNumDebug < 0)
+                                                       renderer.lightNumDebug = (int)renderer.lights.Length - 1;
+                                       } else {
+                                               renderer.lightNumDebug++;
+                                               if (renderer.lightNumDebug >= renderer.lights.Length)
+                                                       renderer.lightNumDebug = 0;
+                                       }
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.Keypad0:
+                               case Key.Keypad1:
+                               case Key.Keypad2:
+                               case Key.Keypad3:
+                               case Key.Keypad4:
+                               case Key.Keypad5:
+                               case Key.Keypad6:
+                               case Key.Keypad7:
+                               case Key.Keypad8:
+                               case Key.Keypad9:
+                                       renderer.currentDebugView = (DeferredPbrRenderer.DebugView)(int)key-320;
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.KeypadDivide:
+                                       renderer.currentDebugView = DeferredPbrRenderer.DebugView.irradiance;
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.S:
+                                       if (modifiers.HasFlag (Modifier.Control)) {
+                                               renderer.pipelineCache.Save ();
+                                               Console.WriteLine ($"Pipeline Cache saved.");
+                                       } else {
+                                               renderer.currentDebugView = DeferredPbrRenderer.DebugView.shadowMap;
+                                               rebuildBuffers = true; 
+                                       }
+                                       break;
+                               case Key.Up:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight(-Vector4.UnitZ);
+                                       else
+                                               camera.Move (0, 0, 1);
+                                       break;
+                               case Key.Down:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (Vector4.UnitZ);
+                                       else
+                                               camera.Move (0, 0, -1);
+                                       break;
+                               case Key.Left:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (-Vector4.UnitX);
+                                       else
+                                               camera.Move (1, 0, 0);
+                                       break;
+                               case Key.Right:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (Vector4.UnitX);
+                                       else
+                                               camera.Move (-1, 0, 0);
+                                       break;
+                               case Key.PageUp:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (Vector4.UnitY);
+                                       else
+                                               camera.Move (0, 1, 0);
+                                       break;
+                               case Key.PageDown:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.MoveLight (-Vector4.UnitY);
+                                       else
+                                               camera.Move (0, -1, 0);
+                                       break;
+                               case Key.F2:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.exposure -= 0.3f;
+                                       else
+                                               renderer.exposure += 0.3f;
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.F3:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               renderer.gamma -= 0.1f;
+                                       else
+                                               renderer.gamma += 0.1f;
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.D:
+                                       finalDebug = -finalDebug;
+                                       rebuildBuffers = true;
+                                       break;
+                               case Key.B:
+                                       if (modifiers.HasFlag (Modifier.Control)) {
+                                               if (modifiers.HasFlag (Modifier.Shift))
+                                                       pcBloom.strength -= 0.1f;
+                                               else
+                                                       pcBloom.strength += 0.1f;
+                                       } else {
+                                               if (modifiers.HasFlag (Modifier.Shift))
+                                                       pcBloom.scale *= 1.1f;
+                                               else
+                                                       pcBloom.scale *= 0.9f;
+                                       }
+                                       Console.WriteLine ($"Bloom: scale = {pcBloom.scale}, strength = {pcBloom.strength}");
+                                       rebuildBuffers = true;
+                                       //if (camera.Type == Camera.CamType.FirstPerson)
+                                       //      camera.Type = Camera.CamType.LookAt;
+                                       //else
+                                       //      camera.Type = Camera.CamType.FirstPerson;
+                                       //Console.WriteLine ($"camera type = {camera.Type}");
+                                       break;
+                               case Key.KeypadAdd:
+                                       curModelIndex++;
+                                       if (curModelIndex >= modelPathes.Length)
+                                               curModelIndex = 0;
+                                       reloadModel = true;
+                                       break;
+                               case Key.KeypadSubtract:
+                                       curModelIndex--;
+                                       if (curModelIndex < 0)
+                                               curModelIndex = modelPathes.Length -1;
+                                       reloadModel = true;
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       return;
+                       }
+                       updateViewRequested = true;
+               }
+               #endregion
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       computeCmdPool.Dispose ();
+                                       downSamp?.Dispose ();
+                                       downSamp2?.Dispose ();
+                                       if (frameBuffers != null)
+                                               foreach (Framebuffer fb in frameBuffers)
+                                                       fb.Dispose ();
+                                       renderer.Dispose ();
+                                       plBlur.Dispose ();
+                                       plToneMap.Dispose ();
+                                       descriptorPool.Dispose ();
+                               }
+                               dev.DestroySemaphore (blurComplete);
+                       }
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/deferred/mainShadow.cs b/samples/deferred/mainShadow.cs
new file mode 100644 (file)
index 0000000..0bc3103
--- /dev/null
@@ -0,0 +1,415 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Glfw;
+using VK;
+using CVKL;
+
+namespace deferredShadow {
+       class Program : VkWindow{       
+               static void Main (string[] args) {
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1;
+
+               public struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 view;
+                       public Matrix4x4 model;
+                       public Vector4 lightPos;
+                       public float gamma;
+                       public float exposure;
+               }
+
+               public Matrices matrices = new Matrices {
+                       lightPos = new Vector4 (1.0f, 0.0f, 0.0f, 1.0f),
+                       gamma = 1.0f,
+                       exposure = 2.0f,
+               };
+
+               const uint SHADOW_MAP_SIZE = 4096;
+
+#if DEBUG
+               PipelineStatisticsQueryPool statPool;
+               TimestampQueryPool timestampQPool;
+               ulong[] results;
+#endif
+
+               protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) {
+                       base.configureEnabledFeatures (ref features);
+#if DEBUG
+                       features.pipelineStatisticsQuery = true;
+#endif
+               }
+
+               Program () : base(true) {
+                       //camera.Model = Matrix4x4.CreateRotationX (Utils.DegreesToRadians (-90)) * Matrix4x4.CreateRotationY (Utils.DegreesToRadians (180));
+                       //camera.SetRotation (-0.1f,-0.4f);
+                       camera.SetPosition (0, 0, -3);
+
+                       init ();
+
+#if DEBUG
+                       statPool = new PipelineStatisticsQueryPool (dev,
+                               VkQueryPipelineStatisticFlags.InputAssemblyVertices |
+                               VkQueryPipelineStatisticFlags.InputAssemblyPrimitives |
+                               VkQueryPipelineStatisticFlags.ClippingInvocations |
+                               VkQueryPipelineStatisticFlags.ClippingPrimitives |
+                               VkQueryPipelineStatisticFlags.FragmentShaderInvocations);
+
+                       timestampQPool = new TimestampQueryPool (dev);
+#endif
+               }
+
+               Framebuffer[] frameBuffers;
+               Image gbColorRough, gbEmitMetal, gbN, gbPos;
+
+               DescriptorPool descriptorPool;
+               DescriptorSetLayout descLayoutMain, descLayoutModelTextures, descLayoutGBuff;
+               DescriptorSet dsMain, dsGBuff;
+
+               Pipeline gBuffPipeline, composePipeline;
+
+               HostBuffer uboMats;
+
+               RenderPass renderPass;
+
+               Model model;
+               EnvironmentCube envCube;
+
+               DebugDrawPipeline debugDraw;
+               Framebuffer[] debugFB;
+
+               void init () {
+                       VkFormat depthFormat = dev.GetSuitableDepthFormat ();
+                       renderPass = new RenderPass (dev);
+
+                       renderPass.AddAttachment (swapChain.ColorFormat, VkImageLayout.ColorAttachmentOptimal, VkSampleCountFlags.SampleCount1);
+                       renderPass.AddAttachment (depthFormat, VkImageLayout.DepthStencilAttachmentOptimal, samples);
+                       renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal);
+                       renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal);
+                       renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal);
+                       renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal);
+                       //renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.DepthStencilReadOnlyOptimal);
+
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+               renderPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+
+                       SubPass[] subpass = { new SubPass (), new SubPass (), new SubPass() };
+                       subpass[0].AddColorReference (  new VkAttachmentReference (2, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (3, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (4, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (5, VkImageLayout.ColorAttachmentOptimal));
+                       subpass[0].SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal);
+
+                       subpass[1].AddColorReference (0, VkImageLayout.ColorAttachmentOptimal);
+                       subpass[1].AddInputReference (  new VkAttachmentReference (2, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (3, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (4, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (5, VkImageLayout.ShaderReadOnlyOptimal));
+                       renderPass.AddSubpass (subpass);
+
+                       renderPass.AddDependency (Vk.SubpassExternal, 0,
+                VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput,
+                VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite);
+                       renderPass.AddDependency (0, 1,
+                VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader,
+                VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead);
+               renderPass.AddDependency (1, Vk.SubpassExternal,
+                VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe,
+                VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead);
+
+                        
+                       descriptorPool = new DescriptorPool (dev, 3,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 3),
+                               new VkDescriptorPoolSize (VkDescriptorType.InputAttachment, 4)
+                       );
+
+                       descLayoutMain = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));
+
+                       descLayoutModelTextures = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       descLayoutGBuff = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment));
+
+                       dsMain = descriptorPool.Allocate (descLayoutMain);
+                       dsGBuff = descriptorPool.Allocate (descLayoutGBuff);
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples);
+                       cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutModelTextures, descLayoutGBuff);
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf<Matrix4x4> ()),
+                               new VkPushConstantRange (VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf<Model.PbrMaterial> (), 64)
+                       );
+                       cfg.RenderPass = renderPass;
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+
+                       cfg.AddVertexBinding<Model.Vertex> (0);
+                       cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat);
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/pbrtest.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/GBuffPbr.frag.spv");
+
+                       gBuffPipeline = new GraphicPipeline (cfg);
+                       cfg.blendAttachments.Clear ();
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.ResetShadersAndVerticesInfos ();
+                       cfg.SubpassIndex = 1;
+                       cfg.Layout = gBuffPipeline.Layout;
+                       cfg.depthStencilState.depthTestEnable = false;
+                       cfg.depthStencilState.depthWriteEnable = false;
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/FullScreenQuad.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/pbrtest.frag.spv");
+                       composePipeline = new GraphicPipeline (cfg);
+
+                       envCube = new EnvironmentCube (presentQueue, renderPass);
+
+                       uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, (ulong)Marshal.SizeOf<Matrices> () * 2);
+                       uboMats.Map ();//permanent map
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutMain);
+                       uboUpdate.Write (dev, dsMain, uboMats.Descriptor,
+                               envCube.lutBrdf.Descriptor,
+                               envCube.irradianceCube.Descriptor,
+                               envCube.prefilterCube.Descriptor);
+                       uboMats.Descriptor.offset = (ulong)Marshal.SizeOf<Matrices> ();
+                       envCube.WriteDesc (uboMats.Descriptor);
+#if DEBUG
+                       debugDraw = new DebugDrawPipeline (dev, descLayoutMain, swapChain.ColorFormat);
+                       debugDraw.AddLine (Vector3.Zero, new Vector3(matrices.lightPos.X,matrices.lightPos.Y,matrices.lightPos.Z)*3, 1, 1, 1);
+                       debugDraw.AddLine (Vector3.Zero, Vector3.UnitX, 1, 0, 0);
+                       debugDraw.AddLine (Vector3.Zero, Vector3.UnitY, 0, 1, 0);
+                       debugDraw.AddLine (Vector3.Zero, Vector3.UnitZ, 0, 0, 1);
+#endif
+
+
+                       model = new Model (presentQueue, "../data/models/DamagedHelmet/glTF/DamagedHelmet.gltf");
+                       //model = new Model (presentQueue, "../data/models/chess.gltf");
+                       //model = new Model (presentQueue, "../data/models/Sponza/glTF/Sponza.gltf");
+                       //model = new Model (dev, presentQueue, "../data/models/icosphere.gltf");
+                       //model = new Model (dev, presentQueue, cmdPool, "../data/models/cube.gltf");
+                       model.WriteMaterialsDescriptorSets (descLayoutModelTextures,
+                               VK.AttachmentType.Color,
+                               VK.AttachmentType.Normal,
+                               VK.AttachmentType.AmbientOcclusion,
+                               VK.AttachmentType.PhysicalProps,
+                               VK.AttachmentType.Emissive);                            
+               }
+
+               void buildCommandBuffers () {
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               cmds[i]?.Free ();
+                               cmds[i] = cmdPool.AllocateCommandBuffer ();
+                               cmds[i].Start ();
+
+#if DEBUG
+                               statPool.Begin (cmds[i]);
+                               recordDraw (cmds[i], frameBuffers[i]);
+                               statPool.End (cmds[i]);
+
+                               debugDraw.RecordDraw (cmds[i], debugFB[i], camera);
+#else
+                               recordDraw (cmds[i], frameBuffers[i]);
+#endif
+
+                               cmds[i].End ();
+                       }
+               }
+               void recordDraw (CommandBuffer cmd, Framebuffer fb) {
+                       renderPass.Begin (cmd, fb);
+
+                       cmd.SetViewport (fb.Width, fb.Height);
+                       cmd.SetScissor (fb.Width, fb.Height);
+
+                       cmd.BindDescriptorSet (gBuffPipeline.Layout, dsMain);
+                       gBuffPipeline.Bind (cmd);
+                       model.Bind (cmd);
+                       model.DrawAll (cmd, gBuffPipeline.Layout);
+
+                       renderPass.BeginSubPass (cmd);
+
+                       cmd.BindDescriptorSet (composePipeline.Layout, dsGBuff, 2);
+                       composePipeline.Bind (cmd);
+
+                       cmd.Draw (3, 1, 0, 0);
+
+                       renderPass.End (cmd);
+               }
+
+#region update
+               void updateMatrices () {
+                       camera.AspectRatio = (float)swapChain.Width / swapChain.Height;
+
+                       matrices.projection = camera.Projection;
+                       matrices.view = camera.View;
+                       matrices.model = camera.Model;
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+                       matrices.view *= Matrix4x4.CreateTranslation (-matrices.view.Translation);
+                       matrices.model = Matrix4x4.Identity;
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> (), (uint)Marshal.SizeOf<Matrices> ());
+               }
+
+               public override void UpdateView () {
+                       updateMatrices ();
+                       updateViewRequested = false;
+               }
+               public override void Update () {
+#if DEBUG
+                       results = statPool.GetResults ();
+#endif
+               }
+#endregion
+
+
+
+               void createGBuff () {
+                       gbColorRough?.Dispose ();
+                       gbEmitMetal?.Dispose ();
+                       gbN?.Dispose ();
+                       gbPos?.Dispose ();
+
+                       gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height);
+                       gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height);
+                       gbN = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height);
+                       gbPos = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height);
+
+                       gbColorRough.CreateView ();
+                       gbColorRough.CreateSampler ();
+                       gbEmitMetal.CreateView ();
+                       gbEmitMetal.CreateSampler ();
+                       gbN.CreateView ();
+                       gbN.CreateSampler ();
+                       gbPos.CreateView ();
+                       gbPos.CreateSampler ();
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff);
+                       uboUpdate.Write (dev, dsGBuff,  gbColorRough.Descriptor,
+                                                                               gbEmitMetal.Descriptor,
+                                                                               gbN.Descriptor,
+                                                                               gbPos.Descriptor);
+                       gbColorRough.SetName ("GBuffColorRough");
+                       gbEmitMetal.SetName ("GBuffEmitMetal");
+                       gbN.SetName ("GBuffN");
+                       gbPos.SetName ("GBuffPos");
+               }
+
+               protected override void OnResize () {
+                       updateMatrices ();
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+#if DEBUG
+                       if (debugFB != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       debugFB[i]?.Dispose ();
+#endif
+
+                       createGBuff ();
+
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+                       debugFB = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (renderPass, swapChain.Width, swapChain.Height, new Image[] {
+                                       swapChain.images[i], null, gbColorRough, gbEmitMetal, gbN, gbPos});
+#if DEBUG
+                               debugFB[i] = new Framebuffer (debugDraw.RenderPass, swapChain.Width, swapChain.Height,(debugDraw.Samples == VkSampleCountFlags.SampleCount1)
+                                       ? new Image[] { swapChain.images[i] } : new Image[] { null, swapChain.images[i] });
+                               debugFB[i].SetName ("main FB " + i);
+#endif
+                       }
+
+                       buildCommandBuffers ();
+               }
+
+
+               #region Mouse and keyboard
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.F1:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               matrices.exposure -= 0.3f;
+                                       else
+                                               matrices.exposure += 0.3f;
+                                       break;
+                               case Key.F2:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               matrices.gamma -= 0.1f;
+                                       else
+                                               matrices.gamma += 0.1f;
+                                       break;
+                               case Key.F3:
+                                       if (camera.Type == Camera.CamType.FirstPerson)
+                                               camera.Type = Camera.CamType.LookAt;
+                                       else
+                                               camera.Type = Camera.CamType.FirstPerson;
+                                       Console.WriteLine ($"camera type = {camera.Type}");
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       return;
+                       }
+                       updateViewRequested = true;
+               }
+#endregion
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+                                       for (int i = 0; i < swapChain.ImageCount; ++i)
+                                               frameBuffers[i]?.Dispose ();
+
+                                       gbColorRough.Dispose ();
+                                       gbEmitMetal.Dispose ();
+                                       gbN.Dispose ();
+                                       gbPos.Dispose ();
+
+                                       gBuffPipeline.Dispose ();
+                                       composePipeline.Dispose ();
+
+                                       descLayoutMain.Dispose ();
+                                       descLayoutModelTextures.Dispose ();
+                                       descLayoutGBuff.Dispose ();
+
+                                       uboMats.Dispose ();
+                                       model.Dispose ();
+                                       envCube.Dispose ();
+
+                                       descriptorPool.Dispose ();
+#if DEBUG
+                                       debugDraw.Dispose ();
+                                       timestampQPool?.Dispose ();
+                                       statPool?.Dispose ();
+#endif
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/deferred/mainWithDebugDrawer.cs b/samples/deferred/mainWithDebugDrawer.cs
new file mode 100644 (file)
index 0000000..f95ed0a
--- /dev/null
@@ -0,0 +1,412 @@
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Glfw;
+using VK;
+using CVKL;
+
+namespace deferredDebug {
+       class Program : VkWindow{       
+               static void Main (string[] args) {
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1;
+
+               public struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 view;
+                       public Matrix4x4 model;
+                       public Vector4 lightPos;
+                       public float gamma;
+                       public float exposure;
+               }
+
+               public Matrices matrices = new Matrices {
+                       lightPos = new Vector4 (1.0f, 0, 0, 1.0f),
+                       gamma = 1.0f,
+                       exposure = 2.0f,
+               };
+
+#if DEBUG
+               PipelineStatisticsQueryPool statPool;
+               TimestampQueryPool timestampQPool;
+               ulong[] results;
+#endif
+
+               protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) {
+                       base.configureEnabledFeatures (ref features);
+#if DEBUG
+                       features.pipelineStatisticsQuery = true;
+#endif
+               }
+
+               Program () : base(true) {
+                       camera.Model = Matrix4x4.CreateRotationX (Utils.DegreesToRadians (-90)) * Matrix4x4.CreateRotationY (Utils.DegreesToRadians (180));
+                       camera.SetRotation (-0.1f,-0.4f);
+                       camera.SetPosition (0, 0, -3);
+
+                       init ();
+
+#if DEBUG
+                       statPool = new PipelineStatisticsQueryPool (dev,
+                               VkQueryPipelineStatisticFlags.InputAssemblyVertices |
+                               VkQueryPipelineStatisticFlags.InputAssemblyPrimitives |
+                               VkQueryPipelineStatisticFlags.ClippingInvocations |
+                               VkQueryPipelineStatisticFlags.ClippingPrimitives |
+                               VkQueryPipelineStatisticFlags.FragmentShaderInvocations);
+
+                       timestampQPool = new TimestampQueryPool (dev);
+#endif
+               }
+
+               Framebuffer[] frameBuffers;
+               Image gbColorRough, gbEmitMetal, gbN, gbPos;
+
+               DescriptorPool descriptorPool;
+               DescriptorSetLayout descLayoutMain, descLayoutModelTextures, descLayoutGBuff;
+               DescriptorSet dsMain, dsGBuff;
+
+               Pipeline gBuffPipeline, composePipeline;
+
+               HostBuffer uboMats;
+
+               RenderPass renderPass;
+
+               Model model;
+               EnvironmentCube envCube;
+
+               DebugDrawPipeline debugDraw;
+               Framebuffer[] debugFB;
+
+               void init () {
+                       renderPass = new RenderPass (dev);
+                       renderPass.AddAttachment (swapChain.ColorFormat, VkImageLayout.ColorAttachmentOptimal, VkSampleCountFlags.SampleCount1);
+                       renderPass.AddAttachment (dev.GetSuitableDepthFormat(), VkImageLayout.DepthStencilAttachmentOptimal, samples);
+                       renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal);
+                       renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal);
+                       renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal);
+                       renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal);
+
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+               renderPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                       renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+
+                       SubPass[] subpass = { new SubPass (), new SubPass () };
+                       subpass[0].AddColorReference (  new VkAttachmentReference (2, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (3, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (4, VkImageLayout.ColorAttachmentOptimal),
+                                                                       new VkAttachmentReference (5, VkImageLayout.ColorAttachmentOptimal));
+                       subpass[0].SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal);
+
+                       subpass[1].AddColorReference (0, VkImageLayout.ColorAttachmentOptimal);
+                       subpass[1].AddInputReference (  new VkAttachmentReference (2, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (3, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (4, VkImageLayout.ShaderReadOnlyOptimal),
+                                                                       new VkAttachmentReference (5, VkImageLayout.ShaderReadOnlyOptimal));
+                       renderPass.AddSubpass (subpass);
+
+                       renderPass.AddDependency (Vk.SubpassExternal, 0,
+                VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput,
+                VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite);
+                       renderPass.AddDependency (0, 1,
+                VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader,
+                VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead);
+               renderPass.AddDependency (1, Vk.SubpassExternal,
+                VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe,
+                VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead);
+
+                        
+                       descriptorPool = new DescriptorPool (dev, 3,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 3),
+                               new VkDescriptorPoolSize (VkDescriptorType.InputAttachment, 4)
+                       );
+
+                       descLayoutMain = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));
+
+                       descLayoutModelTextures = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       descLayoutGBuff = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment));
+
+                       dsMain = descriptorPool.Allocate (descLayoutMain);
+                       dsGBuff = descriptorPool.Allocate (descLayoutGBuff);
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples);
+                       cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutModelTextures, descLayoutGBuff);
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf<Matrix4x4> ()),
+                               new VkPushConstantRange (VkShaderStageFlags.Fragment, sizeof(int), 64)
+                       );
+                       cfg.RenderPass = renderPass;
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+
+                       cfg.AddVertexBinding<Model.Vertex> (0);
+                       cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat);
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/pbrtest.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/GBuffPbr.frag.spv");
+
+                       gBuffPipeline = new GraphicPipeline (cfg);
+                       cfg.blendAttachments.Clear ();
+                       cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+                       cfg.ResetShadersAndVerticesInfos ();
+                       cfg.SubpassIndex = 1;
+                       cfg.Layout = gBuffPipeline.Layout;
+                       cfg.depthStencilState.depthTestEnable = false;
+                       cfg.depthStencilState.depthWriteEnable = false;
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/FullScreenQuad.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/pbrtest.frag.spv");
+                       composePipeline = new GraphicPipeline (cfg);
+
+                       envCube = new EnvironmentCube (presentQueue, renderPass);
+
+                       uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, (ulong)Marshal.SizeOf<Matrices> () * 2);
+                       uboMats.Map ();//permanent map
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutMain);
+                       uboUpdate.Write (dev, dsMain, uboMats.Descriptor,
+                               envCube.lutBrdf.Descriptor,
+                               envCube.irradianceCube.Descriptor,
+                               envCube.prefilterCube.Descriptor);
+                       uboMats.Descriptor.offset = (ulong)Marshal.SizeOf<Matrices> ();
+                       envCube.WriteDesc (uboMats.Descriptor);
+#if DEBUG
+                       debugDraw = new DebugDrawPipeline (dev, descLayoutMain, swapChain.ColorFormat);
+                       debugDraw.AddLine (Vector3.Zero, new Vector3(matrices.lightPos.X,matrices.lightPos.Y,matrices.lightPos.Z)*3, 1, 1, 1);
+                       debugDraw.AddLine (Vector3.Zero, Vector3.UnitX, 1, 0, 0);
+                       debugDraw.AddLine (Vector3.Zero, Vector3.UnitY, 0, 1, 0);
+                       debugDraw.AddLine (Vector3.Zero, Vector3.UnitZ, 0, 0, 1);
+#endif
+
+
+                       model = new Model (presentQueue, "../data/models/DamagedHelmet/glTF/DamagedHelmet.gltf");
+                       //model = new Model (presentQueue, "../data/models/chess.gltf");
+                       //model = new Model (presentQueue, "../data/models/Sponza/glTF/Sponza.gltf");
+                       //model = new Model (dev, presentQueue, "../data/models/icosphere.gltf");
+                       //model = new Model (dev, presentQueue, cmdPool, "../data/models/cube.gltf");
+                       model.WriteMaterialsDescriptorSets (descLayoutModelTextures,
+                               VK.AttachmentType.Color,
+                               VK.AttachmentType.Normal,
+                               VK.AttachmentType.AmbientOcclusion,
+                               VK.AttachmentType.PhysicalProps,
+                               VK.AttachmentType.Emissive);                            
+               }
+
+               void buildCommandBuffers () {
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               cmds[i]?.Free ();
+                               cmds[i] = cmdPool.AllocateCommandBuffer ();
+                               cmds[i].Start ();
+
+#if DEBUG
+                               statPool.Begin (cmds[i]);
+                               recordDraw (cmds[i], frameBuffers[i]);
+                               statPool.End (cmds[i]);
+
+                               debugDraw.RecordDraw (cmds[i], debugFB[i], camera);
+#else
+                               recordDraw (cmds[i], frameBuffers[i]);
+#endif
+
+                               cmds[i].End ();
+                       }
+               }
+               void recordDraw (CommandBuffer cmd, Framebuffer fb) {
+                       renderPass.Begin (cmd, fb);
+
+                       cmd.SetViewport (fb.Width, fb.Height);
+                       cmd.SetScissor (fb.Width, fb.Height);
+
+                       cmd.BindDescriptorSet (gBuffPipeline.Layout, dsMain);
+                       gBuffPipeline.Bind (cmd);
+                       model.Bind (cmd);
+                       model.DrawAll (cmd, gBuffPipeline.Layout);
+
+                       renderPass.BeginSubPass (cmd);
+
+                       cmd.BindDescriptorSet (composePipeline.Layout, dsGBuff, 2);
+                       composePipeline.Bind (cmd);
+
+                       cmd.Draw (3, 1, 0, 0);
+
+                       renderPass.End (cmd);
+               }
+
+#region update
+               void updateMatrices () {
+                       camera.AspectRatio = (float)swapChain.Width / swapChain.Height;
+
+                       matrices.projection = camera.Projection;
+                       matrices.view = camera.View;
+                       matrices.model = camera.Model;
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+                       matrices.view *= Matrix4x4.CreateTranslation (-matrices.view.Translation);
+                       matrices.model = Matrix4x4.Identity;
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> (), (uint)Marshal.SizeOf<Matrices> ());
+               }
+
+               public override void UpdateView () {
+                       updateMatrices ();
+                       updateViewRequested = false;
+               }
+               public override void Update () {
+#if DEBUG
+                       results = statPool.GetResults ();
+#endif
+               }
+#endregion
+
+
+
+               void createGBuff () {
+                       gbColorRough?.Dispose ();
+                       gbEmitMetal?.Dispose ();
+                       gbN?.Dispose ();
+                       gbPos?.Dispose ();
+
+                       gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height);
+                       gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height);
+                       gbN = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height);
+                       gbPos = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height);
+
+                       gbColorRough.CreateView ();
+                       gbColorRough.CreateSampler ();
+                       gbEmitMetal.CreateView ();
+                       gbEmitMetal.CreateSampler ();
+                       gbN.CreateView ();
+                       gbN.CreateSampler ();
+                       gbPos.CreateView ();
+                       gbPos.CreateSampler ();
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff);
+                       uboUpdate.Write (dev, dsGBuff,  gbColorRough.Descriptor,
+                                                                               gbEmitMetal.Descriptor,
+                                                                               gbN.Descriptor,
+                                                                               gbPos.Descriptor);
+                       gbColorRough.SetName ("GBuffColorRough");
+                       gbEmitMetal.SetName ("GBuffEmitMetal");
+                       gbN.SetName ("GBuffN");
+                       gbPos.SetName ("GBuffPos");
+               }
+
+               protected override void OnResize () {
+                       updateMatrices ();
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+#if DEBUG
+                       if (debugFB != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       debugFB[i]?.Dispose ();
+#endif
+
+                       createGBuff ();
+
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+                       debugFB = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (renderPass, swapChain.Width, swapChain.Height, new Image[] {
+                                       swapChain.images[i], null, gbColorRough, gbEmitMetal, gbN, gbPos});
+#if DEBUG
+                               debugFB[i] = new Framebuffer (debugDraw.RenderPass, swapChain.Width, swapChain.Height,(debugDraw.Samples == VkSampleCountFlags.SampleCount1)
+                                       ? new Image[] { swapChain.images[i] } : new Image[] { null, swapChain.images[i] });
+                               debugFB[i].SetName ("main FB " + i);
+#endif
+                       }
+
+                       buildCommandBuffers ();
+               }
+
+
+               #region Mouse and keyboard
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.F1:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               matrices.exposure -= 0.3f;
+                                       else
+                                               matrices.exposure += 0.3f;
+                                       break;
+                               case Key.F2:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               matrices.gamma -= 0.1f;
+                                       else
+                                               matrices.gamma += 0.1f;
+                                       break;
+                               case Key.F3:
+                                       if (camera.Type == Camera.CamType.FirstPerson)
+                                               camera.Type = Camera.CamType.LookAt;
+                                       else
+                                               camera.Type = Camera.CamType.FirstPerson;
+                                       Console.WriteLine ($"camera type = {camera.Type}");
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       return;
+                       }
+                       updateViewRequested = true;
+               }
+#endregion
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+                                       for (int i = 0; i < swapChain.ImageCount; ++i)
+                                               frameBuffers[i]?.Dispose ();
+
+                                       gbColorRough.Dispose ();
+                                       gbEmitMetal.Dispose ();
+                                       gbN.Dispose ();
+                                       gbPos.Dispose ();
+
+                                       gBuffPipeline.Dispose ();
+                                       composePipeline.Dispose ();
+
+                                       descLayoutMain.Dispose ();
+                                       descLayoutModelTextures.Dispose ();
+                                       descLayoutGBuff.Dispose ();
+
+                                       uboMats.Dispose ();
+                                       model.Dispose ();
+                                       envCube.Dispose ();
+
+                                       descriptorPool.Dispose ();
+#if DEBUG
+                                       foreach (Framebuffer fb in debugFB) 
+                                               fb.Dispose ();
+
+                                       debugDraw.Dispose ();
+                                       timestampQPool?.Dispose ();
+                                       statPool?.Dispose ();
+#endif
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/deferred/modelWithVkvgStats.cs b/samples/deferred/modelWithVkvgStats.cs
new file mode 100644 (file)
index 0000000..f4ea123
--- /dev/null
@@ -0,0 +1,359 @@
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Glfw;
+using CVKL;
+using VK;
+using static CVKL.Camera;
+
+namespace modelWithVkvgStats {
+       class Program : VkWindow {
+
+               PipelineStatisticsQueryPool statPool;
+               TimestampQueryPool timestampQPool;
+
+               ulong[] results;
+
+               protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) {
+                       base.configureEnabledFeatures (ref features);
+                       features.pipelineStatisticsQuery = true;
+               }
+               public struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 view;
+                       public Matrix4x4 model;
+                       public Vector4 lightPos;
+                       public float gamma;
+                       public float exposure;
+               }
+
+               public Matrices matrices = new Matrices { 
+                       lightPos = new Vector4 (0.0f, 0.0f, -2.0f, 1.0f),
+                       gamma = 1.0f,
+                       exposure = 2.0f,
+               };
+
+               HostBuffer uboMats;
+
+               DescriptorPool descriptorPool;
+               DescriptorSetLayout descLayoutMatrix;
+               DescriptorSetLayout descLayoutTextures;
+               DescriptorSet dsMats;
+
+               GraphicPipeline pipeline;
+               GraphicPipeline uiPipeline;
+               Framebuffer[] frameBuffers;
+
+               Model model;
+
+               vkvg.Device vkvgDev;
+        vkvg.Surface vkvgSurf;
+               Image vkvgImage;
+
+        void vkvgDraw () {
+
+            using (vkvg.Context ctx = new vkvg.Context (vkvgSurf)) {
+                               ctx.Operator = vkvg.Operator.Clear;
+                               ctx.Paint ();
+                               ctx.Operator = vkvg.Operator.Over;
+
+                               ctx.LineWidth = 1;
+                               ctx.SetSource (0.1, 0.1, 0.1, 0.3);
+                               ctx.Rectangle (5.5, 5.5, 400, 250);
+                               ctx.FillPreserve ();
+                               ctx.Flush ();
+                               ctx.SetSource (0.8, 0.8, 0.8);
+                               ctx.Stroke ();
+
+                               ctx.FontFace = "mono";
+                               ctx.FontSize = 10;
+                               int x = 16;
+                               int y = 40, dy = 16;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"fps:     {fps,5} "));
+                               ctx.MoveTo (x + 200, y - 0.5);
+                               ctx.Rectangle (x + 200, y - 8.5, 0.1 * fps, 10);
+                               ctx.SetSource (0.1, 0.9, 0.1);
+                               ctx.Fill ();
+                               ctx.SetSource (0.8, 0.8, 0.8);
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"Exposure:{matrices.exposure,5} "));
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"Gamma:   {matrices.gamma,5} "));
+                               if (results == null)
+                                       return;
+
+                               y += dy*2;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText ("Pipeline Statistics");
+                               ctx.MoveTo (x-2, 2.5+y);
+                               ctx.LineTo (x+160, 2.5+y);
+                               ctx.Stroke ();
+                               y += 4;
+                               x += 20;
+
+                               for (int i = 0; i < statPool.RequestedStats.Length; i++) {
+                                       y += dy;
+                                       ctx.MoveTo (x, y);
+                                       ctx.ShowText (string.Format ($"{statPool.RequestedStats[i].ToString(),-30} :{results[i],12:0,0} "));
+                               }
+
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"{"Elapsed microsecond",-20} :{timestampQPool.ElapsedMiliseconds:0.0000} "));
+                       }
+               }
+
+               Program () : base () {
+                       vkvgDev = new vkvg.Device (instance.Handle, phy.Handle, dev.VkDev.Handle, presentQueue.qFamIndex,
+                               vkvg.SampleCount.Sample_4, presentQueue.index);
+
+                       camera.Type = CamType.FirstPerson;
+                       camera.Model = Matrix4x4.CreateScale (0.05f) * Matrix4x4.CreateRotationX((float)Math.PI);
+                       //Camera.SetRotation (-0.1f,-0.4f);
+                       camera.SetPosition (0, 2, -3);
+                                       
+                       init ();
+
+                       model = new Model (presentQueue, "../data/models/Sponza/glTF/Sponza.gltf");
+                       model.WriteMaterialsDescriptorSets (descLayoutTextures,
+                               VK.AttachmentType.Color,
+                               VK.AttachmentType.Normal,
+                               VK.AttachmentType.AmbientOcclusion,
+                               VK.AttachmentType.PhysicalProps,
+                               VK.AttachmentType.Emissive);
+               }
+
+               void init (VkSampleCountFlags samples = VkSampleCountFlags.SampleCount4) { 
+                       descriptorPool = new DescriptorPool (dev, 2,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       descLayoutMatrix = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex|VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       descLayoutTextures = new DescriptorSetLayout (dev, 
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+
+                       dsMats = descriptorPool.Allocate (descLayoutMatrix);
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples);
+
+                       cfg.Layout = new PipelineLayout (dev, descLayoutMatrix, descLayoutTextures);
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf<Matrix4x4> ()),
+                               new VkPushConstantRange (VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf<Model.PbrMaterial> (), 64)
+                       );
+                       cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), samples);
+
+                       cfg.AddVertexBinding<Model.Vertex> (0);
+                       cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/pbrtest.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/pbrtest.frag.spv");
+
+                       pipeline = new GraphicPipeline (cfg);
+
+                       cfg.ResetShadersAndVerticesInfos ();
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/FullScreenQuad.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/simpletexture.frag.spv");
+
+                       cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true);
+
+                       uiPipeline = new GraphicPipeline (cfg);
+
+                       uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, (ulong)Marshal.SizeOf<Matrices>());
+                       uboMats.Map ();//permanent map
+
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMats, descLayoutMatrix.Bindings[0]);                         
+                       uboUpdate.Write (dev, uboMats.Descriptor);
+
+                       cfg.Layout.SetName ("Main Pipeline layout");
+                       uboMats.SetName ("uboMats");
+                       descriptorPool.SetName ("main pool");
+                       descLayoutTextures.SetName ("descLayoutTextures");
+
+                       statPool = new PipelineStatisticsQueryPool (dev,
+                               VkQueryPipelineStatisticFlags.InputAssemblyVertices |
+                               VkQueryPipelineStatisticFlags.InputAssemblyPrimitives |
+                               VkQueryPipelineStatisticFlags.ClippingInvocations |
+                               VkQueryPipelineStatisticFlags.ClippingPrimitives |
+                               VkQueryPipelineStatisticFlags.FragmentShaderInvocations);
+
+                       timestampQPool = new TimestampQueryPool (dev);
+
+               }
+
+               void buildCommandBuffers () {
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {                                                                
+                cmds[i]?.Free ();
+
+                               cmds[i] = cmdPool.AllocateCommandBuffer ();
+                               cmds[i].Start ();
+
+                               statPool.Begin (cmds[i]);
+                               cmds[i].BeginRegion ("draw" + i, 0.5f, 1f, 0f);
+                               cmds[i].Handle.SetDebugMarkerName (dev, "cmd Draw" + i); 
+                               recordDraw (cmds[i], frameBuffers[i]);
+                               cmds[i].EndRegion ();
+                               statPool.End (cmds[i]);                 
+                               cmds[i].End ();
+                       }
+               } 
+               void recordDraw (CommandBuffer cmd, Framebuffer fb) {
+                       cmd.BeginRegion ("models", 0.5f, 1f, 0f);
+                       pipeline.RenderPass.Begin (cmd, fb);
+
+                       cmd.SetViewport (fb.Width, fb.Height);
+                       cmd.SetScissor (fb.Width, fb.Height);
+
+                       cmd.BindDescriptorSet (pipeline.Layout, dsMats);
+                       pipeline.Bind (cmd);
+                       model.Bind (cmd);
+                       model.DrawAll (cmd, pipeline.Layout);
+
+                       cmd.EndRegion ();
+                       cmd.BeginRegion ("vkvg", 0.5f, 1f, 0f);
+                       uiPipeline.Bind (cmd);
+
+                       timestampQPool.Start (cmd);
+
+                       vkvgImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ColorAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal,
+                               VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader);
+
+                       cmd.Draw (3, 1, 0, 0);
+
+                       vkvgImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.ColorAttachmentOptimal,
+                               VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.BottomOfPipe);
+
+                       timestampQPool.End (cmd);
+
+                       pipeline.RenderPass.End (cmd);
+                       cmd.EndRegion ();
+               }
+
+               #region update
+               void updateMatrices () {
+
+                       camera.AspectRatio = (float)swapChain.Width / swapChain.Height;
+
+                       matrices.projection = camera.Projection;
+                       matrices.view = camera.View;
+                       matrices.model = camera.Model;
+                       uboMats.Update (matrices, (uint)Marshal.SizeOf<Matrices> ());
+               }
+                       
+               public override void UpdateView () {
+                       updateMatrices ();
+                       updateViewRequested = false;
+               }
+               public override void Update () {
+                       results = statPool.GetResults ();
+                       vkvgDraw ();
+               }
+               #endregion
+
+               #region mouse and keyboard
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.F1:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               matrices.exposure -= 0.3f;
+                                       else
+                                               matrices.exposure += 0.3f;
+                                       break;
+                               case Key.F2:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               matrices.gamma -= 0.1f;
+                                       else
+                                               matrices.gamma += 0.1f;
+                                       break;
+                               case Key.F3:
+                                       if (camera.Type == CamType.FirstPerson)
+                                               camera.Type = CamType.LookAt;
+                                       else
+                                               camera.Type = CamType.FirstPerson;
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       return;
+                       }
+                       updateViewRequested = true;
+               }
+               #endregion
+
+               protected override void OnResize () {
+
+                       vkvgImage?.Dispose ();
+                       vkvgSurf?.Dispose ();
+                       vkvgSurf = new vkvg.Surface (vkvgDev, (int)swapChain.Width, (int)swapChain.Height);
+                       vkvgImage = new Image (dev, new VkImage ((ulong)vkvgSurf.VkImage.ToInt64 ()), VkFormat.B8g8r8a8Unorm,
+                               VkImageUsageFlags.ColorAttachment, (uint)vkvgSurf.Width, (uint)vkvgSurf.Height);
+                       vkvgImage.CreateView (VkImageViewType.ImageView2D, VkImageAspectFlags.Color);
+                       vkvgImage.CreateSampler (VkFilter.Nearest,VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder);
+
+                       vkvgImage.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMats, descLayoutMatrix.Bindings[1]);                         
+                       uboUpdate.Write (dev, vkvgImage.Descriptor);
+
+                       updateMatrices ();
+
+                       if (frameBuffers!=null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                               null
+                                       } : new Image[] {
+                                               null,
+                                               null,
+                                               swapChain.images[i]
+                                       });
+                               frameBuffers[i].SetName ("main FB " + i);
+
+                       }
+
+                       buildCommandBuffers ();
+               }       
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+                                       for (int i = 0; i < swapChain.ImageCount; ++i)
+                                               frameBuffers[i]?.Dispose ();
+                                       model.Dispose ();
+                                       pipeline.Dispose ();
+                                       descLayoutMatrix.Dispose ();
+                                       descLayoutTextures.Dispose ();
+                                       descriptorPool.Dispose ();
+
+                                       uboMats.Dispose ();
+
+                                       vkvgSurf?.Dispose ();
+                                       vkvgDev.Dispose ();
+
+                                       timestampQPool.Dispose ();
+                                       statPool.Dispose ();
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/deferred/shaders/FullScreenQuad.vert b/samples/deferred/shaders/FullScreenQuad.vert
new file mode 100644 (file)
index 0000000..826720b
--- /dev/null
@@ -0,0 +1,10 @@
+#version 450
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+       outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+       gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
+       //gl_Position = vec4(outUV -1.0f, 0.0f, 1.0f);
+}
diff --git a/samples/deferred/shaders/GBuffPbr.frag b/samples/deferred/shaders/GBuffPbr.frag
new file mode 100644 (file)
index 0000000..ca398e6
--- /dev/null
@@ -0,0 +1,212 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+#define MANUAL_SRGB 0
+#define DEBUG 0
+
+layout (constant_id = 0) const float NEAR_PLANE = 0.1f;
+layout (constant_id = 1) const float FAR_PLANE = 256.0f;
+layout (constant_id = 2) const int MAT_COUNT = 1;
+
+struct Material {
+    vec4 baseColorFactor;
+    vec4 emissiveFactor;
+    vec4 diffuseFactor;
+    vec4 specularFactor;
+    float workflow;
+    uint tex0;
+    uint tex1;
+    float metallicFactor;   
+    float roughnessFactor;  
+    float alphaMask;    
+    float alphaMaskCutoff;
+    int pad0;
+};
+const float M_PI = 3.141592653589793;
+const float c_MinRoughness = 0.04;
+
+const float PBR_WORKFLOW_METALLIC_ROUGHNESS = 1.0;
+const float PBR_WORKFLOW_SPECULAR_GLOSINESS = 2.0f;
+
+const uint MAP_COLOR = 0x1;
+const uint MAP_NORMAL = 0x2;
+const uint MAP_AO = 0x4;
+const uint MAP_METAL = 0x8;
+const uint MAP_ROUGHNESS = 0x10;
+const uint MAP_METALROUGHNESS = 0x20;
+const uint MAP_EMISSIVE = 0x40;
+
+layout (location = 0) in vec3 inWorldPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inUV0;
+layout (location = 3) in vec2 inUV1;
+
+
+layout (set = 0, binding = 5) uniform UBOMaterials {
+    Material materials[MAT_COUNT];
+};
+
+// Material bindings
+layout (set = 2, binding = 0) uniform sampler2D colorMap;
+layout (set = 2, binding = 1) uniform sampler2D physicalDescriptorMap;
+layout (set = 2, binding = 2) uniform sampler2D normalMap;
+layout (set = 2, binding = 3) uniform sampler2D aoMap;
+layout (set = 2, binding = 4) uniform sampler2D emissiveMap;
+
+
+layout (push_constant) uniform PushCsts {
+    layout(offset = 64)
+    int materialIdx;
+};
+
+
+layout (location = 0) out vec4 outColorRough;
+layout (location = 1) out vec4 outEmitMetal;
+layout (location = 2) out vec4 outN_AO;
+layout (location = 3) out vec4 outPos;
+
+vec4 SRGBtoLINEAR(vec4 srgbIn)
+{
+    #ifdef MANUAL_SRGB
+    #ifdef SRGB_FAST_APPROXIMATION
+    vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
+    #else //SRGB_FAST_APPROXIMATION
+    vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
+    vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
+    #endif //SRGB_FAST_APPROXIMATION
+    return vec4(linOut,srgbIn.w);;
+    #else //MANUAL_SRGB
+    return srgbIn;
+    #endif //MANUAL_SRGB
+}
+
+// Find the normal for this fragment, pulling either from a predefined normal map
+// or from the interpolated mesh normal and tangent attributes.
+vec3 getNormal()
+{
+    vec3 tangentNormal;
+    // Perturb normal, see http://www.thetenthplanet.de/archives/1180
+    if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL)
+        tangentNormal = texture(normalMap, inUV0).xyz * 2.0 - 1.0;
+    else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL)
+        tangentNormal = texture(normalMap, inUV1).xyz * 2.0 - 1.0;
+    else
+        return normalize(inNormal);
+        
+    vec3 q1 = dFdx(inWorldPos);
+    vec3 q2 = dFdy(inWorldPos);
+    vec2 st1 = dFdx(inUV0);
+    vec2 st2 = dFdy(inUV0);
+
+    vec3 N = normalize(inNormal);
+    vec3 T = normalize(q1 * st2.t - q2 * st1.t);
+    vec3 B = -normalize(cross(N, T));
+    mat3 TBN = mat3(T, B, N);
+
+    return normalize(TBN * tangentNormal);
+}
+
+// Gets metallic factor from specular glossiness workflow inputs 
+float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) {
+    float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b);
+    float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b);
+    if (perceivedSpecular < c_MinRoughness) {
+        return 0.0;
+    }
+    float a = c_MinRoughness;
+    float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness;
+    float c = c_MinRoughness - perceivedSpecular;
+    float D = max(b * b - 4.0 * a * c, 0.0);
+    return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);
+}
+
+float linearDepth(float depth)
+{
+    float z = depth * 2.0f - 1.0f; 
+    return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE));   
+}
+
+void main() 
+{
+    float perceptualRoughness;
+    float metallic;    
+    vec4 baseColor;
+    vec3 emissive = vec3(0);    
+    
+    baseColor = materials[materialIdx].baseColorFactor;
+    
+    if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) {
+        perceptualRoughness = materials[materialIdx].roughnessFactor;
+        metallic = materials[materialIdx].metallicFactor;        
+        // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
+        // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
+        if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){
+            perceptualRoughness *= texture(physicalDescriptorMap, inUV0).g;
+            metallic *= texture(physicalDescriptorMap, inUV0).b;
+        }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){
+            perceptualRoughness *= texture(physicalDescriptorMap, inUV1).g;
+            metallic *= texture(physicalDescriptorMap, inUV1).b;
+        }               
+        perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
+        metallic = clamp(metallic, 0.0, 1.0);        
+
+        // The albedo may be defined from a base texture or a flat color
+        if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR)        
+            baseColor *= SRGBtoLINEAR(texture(colorMap, inUV0));
+        else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR)
+            baseColor *= SRGBtoLINEAR(texture(colorMap, inUV1));        
+    }
+    
+    if (materials[materialIdx].alphaMask == 1.0f) {            
+        if (baseColor.a < materials[materialIdx].alphaMaskCutoff) 
+            discard;        
+    }
+
+    if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) {
+        // Values from specular glossiness workflow are converted to metallic roughness
+        if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)
+            perceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV0).a;            
+        else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)
+            perceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV1).a;            
+        else
+            perceptualRoughness = 0.0;
+
+        const float epsilon = 1e-6;
+
+        vec4 diffuse = SRGBtoLINEAR(texture(colorMap, inUV0));
+        vec3 specular = SRGBtoLINEAR(texture(physicalDescriptorMap, inUV0)).rgb;
+
+        float maxSpecular = max(max(specular.r, specular.g), specular.b);
+
+        // Convert metallic value from specular glossiness inputs
+        metallic = convertMetallic(diffuse.rgb, specular, maxSpecular);
+
+        vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb;
+        vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb;
+        baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a);
+
+    }        
+    
+    const float u_OcclusionStrength = 1.0f;
+    const float u_EmissiveFactor = 1.0f;
+    float ao = 1.0f;
+    
+    if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE)    
+        emissive = SRGBtoLINEAR(texture(emissiveMap, inUV0)).rgb * u_EmissiveFactor;
+    else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE)    
+        emissive = SRGBtoLINEAR(texture(emissiveMap, inUV1)).rgb * u_EmissiveFactor;
+    
+    if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO)
+        ao = texture(aoMap, inUV0).r;
+    else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO)
+        ao = texture(aoMap, inUV1).r;
+        
+    vec3 n = getNormal();    
+    vec3 p = inWorldPos;
+    
+    outColorRough = vec4 (baseColor.rgb, perceptualRoughness);
+    outEmitMetal = vec4 (emissive, metallic);
+    outN_AO = vec4 (n, ao);
+    outPos = vec4 (p, linearDepth(gl_FragCoord.z));
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/GBuffPbr.vert b/samples/deferred/shaders/GBuffPbr.vert
new file mode 100644 (file)
index 0000000..7cafe49
--- /dev/null
@@ -0,0 +1,37 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inUV0;
+layout (location = 3) in vec2 inUV1;
+
+layout (set = 0, binding = 0) uniform UBO {
+    mat4 projection;
+    mat4 model;
+    mat4 view;    
+} ubo;
+
+layout(push_constant) uniform PushConsts {
+    mat4 model;
+} pc;
+
+layout (location = 0) out vec3 outWorldPos;
+layout (location = 1) out vec3 outNormal;
+layout (location = 2) out vec2 outUV0;
+layout (location = 3) out vec2 outUV1;
+
+out gl_PerVertex
+{
+    vec4 gl_Position;
+};
+
+void main() 
+{        
+    vec4 locPos = ubo.model * pc.model * vec4(inPos, 1.0);
+    outNormal = normalize(transpose(inverse(mat3(ubo.model * pc.model))) * inNormal);        
+        
+    outWorldPos = locPos.xyz;
+    outUV0 = inUV0;
+    outUV1 = inUV1;
+    gl_Position =  ubo.projection * ubo.view * vec4(outWorldPos, 1.0);
+}
diff --git a/samples/deferred/shaders/GBuffPbrTexArray.frag b/samples/deferred/shaders/GBuffPbrTexArray.frag
new file mode 100644 (file)
index 0000000..dee3471
--- /dev/null
@@ -0,0 +1,211 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+#define MANUAL_SRGB 0
+#define DEBUG 0
+
+layout (constant_id = 0) const float NEAR_PLANE = 0.1f;
+layout (constant_id = 1) const float FAR_PLANE = 256.0f;
+layout (constant_id = 2) const int MAT_COUNT = 1;
+
+struct Material {
+    vec4 baseColorFactor;
+    vec4 emissiveFactor;
+    vec4 diffuseFactor;
+    vec4 specularFactor;
+    
+    float workflow;    
+    uint tex0;    
+    uint tex1;    
+    int baseColorTex;
+    
+    int physicalDescTex;    
+    int normalTex;
+    int occlusionTex;
+    int emissiveTex;
+    
+    float metallicFactor;       
+    float roughnessFactor;  
+    float alphaMask;    
+    float alphaMaskCutoff;    
+};
+const float M_PI = 3.141592653589793;
+const float c_MinRoughness = 0.04;
+
+const float PBR_WORKFLOW_METALLIC_ROUGHNESS = 1.0;
+const float PBR_WORKFLOW_SPECULAR_GLOSINESS = 2.0f;
+
+const uint MAP_COLOR = 0x1;
+const uint MAP_NORMAL = 0x2;
+const uint MAP_AO = 0x4;
+const uint MAP_METAL = 0x8;
+const uint MAP_ROUGHNESS = 0x10;
+const uint MAP_METALROUGHNESS = 0x20;
+const uint MAP_EMISSIVE = 0x40;
+
+layout (location = 0) in vec3 inWorldPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inUV0;
+layout (location = 3) in vec2 inUV1;
+
+layout (set = 0, binding = 5) uniform UBOMaterials {
+    Material materials[MAT_COUNT];
+};
+layout (set = 0, binding = 7) uniform sampler2DArray texArray;
+
+layout (push_constant) uniform PushCsts {
+    layout(offset = 64)
+    int materialIdx;
+};
+
+
+layout (location = 0) out vec4 outColorRough;
+layout (location = 1) out vec4 outEmitMetal;
+layout (location = 2) out vec4 outN_AO;
+layout (location = 3) out vec4 outPos;
+
+vec4 SRGBtoLINEAR(vec4 srgbIn)
+{
+    #ifdef MANUAL_SRGB
+    #ifdef SRGB_FAST_APPROXIMATION
+    vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
+    #else //SRGB_FAST_APPROXIMATION
+    vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
+    vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
+    #endif //SRGB_FAST_APPROXIMATION
+    return vec4(linOut,srgbIn.w);;
+    #else //MANUAL_SRGB
+    return srgbIn;
+    #endif //MANUAL_SRGB
+}
+
+// Find the normal for this fragment, pulling either from a predefined normal map
+// or from the interpolated mesh normal and tangent attributes.
+vec3 getNormal()
+{
+    vec3 tangentNormal;
+    // Perturb normal, see http://www.thetenthplanet.de/archives/1180
+    if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL)
+        tangentNormal = texture(texArray, vec3(inUV0, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0;
+    else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL)
+        tangentNormal = texture(texArray, vec3(inUV1, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0;
+    else
+        return normalize(inNormal);
+        
+    vec3 q1 = dFdx(inWorldPos);
+    vec3 q2 = dFdy(inWorldPos);
+    vec2 st1 = dFdx(inUV0);
+    vec2 st2 = dFdy(inUV0);
+
+    vec3 N = normalize(inNormal);
+    vec3 T = normalize(q1 * st2.t - q2 * st1.t);
+    vec3 B = -normalize(cross(N, T));
+    mat3 TBN = mat3(T, B, N);
+
+    return normalize(TBN * tangentNormal);
+}
+
+// Gets metallic factor from specular glossiness workflow inputs 
+float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) {
+    float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b);
+    float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b);
+    if (perceivedSpecular < c_MinRoughness) {
+        return 0.0;
+    }
+    float a = c_MinRoughness;
+    float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness;
+    float c = c_MinRoughness - perceivedSpecular;
+    float D = max(b * b - 4.0 * a * c, 0.0);
+    return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);
+}
+
+float linearDepth(float depth)
+{
+    float z = depth * 2.0f - 1.0f; 
+    return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE));   
+}
+
+void main() 
+{
+    float perceptualRoughness;
+    float metallic;    
+    vec4 baseColor;
+    vec3 emissive = vec3(0);    
+    
+    baseColor = materials[materialIdx].baseColorFactor;
+    
+    if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) {
+        perceptualRoughness = materials[materialIdx].roughnessFactor;
+        metallic = materials[materialIdx].metallicFactor;        
+        // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
+        // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
+        if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){
+            perceptualRoughness *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).g;            
+            metallic *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).b;
+        }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){
+            perceptualRoughness *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).g;            
+            metallic *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).b;
+        }               
+        perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
+        metallic = clamp(metallic, 0.0, 1.0);        
+
+        // The albedo may be defined from a base texture or a flat color
+        if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR)        
+            baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex)));
+        else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR)
+            baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].baseColorTex)));
+    }
+    
+    if (materials[materialIdx].alphaMask == 1.0f) {            
+        if (baseColor.a < materials[materialIdx].alphaMaskCutoff) 
+            discard;        
+    }
+
+    if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) {
+        // Values from specular glossiness workflow are converted to metallic roughness
+        if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)
+            perceptualRoughness = 1.0 - texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).a;            
+        else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)
+            perceptualRoughness = 1.0 - texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).a;            
+        else
+            perceptualRoughness = 0.0;
+
+        const float epsilon = 1e-6;
+
+        vec4 diffuse = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex)));
+        vec3 specular = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex))).rgb;
+
+        float maxSpecular = max(max(specular.r, specular.g), specular.b);
+
+        // Convert metallic value from specular glossiness inputs
+        metallic = convertMetallic(diffuse.rgb, specular, maxSpecular);
+
+        vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb;
+        vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb;
+        baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a);
+
+    }        
+    
+    const float u_OcclusionStrength = 1.0f;
+    const float u_EmissiveFactor = 1.0f;
+    float ao = 1.0f;
+    
+    if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE)    
+        emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor;
+    else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE)    
+        emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor;
+    
+    if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO)
+        ao = texture(texArray, vec3(inUV0, materials[materialIdx].occlusionTex)).r;
+    else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO)
+        ao = texture(texArray, vec3(inUV1, materials[materialIdx].occlusionTex)).r;
+        
+    vec3 n = getNormal();    
+    vec3 p = inWorldPos;
+    
+    outColorRough = vec4 (baseColor.rgb, perceptualRoughness);
+    outEmitMetal = vec4 (emissive, metallic);
+    outN_AO = vec4 (n, ao);
+    outPos = vec4 (p, linearDepth(gl_FragCoord.z));
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/bloom.comp b/samples/deferred/shaders/bloom.comp
new file mode 100644 (file)
index 0000000..e2b1318
--- /dev/null
@@ -0,0 +1,60 @@
+#version 450
+
+layout (local_size_x = 16, local_size_y = 16) in;
+layout (binding = 0, rgba16f) uniform readonly image2D inputImage;
+layout (binding = 1, rgba16f) uniform image2D resultImage;
+
+layout(push_constant) uniform PushConsts {
+    vec2 texSize;
+    int dir;
+    float scale;
+    float strength;
+} pc;
+
+void main(void)
+{
+       // From the OpenGL Super bible
+       const float weights[] = float[](0.0024499299678342,
+                                                                       0.0043538453346397,
+                                                                       0.0073599963704157,
+                                                                       0.0118349786570722,
+                                                                       0.0181026699707781,
+                                                                       0.0263392293891488,
+                                                                       0.0364543006660986,
+                                                                       0.0479932050577658,
+                                                                       0.0601029809166942,
+                                                                       0.0715974486241365,
+                                                                       0.0811305381519717,
+                                                                       0.0874493212267511,
+                                                                       0.0896631113333857,
+                                                                       0.0874493212267511,
+                                                                       0.0811305381519717,
+                                                                       0.0715974486241365,
+                                                                       0.0601029809166942,
+                                                                       0.0479932050577658,
+                                                                       0.0364543006660986,
+                                                                       0.0263392293891488,
+                                                                       0.0181026699707781,
+                                                                       0.0118349786570722,
+                                                                       0.0073599963704157,
+                                                                       0.0043538453346397,
+                                                                       0.0024499299678342);
+                                                                       
+
+       float ar = 1.0;
+    vec4 color = vec4(0.0);
+    
+       // Aspect ratio for vertical blur pass
+       if (pc.dir == 1)
+               ar = pc.texSize.y / pc.texSize.x;
+            
+       vec2 P = gl_GlobalInvocationID.yx - vec2(0, (weights.length() >> 1) * ar * pc.scale);
+       
+       for (int i = 0; i < weights.length(); i++)
+       {
+               vec2 dv = vec2(0.0, i * pc.scale) * ar;
+               color += imageLoad (inputImage, ivec2(P + dv)) * weights[i] * pc.strength;
+       }
+  
+    imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), color);
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/bloom.frag b/samples/deferred/shaders/bloom.frag
new file mode 100644 (file)
index 0000000..c3bf402
--- /dev/null
@@ -0,0 +1,63 @@
+#version 450
+
+layout (binding = 0) uniform sampler2D samplerColor0;
+layout (binding = 1) uniform sampler2D samplerColor1;
+
+layout (location = 0) in vec2 inUV;
+
+layout (location = 0) out vec4 outColor;
+
+layout (constant_id = 0) const int dir = 0;
+
+void main(void)
+{
+       // From the OpenGL Super bible
+       const float weights[] = float[](0.0024499299678342,
+                                                                       0.0043538453346397,
+                                                                       0.0073599963704157,
+                                                                       0.0118349786570722,
+                                                                       0.0181026699707781,
+                                                                       0.0263392293891488,
+                                                                       0.0364543006660986,
+                                                                       0.0479932050577658,
+                                                                       0.0601029809166942,
+                                                                       0.0715974486241365,
+                                                                       0.0811305381519717,
+                                                                       0.0874493212267511,
+                                                                       0.0896631113333857,
+                                                                       0.0874493212267511,
+                                                                       0.0811305381519717,
+                                                                       0.0715974486241365,
+                                                                       0.0601029809166942,
+                                                                       0.0479932050577658,
+                                                                       0.0364543006660986,
+                                                                       0.0263392293891488,
+                                                                       0.0181026699707781,
+                                                                       0.0118349786570722,
+                                                                       0.0073599963704157,
+                                                                       0.0043538453346397,
+                                                                       0.0024499299678342);
+
+
+       const float blurScale = 0.003;
+       const float blurStrength = 1.0;
+
+       float ar = 1.0;
+       // Aspect ratio for vertical blur pass
+       if (dir == 1)
+       {
+               vec2 ts = textureSize(samplerColor1, 0);
+               ar = ts.y / ts.x;
+       }
+
+       vec2 P = inUV.yx - vec2(0, (weights.length() >> 1) * ar * blurScale);
+
+       vec4 color = vec4(0.0);
+       for (int i = 0; i < weights.length(); i++)
+       {
+               vec2 dv = vec2(0.0, i * blurScale) * ar;
+               color += texture(samplerColor1, P + dv) * weights[i] * blurStrength;
+       }
+
+       outColor = color;
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/compose.frag b/samples/deferred/shaders/compose.frag
new file mode 100644 (file)
index 0000000..81312bd
--- /dev/null
@@ -0,0 +1,230 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set = 0, binding = 0) uniform UBO {
+    mat4 projection;
+    mat4 model;
+    mat4 view;
+    vec4 camPos;    
+    float exposure;
+    float gamma;
+    float prefilteredCubeMipLevels;
+    float scaleIBLAmbient;
+} ubo;
+
+layout (constant_id = 0) const uint NUM_LIGHTS = 1;
+
+struct Light {
+    vec4 position;
+    vec4 color;
+    mat4 mvp;
+};
+
+layout (set = 0, binding = 4) uniform UBOLights {
+    Light lights[NUM_LIGHTS];
+};
+
+const float M_PI = 3.141592653589793;
+const float c_MinRoughness = 0.04;
+
+layout (input_attachment_index = 0, set = 1, binding = 0) uniform subpassInputMS samplerColorRough;
+layout (input_attachment_index = 1, set = 1, binding = 1) uniform subpassInputMS samplerEmitMetal;
+layout (input_attachment_index = 2, set = 1, binding = 2) uniform subpassInputMS samplerN_AO;
+layout (input_attachment_index = 3, set = 1, binding = 3) uniform subpassInputMS samplerPos;
+
+layout (set = 0, binding = 1) uniform samplerCube samplerIrradiance;
+layout (set = 0, binding = 2) uniform samplerCube prefilteredMap;
+layout (set = 0, binding = 3) uniform sampler2D samplerBRDFLUT;
+
+layout (location = 0) in vec2 inUV;
+
+layout (location = 0) out vec4 outColor;
+
+// Encapsulate the various inputs used by the various functions in the shading equation
+// We store values in this struct to simplify the integration of alternative implementations
+// of the shading terms, outlined in the Readme.MD Appendix.
+struct PBRInfo
+{
+    float NdotL;                  // cos angle between normal and light direction
+    float NdotV;                  // cos angle between normal and view direction
+    float NdotH;                  // cos angle between normal and half vector
+    float LdotH;                  // cos angle between light direction and half vector
+    float VdotH;                  // cos angle between view direction and half vector
+    float perceptualRoughness;    // roughness value, as authored by the model creator (input to shader)
+    float metalness;              // metallic value at the surface
+    vec3 reflectance0;            // full reflectance color (normal incidence angle)
+    vec3 reflectance90;           // reflectance color at grazing angle
+    float alphaRoughness;         // roughness mapped to a more linear change in the roughness (proposed by [2])
+    vec3 diffuseColor;            // color contribution from diffuse lighting
+    vec3 specularColor;           // color contribution from specular lighting
+};
+
+// Calculation of the lighting contribution from an optional Image Based Light source.
+// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
+// See our README.md on Environment Maps [3] for additional discussion.
+vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)
+{
+    float lod = (pbrInputs.perceptualRoughness * ubo.prefilteredCubeMipLevels);
+    // retrieve a scale and bias to F0. See [1], Figure 3
+    vec3 brdf = (texture(samplerBRDFLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb;
+    vec3 diffuseLight = texture(samplerIrradiance, reflection).rgb;
+
+    vec3 specularLight = textureLod(prefilteredMap, reflection, lod).rgb;
+
+    vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
+    vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
+
+    // For presentation, this allows us to disable IBL terms
+    // For presentation, this allows us to disable IBL terms
+    diffuse *= ubo.scaleIBLAmbient;
+    specular *= ubo.scaleIBLAmbient;
+
+    return diffuse + specular;
+}
+
+// Basic Lambertian diffuse
+// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog
+// See also [1], Equation 1
+vec3 diffuse(PBRInfo pbrInputs)
+{
+    return pbrInputs.diffuseColor / M_PI;
+}
+
+// The following equation models the Fresnel reflectance term of the spec equation (aka F())
+// Implementation of fresnel from [4], Equation 15
+vec3 specularReflection(PBRInfo pbrInputs)
+{
+    return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
+}
+
+// This calculates the specular geometric attenuation (aka G()),
+// where rougher material will reflect less light back to the viewer.
+// This implementation is based on [1] Equation 4, and we adopt their modifications to
+// alphaRoughness as input as originally proposed in [2].
+float geometricOcclusion(PBRInfo pbrInputs)
+{
+    float NdotL = pbrInputs.NdotL;
+    float NdotV = pbrInputs.NdotV;
+    float r = pbrInputs.alphaRoughness;
+
+    float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
+    float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
+    return attenuationL * attenuationV;
+}
+
+// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
+// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
+// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
+float microfacetDistribution(PBRInfo pbrInputs)
+{
+    float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
+    float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
+    return roughnessSq / (M_PI * f * f);
+}
+
+// Gets metallic factor from specular glossiness workflow inputs 
+float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) {
+    float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b);
+    float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b);
+    if (perceivedSpecular < c_MinRoughness) {
+        return 0.0;
+    }
+    float a = c_MinRoughness;
+    float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness;
+    float c = c_MinRoughness - perceivedSpecular;
+    float D = max(b * b - 4.0 * a * c, 0.0);
+    return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);
+}
+
+void main() 
+{
+    if (subpassLoad(samplerPos, gl_SampleID).a == 1.0f)
+        discard;
+    
+    float perceptualRoughness = subpassLoad(samplerColorRough, gl_SampleID).a;
+    float metallic = subpassLoad(samplerEmitMetal, gl_SampleID).a;
+    vec3 diffuseColor;
+    vec4 baseColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).rgb, 1);    
+    vec3 emissive = subpassLoad (samplerEmitMetal, gl_SampleID).rgb;        
+
+    vec3 f0 = vec3(0.04);
+    
+    diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
+    diffuseColor *= 1.0 - metallic;
+        
+    float alphaRoughness = perceptualRoughness * perceptualRoughness;
+
+    vec3 specularColor = mix(f0, baseColor.rgb, metallic);
+
+    // Compute reflectance.
+    float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
+
+    // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
+    // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
+    float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
+    vec3 specularEnvironmentR0 = specularColor.rgb;
+    vec3 specularEnvironmentR90 = vec3(1.0) * reflectance90;
+
+    vec3 n = subpassLoad(samplerN_AO, gl_SampleID).rgb;
+    vec3 pos = subpassLoad(samplerPos, gl_SampleID).rgb;
+    vec3 v = normalize(ubo.camPos.xyz - pos); // Vector from surface point to camera
+    
+    vec3 colors = vec3(0);
+    vec3 lightTarget = vec3(0);
+    
+    for (int i=0; i<NUM_LIGHTS; i++) {
+    
+        vec3 l = normalize(lights[i].position.xyz-pos);     // Vector from surface point to light
+        vec3 h = normalize(l+v);                        // Half vector between both l and v
+        vec3 reflection = -normalize(reflect(v, n));
+        reflection.y *= -1.0f;
+
+        float NdotL = clamp(dot(n, l), 0.001, 1.0);
+        float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
+        float NdotH = clamp(dot(n, h), 0.0, 1.0);
+        float LdotH = clamp(dot(l, h), 0.0, 1.0);
+        float VdotH = clamp(dot(v, h), 0.0, 1.0);
+
+        PBRInfo pbrInputs = PBRInfo(
+            NdotL,
+            NdotV,
+            NdotH,
+            LdotH,
+            VdotH,
+            perceptualRoughness,
+            metallic,
+            specularEnvironmentR0,
+            specularEnvironmentR90,
+            alphaRoughness,
+            diffuseColor,
+            specularColor
+        );
+
+        // Calculate the shading terms for the microfacet specular shading model
+        vec3 F = specularReflection(pbrInputs);
+        float G = geometricOcclusion(pbrInputs);
+        float D = microfacetDistribution(pbrInputs);
+
+        // Calculation of analytical lighting contribution
+        vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs);
+        vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV);        
+        // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
+        vec3 color = NdotL * lights[i].color.rgb * (diffuseContrib + specContrib);
+        // Calculate lighting contribution from image based lighting source (IBL)
+        colors += (color + getIBLContribution(pbrInputs, n, reflection));
+        
+        
+    }
+    colors /= NUM_LIGHTS;
+    
+    
+    const float u_OcclusionStrength = 1.0f;
+    const float u_EmissiveFactor = 1.0f;
+    
+    //AO is in the alpha channel of the normalAttachment    
+    colors = mix(colors, colors * subpassLoad(samplerN_AO, gl_SampleID).a, u_OcclusionStrength);
+    colors += emissive;             
+    
+    outColor = vec4(colors, baseColor.a);       
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/compose_with_shadows.frag b/samples/deferred/shaders/compose_with_shadows.frag
new file mode 100644 (file)
index 0000000..4247393
--- /dev/null
@@ -0,0 +1,277 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set = 0, binding = 0) uniform UBO {
+    mat4 projection;
+    mat4 model;
+    mat4 view;
+    vec4 camPos;    
+    float prefilteredCubeMipLevels;
+    float scaleIBLAmbient;
+} ubo;
+
+layout (constant_id = 0) const uint NUM_LIGHTS = 1;
+
+struct Light {
+    vec4 position;
+    vec4 color;
+    mat4 mvp;
+};
+
+layout (set = 0, binding = 4) uniform UBOLights {
+    Light lights[NUM_LIGHTS];
+};
+
+const float M_PI = 3.141592653589793;
+const float c_MinRoughness = 0.04;
+#define SHADOW_FACTOR 0.15
+
+layout (input_attachment_index = 0, set = 1, binding = 0) uniform subpassInputMS samplerColorRough;
+layout (input_attachment_index = 1, set = 1, binding = 1) uniform subpassInputMS samplerEmitMetal;
+layout (input_attachment_index = 2, set = 1, binding = 2) uniform subpassInputMS samplerN_AO;
+layout (input_attachment_index = 3, set = 1, binding = 3) uniform subpassInputMS samplerPos;
+
+layout (set = 0, binding = 1) uniform samplerCube samplerIrradiance;
+layout (set = 0, binding = 2) uniform samplerCube prefilteredMap;
+layout (set = 0, binding = 3) uniform sampler2D samplerBRDFLUT;
+layout (set = 0, binding = 6) uniform sampler2DArray samplerShadowMap;
+
+layout (location = 0) in vec2 inUV;
+
+layout (location = 0) out vec4 outColor;
+
+// Encapsulate the various inputs used by the various functions in the shading equation
+// We store values in this struct to simplify the integration of alternative implementations
+// of the shading terms, outlined in the Readme.MD Appendix.
+struct PBRInfo
+{
+    float NdotL;                  // cos angle between normal and light direction
+    float NdotV;                  // cos angle between normal and view direction
+    float NdotH;                  // cos angle between normal and half vector
+    float LdotH;                  // cos angle between light direction and half vector
+    float VdotH;                  // cos angle between view direction and half vector
+    float perceptualRoughness;    // roughness value, as authored by the model creator (input to shader)
+    float metalness;              // metallic value at the surface
+    vec3 reflectance0;            // full reflectance color (normal incidence angle)
+    vec3 reflectance90;           // reflectance color at grazing angle
+    float alphaRoughness;         // roughness mapped to a more linear change in the roughness (proposed by [2])
+    vec3 diffuseColor;            // color contribution from diffuse lighting
+    vec3 specularColor;           // color contribution from specular lighting
+};
+
+// Calculation of the lighting contribution from an optional Image Based Light source.
+// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
+// See our README.md on Environment Maps [3] for additional discussion.
+vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)
+{
+    float lod = (pbrInputs.perceptualRoughness * ubo.prefilteredCubeMipLevels);
+    // retrieve a scale and bias to F0. See [1], Figure 3
+    vec3 brdf = (texture(samplerBRDFLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb;
+    vec3 diffuseLight = texture(samplerIrradiance, reflection).rgb;
+
+    vec3 specularLight = textureLod(prefilteredMap, reflection, lod).rgb;
+
+    vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
+    vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
+
+    // For presentation, this allows us to disable IBL terms
+    // For presentation, this allows us to disable IBL terms
+    diffuse *= ubo.scaleIBLAmbient;
+    specular *= ubo.scaleIBLAmbient;
+
+    return diffuse + specular;
+}
+
+// Basic Lambertian diffuse
+// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog
+// See also [1], Equation 1
+vec3 diffuse(PBRInfo pbrInputs)
+{
+    return pbrInputs.diffuseColor / M_PI;
+}
+
+// The following equation models the Fresnel reflectance term of the spec equation (aka F())
+// Implementation of fresnel from [4], Equation 15
+vec3 specularReflection(PBRInfo pbrInputs)
+{
+    return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
+}
+
+// This calculates the specular geometric attenuation (aka G()),
+// where rougher material will reflect less light back to the viewer.
+// This implementation is based on [1] Equation 4, and we adopt their modifications to
+// alphaRoughness as input as originally proposed in [2].
+float geometricOcclusion(PBRInfo pbrInputs)
+{
+    float NdotL = pbrInputs.NdotL;
+    float NdotV = pbrInputs.NdotV;
+    float r = pbrInputs.alphaRoughness;
+
+    float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
+    float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
+    return attenuationL * attenuationV;
+}
+
+// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
+// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
+// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
+float microfacetDistribution(PBRInfo pbrInputs)
+{
+    float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
+    float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
+    return roughnessSq / (M_PI * f * f);
+}
+
+// Gets metallic factor from specular glossiness workflow inputs 
+float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) {
+    float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b);
+    float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b);
+    if (perceivedSpecular < c_MinRoughness) {
+        return 0.0;
+    }
+    float a = c_MinRoughness;
+    float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness;
+    float c = c_MinRoughness - perceivedSpecular;
+    float D = max(b * b - 4.0 * a * c, 0.0);
+    return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);
+}
+
+float textureProj(vec4 P, float layer, vec2 offset)
+{
+    float shadow = 1.0;
+    vec4 shadowCoord = P / P.w;
+    shadowCoord.st = shadowCoord.st * 0.5 + 0.5;
+    
+    if (shadowCoord.z > -1.0 && shadowCoord.z < 1.0) 
+    {
+        float dist = texture(samplerShadowMap, vec3(shadowCoord.st + offset, layer)).r;
+        if (shadowCoord.w > 0.0 && dist < shadowCoord.z)         
+            shadow = SHADOW_FACTOR;
+    }else
+        shadow = 0.05f;//for debug view out of light proj
+    
+    return shadow;
+}
+
+float filterPCF(vec4 sc, float layer)
+{
+    ivec2 texDim = textureSize(samplerShadowMap, 0).xy;
+    float scale = 1.5;
+    float dx = scale * 1.0 / float(texDim.x);
+    float dy = scale * 1.0 / float(texDim.y);
+
+    float shadowFactor = 0.0;
+    int count = 0;
+    int range = 1;
+    
+    for (int x = -range; x <= range; x++)
+    {
+        for (int y = -range; y <= range; y++)
+        {
+            shadowFactor += textureProj(sc, layer, vec2(dx*x, dy*y));
+            count++;
+        }
+    
+    }
+    return shadowFactor / count;
+}
+
+void main() 
+{
+    if (subpassLoad(samplerPos, gl_SampleID).a == 1.0f)
+        discard;
+    
+    float perceptualRoughness = subpassLoad(samplerColorRough, gl_SampleID).a;
+    float metallic = subpassLoad(samplerEmitMetal, gl_SampleID).a;
+    vec3 diffuseColor;
+    vec4 baseColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).rgb, 1);    
+    vec3 emissive = subpassLoad (samplerEmitMetal, gl_SampleID).rgb;        
+
+    vec3 f0 = vec3(0.04);
+    
+    diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
+    diffuseColor *= 1.0 - metallic;
+        
+    float alphaRoughness = perceptualRoughness * perceptualRoughness;
+
+    vec3 specularColor = mix(f0, baseColor.rgb, metallic);
+
+    // Compute reflectance.
+    float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
+
+    // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
+    // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
+    float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
+    vec3 specularEnvironmentR0 = specularColor.rgb;
+    vec3 specularEnvironmentR90 = vec3(1.0) * reflectance90;
+
+    vec3 n = subpassLoad(samplerN_AO, gl_SampleID).rgb;
+    vec3 pos = subpassLoad(samplerPos, gl_SampleID).rgb;
+    vec3 v = normalize(ubo.camPos.xyz - pos); // Vector from surface point to camera
+    
+    vec3 colors = vec3(0);
+    vec3 lightTarget = vec3(0);
+    
+    for (int i=0; i<NUM_LIGHTS; i++) {
+    
+        vec3 l = normalize(lights[i].position.xyz-pos);     // Vector from surface point to light
+        vec3 h = normalize(l+v);                        // Half vector between both l and v
+        vec3 reflection = -normalize(reflect(v, n));
+        reflection.y *= -1.0f;
+
+        float NdotL = clamp(dot(n, l), 0.001, 1.0);
+        float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
+        float NdotH = clamp(dot(n, h), 0.0, 1.0);
+        float LdotH = clamp(dot(l, h), 0.0, 1.0);
+        float VdotH = clamp(dot(v, h), 0.0, 1.0);
+
+        PBRInfo pbrInputs = PBRInfo(
+            NdotL,
+            NdotV,
+            NdotH,
+            LdotH,
+            VdotH,
+            perceptualRoughness,
+            metallic,
+            specularEnvironmentR0,
+            specularEnvironmentR90,
+            alphaRoughness,
+            diffuseColor,
+            specularColor
+        );
+
+        // Calculate the shading terms for the microfacet specular shading model
+        vec3 F = specularReflection(pbrInputs);
+        float G = geometricOcclusion(pbrInputs);
+        float D = microfacetDistribution(pbrInputs);
+
+        // Calculation of analytical lighting contribution
+        vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs);
+        vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV);        
+        // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
+        vec3 color = NdotL * lights[i].color.rgb * (diffuseContrib + specContrib);
+        
+        #if SHADOW
+        vec4 shadowClip = lights[i].mvp * vec4(pos, 1);
+        float shadowFactor = filterPCF(shadowClip, i);
+        // Calculate lighting contribution from image based lighting source (IBL)
+        colors += shadowFactor * (color + getIBLContribution(pbrInputs, n, reflection));
+        #else
+        colors += color + getIBLContribution(pbrInputs, n, reflection);
+        #endif
+        
+        
+    }
+    colors /= NUM_LIGHTS;
+    
+    
+    const float u_OcclusionStrength = 1.0f;
+    const float u_EmissiveFactor = 3.0f;
+    
+    //AO is in the alpha channel of the normalAttachment    
+    colors = mix(colors, colors * subpassLoad(samplerN_AO, gl_SampleID).a, u_OcclusionStrength);
+    colors += emissive * u_EmissiveFactor;             
+    
+    outColor = vec4(colors, baseColor.a);       
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/debug.frag b/samples/deferred/shaders/debug.frag
new file mode 100644 (file)
index 0000000..fd419ff
--- /dev/null
@@ -0,0 +1,12 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inColor;
+layout (location = 0) out vec4 outFragColor;
+
+void main() 
+{
+  outFragColor = vec4(inColor, 1.0);
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/debug.vert b/samples/deferred/shaders/debug.vert
new file mode 100644 (file)
index 0000000..180e58e
--- /dev/null
@@ -0,0 +1,34 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inColor;
+
+layout (location = 0) out vec3 outColor;
+
+layout (binding = 0) uniform UBO 
+{
+    mat4 projectionMatrix;
+    mat4 viewMatrix;
+    mat4 modelMatrix;    
+    float gamma;
+    float exposure;    
+} ubo;
+
+layout(push_constant) uniform PushConsts {
+    mat4 projectionMatrix;
+} pc;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+void main() 
+{
+    outColor = inColor;
+    
+       gl_Position = pc.projectionMatrix * vec4 ((ubo.viewMatrix * vec4(inPos.xyz, 0.0)).xyz, 1);
+}
diff --git a/samples/deferred/shaders/emissive.frag b/samples/deferred/shaders/emissive.frag
new file mode 100644 (file)
index 0000000..2efe331
--- /dev/null
@@ -0,0 +1,25 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (input_attachment_index = 0, set = 2, binding = 0) uniform subpassInputMS samplerColorRough;
+layout (input_attachment_index = 1, set = 2, binding = 1) uniform subpassInputMS samplerEmitMetal;
+layout (input_attachment_index = 2, set = 2, binding = 2) uniform subpassInputMS samplerN_AO;
+layout (input_attachment_index = 3, set = 2, binding = 3) uniform subpassInputMS samplerPos;
+
+layout (location = 0) in vec2 inUV;
+
+layout (location = 0) out vec4 outColor;
+
+const float offset[5] = float[](0.0, 1.0, 2.0, 3.0, 4.0);
+const float weight[5] = float[](0.2270270270, 0.1945945946, 0.1216216216,
+                                  0.0540540541, 0.0162162162);
+void main() 
+{
+    if (subpassLoad(samplerPos, gl_SampleID).a == 1.0f)
+        discard;
+    vec3 emissive = subpassLoad (samplerEmitMetal, gl_SampleID).rgb;        
+        
+    outColor = vec4(emissive, 1);
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/filtercube.vert b/samples/deferred/shaders/filtercube.vert
new file mode 100644 (file)
index 0000000..1226e28
--- /dev/null
@@ -0,0 +1,19 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+
+layout(push_constant) uniform PushConsts {
+       layout (offset = 0) mat4 mvp;
+} pushConsts;
+
+layout (location = 0) out vec3 outUVW;
+
+out gl_PerVertex {
+       vec4 gl_Position;
+};
+
+void main() 
+{
+       outUVW = inPos;
+       gl_Position = pushConsts.mvp * vec4(inPos.xyz, 1.0);
+}
diff --git a/samples/deferred/shaders/genbrdflut.frag b/samples/deferred/shaders/genbrdflut.frag
new file mode 100644 (file)
index 0000000..b6290dd
--- /dev/null
@@ -0,0 +1,90 @@
+#version 450
+
+layout (location = 0) in vec2 inUV;
+layout (location = 0) out vec4 outColor;
+layout (constant_id = 0) const uint NUM_SAMPLES = 1024u;
+
+const float PI = 3.1415926536;
+
+// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
+float random(vec2 co)
+{
+       float a = 12.9898;
+       float b = 78.233;
+       float c = 43758.5453;
+       float dt= dot(co.xy ,vec2(a,b));
+       float sn= mod(dt,3.14);
+       return fract(sin(sn) * c);
+}
+
+vec2 hammersley2d(uint i, uint N) 
+{
+       // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
+       uint bits = (i << 16u) | (i >> 16u);
+       bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+       bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+       bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+       bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+       float rdi = float(bits) * 2.3283064365386963e-10;
+       return vec2(float(i) /float(N), rdi);
+}
+
+// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf
+vec3 importanceSample_GGX(vec2 Xi, float roughness, vec3 normal) 
+{
+       // Maps a 2D point to a hemisphere with spread based on roughness
+       float alpha = roughness * roughness;
+       float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1;
+       float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y));
+       float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+       vec3 H = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+
+       // Tangent space
+       vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+       vec3 tangentX = normalize(cross(up, normal));
+       vec3 tangentY = normalize(cross(normal, tangentX));
+
+       // Convert to world Space
+       return normalize(tangentX * H.x + tangentY * H.y + normal * H.z);
+}
+
+// Geometric Shadowing function
+float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness)
+{
+       float k = (roughness * roughness) / 2.0;
+       float GL = dotNL / (dotNL * (1.0 - k) + k);
+       float GV = dotNV / (dotNV * (1.0 - k) + k);
+       return GL * GV;
+}
+
+vec2 BRDF(float NoV, float roughness)
+{
+       // Normal always points along z-axis for the 2D lookup 
+       const vec3 N = vec3(0.0, 0.0, 1.0);
+       vec3 V = vec3(sqrt(1.0 - NoV*NoV), 0.0, NoV);
+
+       vec2 LUT = vec2(0.0);
+       for(uint i = 0u; i < NUM_SAMPLES; i++) {
+               vec2 Xi = hammersley2d(i, NUM_SAMPLES);
+               vec3 H = importanceSample_GGX(Xi, roughness, N);
+               vec3 L = 2.0 * dot(V, H) * H - V;
+
+               float dotNL = max(dot(N, L), 0.0);
+               float dotNV = max(dot(N, V), 0.0);
+               float dotVH = max(dot(V, H), 0.0); 
+               float dotNH = max(dot(H, N), 0.0);
+
+               if (dotNL > 0.0) {
+                       float G = G_SchlicksmithGGX(dotNL, dotNV, roughness);
+                       float G_Vis = (G * dotVH) / (dotNH * dotNV);
+                       float Fc = pow(1.0 - dotVH, 5.0);
+                       LUT += vec2((1.0 - Fc) * G_Vis, Fc * G_Vis);
+               }
+       }
+       return LUT / float(NUM_SAMPLES);
+}
+
+void main() 
+{
+       outColor = vec4(BRDF(inUV.s, 1.0-inUV.t), 0.0, 1.0);
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/genbrdflut.vert b/samples/deferred/shaders/genbrdflut.vert
new file mode 100644 (file)
index 0000000..f3dd233
--- /dev/null
@@ -0,0 +1,9 @@
+#version 450
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+       outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+       gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f);
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/irradiancecube.frag b/samples/deferred/shaders/irradiancecube.frag
new file mode 100644 (file)
index 0000000..340d679
--- /dev/null
@@ -0,0 +1,37 @@
+// Generates an irradiance cube from an environment map using convolution
+
+#version 450
+
+layout (location = 0) in vec3 inPos;
+layout (location = 0) out vec4 outColor;
+layout (binding = 0) uniform samplerCube samplerEnv;
+
+layout(push_constant) uniform PushConsts {
+       layout (offset = 64) float deltaPhi;
+       layout (offset = 68) float deltaTheta;
+} consts;
+
+#define PI 3.1415926535897932384626433832795
+
+void main()
+{
+       vec3 N = normalize(inPos);
+       vec3 up = vec3(0.0, 1.0, 0.0);
+       vec3 right = normalize(cross(up, N));
+       up = cross(N, right);
+
+       const float TWO_PI = PI * 2.0;
+       const float HALF_PI = PI * 0.5;
+
+       vec3 color = vec3(0.0);
+       uint sampleCount = 0u;
+       for (float phi = 0.0; phi < TWO_PI; phi += consts.deltaPhi) {
+               for (float theta = 0.0; theta < HALF_PI; theta += consts.deltaTheta) {
+                       vec3 tempVec = cos(phi) * right + sin(phi) * up;
+                       vec3 sampleVector = cos(theta) * N + sin(theta) * tempVec;
+                       color += texture(samplerEnv, sampleVector).rgb * cos(theta) * sin(theta);
+                       sampleCount++;
+               }
+       }
+       outColor = vec4(PI * color / float(sampleCount), 1.0);//texture(samplerEnv, inPos).rgba;
+}
diff --git a/samples/deferred/shaders/prefilterenvmap.frag b/samples/deferred/shaders/prefilterenvmap.frag
new file mode 100644 (file)
index 0000000..ae1212e
--- /dev/null
@@ -0,0 +1,105 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+layout (location = 0) out vec4 outColor;
+
+layout (binding = 0) uniform samplerCube samplerEnv;
+
+layout(push_constant) uniform PushConsts {
+       layout (offset = 64) float roughness;
+       layout (offset = 68) uint numSamples;
+} consts;
+
+const float PI = 3.1415926536;
+
+// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
+float random(vec2 co)
+{
+       float a = 12.9898;
+       float b = 78.233;
+       float c = 43758.5453;
+       float dt= dot(co.xy ,vec2(a,b));
+       float sn= mod(dt,3.14);
+       return fract(sin(sn) * c);
+}
+
+vec2 hammersley2d(uint i, uint N) 
+{
+       // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
+       uint bits = (i << 16u) | (i >> 16u);
+       bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+       bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+       bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+       bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+       float rdi = float(bits) * 2.3283064365386963e-10;
+       return vec2(float(i) /float(N), rdi);
+}
+
+// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf
+vec3 importanceSample_GGX(vec2 Xi, float roughness, vec3 normal) 
+{
+       // Maps a 2D point to a hemisphere with spread based on roughness
+       float alpha = roughness * roughness;
+       float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1;
+       float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y));
+       float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+       vec3 H = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+
+       // Tangent space
+       vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+       vec3 tangentX = normalize(cross(up, normal));
+       vec3 tangentY = normalize(cross(normal, tangentX));
+
+       // Convert to world Space
+       return normalize(tangentX * H.x + tangentY * H.y + normal * H.z);
+}
+
+// Normal Distribution function
+float D_GGX(float dotNH, float roughness)
+{
+       float alpha = roughness * roughness;
+       float alpha2 = alpha * alpha;
+       float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0;
+       return (alpha2)/(PI * denom*denom); 
+}
+
+vec3 prefilterEnvMap(vec3 R, float roughness)
+{
+       vec3 N = R;
+       vec3 V = R;
+       vec3 color = vec3(0.0);
+       float totalWeight = 0.0;
+       float envMapDim = float(textureSize(samplerEnv, 0).s);
+       for(uint i = 0u; i < consts.numSamples; i++) {
+               vec2 Xi = hammersley2d(i, consts.numSamples);
+               vec3 H = importanceSample_GGX(Xi, roughness, N);
+               vec3 L = 2.0 * dot(V, H) * H - V;
+               float dotNL = clamp(dot(N, L), 0.0, 1.0);
+               if(dotNL > 0.0) {
+                       // Filtering based on https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/
+
+                       float dotNH = clamp(dot(N, H), 0.0, 1.0);
+                       float dotVH = clamp(dot(V, H), 0.0, 1.0);
+
+                       // Probability Distribution Function
+                       float pdf = D_GGX(dotNH, roughness) * dotNH / (4.0 * dotVH) + 0.0001;
+                       // Slid angle of current smple
+                       float omegaS = 1.0 / (float(consts.numSamples) * pdf);
+                       // Solid angle of 1 pixel across all cube faces
+                       float omegaP = 4.0 * PI / (6.0 * envMapDim * envMapDim);
+                       // Biased (+1.0) mip level for better result
+                       float mipLevel = roughness == 0.0 ? 0.0 : max(0.5 * log2(omegaS / omegaP) + 1.0, 0.0f);
+                       color += textureLod(samplerEnv, L, mipLevel).rgb * dotNL;
+                       totalWeight += dotNL;
+
+               }
+       }
+       return (color / totalWeight);
+}
+
+
+void main()
+{              
+       vec3 N = normalize(inPos);
+       outColor = vec4(prefilterEnvMap(N, consts.roughness), 1.0);
+}
diff --git a/samples/deferred/shaders/shadow.geom b/samples/deferred/shaders/shadow.geom
new file mode 100644 (file)
index 0000000..9fd322c
--- /dev/null
@@ -0,0 +1,34 @@
+#version 420
+
+#define NUM_LIGHTS 2
+
+layout (triangles, invocations = NUM_LIGHTS) in;
+layout (triangle_strip, max_vertices = 3) out;
+
+struct Light {
+    vec4 position;
+    vec4 color;
+    mat4 mvp;
+};
+layout (set = 0, binding = 0) uniform UBO {
+    mat4 projection;
+    mat4 model;
+    mat4 view;    
+};
+layout (set = 0, binding = 1) uniform UBOLights {
+    Light lights[NUM_LIGHTS];
+};
+
+//layout (location = 0) in int inInstanceIndex[];
+
+
+void main() 
+{
+       for (int i = 0; i < gl_in.length(); i++)
+       {
+               gl_Layer = gl_InvocationID;        
+               gl_Position = lights[gl_InvocationID].mvp * model * gl_in[i].gl_Position;        
+               EmitVertex();
+       }
+       EndPrimitive();
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/shadow.vert b/samples/deferred/shaders/shadow.vert
new file mode 100644 (file)
index 0000000..b0bd6c0
--- /dev/null
@@ -0,0 +1,12 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+
+layout(push_constant) uniform PushConsts {
+    mat4 model;
+} pc;
+
+void main()
+{
+       gl_Position = pc.model * vec4(inPos,1);
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/show_gbuff.frag b/samples/deferred/shaders/show_gbuff.frag
new file mode 100644 (file)
index 0000000..7859e36
--- /dev/null
@@ -0,0 +1,92 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (input_attachment_index = 0, set = 1, binding = 0) uniform subpassInputMS samplerColorRough;
+layout (input_attachment_index = 1, set = 1, binding = 1) uniform subpassInputMS samplerEmitMetal;
+layout (input_attachment_index = 2, set = 1, binding = 2) uniform subpassInputMS samplerN_AO;
+layout (input_attachment_index = 3, set = 1, binding = 3) uniform subpassInputMS samplerPos;
+
+layout (set = 0, binding = 1) uniform samplerCube samplerIrradiance;
+layout (set = 0, binding = 2) uniform samplerCube prefilteredMap;
+layout (set = 0, binding = 3) uniform sampler2D samplerBRDFLUT;
+layout (set = 0, binding = 6) uniform sampler2DArray samplerShadowMap;
+
+layout (push_constant) uniform PushCsts {
+    layout(offset = 64)
+    int imgIdx;
+};
+
+layout (location = 0) in vec2 inUV;
+layout (location = 0) out vec4 outColor;
+
+const uint color        = 0;
+const uint normal       = 1;
+const uint pos          = 2;
+const uint occlusion    = 3;
+const uint emissive     = 4;
+const uint metallic     = 5;
+const uint roughness    = 6;
+const uint depth        = 7;
+const uint prefill      = 8;
+const uint irradiance   = 9;
+const uint shadowMap    = 10;
+
+vec4 sampleCubeMap (samplerCube sc, uint face, uint lod) {
+    vec2 uv = 2.0 * inUV - vec2(1.0);
+    switch (face) {
+        case 0:
+            return vec4 (textureLod (sc, vec3(1, uv.t, uv.s), lod).rgb, 1);
+        case 1:
+            return vec4 (textureLod (sc, vec3(-1, uv.t, uv.s), lod).rgb, 1);
+        case 2:
+            return vec4 (textureLod (sc, vec3(uv.s, 1, -uv.t), lod).rgb, 1);
+        case 3:
+            return vec4 (textureLod (sc, vec3(uv.s, -1, uv.t), lod).rgb, 1);
+        case 4:
+            return vec4 (textureLod (sc, vec3(uv, 1), lod).rgb, 1);
+        case 5:
+            return vec4 (textureLod (sc, vec3(-uv.s, uv.t, -1), lod).rgb, 1);
+   }
+}
+
+void main() 
+{
+    uint imgNum = bitfieldExtract (imgIdx, 0, 8);
+    switch (imgNum) {
+        case color:
+            outColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).rgb, 1);
+            break;
+        case normal:
+            outColor = vec4(subpassLoad(samplerN_AO, gl_SampleID).rgb, 1);
+            break;
+        case pos:
+            outColor = vec4(subpassLoad(samplerPos, gl_SampleID).rgb, 1);
+            break;
+        case occlusion:
+            outColor = vec4(subpassLoad(samplerN_AO, gl_SampleID).aaa, 1);
+            break;
+        case emissive:
+            outColor = vec4(subpassLoad(samplerEmitMetal, gl_SampleID).rgb, 1);
+            break;
+        case metallic:
+            outColor = vec4(subpassLoad(samplerEmitMetal, gl_SampleID).aaa, 1);
+            break;
+        case roughness:
+            outColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).aaa, 1);
+            break;
+        case depth:
+            outColor = vec4(subpassLoad(samplerPos, gl_SampleID).aaa, 1);
+            break;
+        case shadowMap:            
+            vec3 d = texture(samplerShadowMap, vec3(inUV, bitfieldExtract (imgIdx, 8, 8))).rrr;
+            outColor = vec4(d*d*d, 1);
+            break;
+        default:
+            if (imgNum == prefill)
+                outColor = sampleCubeMap (prefilteredMap, bitfieldExtract (imgIdx, 8, 8), bitfieldExtract (imgIdx, 16, 8));            
+            else if (imgNum == irradiance)
+                outColor = sampleCubeMap (samplerIrradiance, bitfieldExtract (imgIdx, 8, 8), bitfieldExtract (imgIdx, 16, 8));            
+            break;
+    }
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/simpletexture.frag b/samples/deferred/shaders/simpletexture.frag
new file mode 100644 (file)
index 0000000..a081876
--- /dev/null
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set = 0, binding = 0) uniform sampler2D samplerColor;
+
+layout (location = 0) in       vec2 inUV;
+
+layout (location = 0) out      vec4 outFragColor;
+
+void main()
+{    
+       outFragColor = texture(samplerColor, inUV);
+}
diff --git a/samples/deferred/shaders/skybox.frag b/samples/deferred/shaders/skybox.frag
new file mode 100644 (file)
index 0000000..e505ef1
--- /dev/null
@@ -0,0 +1,12 @@
+#version 450
+
+layout (binding = 2) uniform samplerCube samplerEnv;
+
+layout (set = 0, location = 0) in vec3 inUVW;
+
+layout (set = 0, location = 0) out vec4 outColor;
+
+void main() 
+{    
+    outColor = vec4(textureLod(samplerEnv, inUVW, 1.5).rgb, 1.0);
+}
\ No newline at end of file
diff --git a/samples/deferred/shaders/skybox.vert b/samples/deferred/shaders/skybox.vert
new file mode 100644 (file)
index 0000000..22e285e
--- /dev/null
@@ -0,0 +1,27 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+
+layout (binding = 0) uniform UBO 
+{
+    mat4 projection;
+    mat4 model;
+    mat4 view;
+} ubo;
+
+layout (location = 0) out vec3 outUVW;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;
+};
+
+void main() 
+{
+    outUVW = inPos;
+    outUVW.y = -outUVW.y;        
+    gl_Position = ubo.projection * mat4(mat3(ubo.view)) * vec4(inPos, 1.0);    
+}
diff --git a/samples/deferred/shaders/tone_mapping.frag b/samples/deferred/shaders/tone_mapping.frag
new file mode 100644 (file)
index 0000000..6a1a8d4
--- /dev/null
@@ -0,0 +1,70 @@
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout(push_constant) uniform PushConsts {
+       float exposure;
+       float gamma;
+       float debug;
+} pc;
+
+layout (set = 0, binding = 0) uniform sampler2D samplerHDR;
+layout (set = 0, binding = 1) uniform sampler2D bloom;
+
+layout (location = 0) in vec2 inUV;
+layout (location = 0) out vec4 outColor;
+
+vec3 Uncharted2Tonemap(vec3 color)
+{
+    const float A = 0.15;
+    const float B = 0.50;
+    const float C = 0.10;
+    const float D = 0.20;
+    const float E = 0.02;
+    const float F = 0.30;
+    const float W = 11.2;
+    return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F;
+}
+
+vec3 tonemap(vec3 color)
+{
+    vec3 outcol = Uncharted2Tonemap(color.rgb * pc.exposure);
+    outcol = outcol * (1.0f / Uncharted2Tonemap(vec3(11.2f)));  
+    return pow(outcol, vec3(1.0f / pc.gamma));
+}
+
+vec3 SRGBtoLINEAR(vec3 srgbIn)
+{
+    #ifdef MANUAL_SRGB
+    #ifdef SRGB_FAST_APPROXIMATION
+    return pow(srgbIn.xyz,vec3(2.2));
+    #else //SRGB_FAST_APPROXIMATION
+    vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
+    return linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
+    #endif //SRGB_FAST_APPROXIMATION    
+    #else //MANUAL_SRGB
+    return srgbIn;
+    #endif //MANUAL_SRGB
+}
+                                                                                                         
+void main() 
+{
+       if (pc.debug < 0.0f) {    
+           //vec4 hdrColor = texelFetch (samplerHDR, ivec2(inUV), gl_SampleID);    
+           vec4 hdrColor = texture (samplerHDR, inUV);    
+           //vec4 c = texture (bloom, inUV);
+           //float lum = (0.299*c.r + 0.587*c.g + 0.114*c.b);
+           //if (lum>1.0)
+           //    hdrColor.rgb += c.rgb * 0.05;
+           outColor = vec4(SRGBtoLINEAR(tonemap(hdrColor.rgb)), hdrColor.a);       
+    }else
+       outColor = texture (bloom, inUV);
+    
+    
+    /*
+    outColor = vec4(SRGBtoLINEAR(tonemap(hdrColor.rgb)), hdrColor.a);;*/
+    
+/*  vec3 mapped = vec3(1.0) - exp(-hdrColor.rgb * pc.exposure);        
+    mapped = pow(mapped, vec3(1.0 / pc.gamma));
+    outColor = vec4(mapped, hdrColor.a);*/
+}
\ No newline at end of file
diff --git a/samples/deferred/shadowMapRenderer.cs b/samples/deferred/shadowMapRenderer.cs
new file mode 100644 (file)
index 0000000..a4684ec
--- /dev/null
@@ -0,0 +1,157 @@
+/*shadow mapping greatly inspired from:
+* Vulkan Example - Deferred shading with shadows from multiple light sources using geometry shader instancing
+* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
+* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+*/
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using CVKL;
+using CVKL.glTF;
+using VK;
+using static deferred.DeferredPbrRenderer;
+
+namespace deferred {
+       public class ShadowMapRenderer : IDisposable {
+               Device dev;
+               Queue gQueue;
+
+               public static uint SHADOWMAP_SIZE = 4096;
+               public static VkFormat SHADOWMAP_FORMAT = VkFormat.D32SfloatS8Uint;
+               public static VkSampleCountFlags SHADOWMAP_NUM_SAMPLES = VkSampleCountFlags.SampleCount1;
+               public bool updateShadowMap = true;
+
+               public float depthBiasConstant = 1.5f;
+               public float depthBiasSlope = 1.75f;
+               float lightFOV = Utils.DegreesToRadians (60);
+               float lightFarPlane;
+
+
+
+               RenderPass shadowPass;
+               Framebuffer fbShadowMap;
+               public Image shadowMap { get; private set; }
+               Pipeline shadowPipeline;
+               DescriptorPool descriptorPool;
+               DescriptorSetLayout descLayoutShadow;
+               DescriptorSet dsShadow;
+               DeferredPbrRenderer renderer;
+
+               public ShadowMapRenderer (Queue gQueue, DeferredPbrRenderer renderer, float farPlane = 16f) {
+                       this.lightFarPlane = farPlane;
+                       this.gQueue = gQueue;
+                       this.dev = gQueue.Dev;
+                       this.renderer = renderer;
+
+                       descriptorPool = new DescriptorPool (dev, 1,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2)
+                       );
+
+                       init ();
+               }
+
+               void init () {
+
+                       //Shadow map renderpass
+                       shadowPass = new RenderPass (dev, VkSampleCountFlags.SampleCount1);
+                       shadowPass.AddAttachment (SHADOWMAP_FORMAT, VkImageLayout.DepthStencilReadOnlyOptimal, SHADOWMAP_NUM_SAMPLES);
+                       shadowPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) });
+
+                       SubPass subpass0 = new SubPass ();
+                       subpass0.SetDepthReference (0);
+                       shadowPass.AddSubpass (subpass0);
+
+                       shadowPass.AddDependency (Vk.SubpassExternal, 0,
+                               VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.EarlyFragmentTests,
+                               VkAccessFlags.ShaderRead, VkAccessFlags.DepthStencilAttachmentWrite);
+                       shadowPass.AddDependency (0, Vk.SubpassExternal,
+                               VkPipelineStageFlags.LateFragmentTests, VkPipelineStageFlags.FragmentShader,
+                               VkAccessFlags.DepthStencilAttachmentWrite, VkAccessFlags.ShaderRead);
+
+                       descLayoutShadow = new DescriptorSetLayout (dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Geometry, VkDescriptorType.UniformBuffer),//matrices
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Geometry, VkDescriptorType.UniformBuffer));//lights
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList);
+                       cfg.rasterizationState.cullMode = VkCullModeFlags.Back;
+                       cfg.rasterizationState.depthBiasEnable = true;
+                       cfg.dynamicStates.Add (VkDynamicState.DepthBias);
+
+                       cfg.RenderPass = shadowPass;
+
+                       cfg.Layout = new PipelineLayout (dev, descLayoutShadow);
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex|VkShaderStageFlags.Geometry, (uint)Marshal.SizeOf<Matrix4x4> ())
+                       );
+
+                       cfg.AddVertexBinding<PbrModel.Vertex> (0);
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.shadow.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Geometry, "#deferred.shadow.geom.spv");
+
+                       shadowPipeline = new GraphicPipeline (cfg);
+
+                       //shadow map image
+                       shadowMap = new Image (dev, SHADOWMAP_FORMAT, VkImageUsageFlags.DepthStencilAttachment | VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal, SHADOWMAP_SIZE, SHADOWMAP_SIZE,
+                               VkImageType.Image2D, SHADOWMAP_NUM_SAMPLES, VkImageTiling.Optimal, 1, (uint)renderer.lights.Length);
+                       shadowMap.CreateView (VkImageViewType.ImageView2DArray, VkImageAspectFlags.Depth, shadowMap.CreateInfo.arrayLayers);
+                       shadowMap.CreateSampler (VkSamplerAddressMode.ClampToBorder);
+                       shadowMap.Descriptor.imageLayout = VkImageLayout.DepthStencilReadOnlyOptimal;
+
+                       fbShadowMap = new Framebuffer (shadowPass, SHADOWMAP_SIZE, SHADOWMAP_SIZE, (uint)renderer.lights.Length);
+                       fbShadowMap.attachments.Add (shadowMap);
+                       fbShadowMap.Activate ();
+
+                       dsShadow = descriptorPool.Allocate (descLayoutShadow);
+
+                       DescriptorSetWrites dsWrite = new DescriptorSetWrites (dsShadow, descLayoutShadow);
+                       dsWrite.Write (dev, renderer.uboMatrices.Descriptor, renderer.uboLights.Descriptor);
+               }
+
+               public void update_light_matrices () {
+                       Matrix4x4 proj = Matrix4x4.CreatePerspectiveFieldOfView (lightFOV, 1, 0.1f, lightFarPlane);
+                       for (int i = 0; i < renderer.lights.Length; i++) {
+                               Matrix4x4 view = Matrix4x4.CreateLookAt (renderer.lights[i].position.ToVector3 (), Vector3.Zero, Vector3.UnitY);
+                               renderer.lights[i].mvp = view * proj;
+                       }
+                       renderer.uboLights.Update (renderer.lights);
+                       dev.WaitIdle ();
+               }
+
+               public void update_shadow_map (CommandPool cmdPool) {
+                       update_light_matrices ();
+
+                       CommandBuffer cmd = cmdPool.AllocateAndStart ();
+
+                       shadowPass.Begin (cmd, fbShadowMap);
+
+                       cmd.SetViewport (SHADOWMAP_SIZE, SHADOWMAP_SIZE);
+                       cmd.SetScissor (SHADOWMAP_SIZE, SHADOWMAP_SIZE);
+
+                       cmd.BindDescriptorSet (shadowPipeline.Layout, dsShadow);
+
+                       Vk.vkCmdSetDepthBias (cmd.Handle, depthBiasConstant, 0.0f, depthBiasSlope);
+
+                       shadowPipeline.Bind (cmd);
+
+                       if (renderer.model != null) {
+                               renderer.model.Bind (cmd);
+                               renderer.model.DrawAll (cmd, shadowPipeline.Layout, true);
+                       }
+
+                       shadowPass.End (cmd);
+
+                       gQueue.EndSubmitAndWait (cmd);
+                       updateShadowMap = false;
+               }
+
+
+               public void Dispose () {
+                       shadowPipeline?.Dispose ();
+                       fbShadowMap?.Dispose ();
+                       shadowMap?.Dispose ();
+                       descriptorPool?.Dispose ();
+               }
+       }
+}
diff --git a/samples/deferred/ui/debug.crow b/samples/deferred/ui/debug.crow
new file mode 100644 (file)
index 0000000..4d78bf1
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<Window Caption="Debug" Icon="#deferred.crow.svg" Left="10" Top="100" Width="40%" Height="50%">
+    <VerticalStack Spacing="1" Margin="10" CacheEnabled="true">     
+        <HorizontalStack Height="Fit">
+            <Label Text="Fps:" Width="80"/>            
+            <Label Text="{fps}" Width="Stretched" TextAlignment="Center"/>    
+        </HorizontalStack>       
+        <Expandable Caption="Debug Views">
+            <EnumSelector Left="20" EnumValue="{²CurrentDebugView}" Width="50%"/>        
+        </Expandable>
+    </VerticalStack>
+</Window>
+
diff --git a/samples/deferred/ui/deferred.style b/samples/deferred/ui/deferred.style
new file mode 100644 (file)
index 0000000..c21ffb6
--- /dev/null
@@ -0,0 +1,61 @@
+icon {
+       Width="14";
+       Height="14";
+}
+MemberViewLabel {
+       Margin="1";
+       Height="Fit";
+       Width="50%";
+       Background="White";
+}
+MemberViewHStack {
+       Focusable="true";
+       Height="Fit";
+       Spacing="1";
+       MouseEnter="{Background=SteelBlue}";
+       MouseLeave="{Background=Transparent}";
+}
+
+IcoBut {
+       Template = "#Crow.Coding.ui.IcoBut.template";
+       MinimumSize = "10,10";
+       Width = "8";
+       Height = "14";
+       Background = "White";
+}
+
+WinSchema {
+       Template="#Crow.Coding.ui.DockWindows.WinSchemaItem.template";
+       Focusable = "true";
+       MinimumSize="150,50";
+       Width = "Fit";
+       Height = "Fit";
+}
+TabItem {
+       AllowDrag="false";
+       Background="Jet";
+}
+TreeItemBorder {
+       CornerRadius="2";
+       Margin="0";
+       Focusable="true";
+       Height="Fit";
+       Width="Stretched";
+       Foreground="Transparent";
+       MouseEnter="{Foreground=DimGrey}";
+       MouseLeave="{Foreground=Transparent}";
+}
+TreeIcon {
+       Margin="0";
+       Width="14";
+       Height="14";
+}
+NormalizedFloat {
+    Minimum="0.0";
+    Maximum="1.0";
+    SmallIncrement="0.01";
+    LargeIncrement="0.1";
+    Decimals="2";
+    Width="60";
+    Foreground="Black";
+}
diff --git a/samples/deferred/ui/main.crow b/samples/deferred/ui/main.crow
new file mode 100644 (file)
index 0000000..14e6082
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<Window Caption="Lighting" Icon="#deferred.crow.svg" Left="10" Top="100" Width="40%" Height="40%" CornerRadius="10">
+    <VerticalStack Spacing="1" Margin="10" CacheEnabled="true">     
+        <HorizontalStack Height="Fit" >
+            <Label Text="Exposure:" Width="80"/>
+            <Slider Height="10" Value="{²Exposure}" Maximum="10.0" SmallIncrement="0.1" LargeIncrement="1.0"/>
+            <Label Text="{Exposure}" Width="40" TextAlignment="Right" />
+        </HorizontalStack>
+        <HorizontalStack Height="Fit">
+            <Label Text="Gamma:" Width="80" />
+            <Slider Height="10" Value="{²Gamma}" Maximum="10.0" SmallIncrement="0.1" LargeIncrement="1.0"/>
+            <Label Text="{Gamma}" Width="40" TextAlignment="Right" />
+        </HorizontalStack>
+        <HorizontalStack Height="Fit">
+            <Label Text="Light Strength:" Width="100" />
+            <Slider Height="10" Value="{²LightStrength}" Maximum="50.0" SmallIncrement="0.1" LargeIncrement="5.0"/>
+            <Label Text="{LightStrength}" Width="40" TextAlignment="Right" />
+        </HorizontalStack>
+        <ListBox Name="dv" Data="{Lights}" Width="Stretched" Height="Fit">
+            <ItemTemplate>
+                <Border Height="Fit">
+                    <VerticalStack>
+                        <HorizontalStack>
+                            <Label Text="Position" Width="50"/>
+                            <Label Text="{position}"/>
+                        </HorizontalStack>
+                        <HorizontalStack>
+                            <Label Text="Color" Width="50"/>
+                            <Label Text="{color}"/>
+                        </HorizontalStack>
+                    </VerticalStack>                    
+                </Border>                    
+            </ItemTemplate>            
+        </ListBox>        
+    </VerticalStack>
+</Window>
+
diff --git a/samples/deferred/ui/materials.crow b/samples/deferred/ui/materials.crow
new file mode 100644 (file)
index 0000000..b4c5432
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<Window Icon="#deferred.crow.svg" Left="10" Top="100" Width="40%" Height="40%" CornerRadius="10">
+    <VerticalStack>
+    <ListBox Data="{Materials}">
+        <ItemTemplate  DataType="PbrMaterial">
+            <VerticalStack>
+                <ColorPicker Width="260" Height="Fit"/>
+                <HorizontalStack>
+                    <Label Width="Stretched" Text="Metallic Factor"/>
+                    <Spinner Style="NormalizedFloat" Value="{²metallicFactor}"/>
+                </HorizontalStack>
+                <HorizontalStack>
+                    <Label Width="Stretched" Text="Roughness Factor"/>
+                    <Spinner Style="NormalizedFloat" Value="{²roughnessFactor}"/>
+                </HorizontalStack>
+                <HorizontalStack>
+                    <Label Width="Stretched" Text="Alpha Mask"/>
+                    <Spinner Style="NormalizedFloat" Value="{²alphaMask}"/>
+                </HorizontalStack>
+                <HorizontalStack>
+                    <Label Width="Stretched" Text="Alpha Mask Cutoff"/>
+                    <Spinner Style="NormalizedFloat" Value="{²alphaMaskCutoff}"/>            
+                </HorizontalStack>
+            </VerticalStack>
+        </ItemTemplate>        
+    </ListBox>
+    <Button Caption="Apply" MouseClick="onApplyMaterialChanges"/>
+    </VerticalStack>
+</Window>
+
diff --git a/samples/deferred/ui/menu.crow b/samples/deferred/ui/menu.crow
new file mode 100644 (file)
index 0000000..1cc36be
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<Menu>
+    <MenuItem Caption="View" Fit="true" PopWidth="150">
+        <MenuItem Command="{CMDViewScenes}"/>
+        <MenuItem Command="{CMDViewDebug}"/>
+        <MenuItem Command="{CMDViewEditor}"/>
+        <MenuItem Command="{CMDViewMaterials}"/>
+    </MenuItem>
+</Menu>
\ No newline at end of file
diff --git a/samples/deferred/ui/sceneItem.crow b/samples/deferred/ui/sceneItem.crow
new file mode 100644 (file)
index 0000000..ecb3a53
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<VerticalStack>
+    <Border Style="TreeItemBorder" MouseDoubleClick="./onClickForExpand">
+        <HorizontalStack Spacing="5">
+            <Image Margin="1" Width="10" Height="10" Focusable="true" MouseDown="./onClickForExpand"
+                Path="{./Image}"
+                Visible="{./IsExpandable}"
+                SvgSub="{./IsExpanded}"
+                MouseEnter="{Background=LightGrey}"
+                MouseLeave="{Background=Transparent}"/>
+            <Image Style="TreeIcon"
+                Path="#Crow.Icons.folder.svg" SvgSub="{./IsExpanded}"/>
+            <Label Text="{./Caption}"/>
+        </HorizontalStack>
+    </Border>
+    <Container Name="Content" Visible="false"/>
+</VerticalStack>
+
diff --git a/samples/deferred/ui/scenes.crow b/samples/deferred/ui/scenes.crow
new file mode 100644 (file)
index 0000000..b7f1b4f
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<Window Caption="Scenes" Icon="#deferred.crow.svg" Left="10" Top="100" Width="40%" Height="80%" CornerRadius="10">    
+        <TreeView Data="{Scenes}" Width="Stretched">
+            <ItemTemplate DataType="Scene" Data="GetNodes">
+                <Expandable Caption="{Name}" Template="#deferred.sceneItem.crow" IsExpanded="{²IsExpanded}">
+                    <HorizontalStack Height="Fit">
+                        <Widget Width="8" Height="10"/>
+                        <VerticalStack Height="Fit" Name="ItemsContainer"/>
+                    </HorizontalStack>
+                </Expandable>                
+            </ItemTemplate>
+            <ItemTemplate DataType="Node" Data="Children">
+                <Expandable Caption="Node" IsExpanded="{²IsExpanded}">
+                    <Template>
+                        <VerticalStack>
+                            <Border Style="TreeItemBorder" MouseDoubleClick="./onClickForExpand">
+                                <HorizontalStack Spacing="5">
+                                    <Image Margin="1" Width="10" Height="10" Focusable="true" MouseDown="./onClickForExpand"
+                                        Path="{./Image}"
+                                        Visible="{./IsExpandable}"
+                                        SvgSub="{./IsExpanded}"
+                                        MouseEnter="{Background=LightGrey}"
+                                        MouseLeave="{Background=Transparent}"/>
+                                    <Image Style="TreeIcon"
+                                        Path="#Crow.Icons.folder.svg" SvgSub="{./IsExpanded}"/>
+                                    <Label Text="{./Caption}"/>
+                                    <Label DataSource="{Mesh}" Text="{Name}"/>
+                                </HorizontalStack>
+                            </Border>
+                            <Container Name="Content" Visible="false"/>
+                        </VerticalStack>                        
+                    </Template>>
+                    <HorizontalStack Height="Fit">
+                        <Widget Width="8" Height="10"/>
+                        <VerticalStack Height="Fit" Name="ItemsContainer"/>                    
+                    </HorizontalStack>
+                </Expandable>                
+            </ItemTemplate>
+        </TreeView>           
+</Window>
+
diff --git a/samples/deferred/ui/testImage.crow b/samples/deferred/ui/testImage.crow
new file mode 100644 (file)
index 0000000..c245e14
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<EnumSelector Height="300" EnumValue="{²CurrentDebugView}"/>
+
diff --git a/samples/pbr/EnvironmentPipeline.cs b/samples/pbr/EnvironmentPipeline.cs
new file mode 100644 (file)
index 0000000..7b5428e
--- /dev/null
@@ -0,0 +1,354 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL {
+       class EnvironmentCube : GraphicPipeline {
+
+               GPUBuffer vboSkybox;
+
+               public Image cubemap { get; private set; }
+               public Image lutBrdf { get; private set; }
+               public Image irradianceCube { get; private set; }
+               public Image prefilterCube { get;  set; }
+
+               public Image debugImg;
+               public int debugMip = -1;
+               public int debugFace = 0;
+
+               public EnvironmentCube (DescriptorSet dsSkybox, PipelineLayout plLayout, Queue staggingQ, RenderPass renderPass, PipelineCache pipelineCache = null)
+               : base (renderPass, pipelineCache, "EnvCube pipeline") {
+
+                       using (CommandPool cmdPool = new CommandPool (staggingQ.Dev, staggingQ.index)) {
+
+                               vboSkybox = new GPUBuffer<float> (staggingQ, cmdPool, VkBufferUsageFlags.VertexBuffer, box_vertices);
+
+                               cubemap = KTX.KTX.Load (staggingQ, cmdPool, cubemapPathes[2],
+                                       VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal, true);
+                               cubemap.CreateView (VkImageViewType.Cube, VkImageAspectFlags.Color, 6);
+                               cubemap.CreateSampler (VkSamplerAddressMode.ClampToEdge);
+                               cubemap.SetName ("skybox Texture");
+                               cubemap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                               GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, renderPass.Samples, false);
+                               cfg.RenderPass = renderPass;
+                               cfg.Layout = plLayout;
+                               cfg.AddVertexBinding (0, 3 * sizeof (float));
+                               cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat);
+                               cfg.AddShader (VkShaderStageFlags.Vertex, "#pbr.skybox.vert.spv");
+                               cfg.AddShader (VkShaderStageFlags.Fragment, "#pbr.skybox.frag.spv");
+                               cfg.multisampleState.rasterizationSamples = Samples;
+
+                               layout = cfg.Layout;
+
+                               init (cfg);
+
+                               generateBRDFLUT (staggingQ, cmdPool);
+                               generateCubemaps (staggingQ, cmdPool);
+                       }
+               
+               }
+
+               //public void WriteDesc (VkDescriptorBufferInfo matrixDesc) { 
+               //      DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutMain);
+               //      uboUpdate.Write (Dev, dsSkybox, matrixDesc, cubemap.Descriptor);
+               //}
+
+               public void RecordDraw (CommandBuffer cmd) {
+                       Bind (cmd);
+                       cmd.BindVertexBuffer (vboSkybox);
+                       cmd.Draw (36);
+               }
+
+               #region skybox
+               public List<string> cubemapPathes = new List<string>() {
+                       Utils.DataDirectory + "textures/papermill.ktx",
+                       Utils.DataDirectory + "textures/cubemap_yokohama_bc3_unorm.ktx",
+                       Utils.DataDirectory + "textures/gcanyon_cube.ktx",
+                       Utils.DataDirectory + "textures/pisa_cube.ktx",
+                       Utils.DataDirectory + "textures/uffizi_cube.ktx",
+               };
+               static float[] box_vertices = {                 
+                        1.0f, 1.0f,-1.0f,  // +X side
+                        1.0f, 1.0f, 1.0f, 
+                        1.0f,-1.0f, 1.0f, 
+                        1.0f,-1.0f, 1.0f, 
+                        1.0f,-1.0f,-1.0f, 
+                        1.0f, 1.0f,-1.0f,
+
+                       -1.0f,-1.0f,-1.0f,      // +X side
+                       -1.0f,-1.0f, 1.0f,
+                       -1.0f, 1.0f, 1.0f,
+                       -1.0f, 1.0f, 1.0f,
+                       -1.0f, 1.0f,-1.0f,
+                       -1.0f,-1.0f,-1.0f,
+
+                       -1.0f, 1.0f,-1.0f,  // +Y side
+                       -1.0f, 1.0f, 1.0f,    
+                        1.0f, 1.0f, 1.0f,    
+                       -1.0f, 1.0f,-1.0f,    
+                        1.0f, 1.0f, 1.0f,    
+                        1.0f, 1.0f,-1.0f,
+
+                       -1.0f,-1.0f,-1.0f,  // -Y side
+                        1.0f,-1.0f,-1.0f,
+                        1.0f,-1.0f, 1.0f,
+                       -1.0f,-1.0f,-1.0f,
+                        1.0f,-1.0f, 1.0f,
+                       -1.0f,-1.0f, 1.0f,
+
+                       -1.0f, 1.0f, 1.0f,  // +Z side
+                       -1.0f,-1.0f, 1.0f,    
+                        1.0f, 1.0f, 1.0f,    
+                       -1.0f,-1.0f, 1.0f,    
+                        1.0f,-1.0f, 1.0f,    
+                        1.0f, 1.0f, 1.0f,
+
+                       -1.0f,-1.0f,-1.0f,  // -Z side
+                        1.0f, 1.0f,-1.0f,
+                        1.0f,-1.0f,-1.0f,
+                       -1.0f,-1.0f,-1.0f,
+                       -1.0f, 1.0f,-1.0f,
+                        1.0f, 1.0f,-1.0f,
+
+               };
+               #endregion
+
+               void generateBRDFLUT (Queue staggingQ, CommandPool cmdPool) {
+                       const VkFormat format = VkFormat.R16g16Sfloat;
+                       const int dim = 512;
+
+                       lutBrdf = new Image (Dev, format, VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.Sampled,
+                               VkMemoryPropertyFlags.DeviceLocal, dim, dim);
+                       lutBrdf.SetName ("lutBrdf");
+
+                       lutBrdf.CreateView ();
+                       lutBrdf.CreateSampler (VkSamplerAddressMode.ClampToEdge);
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false);
+
+                       cfg.Layout = new PipelineLayout (Dev, new DescriptorSetLayout (Dev));
+                       cfg.RenderPass = new RenderPass (Dev);
+                       cfg.RenderPass.AddAttachment (format, VkImageLayout.ShaderReadOnlyOptimal);
+                       cfg.RenderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0, 0, 0) });
+                       cfg.RenderPass.AddSubpass (new SubPass (VkImageLayout.ColorAttachmentOptimal));
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#pbr.genbrdflut.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#pbr.genbrdflut.frag.spv");
+
+                       using (GraphicPipeline pl = new GraphicPipeline (cfg)) {
+                               using (Framebuffer fb = new Framebuffer (cfg.RenderPass, dim, dim, lutBrdf)) {
+                                       CommandBuffer cmd = cmdPool.AllocateCommandBuffer ();
+                                       cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit);
+                                       pl.RenderPass.Begin (cmd, fb);
+                                       cmd.SetViewport (dim, dim);
+                                       cmd.SetScissor (dim, dim);
+                                       pl.Bind (cmd);
+                                       cmd.Draw (3, 1, 0, 0);
+                                       pl.RenderPass.End (cmd);
+                                       cmd.End ();
+
+                                       staggingQ.Submit (cmd);
+                                       staggingQ.WaitIdle ();
+
+                                       cmd.Free ();
+                               }
+                       }
+                       lutBrdf.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+               }
+
+               public enum CBTarget { IRRADIANCE = 0, PREFILTEREDENV = 1 };
+
+               public Image generateCubeMap (Queue staggingQ, CommandPool cmdPool, CBTarget target) {
+                       const float deltaPhi = (2.0f * (float)Math.PI) / 180.0f;
+                       const float deltaTheta = (0.5f * (float)Math.PI) / 64.0f;
+
+                       VkFormat format = VkFormat.R32g32b32a32Sfloat;
+                       uint dim = 64;
+
+                       if (target == CBTarget.PREFILTEREDENV) {
+                               format = VkFormat.R16g16b16a16Sfloat;
+                               dim = 512;
+                       }
+
+                       uint numMips = (uint)Math.Floor (Math.Log (dim, 2)) + 1;
+
+                       Image imgFbOffscreen = new Image (Dev, format, VkImageUsageFlags.TransferSrc | VkImageUsageFlags.ColorAttachment,
+                               VkMemoryPropertyFlags.DeviceLocal, dim, dim);
+                       imgFbOffscreen.SetName ("offscreenfb");
+                       imgFbOffscreen.CreateView ();
+
+                       Image cmap = new Image (Dev, format, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled,
+                               VkMemoryPropertyFlags.DeviceLocal, dim, dim, VkImageType.Image2D, VkSampleCountFlags.SampleCount1, VkImageTiling.Optimal,
+                               numMips, 6,  1, VkImageCreateFlags.CubeCompatible);
+                       if (target == CBTarget.PREFILTEREDENV)
+                               cmap.SetName ("prefilterenvmap");
+                       else
+                               cmap.SetName ("irradianceCube");
+                       cmap.CreateView (VkImageViewType.Cube, VkImageAspectFlags.Color, 6);
+                       cmap.CreateSampler (VkSamplerAddressMode.ClampToEdge);
+
+                       DescriptorPool dsPool = new DescriptorPool (Dev, 2,     new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler));
+
+                       DescriptorSetLayout dsLayout = new DescriptorSetLayout (Dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));
+
+
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false);
+
+                       cfg.Layout = new PipelineLayout (Dev, dsLayout);
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf<Matrix4x4> () + 8));
+
+                       cfg.RenderPass = new RenderPass (Dev);
+                       cfg.RenderPass.AddAttachment (format, VkImageLayout.ColorAttachmentOptimal);
+                       cfg.RenderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0, 0, 0) });
+                       cfg.RenderPass.AddSubpass (new SubPass (VkImageLayout.ColorAttachmentOptimal));
+
+                       cfg.AddVertexBinding (0, 3 * sizeof (float));
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#pbr.filtercube.vert.spv");
+                       if (target == CBTarget.PREFILTEREDENV)
+                               cfg.AddShader (VkShaderStageFlags.Fragment, "#pbr.prefilterenvmap.frag.spv");
+                       else
+                               cfg.AddShader (VkShaderStageFlags.Fragment, "#pbr.irradiancecube.frag.spv");
+
+                       Matrix4x4[] matrices = {
+                               // POSITIVE_X
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(90)),
+                               // NEGATIVE_X
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(-90)),
+                               // POSITIVE_Y
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(-90)),
+                               // NEGATIVE_Y
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(90)),
+                               // POSITIVE_Z
+                               Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)),
+                               // NEGATIVE_Z
+                               Matrix4x4.CreateRotationZ(Utils.DegreesToRadians(180))
+                       };
+
+                       VkImageSubresourceRange subRes = new VkImageSubresourceRange (VkImageAspectFlags.Color, 0, numMips, 0, 6);
+
+                       using (GraphicPipeline pl = new GraphicPipeline (cfg)) {
+                               DescriptorSet dset = dsPool.Allocate (dsLayout);
+                               DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dsLayout);
+                               dsUpdate.Write (Dev, dset, cubemap.Descriptor);
+
+                               using (Framebuffer fb = new Framebuffer (pl.RenderPass, dim, dim, imgFbOffscreen)) {
+                                       CommandBuffer cmd = cmdPool.AllocateCommandBuffer ();
+                                       cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                                       cmap.SetLayout (cmd, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subRes);
+                                               
+                                       float roughness = 0;
+
+                                       cmd.SetScissor (dim, dim);
+                                       cmd.SetViewport ((float)(dim), (float)dim);
+
+                                       for (int m = 0; m < numMips; m++) {
+                                               roughness = (float)m / ((float)numMips - 1f);
+
+                                               for (int f = 0; f < 6; f++) {
+                                                       pl.RenderPass.Begin (cmd, fb);
+
+                                                       pl.Bind (cmd);
+
+                                                       float viewPortSize = (float)Math.Pow (0.5, m) * dim;
+                                                       cmd.SetViewport (viewPortSize, viewPortSize);
+                                                       cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment,
+                                                               matrices[f] * Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (90), 1f, 0.1f, 512f));
+                                                       if (target == CBTarget.IRRADIANCE) {
+                                                               cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaPhi, (uint)Marshal.SizeOf<Matrix4x4> ());
+                                                               cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaTheta, (uint)Marshal.SizeOf<Matrix4x4> () + 4);
+                                                       } else {
+                                                               cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, roughness, (uint)Marshal.SizeOf<Matrix4x4> ());
+                                                               cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 32u, (uint)Marshal.SizeOf<Matrix4x4> () + 4);
+                                                       }
+
+                                                       cmd.BindDescriptorSet (pl.Layout, dset);
+                                                       cmd.BindVertexBuffer (vboSkybox);
+                                                       cmd.Draw (36);
+
+                                                       pl.RenderPass.End (cmd);
+
+                                                       imgFbOffscreen.SetLayout (cmd, VkImageAspectFlags.Color,
+                                                               VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal);
+
+                                                       VkImageCopy region = new VkImageCopy ();
+                                                       region.srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1);
+                                                       region.dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1, (uint)m, (uint)f);
+                                                       region.extent = new VkExtent3D { width = (uint)viewPortSize, height = (uint)viewPortSize, depth = 1 };
+
+                                                       Vk.vkCmdCopyImage (cmd.Handle,
+                                                               imgFbOffscreen.Handle, VkImageLayout.TransferSrcOptimal,
+                                                               cmap.Handle, VkImageLayout.TransferDstOptimal,
+                                                               1, region.Pin ());
+                                                       region.Unpin ();
+
+                                                       //debug img
+                                                       if (target == CBTarget.PREFILTEREDENV && m == debugMip && f == debugFace) {
+                                                               debugImg?.Dispose ();
+                                                               debugImg = new Image (Dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferDst, VkMemoryPropertyFlags.DeviceLocal,
+                                                                       (uint)viewPortSize, (uint)viewPortSize);
+                                                               debugImg.CreateView ();
+                                                               debugImg.CreateSampler ();
+                                                               region.dstSubresource.baseArrayLayer = 0;
+                                                               region.dstSubresource.mipLevel = 0;
+                                                               debugImg.SetLayout (cmd, VkImageAspectFlags.Color,
+                                                                       VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal);
+                                                               Vk.vkCmdCopyImage (cmd.Handle,
+                                                                       imgFbOffscreen.Handle, VkImageLayout.TransferSrcOptimal,
+                                                                       debugImg.Handle, VkImageLayout.TransferDstOptimal,
+                                                                       1, region.Pin ());
+                                                               debugImg.SetLayout (cmd, VkImageAspectFlags.Color,
+                                                                       VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal);
+                                                               region.Unpin ();
+                                                       }
+
+                                                       imgFbOffscreen.SetLayout (cmd, VkImageAspectFlags.Color,
+                                                               VkImageLayout.TransferSrcOptimal, VkImageLayout.ColorAttachmentOptimal);
+
+                                               }
+                                       }
+
+                                       cmap.SetLayout (cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, subRes);
+
+                                       cmd.End ();
+
+                                       staggingQ.Submit (cmd);
+                                       staggingQ.WaitIdle ();
+
+                                       cmd.Free ();
+                               }
+                       }
+                       cmap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+
+                       dsLayout.Dispose ();
+                       imgFbOffscreen.Dispose ();
+                       dsPool.Dispose ();
+
+                       return cmap;
+               }
+
+               void generateCubemaps (Queue staggingQ, CommandPool cmdPool) {
+                       irradianceCube = generateCubeMap (staggingQ, cmdPool, CBTarget.IRRADIANCE);
+                       prefilterCube = generateCubeMap (staggingQ, cmdPool, CBTarget.PREFILTEREDENV);
+               }
+
+               protected override void Dispose (bool disposing) {
+                       vboSkybox.Dispose ();
+                       cubemap.Dispose ();
+                       lutBrdf.Dispose ();
+                       irradianceCube.Dispose ();
+                       prefilterCube.Dispose ();
+
+                       debugImg?.Dispose ();
+
+                       base.Dispose (disposing);
+               }
+       }
+
+}
diff --git a/samples/pbr/PbrModel.cs b/samples/pbr/PbrModel.cs
new file mode 100644 (file)
index 0000000..a48fe11
--- /dev/null
@@ -0,0 +1,39 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Numerics;
+using System.Runtime.InteropServices;
+
+using VK;
+
+namespace CVKL {
+       using CVKL.glTF;
+
+
+       //TODO:stride in buffer views?
+       public class PbrModel2 : PbrModelSeparatedTextures {
+               public PbrModel2 (Queue transferQ, string path, DescriptorSetLayout layout, params AttachmentType[] attachments)
+               : base (transferQ, path, layout, attachments) {}
+
+               //TODO:destset for binding must be variable
+               //TODO: ADD REFAULT MAT IF NO MAT DEFINED
+               public override void RenderNode (CommandBuffer cmd, PipelineLayout pipelineLayout, Node node, Matrix4x4 currentTransform, bool shadowPass = false) {
+                       Matrix4x4 localMat = node.localMatrix * currentTransform;
+
+                       cmd.PushConstant (pipelineLayout, VkShaderStageFlags.Vertex, localMat);
+
+                       if (node.Mesh != null) {
+                               foreach (Primitive p in node.Mesh.Primitives) {
+                                       cmd.PushConstant (pipelineLayout, VkShaderStageFlags.Fragment, (int)p.material, (uint)Marshal.SizeOf<Matrix4x4> ());
+                                       if (descriptorSets[p.material] != null)
+                                               cmd.BindDescriptorSet (pipelineLayout, descriptorSets[p.material], 1);
+                                       cmd.DrawIndexed (p.indexCount, 1, p.indexBase, p.vertexBase, 0);
+                               }
+                       }
+                       if (node.Children == null)
+                               return;
+                       foreach (Node child in node.Children)
+                               RenderNode (cmd, pipelineLayout, child, localMat);
+               }
+       }
+}
diff --git a/samples/pbr/PbrPipeline.cs b/samples/pbr/PbrPipeline.cs
new file mode 100644 (file)
index 0000000..a8d3fae
--- /dev/null
@@ -0,0 +1,158 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL {
+       class PBRPipeline : GraphicPipeline {
+
+               public struct Matrices {
+                       public Matrix4x4 projection;
+                       public Matrix4x4 model;
+                       public Matrix4x4 view;
+                       public Vector4 camPos;
+                       public Vector4 lightDir;
+                       public float exposure;
+                       public float gamma;
+                       public float prefilteredCubeMipLevels;
+                       public float scaleIBLAmbient;
+                       public float debugViewInputs;
+                       public float debugViewEquation;
+               }
+
+               public Matrices matrices = new Matrices {
+                       lightDir = Vector4.Normalize (new Vector4 (0.7f, 0.6f, 0.2f, 0.0f)),
+                       gamma = 2.2f,
+                       exposure = 4.5f,
+                       scaleIBLAmbient = 1f,
+                       debugViewInputs = 0,
+                       debugViewEquation = 0
+               };
+
+               public HostBuffer uboMats;
+
+               DescriptorPool descriptorPool;
+
+               DescriptorSetLayout descLayoutMain;
+               DescriptorSetLayout descLayoutTextures;
+               public DescriptorSet dsMain;
+
+               public PbrModel2 model;
+               public EnvironmentCube envCube;
+
+               public PBRPipeline (Queue staggingQ, RenderPass renderPass, PipelineCache pipelineCache = null) :
+                       base (renderPass, pipelineCache, "pbr pipeline") {
+
+                       descriptorPool = new DescriptorPool (Dev, 2,
+                               new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2),
+                               new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 9)
+                       );
+
+                       descLayoutMain = new DescriptorSetLayout (Dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),
+                               new VkDescriptorSetLayoutBinding (5, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));//ui image
+
+                       descLayoutTextures = new DescriptorSetLayout (Dev,
+                               new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler),
+                               new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)
+                       );
+                               
+                       GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, renderPass.Samples);
+                       cfg.Layout = new PipelineLayout (Dev, descLayoutMain, descLayoutTextures);
+                       cfg.Layout.AddPushConstants (
+                               new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf<Matrix4x4> ()),
+                               new VkPushConstantRange (VkShaderStageFlags.Fragment, sizeof(int), 64)
+                       );
+                       cfg.RenderPass = renderPass;
+                       cfg.AddVertexBinding<PbrModel2.Vertex> (0);
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat, VkFormat.R32g32Sfloat);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#pbr.pbr.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#pbr.pbr_khr.frag.spv");
+
+                       layout = cfg.Layout;
+
+                       init (cfg);
+
+                       dsMain = descriptorPool.Allocate (descLayoutMain);
+
+                       envCube = new EnvironmentCube (dsMain, layout, staggingQ, RenderPass);
+
+                       matrices.prefilteredCubeMipLevels = envCube.prefilterCube.CreateInfo.mipLevels;
+                       uboMats = new HostBuffer (Dev, VkBufferUsageFlags.UniformBuffer, matrices, true);
+
+                       string[] modelPathes = {
+                               Utils.DataDirectory + "models/DamagedHelmet/glTF/DamagedHelmet.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/Avocado/glTF/Avocado.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/BarramundiFish/glTF/BarramundiFish.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/BoomBoxWithAxes/glTF/BoomBoxWithAxes.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/Box/glTF/Box.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/EnvironmentTest/glTF/EnvironmentTest.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/MetalRoughSpheres/glTF/MetalRoughSpheres.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/OrientationTest/glTF/OrientationTest.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/Buggy/glTF/Buggy.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/2CylinderEngine/glTF-Embedded/2CylinderEngine.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/FlightHelmet/glTF/FlightHelmet.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/GearboxAssy/glTF/GearboxAssy.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/Lantern/glTF/Lantern.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/SciFiHelmet/glTF/SciFiHelmet.gltf",
+                               "/mnt/devel/vulkan/glTF-Sample-Models-master/2.0/Sponza/glTF/Sponza.gltf",
+                               "/mnt/devel/vkChess/data/chess.gltf"
+                       };
+
+
+                       model = new PbrModel2 (staggingQ, modelPathes[0], descLayoutTextures,
+                               AttachmentType.Color,
+                               AttachmentType.PhysicalProps,
+                               AttachmentType.Normal,
+                               AttachmentType.AmbientOcclusion,
+                               AttachmentType.Emissive);
+                       //model = new Model (Dev, presentQueue, Utils.DataDirectory + "models/icosphere.gltf");
+                       //model = new Model (Dev, presentQueue, cmdPool, Utils.DataDirectory + "models/cube.gltf");
+                       DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutMain.Bindings.GetRange(0,5).ToArray());
+                       uboUpdate.Write (Dev, dsMain,
+                               uboMats.Descriptor,
+                               envCube.irradianceCube.Descriptor,
+                               envCube.prefilterCube.Descriptor,
+                               envCube.lutBrdf.Descriptor,
+                               model.materialUBO.Descriptor);
+               }
+
+               public void RecordDraw (CommandBuffer cmd) {
+                       cmd.BindDescriptorSet (Layout, dsMain);
+                       envCube.RecordDraw (cmd);
+                       drawModel (cmd);
+               }
+               void drawModel (CommandBuffer cmd) {
+                       Bind (cmd);
+                       model.Bind (cmd);
+                       model.DrawAll (cmd, Layout);
+
+               }
+
+               protected override void Dispose (bool disposing) {
+                       model.Dispose ();
+                       envCube.Dispose ();
+
+                       descLayoutMain.Dispose ();
+                       descLayoutTextures.Dispose ();
+                       descriptorPool.Dispose ();
+
+                       uboMats.Dispose ();
+
+                       base.Dispose (disposing);
+               }
+       }
+
+}
diff --git a/samples/pbr/main.cs b/samples/pbr/main.cs
new file mode 100644 (file)
index 0000000..b664c50
--- /dev/null
@@ -0,0 +1,430 @@
+/* Forward pbr sample inspire from https://github.com/SaschaWillems/Vulkan-glTF-PBR
+ *
+ * Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+ *
+ * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+ */
+
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using Glfw;
+using VK;
+using CVKL;
+
+namespace pbrSample {
+       class Program : VkWindow{
+
+               static void Main (string[] args) {
+#if DEBUG
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+                       Instance.RENDER_DOC_CAPTURE = false;
+#endif
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+               protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) {
+                       base.configureEnabledFeatures (available_features, ref enabled_features);
+#if PIPELINE_STATS
+                       features.pipelineStatisticsQuery = true;
+#endif
+                       enabled_features.samplerAnisotropy = available_features.samplerAnisotropy;
+               }
+
+               VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1;
+
+               Framebuffer[] frameBuffers;
+               PBRPipeline pbrPipeline;
+
+               enum DebugView {
+                       none,
+                       color,
+                       normal,
+                       occlusion,
+                       emissive,
+                       metallic,
+                       roughness
+               }
+
+               DebugView currentDebugView = DebugView.none;
+
+#if PIPELINE_STATS
+               PipelineStatisticsQueryPool statPool;
+               TimestampQueryPool timestampQPool;
+               ulong[] results;
+#endif
+               bool queryUpdatePrefilCube, showDebugImg;
+
+#if WITH_VKVG
+               //DescriptorSet dsDebugImg;
+               //void initDebugImg () {
+               //      dsDebugImg = descriptorPool.Allocate (descLayoutMain);
+               //      pbrPipeline.envCube.debugImg.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;
+               //      DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsDebugImg, descLayoutMain);
+               //      uboUpdate.Write (dev, pbrPipeline.envCube.debugImg.Descriptor);
+               //}
+
+               VkvgPipeline.VkvgPipeline vkvgPipeline;
+
+               void vkvgDraw () {
+            using (vkvg.Context ctx = vkvgPipeline.CreateContext ()) {
+                               ctx.Operator = vkvg.Operator.Clear;
+                               ctx.Paint ();
+                               ctx.Operator = vkvg.Operator.Over;
+
+                               ctx.LineWidth = 1;
+                               ctx.SetSource (0.1, 0.1, 0.1, 0.8);
+                               ctx.Rectangle (5.5, 5.5, 320, 300);
+                               ctx.FillPreserve ();
+                               ctx.Flush ();
+                               ctx.SetSource (0.8, 0.8, 0.8);
+                               ctx.Stroke ();
+
+                               ctx.FontFace = "mono";
+                               ctx.FontSize = 8;
+                               int x = 16;
+                               int y = 40, dy = 16;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"fps:     {fps,5} "));
+                               ctx.MoveTo (x + 200, y - 0.5);
+                               ctx.Rectangle (x + 200, y - 8.5, 0.1 * fps, 10);
+                               ctx.SetSource (0.1, 0.9, 0.1);
+                               ctx.Fill ();
+                               ctx.SetSource (0.8, 0.8, 0.8);
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"Exposure:{pbrPipeline.matrices.exposure,5} "));
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"Gamma:   {pbrPipeline.matrices.gamma,5} "));
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"Light pos:   {lightPos.ToString()} "));
+
+#if PIPELINE_STATS
+                               if (results == null)
+                                       return;
+
+                               y += dy*2;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText ("Pipeline Statistics");
+                               ctx.MoveTo (x-2, 4.5+y);
+                               ctx.LineTo (x+160, 4.5+y);
+                               ctx.Stroke ();
+                               y += 4;
+                               x += 20;
+
+                               for (int i = 0; i < statPool.RequestedStats.Length; i++) {
+                                       y += dy;
+                                       ctx.MoveTo (x, y);
+                                       ctx.ShowText (string.Format ($"{statPool.RequestedStats[i].ToString(),-30} :{results[i],12:0,0} "));
+                               }
+                               /*y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"{"Elapsed microsecond",-20} :{timestampQPool.ElapsedMiliseconds:0.0000} "));*/
+#endif
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"{"Debug draw (numpad 0->6)",-30} : {currentDebugView.ToString ()} "));
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"{"Debug Prefil Face: (f)",-30} : {pbrPipeline.envCube.debugFace.ToString ()} "));
+                               y += dy;
+                               ctx.MoveTo (x, y);
+                               ctx.ShowText (string.Format ($"{"Debug Prefil Mip: (m)",-30} : {pbrPipeline.envCube.debugMip.ToString ()} "));
+
+                               vkvgPipeline.DrawResources (ctx, (int)Width, (int)Height);
+                       }
+               }
+#endif
+
+
+               Vector4 lightPos = new Vector4 (1, 0, 0, 0);
+               BoundingBox modelAABB;
+
+               Program () : base() {
+
+                       //UpdateFrequency = 20;
+
+                       camera.SetPosition (0, 0, 5);
+
+
+                       pbrPipeline = new PBRPipeline(presentQueue,
+                               new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), samples));
+
+
+
+                       modelAABB = pbrPipeline.model.DefaultScene.AABB;
+
+                       //camera.Model = Matrix4x4.CreateScale (1f/ Math.Max (Math.Max (modelAABB.max.X, modelAABB.max.Y), modelAABB.max.Z));
+
+#if PIPELINE_STATS
+                       statPool = new PipelineStatisticsQueryPool (dev,
+                               VkQueryPipelineStatisticFlags.InputAssemblyVertices |
+                               VkQueryPipelineStatisticFlags.InputAssemblyPrimitives |
+                               VkQueryPipelineStatisticFlags.ClippingInvocations |
+                               VkQueryPipelineStatisticFlags.ClippingPrimitives |
+                               VkQueryPipelineStatisticFlags.FragmentShaderInvocations);
+
+                       timestampQPool = new TimestampQueryPool (dev);
+#endif
+
+#if WITH_VKVG
+                       vkvgPipeline = new VkvgPipeline.VkvgPipeline (instance, dev, presentQueue, pbrPipeline);
+#endif
+               }
+
+               bool rebuildBuffers;
+
+               void buildCommandBuffers () {
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               cmds[i]?.Free ();
+                               cmds[i] = cmdPool.AllocateAndStart ();
+#if PIPELINE_STATS
+                               statPool.Begin (cmds[i]);
+                               recordDraw (cmds[i], frameBuffers[i]);
+                               statPool.End (cmds[i]);
+#else
+                               recordDraw (cmds[i], frameBuffers[i]);
+#endif
+
+                               cmds[i].End ();
+                       }
+               }
+               void recordDraw (CommandBuffer cmd, Framebuffer fb) {
+                       pbrPipeline.RenderPass.Begin (cmd, fb);
+
+                       cmd.SetViewport (fb.Width, fb.Height);
+                       cmd.SetScissor (fb.Width, fb.Height);
+
+                       pbrPipeline.RecordDraw (cmd);
+
+#if WITH_VKVG
+                       vkvgPipeline.RecordDraw (cmd);
+#endif                                         
+                       pbrPipeline.RenderPass.End (cmd);
+               }
+
+
+#region update
+               public override void UpdateView () {
+                       camera.AspectRatio = (float)swapChain.Width / swapChain.Height;
+
+                       pbrPipeline.matrices.lightDir = lightPos;
+                       pbrPipeline.matrices.projection = camera.Projection;
+                       pbrPipeline.matrices.view = camera.View;
+                       pbrPipeline.matrices.model = camera.Model;
+
+
+                       pbrPipeline.matrices.camPos = new Vector4 (
+                               -camera.Position.Z * (float)Math.Sin (camera.Rotation.Y) * (float)Math.Cos (camera.Rotation.X),
+                                camera.Position.Z * (float)Math.Sin (camera.Rotation.X),
+                                camera.Position.Z * (float)Math.Cos (camera.Rotation.Y) * (float)Math.Cos (camera.Rotation.X),
+                                0
+                       );
+                       pbrPipeline.matrices.debugViewInputs = (float)currentDebugView;
+
+                       pbrPipeline.uboMats.Update (pbrPipeline.matrices, (uint)Marshal.SizeOf<PBRPipeline.Matrices> ());
+
+                       updateViewRequested = false;
+               }
+
+               public override void Update () {
+#if PIPELINE_STATS
+                       results = statPool.GetResults ();
+#endif
+                       if (rebuildBuffers) {
+                               buildCommandBuffers ();
+                               rebuildBuffers = false;
+                       }
+#if WITH_VKVG
+                       vkvgDraw ();
+#endif
+               }
+#endregion
+
+                
+               protected override void OnResize () {
+                       dev.WaitIdle ();
+#if WITH_VKVG
+                       vkvgPipeline.Resize ((int)swapChain.Width, (int)swapChain.Height,
+                               new DescriptorSetWrites (pbrPipeline.dsMain, pbrPipeline.Layout.DescriptorSetLayouts[0].Bindings[5]));
+#endif
+
+                       UpdateView ();
+
+                       if (frameBuffers != null)
+                               for (int i = 0; i < swapChain.ImageCount; ++i)
+                                       frameBuffers[i]?.Dispose ();
+
+                       frameBuffers = new Framebuffer[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; ++i) {
+                               frameBuffers[i] = new Framebuffer (pbrPipeline.RenderPass, swapChain.Width, swapChain.Height,
+                                       (pbrPipeline.RenderPass.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] {
+                                               swapChain.images[i],
+                                               null
+                                       } : new Image[] {
+                                               null,
+                                               null,
+                                               swapChain.images[i]
+                                       });
+                       }
+
+                       buildCommandBuffers ();
+                       dev.WaitIdle ();
+               }
+
+               #region Mouse and keyboard
+               protected override void onMouseMove (double xPos, double yPos) {
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               camera.Rotate ((float)-diffX, (float)-diffY);
+                       } else if (MouseButton[1]) {
+                               camera.SetZoom ((float)diffY);
+                       } else
+                               return;
+
+                       updateViewRequested = true;
+               }
+
+               protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.F:
+                                       if (modifiers.HasFlag (Modifier.Shift)) {
+                                               pbrPipeline.envCube.debugFace --;
+                                               if (pbrPipeline.envCube.debugFace < 0)
+                                                       pbrPipeline.envCube.debugFace = 5;
+                                       } else {
+                                               pbrPipeline.envCube.debugFace ++;
+                                               if (pbrPipeline.envCube.debugFace > 5)
+                                                       pbrPipeline.envCube.debugFace = 0;
+                                       }
+                                       queryUpdatePrefilCube = updateViewRequested = true;
+                                       break;
+                               case Key.M:
+                                       if (modifiers.HasFlag (Modifier.Shift)) {
+                                               pbrPipeline.envCube.debugMip --;
+                                               if (pbrPipeline.envCube.debugMip < 0)
+                                                       pbrPipeline.envCube.debugMip = (int)pbrPipeline.envCube.prefilterCube.CreateInfo.mipLevels - 1;
+                                       } else {
+                                               pbrPipeline.envCube.debugMip ++;
+                                               if (pbrPipeline.envCube.debugMip > pbrPipeline.envCube.prefilterCube.CreateInfo.mipLevels)
+                                                       pbrPipeline.envCube.debugMip = 0;
+                                       }
+                                       queryUpdatePrefilCube = updateViewRequested = true;
+                                       break;
+                               case Key.P:
+                                       showDebugImg = !showDebugImg;
+                                       queryUpdatePrefilCube = updateViewRequested = true;
+                                       break;
+                               case Key.Keypad0:
+                                       currentDebugView = DebugView.none;
+                                       break;
+                               case Key.Keypad1:
+                                       currentDebugView = DebugView.color;
+                                       break;
+                               case Key.Keypad2:
+                                       currentDebugView = DebugView.normal;
+                                       break;
+                               case Key.Keypad3:
+                                       currentDebugView = DebugView.occlusion;
+                                       break;
+                               case Key.Keypad4:
+                                       currentDebugView = DebugView.emissive;
+                                       break;
+                               case Key.Keypad5:
+                                       currentDebugView = DebugView.metallic;
+                                       break;
+                               case Key.Keypad6:
+                                       currentDebugView = DebugView.roughness;
+                                       break;
+                               case Key.Up:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               lightPos -= Vector4.UnitZ;
+                                       else
+                                               camera.Move (0, 0, 1);
+                                       break;
+                               case Key.Down:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               lightPos += Vector4.UnitZ;
+                                       else
+                                               camera.Move (0, 0, -1);
+                                       break;
+                               case Key.Left:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               lightPos -= Vector4.UnitX;
+                                       else
+                                               camera.Move (1, 0, 0);
+                                       break;
+                               case Key.Right:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               lightPos += Vector4.UnitX;
+                                       else
+                                               camera.Move (-1, 0, 0);
+                                       break;
+                               case Key.PageUp:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               lightPos += Vector4.UnitY;
+                                       else
+                                               camera.Move (0, 1, 0);
+                                       break;
+                               case Key.PageDown:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               lightPos -= Vector4.UnitY;
+                                       else
+                                               camera.Move (0, -1, 0);
+                                       break;                          
+                               case Key.F2:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               pbrPipeline.matrices.exposure -= 0.3f;
+                                       else
+                                               pbrPipeline.matrices.exposure += 0.3f;
+                                       break;
+                               case Key.F3:
+                                       if (modifiers.HasFlag (Modifier.Shift))
+                                               pbrPipeline.matrices.gamma -= 0.1f;
+                                       else
+                                               pbrPipeline.matrices.gamma += 0.1f;
+                                       break;
+                               case Key.F4:
+                                       if (camera.Type == Camera.CamType.FirstPerson)
+                                               camera.Type = Camera.CamType.LookAt;
+                                       else
+                                               camera.Type = Camera.CamType.FirstPerson;
+                                       Console.WriteLine ($"camera type = {camera.Type}");
+                                       break;
+                               default:
+                                       base.onKeyDown (key, scanCode, modifiers);
+                                       return;
+                       }
+                       updateViewRequested = true;
+               }
+               #endregion
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dev.WaitIdle ();
+                                       for (int i = 0; i < swapChain.ImageCount; ++i)
+                                               frameBuffers[i]?.Dispose ();
+
+                                       pbrPipeline.Dispose ();
+#if WITH_VKVG
+                                       vkvgPipeline.Dispose ();
+#endif
+
+#if PIPELINE_STATS
+                                       timestampQPool?.Dispose ();
+                                       statPool?.Dispose ();
+#endif
+                               }
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/samples/pbr/pbr.csproj b/samples/pbr/pbr.csproj
new file mode 100644 (file)
index 0000000..deb35f4
--- /dev/null
@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />
+  <PropertyGroup>    
+    <EnableDefaultNoneItems>false</EnableDefaultNoneItems>    
+  </PropertyGroup>
+                
+  <ItemGroup>
+    <GLSLShader Update="shaders/simpletexture.frag">
+               <LogicalName>CVKL.simpletexture.frag.spv</LogicalName>          
+       </GLSLShader>
+  </ItemGroup>    
+       
+  <ItemGroup>
+    <ProjectReference Include="..\..\addons\gltfLoader\gltfLoader.csproj" />
+    <ProjectReference Include="..\..\addons\VkvgPipeline\VkvgPipeline.csproj" />
+  </ItemGroup>    
+  
+  <ItemGroup>    
+    <EmbeddedResource Include="ui\**\*.*">
+      <LogicalName>deferred.%(Filename)%(Extension)</LogicalName>
+    </EmbeddedResource>                
+  </ItemGroup>     
+
+</Project>
diff --git a/samples/pbr/shaders/FullScreenQuad.vert b/samples/pbr/shaders/FullScreenQuad.vert
new file mode 100644 (file)
index 0000000..826720b
--- /dev/null
@@ -0,0 +1,10 @@
+#version 450
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+       outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+       gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
+       //gl_Position = vec4(outUV -1.0f, 0.0f, 1.0f);
+}
diff --git a/samples/pbr/shaders/filtercube.vert b/samples/pbr/shaders/filtercube.vert
new file mode 100644 (file)
index 0000000..34adf73
--- /dev/null
@@ -0,0 +1,20 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+
+layout(push_constant) uniform PushConsts {
+       layout (offset = 0) mat4 mvp;
+} pushConsts;
+
+layout (location = 0) out vec3 outUVW;
+
+out gl_PerVertex {
+       vec4 gl_Position;
+};
+
+void main() 
+{
+       outUVW = inPos;
+    //outUVW.y = -outUVW.y; 
+       gl_Position = pushConsts.mvp * vec4(inPos.xyz, 1.0);    
+}
diff --git a/samples/pbr/shaders/genbrdflut.frag b/samples/pbr/shaders/genbrdflut.frag
new file mode 100644 (file)
index 0000000..b6290dd
--- /dev/null
@@ -0,0 +1,90 @@
+#version 450
+
+layout (location = 0) in vec2 inUV;
+layout (location = 0) out vec4 outColor;
+layout (constant_id = 0) const uint NUM_SAMPLES = 1024u;
+
+const float PI = 3.1415926536;
+
+// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
+float random(vec2 co)
+{
+       float a = 12.9898;
+       float b = 78.233;
+       float c = 43758.5453;
+       float dt= dot(co.xy ,vec2(a,b));
+       float sn= mod(dt,3.14);
+       return fract(sin(sn) * c);
+}
+
+vec2 hammersley2d(uint i, uint N) 
+{
+       // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
+       uint bits = (i << 16u) | (i >> 16u);
+       bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+       bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+       bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+       bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+       float rdi = float(bits) * 2.3283064365386963e-10;
+       return vec2(float(i) /float(N), rdi);
+}
+
+// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf
+vec3 importanceSample_GGX(vec2 Xi, float roughness, vec3 normal) 
+{
+       // Maps a 2D point to a hemisphere with spread based on roughness
+       float alpha = roughness * roughness;
+       float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1;
+       float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y));
+       float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+       vec3 H = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+
+       // Tangent space
+       vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+       vec3 tangentX = normalize(cross(up, normal));
+       vec3 tangentY = normalize(cross(normal, tangentX));
+
+       // Convert to world Space
+       return normalize(tangentX * H.x + tangentY * H.y + normal * H.z);
+}
+
+// Geometric Shadowing function
+float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness)
+{
+       float k = (roughness * roughness) / 2.0;
+       float GL = dotNL / (dotNL * (1.0 - k) + k);
+       float GV = dotNV / (dotNV * (1.0 - k) + k);
+       return GL * GV;
+}
+
+vec2 BRDF(float NoV, float roughness)
+{
+       // Normal always points along z-axis for the 2D lookup 
+       const vec3 N = vec3(0.0, 0.0, 1.0);
+       vec3 V = vec3(sqrt(1.0 - NoV*NoV), 0.0, NoV);
+
+       vec2 LUT = vec2(0.0);
+       for(uint i = 0u; i < NUM_SAMPLES; i++) {
+               vec2 Xi = hammersley2d(i, NUM_SAMPLES);
+               vec3 H = importanceSample_GGX(Xi, roughness, N);
+               vec3 L = 2.0 * dot(V, H) * H - V;
+
+               float dotNL = max(dot(N, L), 0.0);
+               float dotNV = max(dot(N, V), 0.0);
+               float dotVH = max(dot(V, H), 0.0); 
+               float dotNH = max(dot(H, N), 0.0);
+
+               if (dotNL > 0.0) {
+                       float G = G_SchlicksmithGGX(dotNL, dotNV, roughness);
+                       float G_Vis = (G * dotVH) / (dotNH * dotNV);
+                       float Fc = pow(1.0 - dotVH, 5.0);
+                       LUT += vec2((1.0 - Fc) * G_Vis, Fc * G_Vis);
+               }
+       }
+       return LUT / float(NUM_SAMPLES);
+}
+
+void main() 
+{
+       outColor = vec4(BRDF(inUV.s, 1.0-inUV.t), 0.0, 1.0);
+}
\ No newline at end of file
diff --git a/samples/pbr/shaders/genbrdflut.vert b/samples/pbr/shaders/genbrdflut.vert
new file mode 100644 (file)
index 0000000..f3dd233
--- /dev/null
@@ -0,0 +1,9 @@
+#version 450
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+       outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+       gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f);
+}
\ No newline at end of file
diff --git a/samples/pbr/shaders/irradiancecube.frag b/samples/pbr/shaders/irradiancecube.frag
new file mode 100644 (file)
index 0000000..340d679
--- /dev/null
@@ -0,0 +1,37 @@
+// Generates an irradiance cube from an environment map using convolution
+
+#version 450
+
+layout (location = 0) in vec3 inPos;
+layout (location = 0) out vec4 outColor;
+layout (binding = 0) uniform samplerCube samplerEnv;
+
+layout(push_constant) uniform PushConsts {
+       layout (offset = 64) float deltaPhi;
+       layout (offset = 68) float deltaTheta;
+} consts;
+
+#define PI 3.1415926535897932384626433832795
+
+void main()
+{
+       vec3 N = normalize(inPos);
+       vec3 up = vec3(0.0, 1.0, 0.0);
+       vec3 right = normalize(cross(up, N));
+       up = cross(N, right);
+
+       const float TWO_PI = PI * 2.0;
+       const float HALF_PI = PI * 0.5;
+
+       vec3 color = vec3(0.0);
+       uint sampleCount = 0u;
+       for (float phi = 0.0; phi < TWO_PI; phi += consts.deltaPhi) {
+               for (float theta = 0.0; theta < HALF_PI; theta += consts.deltaTheta) {
+                       vec3 tempVec = cos(phi) * right + sin(phi) * up;
+                       vec3 sampleVector = cos(theta) * N + sin(theta) * tempVec;
+                       color += texture(samplerEnv, sampleVector).rgb * cos(theta) * sin(theta);
+                       sampleCount++;
+               }
+       }
+       outColor = vec4(PI * color / float(sampleCount), 1.0);//texture(samplerEnv, inPos).rgba;
+}
diff --git a/samples/pbr/shaders/pbr.vert b/samples/pbr/shaders/pbr.vert
new file mode 100644 (file)
index 0000000..76ee24b
--- /dev/null
@@ -0,0 +1,44 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inUV0;
+layout (location = 3) in vec2 inUV1;
+
+layout (set = 0, binding = 0) uniform UBO {
+    mat4 projection;
+    mat4 model;
+    mat4 view;
+    vec4 camPos;
+    vec4 lightDir;
+    float exposure;
+    float gamma;
+    float prefilteredCubeMipLevels;
+    float scaleIBLAmbient;
+} ubo;
+
+layout(push_constant) uniform PushConsts {
+    mat4 model;
+} pc;
+
+layout (location = 0) out vec3 outWorldPos;
+layout (location = 1) out vec3 outNormal;
+layout (location = 2) out vec2 outUV0;
+layout (location = 3) out vec2 outUV1;
+
+out gl_PerVertex
+{
+    vec4 gl_Position;
+};
+
+void main() 
+{        
+    vec4 locPos = ubo.model * pc.model * vec4(inPos, 1.0);
+    outNormal = normalize(transpose(inverse(mat3(ubo.model * pc.model))) * inNormal);        
+
+    //locPos.y = -locPos.y;
+    outWorldPos = locPos.xyz;// / locPos.w;
+    outUV0 = inUV0;
+    outUV1 = inUV1;
+    gl_Position =  ubo.projection * ubo.view * vec4(outWorldPos, 1.0);
+}
diff --git a/samples/pbr/shaders/pbr_khr.frag b/samples/pbr/shaders/pbr_khr.frag
new file mode 100644 (file)
index 0000000..1f8e056
--- /dev/null
@@ -0,0 +1,459 @@
+// PBR shader based on the Khronos WebGL PBR implementation
+// See https://github.com/KhronosGroup/glTF-WebGL-PBR
+// Supports both metallic roughness and specular glossiness inputs
+
+#version 450
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+#define MANUAL_SRGB 0
+#define DEBUG 1
+
+struct Material {
+    vec4 baseColorFactor;
+    vec4 emissiveFactor;
+    vec4 diffuseFactor;
+    vec4 specularFactor;
+    float workflow;
+    uint tex0;
+    uint tex1;
+    float metallicFactor;   
+    float roughnessFactor;  
+    float alphaMask;    
+    float alphaMaskCutoff;
+    int pad0;
+};
+
+const uint MAP_COLOR = 0x1;
+const uint MAP_NORMAL = 0x2;
+const uint MAP_AO = 0x4;
+const uint MAP_METAL = 0x8;
+const uint MAP_ROUGHNESS = 0x10;
+const uint MAP_METALROUGHNESS = 0x20;
+const uint MAP_EMISSIVE = 0x40;
+
+layout (location = 0) in vec3 inWorldPos;
+layout (location = 1) in vec3 inNormal;
+layout (location = 2) in vec2 inUV0;
+layout (location = 3) in vec2 inUV1;
+
+layout (set = 0, binding = 0) uniform UBO {
+    mat4 projection;
+    mat4 model;
+    mat4 view;
+    vec4 camPos;
+    vec4 lightDir;
+    float exposure;
+    float gamma;
+    float prefilteredCubeMipLevels;
+    float scaleIBLAmbient;
+#if DEBUG
+    float debugViewInputs;
+    float debugViewEquation;
+#endif
+} ubo;
+
+layout (set = 0, binding = 4) uniform UBOMaterials {
+    Material materials[16];
+};
+
+layout (set = 0, binding = 1) uniform samplerCube samplerIrradiance;
+layout (set = 0, binding = 2) uniform samplerCube prefilteredMap;
+layout (set = 0, binding = 3) uniform sampler2D samplerBRDFLUT;
+
+// Material bindings
+
+layout (set = 1, binding = 0) uniform sampler2D colorMap;
+layout (set = 1, binding = 1) uniform sampler2D physicalDescriptorMap;
+layout (set = 1, binding = 2) uniform sampler2D normalMap;
+layout (set = 1, binding = 3) uniform sampler2D aoMap;
+layout (set = 1, binding = 4) uniform sampler2D emissiveMap;
+
+
+layout (push_constant) uniform PushCsts {
+    layout(offset = 64)
+    int materialIdx;
+};
+
+layout (location = 0) out vec4 outColor;
+
+// Encapsulate the various inputs used by the various functions in the shading equation
+// We store values in this struct to simplify the integration of alternative implementations
+// of the shading terms, outlined in the Readme.MD Appendix.
+struct PBRInfo
+{
+    float NdotL;                  // cos angle between normal and light direction
+    float NdotV;                  // cos angle between normal and view direction
+    float NdotH;                  // cos angle between normal and half vector
+    float LdotH;                  // cos angle between light direction and half vector
+    float VdotH;                  // cos angle between view direction and half vector
+    float perceptualRoughness;    // roughness value, as authored by the model creator (input to shader)
+    float metalness;              // metallic value at the surface
+    vec3 reflectance0;            // full reflectance color (normal incidence angle)
+    vec3 reflectance90;           // reflectance color at grazing angle
+    float alphaRoughness;         // roughness mapped to a more linear change in the roughness (proposed by [2])
+    vec3 diffuseColor;            // color contribution from diffuse lighting
+    vec3 specularColor;           // color contribution from specular lighting
+};
+
+const float M_PI = 3.141592653589793;
+const float c_MinRoughness = 0.04;
+
+const float PBR_WORKFLOW_METALLIC_ROUGHNESS = 1.0;
+const float PBR_WORKFLOW_SPECULAR_GLOSINESS = 2.0f;
+
+vec3 Uncharted2Tonemap(vec3 color)
+{
+    float A = 0.15;
+    float B = 0.50;
+    float C = 0.10;
+    float D = 0.20;
+    float E = 0.02;
+    float F = 0.30;
+    float W = 11.2;
+    return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F;
+}
+
+vec4 tonemap(vec4 color)
+{
+    vec3 outcol = Uncharted2Tonemap(color.rgb * ubo.exposure);
+    outcol = outcol * (1.0f / Uncharted2Tonemap(vec3(11.2f)));  
+    return vec4(pow(outcol, vec3(1.0f / ubo.gamma)), color.a);
+}
+
+vec4 SRGBtoLINEAR(vec4 srgbIn)
+{
+    #ifdef MANUAL_SRGB
+    #ifdef SRGB_FAST_APPROXIMATION
+    vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
+    #else //SRGB_FAST_APPROXIMATION
+    vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
+    vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
+    #endif //SRGB_FAST_APPROXIMATION
+    return vec4(linOut,srgbIn.w);;
+    #else //MANUAL_SRGB
+    return srgbIn;
+    #endif //MANUAL_SRGB
+}
+
+// Find the normal for this fragment, pulling either from a predefined normal map
+// or from the interpolated mesh normal and tangent attributes.
+vec3 getNormal()
+{
+    vec3 tangentNormal;
+    // Perturb normal, see http://www.thetenthplanet.de/archives/1180
+    if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL)
+        tangentNormal = texture(normalMap, inUV0).xyz * 2.0 - 1.0;
+    else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL)
+        tangentNormal = texture(normalMap, inUV1).xyz * 2.0 - 1.0;
+    else
+        return normalize(inNormal);
+        
+    vec3 q1 = dFdx(inWorldPos);
+    vec3 q2 = dFdy(inWorldPos);
+    vec2 st1 = dFdx(inUV0);
+    vec2 st2 = dFdy(inUV0);
+
+    vec3 N = normalize(inNormal);
+    vec3 T = normalize(q1 * st2.t - q2 * st1.t);
+    vec3 B = -normalize(cross(N, T));
+    mat3 TBN = mat3(T, B, N);
+
+    return normalize(TBN * tangentNormal);
+}
+
+// Calculation of the lighting contribution from an optional Image Based Light source.
+// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
+// See our README.md on Environment Maps [3] for additional discussion.
+vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)
+{
+    float lod = (pbrInputs.perceptualRoughness * ubo.prefilteredCubeMipLevels);
+    // retrieve a scale and bias to F0. See [1], Figure 3
+    vec3 brdf = (texture(samplerBRDFLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb;
+    vec3 diffuseLight = SRGBtoLINEAR(tonemap(texture(samplerIrradiance, n))).rgb;
+
+    vec3 specularLight = SRGBtoLINEAR(tonemap(textureLod(prefilteredMap, reflection, lod))).rgb;
+
+    vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
+    vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
+
+    // For presentation, this allows us to disable IBL terms    
+    diffuse *= ubo.scaleIBLAmbient;
+    specular *= ubo.scaleIBLAmbient;
+
+    return diffuse + specular;
+}
+
+// Basic Lambertian diffuse
+// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog
+// See also [1], Equation 1
+vec3 diffuse(PBRInfo pbrInputs)
+{
+    return pbrInputs.diffuseColor / M_PI;
+}
+
+// The following equation models the Fresnel reflectance term of the spec equation (aka F())
+// Implementation of fresnel from [4], Equation 15
+vec3 specularReflection(PBRInfo pbrInputs)
+{
+    return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
+}
+
+// This calculates the specular geometric attenuation (aka G()),
+// where rougher material will reflect less light back to the viewer.
+// This implementation is based on [1] Equation 4, and we adopt their modifications to
+// alphaRoughness as input as originally proposed in [2].
+float geometricOcclusion(PBRInfo pbrInputs)
+{
+    float NdotL = pbrInputs.NdotL;
+    float NdotV = pbrInputs.NdotV;
+    float r = pbrInputs.alphaRoughness;
+
+    float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
+    float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
+    return attenuationL * attenuationV;
+}
+
+// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
+// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
+// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
+float microfacetDistribution(PBRInfo pbrInputs)
+{
+    float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
+    float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
+    return roughnessSq / (M_PI * f * f);
+}
+
+// Gets metallic factor from specular glossiness workflow inputs 
+float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) {
+    float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b);
+    float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b);
+    if (perceivedSpecular < c_MinRoughness) {
+        return 0.0;
+    }
+    float a = c_MinRoughness;
+    float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness;
+    float c = c_MinRoughness - perceivedSpecular;
+    float D = max(b * b - 4.0 * a * c, 0.0);
+    return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);
+}
+
+void main()
+{
+    float perceptualRoughness;
+    float metallic;
+    vec3 diffuseColor;
+    vec4 baseColor;    
+
+    vec3 f0 = vec3(0.04);
+    
+    baseColor = materials[materialIdx].baseColorFactor;
+    
+    if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) {
+        perceptualRoughness = materials[materialIdx].roughnessFactor;
+        metallic = materials[materialIdx].metallicFactor;        
+        // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
+        // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
+        if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){
+            perceptualRoughness *= texture(physicalDescriptorMap, inUV0).g;
+            metallic *= texture(physicalDescriptorMap, inUV0).b;
+        }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){
+            perceptualRoughness *= texture(physicalDescriptorMap, inUV1).g;
+            metallic *= texture(physicalDescriptorMap, inUV1).b;
+        }               
+        perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
+        metallic = clamp(metallic, 0.0, 1.0);        
+
+        // The albedo may be defined from a base texture or a flat color
+        if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR)        
+            baseColor *= SRGBtoLINEAR(texture(colorMap, inUV0));
+        else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR)
+            baseColor *= SRGBtoLINEAR(texture(colorMap, inUV1));        
+    }
+    
+    if (materials[materialIdx].alphaMask == 1.0f) {            
+        if (baseColor.a < materials[materialIdx].alphaMaskCutoff) 
+            discard;        
+    }
+
+    if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) {
+        // Values from specular glossiness workflow are converted to metallic roughness
+        if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)
+            perceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV0).a;            
+        else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)
+            perceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV1).a;            
+        else
+            perceptualRoughness = 0.0;
+
+        const float epsilon = 1e-6;
+
+        vec4 diffuse = SRGBtoLINEAR(texture(colorMap, inUV0));
+        vec3 specular = SRGBtoLINEAR(texture(physicalDescriptorMap, inUV0)).rgb;
+
+        float maxSpecular = max(max(specular.r, specular.g), specular.b);
+
+        // Convert metallic value from specular glossiness inputs
+        metallic = convertMetallic(diffuse.rgb, specular, maxSpecular);
+
+        vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb;
+        vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb;
+        baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a);
+
+    }
+
+    diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
+    diffuseColor *= 1.0 - metallic;
+        
+    float alphaRoughness = perceptualRoughness * perceptualRoughness;
+
+    vec3 specularColor = mix(f0, baseColor.rgb, metallic);
+
+    // Compute reflectance.
+    float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
+
+    // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
+    // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
+    float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
+    vec3 specularEnvironmentR0 = specularColor.rgb;
+    vec3 specularEnvironmentR90 = vec3(1.0) * reflectance90;
+
+    vec3 n = getNormal();
+    vec3 v = normalize(ubo.camPos.xyz - inWorldPos);    // Vector from surface point to camera
+    vec3 l = normalize(ubo.lightDir.xyz);     // Vector from surface point to light
+    vec3 h = normalize(l+v);                        // Half vector between both l and v
+    vec3 reflection = -normalize(reflect(v, n));
+    reflection.y *= -1.0f;
+
+    float NdotL = clamp(dot(n, l), 0.001, 1.0);
+    float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
+    float NdotH = clamp(dot(n, h), 0.0, 1.0);
+    float LdotH = clamp(dot(l, h), 0.0, 1.0);
+    float VdotH = clamp(dot(v, h), 0.0, 1.0);
+
+    PBRInfo pbrInputs = PBRInfo(
+        NdotL,
+        NdotV,
+        NdotH,
+        LdotH,
+        VdotH,
+        perceptualRoughness,
+        metallic,
+        specularEnvironmentR0,
+        specularEnvironmentR90,
+        alphaRoughness,
+        diffuseColor,
+        specularColor
+    );
+
+    // Calculate the shading terms for the microfacet specular shading model
+    vec3 F = specularReflection(pbrInputs);
+    float G = geometricOcclusion(pbrInputs);
+    float D = microfacetDistribution(pbrInputs);
+
+    const vec3 u_LightColor = vec3(1.0);
+
+    // Calculation of analytical lighting contribution
+    vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs);
+    vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV);
+    // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
+    vec3 color = NdotL * u_LightColor * (diffuseContrib + specContrib);
+
+    // Calculate lighting contribution from image based lighting source (IBL)
+    color += getIBLContribution(pbrInputs, n, reflection);
+
+    const float u_OcclusionStrength = 1.0f;
+    const float u_EmissiveFactor = 1.0f;
+    
+    // Apply optional PBR terms for additional (optional) shading
+    if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO)
+        color = mix(color, color * texture(aoMap, inUV0).r, u_OcclusionStrength);
+    else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO)
+        color = mix(color, color * texture(aoMap, inUV1).r, u_OcclusionStrength);    
+    
+    if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE)    
+        color += SRGBtoLINEAR(texture(emissiveMap, inUV0)).rgb * u_EmissiveFactor;             
+    else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE)    
+        color += SRGBtoLINEAR(texture(emissiveMap, inUV1)).rgb * u_EmissiveFactor;             
+    
+    outColor = vec4(color, baseColor.a);
+#if DEBUG
+    // Shader inputs debug visualization
+    if (ubo.debugViewInputs > 0.0) {
+        int index = int(ubo.debugViewInputs);
+        switch (index) {
+            case 1:
+                if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR)        
+                    outColor.rgba = texture(colorMap, inUV0);
+                else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR)
+                    outColor.rgba = texture(colorMap, inUV1);
+                else
+                    outColor.rgba = vec4(1.0f);                
+                break;
+            case 2:
+                if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL)        
+                    outColor.rgb = texture(normalMap, inUV0).rgb;
+                else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL)
+                    outColor.rgb = texture(normalMap, inUV1).rgb;
+                else
+                    outColor.rgb = normalize(inNormal);                
+                break;
+            case 3:
+                if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO)        
+                    outColor.rgb = texture(aoMap, inUV0).rrr;
+                else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO)
+                    outColor.rgb = texture(aoMap, inUV1).rrr;
+                else
+                    outColor.rgb = vec3(0);
+                break;
+            case 4:
+                if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE)        
+                    outColor.rgb = texture(emissiveMap, inUV0).rgb;
+                else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE)
+                    outColor.rgb = texture(emissiveMap, inUV1).rgb;
+                else
+                    outColor.rgb = vec3(0);
+                break;
+            case 5:
+                if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)        
+                    outColor.rgb = texture(physicalDescriptorMap, inUV0).bbb;
+                else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)
+                    outColor.rgb = texture(physicalDescriptorMap, inUV1).bbb;
+                else
+                    outColor.rgb = vec3(0);
+                break;
+            case 6:
+                if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)        
+                    outColor.rgb = texture(physicalDescriptorMap, inUV0).ggg;
+                else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS)
+                    outColor.rgb = texture(physicalDescriptorMap, inUV1).ggg;
+                else
+                    outColor.rgb = vec3(0);
+                break;
+        }
+        outColor = SRGBtoLINEAR(outColor);
+    }
+
+    // PBR equation debug visualization
+    // "none", "Diff (l,n)", "F (l,h)", "G (l,v,h)", "D (h)", "Specular"
+    if (ubo.debugViewEquation > 0.0) {
+        int index = int(ubo.debugViewEquation);
+        switch (index) {
+            case 1:
+                outColor.rgb = diffuseContrib;
+                break;
+            case 2:
+                outColor.rgb = F;
+                break;
+            case 3:
+                outColor.rgb = vec3(G);
+                break;
+            case 4: 
+                outColor.rgb = vec3(D);
+                break;
+            case 5:
+                outColor.rgb = specContrib;
+                break;              
+        }
+    }
+#endif
+}
diff --git a/samples/pbr/shaders/prefilterenvmap.frag b/samples/pbr/shaders/prefilterenvmap.frag
new file mode 100644 (file)
index 0000000..ae1212e
--- /dev/null
@@ -0,0 +1,105 @@
+#version 450
+
+layout (location = 0) in vec3 inPos;
+layout (location = 0) out vec4 outColor;
+
+layout (binding = 0) uniform samplerCube samplerEnv;
+
+layout(push_constant) uniform PushConsts {
+       layout (offset = 64) float roughness;
+       layout (offset = 68) uint numSamples;
+} consts;
+
+const float PI = 3.1415926536;
+
+// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
+float random(vec2 co)
+{
+       float a = 12.9898;
+       float b = 78.233;
+       float c = 43758.5453;
+       float dt= dot(co.xy ,vec2(a,b));
+       float sn= mod(dt,3.14);
+       return fract(sin(sn) * c);
+}
+
+vec2 hammersley2d(uint i, uint N) 
+{
+       // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
+       uint bits = (i << 16u) | (i >> 16u);
+       bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+       bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+       bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+       bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+       float rdi = float(bits) * 2.3283064365386963e-10;
+       return vec2(float(i) /float(N), rdi);
+}
+
+// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf
+vec3 importanceSample_GGX(vec2 Xi, float roughness, vec3 normal) 
+{
+       // Maps a 2D point to a hemisphere with spread based on roughness
+       float alpha = roughness * roughness;
+       float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1;
+       float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y));
+       float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
+       vec3 H = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+
+       // Tangent space
+       vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+       vec3 tangentX = normalize(cross(up, normal));
+       vec3 tangentY = normalize(cross(normal, tangentX));
+
+       // Convert to world Space
+       return normalize(tangentX * H.x + tangentY * H.y + normal * H.z);
+}
+
+// Normal Distribution function
+float D_GGX(float dotNH, float roughness)
+{
+       float alpha = roughness * roughness;
+       float alpha2 = alpha * alpha;
+       float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0;
+       return (alpha2)/(PI * denom*denom); 
+}
+
+vec3 prefilterEnvMap(vec3 R, float roughness)
+{
+       vec3 N = R;
+       vec3 V = R;
+       vec3 color = vec3(0.0);
+       float totalWeight = 0.0;
+       float envMapDim = float(textureSize(samplerEnv, 0).s);
+       for(uint i = 0u; i < consts.numSamples; i++) {
+               vec2 Xi = hammersley2d(i, consts.numSamples);
+               vec3 H = importanceSample_GGX(Xi, roughness, N);
+               vec3 L = 2.0 * dot(V, H) * H - V;
+               float dotNL = clamp(dot(N, L), 0.0, 1.0);
+               if(dotNL > 0.0) {
+                       // Filtering based on https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/
+
+                       float dotNH = clamp(dot(N, H), 0.0, 1.0);
+                       float dotVH = clamp(dot(V, H), 0.0, 1.0);
+
+                       // Probability Distribution Function
+                       float pdf = D_GGX(dotNH, roughness) * dotNH / (4.0 * dotVH) + 0.0001;
+                       // Slid angle of current smple
+                       float omegaS = 1.0 / (float(consts.numSamples) * pdf);
+                       // Solid angle of 1 pixel across all cube faces
+                       float omegaP = 4.0 * PI / (6.0 * envMapDim * envMapDim);
+                       // Biased (+1.0) mip level for better result
+                       float mipLevel = roughness == 0.0 ? 0.0 : max(0.5 * log2(omegaS / omegaP) + 1.0, 0.0f);
+                       color += textureLod(samplerEnv, L, mipLevel).rgb * dotNL;
+                       totalWeight += dotNL;
+
+               }
+       }
+       return (color / totalWeight);
+}
+
+
+void main()
+{              
+       vec3 N = normalize(inPos);
+       outColor = vec4(prefilterEnvMap(N, consts.roughness), 1.0);
+}
diff --git a/samples/pbr/shaders/simpletexture.frag b/samples/pbr/shaders/simpletexture.frag
new file mode 100644 (file)
index 0000000..d5bcf79
--- /dev/null
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set = 0, binding = 5) uniform sampler2D samplerColor;
+
+layout (location = 0) in       vec2 inUV;
+
+layout (location = 0) out      vec4 outFragColor;
+
+void main()
+{    
+       outFragColor = texture(samplerColor, inUV);
+}
diff --git a/samples/pbr/shaders/skybox.frag b/samples/pbr/shaders/skybox.frag
new file mode 100644 (file)
index 0000000..57a7dbe
--- /dev/null
@@ -0,0 +1,60 @@
+#version 450
+
+layout (binding = 2) uniform samplerCube samplerEnv;
+
+layout (set = 0, location = 0) in vec3 inUVW;
+
+layout (set = 0, location = 0) out vec4 outColor;
+
+layout (set = 0, binding = 0) uniform UBO {
+    mat4 projection;
+    mat4 model;
+    mat4 view;
+    vec3 camPos;
+    vec4 lightDir;
+    float exposure;
+    float gamma;
+} ubo;
+
+// From http://filmicworlds.com/blog/filmic-tonemapping-operators/
+vec3 Uncharted2Tonemap(vec3 color)
+{
+    float A = 0.15;
+    float B = 0.50;
+    float C = 0.10;
+    float D = 0.20;
+    float E = 0.02;
+    float F = 0.30;
+    float W = 11.2;
+    return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F;
+}
+
+vec4 tonemap(vec4 color)
+{
+    vec3 outcol = Uncharted2Tonemap(color.rgb * ubo.exposure);
+    outcol = outcol * (1.0f / Uncharted2Tonemap(vec3(11.2f)));  
+    return vec4(pow(outcol, vec3(1.0f / ubo.gamma)), color.a);
+}
+
+#define MANUAL_SRGB 1
+
+vec4 SRGBtoLINEAR(vec4 srgbIn)
+{
+    #ifdef MANUAL_SRGB
+    #ifdef SRGB_FAST_APPROXIMATION
+    vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
+    #else //SRGB_FAST_APPROXIMATION
+    vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
+    vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
+    #endif //SRGB_FAST_APPROXIMATION
+    return vec4(linOut,srgbIn.w);;
+    #else //MANUAL_SRGB
+    return srgbIn;
+    #endif //MANUAL_SRGB
+}
+
+void main() 
+{
+    vec3 color = SRGBtoLINEAR(tonemap(textureLod(samplerEnv, inUVW, 1.5))).rgb; 
+    outColor = vec4(color * 1.0, 1.0);
+}
\ No newline at end of file
diff --git a/samples/pbr/shaders/skybox.vert b/samples/pbr/shaders/skybox.vert
new file mode 100644 (file)
index 0000000..22e285e
--- /dev/null
@@ -0,0 +1,27 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+
+layout (binding = 0) uniform UBO 
+{
+    mat4 projection;
+    mat4 model;
+    mat4 view;
+} ubo;
+
+layout (location = 0) out vec3 outUVW;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;
+};
+
+void main() 
+{
+    outUVW = inPos;
+    outUVW.y = -outUVW.y;        
+    gl_Position = ubo.projection * mat4(mat3(ubo.view)) * vec4(inPos, 1.0);    
+}
diff --git a/samples/pbr/ui/fps.crow b/samples/pbr/ui/fps.crow
new file mode 100755 (executable)
index 0000000..351f059
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<VerticalStack Left="10" Top="100" Width="30%" Height="Fit" Spacing="2" Background="0.3,0.3,0.3,0.9" Margin="20">
+    <HorizontalStack Height="Fit">
+       <Label Text="Fps:" Width="80" Background="SteelBlue"/>
+       <Label Text="{fps}" />
+    </HorizontalStack>
+    <HorizontalStack Height="Fit">
+        <Label Text="Gamma:" Width="80" Background="SteelBlue"/>
+        <Slider Height="20" Value="{²Gamma}" Maximum="10.0" SmallIncrement="0.1" LargeIncrement="1.0"/>
+        <Label Text="{Gamma}" Width="40" TextAlignment="Right"/>
+    </HorizontalStack>
+    <HorizontalStack Height="Fit">
+        <Label Text="Exposure:" Width="80" Background="SteelBlue"/>
+        <Slider Height="20" Value="{²Exposure}" Maximum="10.0" SmallIncrement="0.1" LargeIncrement="1.0"/>
+        <Label Text="{Exposure}" Width="40" TextAlignment="Right"/>
+    </HorizontalStack>
+<!--        <ComboBox Data="{cubemapPathes}"/>-->
+</VerticalStack>
+<!--<Window Caption="Showcase" Height="30%" Width="20%" Top="10" Left="10">-->
+<!--</Window>-->
+<!--<Label Text="{fps}" Background="SteelBlue" Margin="0" Font="mono,20"/>-->
+
diff --git a/samples/tests/Program.cs b/samples/tests/Program.cs
new file mode 100644 (file)
index 0000000..f05fdbd
--- /dev/null
@@ -0,0 +1,48 @@
+/* Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+ *
+ * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+ *
+ * port of the 'High dynamic range rendering' sample from
+ * Copyright by Sascha Willems - www.saschawillems.de
+ * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+*/
+
+using System;
+using CVKL;
+using VK;
+using static VK.Vk;
+
+namespace tests {
+       class Program : VkWindow {
+               static void Main (string[] args) {
+                       Instance.VALIDATION = true;
+                       Instance.DEBUG_UTILS = true;
+
+                       using (Program vke = new Program ()) {
+                               vke.Run ();
+                       }
+               }
+
+               DebugReport dbgReport;
+               Image img;
+
+               Program () : base () {
+                       dbgReport = new DebugReport (instance,
+                               VkDebugReportFlagsEXT.DebugEXT |
+                               VkDebugReportFlagsEXT.ErrorEXT |
+                               VkDebugReportFlagsEXT.WarningEXT |
+                               VkDebugReportFlagsEXT.PerformanceWarningEXT |
+                               VkDebugReportFlagsEXT.InformationEXT);
+               }
+
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (!isDisposed) {
+                                       dbgReport?.Dispose ();
+                               }
+                       }
+                       base.Dispose ();
+               }
+       }
+}
diff --git a/samples/tests/shaders/main.vert b/samples/tests/shaders/main.vert
new file mode 100644 (file)
index 0000000..3b395bf
--- /dev/null
@@ -0,0 +1,28 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inColor;
+
+layout (binding = 0) uniform UBO 
+{
+       mat4 projectionMatrix;
+    mat4 viewMatrix;
+       mat4 modelMatrix;
+} ubo;
+
+layout (location = 0) out vec3 outColor;
+
+out gl_PerVertex 
+{
+    vec4 gl_Position;   
+};
+
+
+void main() 
+{
+       outColor = inColor;
+       gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0);
+}
diff --git a/samples/tests/tests.csproj b/samples/tests/tests.csproj
new file mode 100644 (file)
index 0000000..f4c1c6b
--- /dev/null
@@ -0,0 +1,6 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <Import Project="$(RootDirectory)netfx.props" />
+  <ItemGroup>
+    <Folder Include="shaders\" />
+  </ItemGroup>
+</Project>
diff --git a/vke.net.sln b/vke.net.sln
new file mode 100644 (file)
index 0000000..815782b
--- /dev/null
@@ -0,0 +1,125 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "addons", "addons", "{4AA67AB0-C331-4CB2-9C00-B74F5DE31658}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vke", "vke\vke.csproj", "{642726F4-0592-4846-8EAF-A5D1964C85A7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistanceFieldFont", "addons\DistanceFieldFont\DistanceFieldFont.csproj", "{FEF3AF30-5B88-4D3C-8BD7-8734200E0D1E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gltfLoader", "addons\gltfLoader\gltfLoader.csproj", "{F3BBF67D-7E63-48F3-8156-ADC014D268BB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VkvgPipeline", "addons\VkvgPipeline\VkvgPipeline.csproj", "{611541A0-CE88-4A83-A6FF-3917971841C9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{16439374-B8DB-4643-8116-EB3358B49A12}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "compute", "samples\compute\compute.csproj", "{5000CDE2-99B9-47EA-B4D9-EA1631F0E14A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "deferred", "samples\deferred\deferred.csproj", "{7E10A906-8633-48E5-8FEF-94F84CD8844C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistanceFieldFontTest", "samples\DistanceFieldFontTest\DistanceFieldFontTest.csproj", "{77437C6D-28B5-4798-96CA-68F987770D65}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "samples\Model\Model.csproj", "{A7D3FB7F-769B-4F36-9E3E-3FB71F24D306}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pbr", "samples\pbr\pbr.csproj", "{96921211-C5A8-41FC-9F3D-F35A696203F7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Textured", "samples\Textured\Textured.csproj", "{1B2DF710-E500-49E5-8802-EBA71A05E827}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TexturedCube", "samples\TexturedCube\TexturedCube.csproj", "{8185163E-A67C-4C0E-8548-67E2A9F16309}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Triangle", "samples\Triangle\Triangle.csproj", "{A30AEC45-54A3-4120-B341-B3299CC1B00E}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Any CPU = Debug|Any CPU
+               Release|Any CPU = Release|Any CPU
+               DebugCrow|Any CPU = DebugCrow|Any CPU
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {642726F4-0592-4846-8EAF-A5D1964C85A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {642726F4-0592-4846-8EAF-A5D1964C85A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {642726F4-0592-4846-8EAF-A5D1964C85A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {642726F4-0592-4846-8EAF-A5D1964C85A7}.Release|Any CPU.Build.0 = Release|Any CPU
+               {642726F4-0592-4846-8EAF-A5D1964C85A7}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {642726F4-0592-4846-8EAF-A5D1964C85A7}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {FEF3AF30-5B88-4D3C-8BD7-8734200E0D1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {FEF3AF30-5B88-4D3C-8BD7-8734200E0D1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {FEF3AF30-5B88-4D3C-8BD7-8734200E0D1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {FEF3AF30-5B88-4D3C-8BD7-8734200E0D1E}.Release|Any CPU.Build.0 = Release|Any CPU
+               {FEF3AF30-5B88-4D3C-8BD7-8734200E0D1E}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {FEF3AF30-5B88-4D3C-8BD7-8734200E0D1E}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {F3BBF67D-7E63-48F3-8156-ADC014D268BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {F3BBF67D-7E63-48F3-8156-ADC014D268BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {F3BBF67D-7E63-48F3-8156-ADC014D268BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {F3BBF67D-7E63-48F3-8156-ADC014D268BB}.Release|Any CPU.Build.0 = Release|Any CPU
+               {F3BBF67D-7E63-48F3-8156-ADC014D268BB}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {F3BBF67D-7E63-48F3-8156-ADC014D268BB}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {611541A0-CE88-4A83-A6FF-3917971841C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {611541A0-CE88-4A83-A6FF-3917971841C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {611541A0-CE88-4A83-A6FF-3917971841C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {611541A0-CE88-4A83-A6FF-3917971841C9}.Release|Any CPU.Build.0 = Release|Any CPU
+               {611541A0-CE88-4A83-A6FF-3917971841C9}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {611541A0-CE88-4A83-A6FF-3917971841C9}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {5000CDE2-99B9-47EA-B4D9-EA1631F0E14A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {5000CDE2-99B9-47EA-B4D9-EA1631F0E14A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {5000CDE2-99B9-47EA-B4D9-EA1631F0E14A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {5000CDE2-99B9-47EA-B4D9-EA1631F0E14A}.Release|Any CPU.Build.0 = Release|Any CPU
+               {5000CDE2-99B9-47EA-B4D9-EA1631F0E14A}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {5000CDE2-99B9-47EA-B4D9-EA1631F0E14A}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {7E10A906-8633-48E5-8FEF-94F84CD8844C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {7E10A906-8633-48E5-8FEF-94F84CD8844C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {7E10A906-8633-48E5-8FEF-94F84CD8844C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {7E10A906-8633-48E5-8FEF-94F84CD8844C}.Release|Any CPU.Build.0 = Release|Any CPU
+               {7E10A906-8633-48E5-8FEF-94F84CD8844C}.DebugCrow|Any CPU.ActiveCfg = DebugCrow|Any CPU
+               {7E10A906-8633-48E5-8FEF-94F84CD8844C}.DebugCrow|Any CPU.Build.0 = DebugCrow|Any CPU
+               {77437C6D-28B5-4798-96CA-68F987770D65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {77437C6D-28B5-4798-96CA-68F987770D65}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {77437C6D-28B5-4798-96CA-68F987770D65}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {77437C6D-28B5-4798-96CA-68F987770D65}.Release|Any CPU.Build.0 = Release|Any CPU
+               {77437C6D-28B5-4798-96CA-68F987770D65}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {77437C6D-28B5-4798-96CA-68F987770D65}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {A7D3FB7F-769B-4F36-9E3E-3FB71F24D306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {A7D3FB7F-769B-4F36-9E3E-3FB71F24D306}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {A7D3FB7F-769B-4F36-9E3E-3FB71F24D306}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {A7D3FB7F-769B-4F36-9E3E-3FB71F24D306}.Release|Any CPU.Build.0 = Release|Any CPU
+               {A7D3FB7F-769B-4F36-9E3E-3FB71F24D306}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {A7D3FB7F-769B-4F36-9E3E-3FB71F24D306}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {96921211-C5A8-41FC-9F3D-F35A696203F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {96921211-C5A8-41FC-9F3D-F35A696203F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {96921211-C5A8-41FC-9F3D-F35A696203F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {96921211-C5A8-41FC-9F3D-F35A696203F7}.Release|Any CPU.Build.0 = Release|Any CPU
+               {96921211-C5A8-41FC-9F3D-F35A696203F7}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {96921211-C5A8-41FC-9F3D-F35A696203F7}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {1B2DF710-E500-49E5-8802-EBA71A05E827}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {1B2DF710-E500-49E5-8802-EBA71A05E827}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {1B2DF710-E500-49E5-8802-EBA71A05E827}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {1B2DF710-E500-49E5-8802-EBA71A05E827}.Release|Any CPU.Build.0 = Release|Any CPU
+               {1B2DF710-E500-49E5-8802-EBA71A05E827}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {1B2DF710-E500-49E5-8802-EBA71A05E827}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {8185163E-A67C-4C0E-8548-67E2A9F16309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {8185163E-A67C-4C0E-8548-67E2A9F16309}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {8185163E-A67C-4C0E-8548-67E2A9F16309}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {8185163E-A67C-4C0E-8548-67E2A9F16309}.Release|Any CPU.Build.0 = Release|Any CPU
+               {8185163E-A67C-4C0E-8548-67E2A9F16309}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {8185163E-A67C-4C0E-8548-67E2A9F16309}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+               {A30AEC45-54A3-4120-B341-B3299CC1B00E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {A30AEC45-54A3-4120-B341-B3299CC1B00E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {A30AEC45-54A3-4120-B341-B3299CC1B00E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {A30AEC45-54A3-4120-B341-B3299CC1B00E}.Release|Any CPU.Build.0 = Release|Any CPU
+               {A30AEC45-54A3-4120-B341-B3299CC1B00E}.DebugCrow|Any CPU.ActiveCfg = Debug|Any CPU
+               {A30AEC45-54A3-4120-B341-B3299CC1B00E}.DebugCrow|Any CPU.Build.0 = Debug|Any CPU
+       EndGlobalSection
+       GlobalSection(NestedProjects) = preSolution
+               {FEF3AF30-5B88-4D3C-8BD7-8734200E0D1E} = {4AA67AB0-C331-4CB2-9C00-B74F5DE31658}
+               {F3BBF67D-7E63-48F3-8156-ADC014D268BB} = {4AA67AB0-C331-4CB2-9C00-B74F5DE31658}
+               {611541A0-CE88-4A83-A6FF-3917971841C9} = {4AA67AB0-C331-4CB2-9C00-B74F5DE31658}
+               {5000CDE2-99B9-47EA-B4D9-EA1631F0E14A} = {16439374-B8DB-4643-8116-EB3358B49A12}
+               {7E10A906-8633-48E5-8FEF-94F84CD8844C} = {16439374-B8DB-4643-8116-EB3358B49A12}
+               {77437C6D-28B5-4798-96CA-68F987770D65} = {16439374-B8DB-4643-8116-EB3358B49A12}
+               {A7D3FB7F-769B-4F36-9E3E-3FB71F24D306} = {16439374-B8DB-4643-8116-EB3358B49A12}
+               {96921211-C5A8-41FC-9F3D-F35A696203F7} = {16439374-B8DB-4643-8116-EB3358B49A12}
+               {1B2DF710-E500-49E5-8802-EBA71A05E827} = {16439374-B8DB-4643-8116-EB3358B49A12}
+               {8185163E-A67C-4C0E-8548-67E2A9F16309} = {16439374-B8DB-4643-8116-EB3358B49A12}
+               {A30AEC45-54A3-4120-B341-B3299CC1B00E} = {16439374-B8DB-4643-8116-EB3358B49A12}
+       EndGlobalSection
+EndGlobal
diff --git a/vke/netfx.props b/vke/netfx.props
new file mode 120000 (symlink)
index 0000000..728e74b
--- /dev/null
@@ -0,0 +1 @@
+../netfx.props
\ No newline at end of file
diff --git a/vke/shaders/FullScreenQuad.vert b/vke/shaders/FullScreenQuad.vert
new file mode 100644 (file)
index 0000000..826720b
--- /dev/null
@@ -0,0 +1,10 @@
+#version 450
+
+layout (location = 0) out vec2 outUV;
+
+void main()
+{
+       outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
+       gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f);
+       //gl_Position = vec4(outUV -1.0f, 0.0f, 1.0f);
+}
diff --git a/vke/shaders/simpletexture.frag b/vke/shaders/simpletexture.frag
new file mode 100644 (file)
index 0000000..cee63cb
--- /dev/null
@@ -0,0 +1,15 @@
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (set = 0, binding = 1) uniform sampler2D samplerColor;
+
+layout (location = 0) in       vec2 inUV;
+
+layout (location = 0) out      vec4 outFragColor;
+
+void main()
+{
+       outFragColor = texture(samplerColor, inUV);
+}
diff --git a/vke/src/Camera.cs b/vke/src/Camera.cs
new file mode 100644 (file)
index 0000000..0410e0e
--- /dev/null
@@ -0,0 +1,130 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Numerics;
+
+namespace CVKL {
+       public class Camera {
+               /// <summary>Corection matrix for vulkan projection</summary>
+               public static readonly Matrix4x4 VKProjectionCorrection =
+                       new Matrix4x4 (
+                               1,  0,    0,    0,
+                               0, -1,    0,    0,
+                               0,  0, 1f/2,    0,
+                               0,  0, 1f/2,    1
+                       );
+
+               public enum CamType {LookAt, FirstPerson};
+
+               float fov, aspectRatio, zNear = 0.1f, zFar = 128f, zoom = 1.0f;
+               float moveSpeed = 0.1f, rotSpeed = 0.01f, zoomSpeed = 0.01f;
+
+               Vector3 rotation = Vector3.Zero;
+               Vector3 position = Vector3.Zero;
+               Matrix4x4 model = Matrix4x4.Identity;
+
+               public Vector3 Position => position;
+               public Vector3 Rotation => rotation;
+               public float NearPlane => zNear;
+               public float FarPlane => zFar;
+
+               public CamType Type;
+               
+               public float AspectRatio {
+                       get { return aspectRatio; }
+                       set {
+                               aspectRatio = value;
+                               Update ();
+                       }
+               }
+               public float FieldOfView {
+                       get { return fov; }
+                       set {
+                               fov = value;
+                               Update ();
+                       }
+               }
+               public Matrix4x4 Perspective {
+                       get { return Matrix4x4.CreatePerspectiveFieldOfView (fov, aspectRatio, zNear, zFar); }
+               }
+
+               public Camera (float fieldOfView, float aspectRatio, float nearPlane = 0.1f, float farPlane = 256f) {
+                       fov = fieldOfView;
+                       this.aspectRatio = aspectRatio;
+                       zNear = nearPlane;
+                       zFar = farPlane;
+                       Update ();
+               }
+
+               public void Rotate (float x, float y, float z = 0) {
+                       rotation.Y += rotSpeed * x;
+                       rotation.X += rotSpeed * y;
+                       //Update ();
+               }
+               public float Zoom {
+                       get { return zoom; }
+                       set {
+                               zoom = value;
+                               Update ();
+                       }
+               }
+               public void SetRotation (float x, float y, float z = 0) {
+                       rotation.X = x;
+                       rotation.Y = y;
+                       rotation.Z = z;
+                       Update ();
+               }
+               public void SetPosition (float x, float y, float z = 0) {
+                       position.X = x;
+                       position.Y = y;
+                       position.Z = z;
+                       Update ();
+               }
+               public void Move (float x, float y, float z = 0) {
+                       position.X += moveSpeed * x;
+                       position.Y += moveSpeed * y;
+                       position.Z += moveSpeed * z;
+                       Update ();
+               }
+               public void SetZoom (float factor) {
+                       zoom += zoomSpeed * factor;
+                       Update ();
+               }
+
+               public Matrix4x4 Projection { get; private set;}
+               public Matrix4x4 View { get; private set;}
+               public Matrix4x4 Model {
+                       get { return model; }
+                       set {
+                               model = value;
+                               Update ();
+                       } 
+               }
+
+               public Matrix4x4 SkyboxView {
+                       get { 
+                               return
+                                       Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotation.Z) *
+                                       Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotation.Y) *
+                                       Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotation.X);
+                       }
+               }
+
+               public void Update () {
+                       Projection = Matrix4x4.CreatePerspectiveFieldOfView (fov, aspectRatio, zNear, zFar) * VKProjectionCorrection;
+                       Matrix4x4 translation = Matrix4x4.CreateTranslation (position * zoom * new Vector3(1,1,-1)) ;
+                       if (Type == CamType.LookAt) {
+                               View = 
+                                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotation.Z) *
+                                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotation.Y) *
+                                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotation.X) *
+                                               translation ;
+                       } else {
+                               View =  translation *
+                                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotation.X) *
+                                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotation.Y) *
+                                               Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotation.Z);
+                       }
+               }
+       }
+}
diff --git a/vke/src/ExtensionMethods.cs b/vke/src/ExtensionMethods.cs
new file mode 100644 (file)
index 0000000..287e887
--- /dev/null
@@ -0,0 +1,144 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       public static class ExtensionMethods {
+               /// <summary>
+               /// Extensions method to check byte array equality.
+               /// </summary>
+               public static bool AreEquals (this byte[] b, byte[] other) {
+                       if (b.Length != other.Length)
+                               return false;
+                       for (int i = 0; i < b.Length; i++) {
+                               if (b[i] != other[i])
+                                       return false;
+                       }
+                       return true;
+               }
+
+               #region pinning
+               /// <summary>
+               /// list of pinned GCHandles used to pass value from managed to unmanaged code.
+               /// </summary>
+               public static Dictionary<object, GCHandle> handles = new Dictionary<object, GCHandle>();
+
+               /// <summary>
+               /// Pin the specified object and return a pointer. MUST be Unpined as soon as possible.
+               /// </summary>
+        public static IntPtr Pin (this object obj) {
+                       if (handles.ContainsKey (obj)) {
+                               Debug.WriteLine ("Trying to pin already pinned object: {0}", obj);
+                               return handles[obj].AddrOfPinnedObject ();
+                       }
+                
+            GCHandle hnd = GCHandle.Alloc (obj, GCHandleType.Pinned);
+            handles.Add (obj, hnd);
+            return hnd.AddrOfPinnedObject ();
+        }
+               /// <summary>
+               /// Unpin the specified object and free the GCHandle associated.
+               /// </summary>
+        public static void Unpin (this object obj) {
+            if (!handles.ContainsKey (obj)) {
+                Debug.WriteLine ("Trying to unpin unpinned object: {0}.", obj);
+                return;
+            }
+            handles[obj].Free ();
+            handles.Remove (obj);
+        }
+        public static IntPtr Pin<T> (this List<T> obj) {
+            if (handles.ContainsKey (obj)) 
+                Debug.WriteLine ("Pinning already pinned object: {0}", obj);
+                
+            GCHandle hnd = GCHandle.Alloc (obj.ToArray(), GCHandleType.Pinned);
+            handles.Add (obj, hnd);
+            return hnd.AddrOfPinnedObject ();
+        }
+               public static IntPtr Pin<T> (this T[] obj) {
+                       if (handles.ContainsKey (obj))
+                               Debug.WriteLine ("Pinning already pinned object: {0}", obj);
+
+                       GCHandle hnd = GCHandle.Alloc (obj, GCHandleType.Pinned);
+                       handles.Add (obj, hnd);
+                       return hnd.AddrOfPinnedObject ();
+               }
+               public static IntPtr Pin (this string obj) {
+                       if (handles.ContainsKey (obj)) {
+                               Debug.WriteLine ("Trying to pin already pinned object: {0}", obj);
+                               return handles[obj].AddrOfPinnedObject ();
+                       }
+            byte[] n = System.Text.Encoding.UTF8.GetBytes(obj +'\0');
+                       GCHandle hnd = GCHandle.Alloc (n, GCHandleType.Pinned);            
+            handles.Add (obj, hnd);
+            return hnd.AddrOfPinnedObject ();
+        }
+               #endregion
+
+               #region DebugMarkers
+               public static void SetDebugMarkerName (this VkCommandBuffer obj, Device dev, string name) {
+                       if (!dev.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerObjectNameInfoEXT dmo = new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.CommandBufferEXT,
+                               (ulong)obj.Handle.ToInt64 ()) { pObjectName = name.Pin () };
+                       Utils.CheckResult (vkDebugMarkerSetObjectNameEXT (dev.VkDev, ref dmo));
+                       name.Unpin ();
+               }
+               public static void SetDebugMarkerName (this VkImageView obj, Device dev, string name) {
+                       if (!dev.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerObjectNameInfoEXT dmo = new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.ImageViewEXT,
+                               obj.Handle) { pObjectName = name.Pin () };
+                       Utils.CheckResult (vkDebugMarkerSetObjectNameEXT (dev.VkDev, ref dmo));
+                       name.Unpin ();
+               }
+               public static void SetDebugMarkerName (this VkSampler obj, Device dev, string name) {
+                       if (!dev.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerObjectNameInfoEXT dmo = new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.SamplerEXT,
+                               obj.Handle) { pObjectName = name.Pin () };
+                       Utils.CheckResult (vkDebugMarkerSetObjectNameEXT (dev.VkDev, ref dmo));
+                       name.Unpin ();
+               }
+               public static void SetDebugMarkerName (this VkPipeline obj, Device dev, string name) {
+                       if (!dev.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerObjectNameInfoEXT dmo = new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.PipelineEXT,
+                               obj.Handle) { pObjectName = name.Pin () };
+                       Utils.CheckResult (vkDebugMarkerSetObjectNameEXT (dev.VkDev, ref dmo));
+                       name.Unpin ();
+               }
+               public static void SetDebugMarkerName (this VkDescriptorSet obj, Device dev, string name) {
+                       if (!dev.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerObjectNameInfoEXT dmo = new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.DescriptorSetEXT,
+                               obj.Handle) { pObjectName = name.Pin () };
+                       Utils.CheckResult (vkDebugMarkerSetObjectNameEXT (dev.VkDev, ref dmo));
+                       name.Unpin ();
+               }
+               public static void SetDebugMarkerName (this VkSemaphore obj, Device dev, string name) {
+                       if (!dev.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerObjectNameInfoEXT dmo = new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.SemaphoreEXT,
+                               obj.Handle) { pObjectName = name.Pin () };
+                       Utils.CheckResult (vkDebugMarkerSetObjectNameEXT (dev.VkDev, ref dmo));
+                       name.Unpin ();
+               }
+               public static void SetDebugMarkerName (this VkFence obj, Device dev, string name) {
+                       if (!dev.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerObjectNameInfoEXT dmo = new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.FenceEXT,
+                               obj.Handle) { pObjectName = name.Pin () };
+                       Utils.CheckResult (vkDebugMarkerSetObjectNameEXT (dev.VkDev, ref dmo));
+                       name.Unpin ();
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/FixedUtf8String.cs b/vke/src/FixedUtf8String.cs
new file mode 100644 (file)
index 0000000..7b3d7a6
--- /dev/null
@@ -0,0 +1,85 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace VK
+{
+    public unsafe class FixedUtf8String : IDisposable
+    {
+        GCHandle _handle;
+        uint _numBytes;
+
+        public byte* StringPtr => (byte*)_handle.AddrOfPinnedObject().ToPointer();
+
+        public FixedUtf8String(string s)
+        {
+            if (s == null)
+            {
+                throw new ArgumentNullException(nameof(s));
+            }
+
+            byte[] text = Encoding.UTF8.GetBytes(s + "\0");
+            _handle = GCHandle.Alloc(text, GCHandleType.Pinned);
+            _numBytes = (uint)text.Length;
+        }
+
+        public void SetText(string s)
+        {
+            if (s == null)
+            {
+                throw new ArgumentNullException(nameof(s));
+            }
+
+            _handle.Free();
+            byte[] text = Encoding.UTF8.GetBytes(s);
+            _handle = GCHandle.Alloc(text, GCHandleType.Pinned);
+            _numBytes = (uint)text.Length;
+        }
+
+        private string GetString()
+        {
+            return Encoding.UTF8.GetString(StringPtr, (int)_numBytes);
+        }
+
+        //public void Dispose()
+        //{
+        //    _handle.Free();
+        //}
+
+        public static implicit operator byte* (FixedUtf8String utf8String) => utf8String.StringPtr;
+        public static implicit operator IntPtr (FixedUtf8String utf8String) => utf8String._handle.AddrOfPinnedObject ();
+        public static implicit operator FixedUtf8String(string s) => new FixedUtf8String(s);
+        public static implicit operator string(FixedUtf8String utf8String) => utf8String.GetString();
+
+        #region IDisposable Support
+        private bool disposedValue = false; // Pour détecter les appels redondants
+
+        protected virtual void Dispose (bool disposing) {
+            if (!disposedValue) {
+                if (disposing) {
+                    // TODO: supprimer l'état managé (objets managés).
+                }
+
+                // TODO: libérer les ressources non managées (objets non managés) et remplacer un finaliseur ci-dessous.
+                // TODO: définir les champs de grande taille avec la valeur Null.
+                _handle.Free ();
+                disposedValue = true;
+            }
+        }
+
+        // TODO: remplacer un finaliseur seulement si la fonction Dispose(bool disposing) ci-dessus a du code pour libérer les ressources non managées.
+        ~FixedUtf8String() {
+          // Ne modifiez pas ce code. Placez le code de nettoyage dans Dispose(bool disposing) ci-dessus.
+          Dispose(false);
+        }
+
+        // Ce code est ajouté pour implémenter correctement le modèle supprimable.
+        public void Dispose () {
+            // Ne modifiez pas ce code. Placez le code de nettoyage dans Dispose(bool disposing) ci-dessus.
+            Dispose (true);
+            // TODO: supprimer les marques de commentaire pour la ligne suivante si le finaliseur est remplacé ci-dessus.
+             GC.SuppressFinalize(this);
+        }
+        #endregion
+    }
+}
\ No newline at end of file
diff --git a/vke/src/MarshaledObject.cs b/vke/src/MarshaledObject.cs
new file mode 100644 (file)
index 0000000..6116071
--- /dev/null
@@ -0,0 +1,65 @@
+//
+// MarshaledObject.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Runtime.InteropServices;
+
+namespace CVKL {
+       public class MarshaledObject<T> : IDisposable where T : struct {
+
+        GCHandle handle;
+
+        public IntPtr Pointer {
+            get {
+                if (!handle.IsAllocated)
+                    throw new InvalidOperationException ("Unalocated MarshaledObject");
+                return handle.AddrOfPinnedObject ();
+            }
+        }
+
+        public MarshaledObject (T mobj) {
+            handle = GCHandle.Alloc (mobj, GCHandleType.Pinned);
+        }
+
+        void freeHandle () {
+            if (!disposed) 
+                handle.Free ();
+                       disposed = true;
+               }
+
+        #region IDisposable Support
+        private bool disposed;
+
+        ~MarshaledObject() {
+            freeHandle ();
+        }
+
+        public void Dispose () {
+            freeHandle ();
+            GC.SuppressFinalize(this);
+        }
+        #endregion
+    }
+}
diff --git a/vke/src/MemoryPool.cs b/vke/src/MemoryPool.cs
new file mode 100644 (file)
index 0000000..1c5e3ab
--- /dev/null
@@ -0,0 +1,147 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+#if MEMORY_POOLS
+       public enum MemoryPoolType {
+               Random,
+               Linear 
+       }
+       public class MemoryPool : IDisposable {
+               Device dev;
+               internal VkDeviceMemory vkMemory;
+               VkMemoryAllocateInfo memInfo = VkMemoryAllocateInfo.New ();
+
+               //Resource firstResource;
+               Resource lastResource;
+
+               //Resource mappedFrom;
+               //Resource mappedTo;
+
+               //ulong freeMemPointer;
+               IntPtr mappedPointer;
+
+               public ulong Size => memInfo.allocationSize;
+               public bool IsMapped => mappedPointer != IntPtr.Zero;
+               public IntPtr MappedData => mappedPointer;
+               public Resource Last => lastResource;
+
+               public MemoryPool (Device dev, uint memoryTypeIndex, UInt64 size) {
+                       this.dev = dev;
+                       memInfo.allocationSize = size;
+                       memInfo.memoryTypeIndex = memoryTypeIndex;
+                       Utils.CheckResult (vkAllocateMemory (dev.VkDev, ref memInfo, IntPtr.Zero, out vkMemory));
+               }
+
+               public void Add (Resource resource) {
+                       resource.memoryPool = this;
+
+                       ulong limit = Size;
+                       ulong offset = 0;
+                       Resource previous = lastResource;
+
+                       if (previous != null) {
+                               do {
+                                       offset = previous.poolOffset + previous.AllocatedDeviceMemorySize;
+                                       offset += resource.MemoryAlignment - (offset % resource.MemoryAlignment);
+
+                                       if (previous.next == null) {
+                                               if (offset + resource.AllocatedDeviceMemorySize >= limit) {
+                                                       offset = 0;
+                                                       limit = previous.poolOffset;
+                                               }
+                                               break;
+                                       }
+
+                                       if (previous.next.poolOffset < previous.poolOffset) {
+                                               limit = Size;
+                                               if (offset + resource.AllocatedDeviceMemorySize < Size) 
+                                                       break;
+                                               offset = 0;
+                                               limit = previous.next.poolOffset;
+                                       }else
+                                               limit = previous.next.poolOffset;
+
+                                       if (offset + resource.AllocatedDeviceMemorySize < limit)
+                                               break;
+
+                                       previous = previous.next;
+
+                               } while (previous != lastResource);
+
+                       }
+
+                       if (offset + resource.AllocatedDeviceMemorySize >= limit)
+                               throw new Exception ($"Out of Memory pool: {memInfo.memoryTypeIndex}");
+
+                       resource.poolOffset = offset;
+                       resource.previous = previous;
+                       if (previous != null) {
+                               if (previous.next == null) {
+                                       resource.next = resource.previous = previous;
+                                       previous.next = previous.previous = resource;
+                               } else {
+                                       resource.next = previous.next;
+                                       previous.next = resource.next.previous = resource;
+                               }
+                       }
+                       lastResource = resource;
+
+                       resource.bindMemory ();
+               }
+
+
+
+               public void Defrag () { 
+
+               }
+
+               public void Remove (Resource resource) {
+                       if (resource == lastResource)
+                               lastResource = resource.previous;
+                       if (lastResource != null) {
+                               if (resource.previous == resource.next)//only 1 resources remaining
+                                       lastResource.next = lastResource.previous = null;
+                               else {
+                                       resource.previous.next = resource.next;
+                                       resource.next.previous = resource.previous;
+                               }
+                       }
+                       resource.next = resource.previous = null;
+               }
+
+               public void Map (ulong size = Vk.WholeSize, ulong offset = 0) {
+                       Utils.CheckResult (vkMapMemory (dev.VkDev, vkMemory, offset, size, 0, ref mappedPointer));
+               }
+               public void Unmap () {
+                       vkUnmapMemory (dev.VkDev, vkMemory);
+                       mappedPointer = IntPtr.Zero;
+               }
+
+#region IDisposable Support
+               private bool disposedValue;
+
+               protected virtual void Dispose (bool disposing) {
+                       if (!disposedValue) {
+                               if (!disposing)
+                                       System.Diagnostics.Debug.WriteLine ("MemoryPool disposed by Finalizer.");
+                               vkFreeMemory (dev.VkDev, vkMemory, IntPtr.Zero);
+                               disposedValue = true;
+                       }
+               }
+
+               ~MemoryPool() { 
+                       Dispose(false);
+               }
+               public void Dispose () {
+                       Dispose (true);
+                       GC.SuppressFinalize(this);
+               }
+#endregion
+       }
+#endif
+}
\ No newline at end of file
diff --git a/vke/src/ResourceManager.cs b/vke/src/ResourceManager.cs
new file mode 100644 (file)
index 0000000..e450754
--- /dev/null
@@ -0,0 +1,56 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+#if MEMORY_POOLS
+       public class ResourceManager : IDisposable {
+               VkPhysicalDeviceMemoryProperties memoryProperties;
+               public MemoryPool[] memoryPools;
+               ulong[] reservedHeapMemory;
+
+               VkMemoryHeap getHeapFromMemoryIndex (uint i) => memoryProperties.memoryHeaps[memoryProperties.memoryTypes[i].heapIndex];
+
+
+               public ResourceManager (Device dev, ulong defaultPoolsBlockDivisor = 4) {
+                       memoryProperties = dev.phy.memoryProperties;
+                       memoryPools = new MemoryPool[memoryProperties.memoryTypeCount];
+                       reservedHeapMemory = new ulong[memoryProperties.memoryHeapCount];
+
+                       for (uint i = 0; i < memoryPools.Length; i++) {
+                               ulong size = getHeapFromMemoryIndex (i).size / defaultPoolsBlockDivisor;
+                               memoryPools[i] = new MemoryPool (dev, i, size);
+                               reservedHeapMemory[memoryProperties.memoryTypes[i].heapIndex] += size;
+                       }
+               }
+
+               public void Add (params Resource[] resources) {
+                       foreach (Resource res in resources) {
+                               res.updateMemoryRequirements ();
+                               GetMemoryPool (res.TypeBits, res.MemoryFlags).Add (res);
+                       }
+               }
+
+               MemoryPool GetMemoryPool (uint typeBits, VkMemoryPropertyFlags properties) {
+                       // Iterate over all memory types available for the Device used in this example
+                       for (uint i = 0; i < memoryProperties.memoryTypeCount; i++) {
+                               if ((typeBits & 1) == 1) {
+                                       if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) 
+                                               return memoryPools[i];
+                               }
+                               typeBits >>= 1;
+                       }
+                       throw new InvalidOperationException ("Could not find a suitable memory type!");
+               }
+
+               public void Dispose () {
+                       for (uint i = 0; i < memoryPools.Length; i++)
+                               memoryPools[i].Dispose ();
+               }
+       }
+#endif
+}
\ No newline at end of file
diff --git a/vke/src/ShaderInfo.cs b/vke/src/ShaderInfo.cs
new file mode 100644 (file)
index 0000000..f523f06
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using VK;
+
+namespace CVKL {
+       public class ShaderInfo : IDisposable {
+               public VkShaderStageFlags StageFlags;
+               public string SpirvPath;
+               public FixedUtf8String EntryPoint;
+               public SpecializationInfo SpecializationInfo;
+
+               public ShaderInfo (VkShaderStageFlags _stageFlags, string _spirvPath, SpecializationInfo specializationInfo = null, string _entryPoint = "main") {
+                       StageFlags = _stageFlags;
+                       SpirvPath = _spirvPath;
+                       EntryPoint = new FixedUtf8String (_entryPoint);
+                       this.SpecializationInfo = specializationInfo;
+               }
+
+               public VkPipelineShaderStageCreateInfo GetStageCreateInfo (Device dev) {
+                       return new VkPipelineShaderStageCreateInfo {
+                               sType = VkStructureType.PipelineShaderStageCreateInfo,
+                               stage = StageFlags,
+                               pName = EntryPoint,
+                               module = dev.LoadSPIRVShader (SpirvPath),
+                               pSpecializationInfo = (SpecializationInfo == null) ? IntPtr.Zero : SpecializationInfo.InfosPtr
+                       };
+               }
+
+               #region IDisposable Support
+               private bool disposedValue = false; // Pour détecter les appels redondants
+
+               protected virtual void Dispose (bool disposing) {
+                       if (!disposedValue) {
+                               if (disposing)
+                                       EntryPoint.Dispose ();
+
+                               disposedValue = true;
+                       }
+               }
+               public void Dispose () {
+                       Dispose (true);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/SpecializationConstant.cs b/vke/src/SpecializationConstant.cs
new file mode 100644 (file)
index 0000000..b455cca
--- /dev/null
@@ -0,0 +1,123 @@
+//
+// SpecializationConstant.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL {
+       /// <summary>
+       /// Hold shader specialization constant value and type
+       /// </summary>
+       public class SpecializationConstant<T> : SpecializationConstant {
+               T val;
+               IntPtr ptr = IntPtr.Zero;
+
+        public T Value {
+                       get { return val; }
+                       set {
+                               val = value;
+                               if (ptr != IntPtr.Zero)
+                                       WriteTo (ptr);
+                       }
+        }
+
+               #region CTOR
+               public SpecializationConstant (uint id, T value) : base(id) {            
+            val = value;
+        }
+               #endregion
+
+               public override uint Size => (uint)Marshal.SizeOf<T> ();
+               public unsafe override void WriteTo (IntPtr ptr) {
+                       this.ptr = ptr;
+
+                       if (typeof (T) == typeof (float)) {
+                               float v = Convert.ToSingle (Value);
+                               System.Buffer.MemoryCopy (&v, ptr.ToPointer (), 4, 4);
+                       } else if (typeof (T) == typeof (int) || typeof (T) == typeof (uint)) {
+                               Marshal.WriteInt32 (ptr, Convert.ToInt32 (val));
+                       } else if (typeof (T) == typeof (long) || typeof (T) == typeof (ulong)) {
+                               Marshal.WriteInt64 (ptr, Convert.ToInt64 (val));
+                       } else if (typeof (T) == typeof (byte)) {
+                               Marshal.WriteByte (ptr, Convert.ToByte (val));
+                       }
+               }
+       }
+       public abstract class SpecializationConstant {
+               public uint id;
+               public SpecializationConstant (uint id) {
+                       this.id = id;
+               }
+               public abstract uint Size { get; }
+               public abstract void WriteTo (IntPtr ptr);
+       }
+
+       /// <summary>
+       /// Specialization constant infos, MUST be disposed after pipeline creation
+       /// </summary>
+       public class SpecializationInfo : IDisposable {
+               IntPtr pData;
+               VkSpecializationMapEntry[] entries;
+
+               VkSpecializationInfo infos;
+
+               public IntPtr InfosPtr { get; private set; }
+
+               #region CTOR
+               public SpecializationInfo (params SpecializationConstant[] constants) {
+                       uint offset = 0;
+                       entries = new VkSpecializationMapEntry[constants.Length];
+                       for (int i = 0; i < constants.Length; i++) {
+                               entries[i] = new VkSpecializationMapEntry { constantID = constants[i].id, offset = offset, size = (UIntPtr)constants[i].Size };
+                               offset += constants[i].Size;
+                       }
+                       int totSize = (int)offset;
+                       offset = 0;
+                       pData = Marshal.AllocHGlobal (totSize);
+                       IntPtr curPtr = pData;
+                       foreach (SpecializationConstant sc in constants) {
+                               sc.WriteTo (curPtr);
+                               curPtr += (int)sc.Size;
+                       }
+
+                       infos = new VkSpecializationInfo {
+                               mapEntryCount = (uint)constants.Length,
+                               pMapEntries = entries.Pin (),
+                               pData = pData,
+                               dataSize = (UIntPtr)totSize
+                       };
+                       InfosPtr = infos.Pin ();
+               }
+               #endregion
+
+               public void Dispose () {
+                       infos.Unpin ();
+                       Marshal.FreeHGlobal (pData);
+                       entries.Unpin ();
+               }
+       }
+}
diff --git a/vke/src/StbImage.cs b/vke/src/StbImage.cs
new file mode 100644 (file)
index 0000000..3ea1d7f
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace CVKL {
+    public class StbImage : IDisposable {
+        const string stblib = "stb";
+
+        [DllImport (stblib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load")]
+        static extern IntPtr Load ([MarshalAs (UnmanagedType.LPStr)] string filename, out int x, out int y, out int channels_in_file, int desired_channels);
+
+               [DllImport (stblib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_load_from_memory")]
+        static extern IntPtr Load (IntPtr bitmap, int byteCount, out int x, out int y, out int channels_in_file, int desired_channels);        
+
+        [DllImport (stblib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "stbi_image_free")]
+        static extern void FreeImage (IntPtr img);
+
+               public readonly IntPtr Handle;
+               public readonly int Width;
+               public readonly int Height;
+               public readonly int Channels;
+               public int Size => Width * Height * Channels;
+
+               /// <summary>
+               /// Open image with STBI library
+               /// </summary>
+               /// <param name="path">file path</param>
+               /// <param name="requestedChannels">Force returned channels count, set 0 for original count</param>
+               public StbImage (string path, int requestedChannels = 4) {
+                       Handle = StbImage.Load (path, out Width, out Height, out Channels, requestedChannels);
+                       if (Handle == IntPtr.Zero)
+                               throw new Exception ($"STBI image loading error.");
+                       if (requestedChannels > 0)
+                               Channels = requestedChannels;
+               }
+               /// <summary>
+               /// Open image with STBI library
+               /// </summary>
+               /// <param name="bitmap">raw bitmap datas</param>
+               /// <param name="bitmapByteCount">Bitmap byte count.</param>
+               /// <param name="requestedChannels">Force returned channels count, set 0 for original count</param>
+               public StbImage (IntPtr bitmap, ulong bitmapByteCount, int requestedChannels = 4) {
+                       Handle = StbImage.Load (bitmap, (int)bitmapByteCount, out Width, out Height, out Channels, requestedChannels);
+                       if (Handle == IntPtr.Zero)
+                               throw new Exception ($"STBI image loading error.");
+                       if (requestedChannels > 0)
+                               Channels = requestedChannels;
+               }
+               /// <summary>
+               /// copy pixels to destination.
+               /// </summary>
+               /// <param name="destPtr">Destination pointer.</param>
+               public void CoptyTo (IntPtr destPtr) {
+                       unsafe {
+                               System.Buffer.MemoryCopy (Handle.ToPointer (), destPtr.ToPointer (), Size, Size);
+                       }
+               }
+               public void Dispose () {
+                       StbImage.FreeImage (Handle);
+               }
+       }
+}
diff --git a/vke/src/Utils.cs b/vke/src/Utils.cs
new file mode 100644 (file)
index 0000000..9205b97
--- /dev/null
@@ -0,0 +1,289 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Numerics;
+
+namespace VK {
+       public static partial class Utils {
+               public static string DataDirectory => "../../../datas/";
+               /// <summary>Throw an erro if VkResult != Success.</summary>
+               public static void CheckResult (VkResult result, string errorString = "Call failed") {
+            if (result != VkResult.Success)
+                throw new InvalidOperationException (errorString + ": " + result.ToString ());
+        }
+               /// <summary>Convert angle from degree to radian.</summary>
+               public static float DegreesToRadians (float degrees) {
+            return degrees * (float)Math.PI / 180f;
+        }
+
+               /// <summary>
+               /// Populate a Vector3 with values from a float array
+               /// </summary>
+               public static void FromFloatArray (ref Vector3 v, float[] floats) {
+                       if (floats.Length > 0)
+                               v.X = floats[0];
+                       if (floats.Length > 1)
+                               v.Y = floats[1];
+                       if (floats.Length > 2)
+                               v.Z = floats[2];
+               }
+               /// <summary>
+               /// Populate a Vector4 with values from a float array
+               /// </summary>
+               public static void FromFloatArray (ref Vector4 v, float[] floats) {
+                       if (floats.Length > 0)
+                               v.X = floats[0];
+                       if (floats.Length > 1)
+                               v.Y = floats[1];
+                       if (floats.Length > 2)
+                               v.Z = floats[2];
+                       if (floats.Length > 3)
+                               v.W = floats[3];            
+        }
+               /// <summary>
+               /// Populate a Quaternion with values from a float array
+               /// </summary>
+               public static void FromFloatArray (ref Quaternion v, float[] floats) {
+                       if (floats.Length > 0)
+                               v.X = floats[0];
+                       if (floats.Length > 1)
+                               v.Y = floats[1];
+                       if (floats.Length > 2)
+                               v.Z = floats[2];
+                       if (floats.Length > 3)
+                               v.W = floats[3];
+               }
+               /// <summary>
+               /// Populate a Vector2 with values from a byte array starting at offset
+               /// </summary>
+               public static void FromByteArray (ref Vector2 v, byte[] byteArray, int offset) {
+            v.X = BitConverter.ToSingle (byteArray, offset);
+            v.Y = BitConverter.ToSingle (byteArray, offset + 4);
+        }
+               /// <summary>
+               /// Populate a Vector3 with values from a byte array starting at offset
+               /// </summary>
+               public static void FromByteArray (ref Vector3 v, byte[] byteArray, int offset) {
+            v.X = BitConverter.ToSingle (byteArray, offset);
+            v.Y = BitConverter.ToSingle (byteArray, offset + 4);
+            v.Z = BitConverter.ToSingle (byteArray, offset + 8);
+        }
+               /// <summary>
+               /// Populate a Vector4 with values from a byte array starting at offset
+               /// </summary>
+               public static void FromByteArray (ref Vector4 v, byte[] byteArray, int offset) {
+            v.X = BitConverter.ToSingle (byteArray, offset);
+            v.Y = BitConverter.ToSingle (byteArray, offset + 4);
+            v.Z = BitConverter.ToSingle (byteArray, offset + 8);
+            v.W = BitConverter.ToSingle (byteArray, offset + 12);
+        }
+               /// <summary>
+               /// Populate a Quaternion with values from a byte array starting at offset
+               /// </summary>
+               public static void FromByteArray (ref Quaternion v, byte[] byteArray, int offset) {
+            v.X = BitConverter.ToSingle (byteArray, offset);
+            v.Y = BitConverter.ToSingle (byteArray, offset + 4);
+            v.Z = BitConverter.ToSingle (byteArray, offset + 8);
+            v.W = BitConverter.ToSingle (byteArray, offset + 12);
+        }
+
+               #region Extensions methods
+               public static void ImportFloatArray (this ref Vector3 v, float[] floats) {
+                       if (floats.Length > 0)
+                               v.X = floats[0];
+                       if (floats.Length > 1)
+                               v.Y = floats[1];
+                       if (floats.Length > 2)
+                               v.Z = floats[2];
+               }
+               public static Vector3 Transform (this Vector3 v, ref Matrix4x4 mat, bool translate = false) {
+                       Vector4 v4 = Vector4.Transform (new Vector4 (v, translate ? 1f : 0f), mat);
+                       return new Vector3 (v4.X, v4.Y, v4.Z);
+               }
+               public static Vector3 ToVector3 (this Vector4 v) {
+                       return new Vector3 (v.X, v.Y, v.Z);
+               }
+               #endregion
+
+               // Fixed sub resource on first mip level and layer
+               public static void setImageLayout (
+            VkCommandBuffer cmdbuffer,
+            VkImage image,
+            VkImageAspectFlags aspectMask,
+            VkImageLayout oldImageLayout,
+            VkImageLayout newImageLayout,
+            VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands,
+            VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) {
+            VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange {
+                aspectMask = aspectMask,
+                baseMipLevel = 0,
+                levelCount = 1,
+                layerCount = 1,
+            };
+            setImageLayout (cmdbuffer, image, aspectMask, oldImageLayout, newImageLayout, subresourceRange);
+        }
+
+        // Create an image memory barrier for changing the layout of
+        // an image and put it into an active command buffer
+        // See chapter 11.4 "Image Layout" for details
+
+        public static void setImageLayout (
+            VkCommandBuffer cmdbuffer,
+            VkImage image,
+            VkImageAspectFlags aspectMask,
+            VkImageLayout oldImageLayout,
+            VkImageLayout newImageLayout,
+            VkImageSubresourceRange subresourceRange,
+            VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands,
+            VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) {
+            // Create an image barrier object
+            VkImageMemoryBarrier imageMemoryBarrier = VkImageMemoryBarrier.New();
+            imageMemoryBarrier.srcQueueFamilyIndex = Vk.QueueFamilyIgnored;
+            imageMemoryBarrier.dstQueueFamilyIndex = Vk.QueueFamilyIgnored;
+            imageMemoryBarrier.oldLayout = oldImageLayout;
+            imageMemoryBarrier.newLayout = newImageLayout;
+            imageMemoryBarrier.image = image;
+            imageMemoryBarrier.subresourceRange = subresourceRange;
+
+            // Source layouts (old)
+            // Source access mask controls actions that have to be finished on the old layout
+            // before it will be transitioned to the new layout
+            switch (oldImageLayout) {
+                case VkImageLayout.Undefined:
+                    // Image layout is undefined (or does not matter)
+                    // Only valid as initial layout
+                    // No flags required, listed only for completeness
+                    imageMemoryBarrier.srcAccessMask = 0;
+                    break;
+
+                case VkImageLayout.Preinitialized:
+                    // Image is preinitialized
+                    // Only valid as initial layout for linear images, preserves memory contents
+                    // Make sure host writes have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite;
+                    break;
+
+                case VkImageLayout.ColorAttachmentOptimal:
+                    // Image is a color attachment
+                    // Make sure any writes to the color buffer have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.ColorAttachmentWrite;
+                    break;
+
+                case VkImageLayout.DepthStencilAttachmentOptimal:
+                    // Image is a depth/stencil attachment
+                    // Make sure any writes to the depth/stencil buffer have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.DepthStencilAttachmentWrite;
+                    break;
+
+                case VkImageLayout.TransferSrcOptimal:
+                    // Image is a transfer source 
+                    // Make sure any reads from the image have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead;
+                    break;
+
+                case VkImageLayout.TransferDstOptimal:
+                    // Image is a transfer destination
+                    // Make sure any writes to the image have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferWrite;
+                    break;
+
+                case VkImageLayout.ShaderReadOnlyOptimal:
+                    // Image is read by a shader
+                    // Make sure any shader reads from the image have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.ShaderRead;
+                    break;
+            }
+
+            // Target layouts (new)
+            // Destination access mask controls the dependency for the new image layout
+            switch (newImageLayout) {
+                case VkImageLayout.TransferDstOptimal:
+                    // Image will be used as a transfer destination
+                    // Make sure any writes to the image have been finished
+                    imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferWrite;
+                    break;
+
+                case VkImageLayout.TransferSrcOptimal:
+                    // Image will be used as a transfer source
+                    // Make sure any reads from and writes to the image have been finished
+                    imageMemoryBarrier.srcAccessMask = imageMemoryBarrier.srcAccessMask | VkAccessFlags.TransferRead;
+                    imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferRead;
+                    break;
+
+                case VkImageLayout.ColorAttachmentOptimal:
+                    // Image will be used as a color attachment
+                    // Make sure any writes to the color buffer have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead;
+                    imageMemoryBarrier.dstAccessMask = VkAccessFlags.ColorAttachmentWrite;
+                    break;
+
+                case VkImageLayout.DepthStencilAttachmentOptimal:
+                    // Image layout will be used as a depth/stencil attachment
+                    // Make sure any writes to depth/stencil buffer have been finished
+                    imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VkAccessFlags.DepthStencilAttachmentWrite;
+                    break;
+
+                case VkImageLayout.ShaderReadOnlyOptimal:
+                    // Image will be read in a shader (sampler, input attachment)
+                    // Make sure any writes to the image have been finished
+                    if (imageMemoryBarrier.srcAccessMask == 0) {
+                        imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite | VkAccessFlags.TransferWrite;
+                    }
+                    imageMemoryBarrier.dstAccessMask = VkAccessFlags.ShaderRead;
+                    break;
+            }
+
+            // Put barrier inside setup command buffer
+            Vk.vkCmdPipelineBarrier (
+                cmdbuffer,
+                srcStageMask,
+                dstStageMask,
+                0,
+                0, IntPtr.Zero,
+                0, IntPtr.Zero,
+                1, ref imageMemoryBarrier);
+        }
+               /// <summary>
+               /// Find usage flags and aspect flag from image layout
+               /// </summary>
+               public static void QueryLayoutRequirements (VkImageLayout layout, ref VkImageUsageFlags usage, ref VkImageAspectFlags aspectFlags) {
+                       switch (layout) {
+                               case VkImageLayout.ColorAttachmentOptimal:
+                               case VkImageLayout.PresentSrcKHR:
+                               case VkImageLayout.SharedPresentKHR:
+                                       aspectFlags |= VkImageAspectFlags.Color;
+                                       if (usage.HasFlag (VkImageUsageFlags.Sampled))
+                                               usage |= VkImageUsageFlags.InputAttachment;
+                                       usage |= VkImageUsageFlags.ColorAttachment;
+                                       break;
+                               case VkImageLayout.DepthStencilAttachmentOptimal:
+                                       aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil;
+                                       usage |= VkImageUsageFlags.DepthStencilAttachment;
+                                       break;
+                               case VkImageLayout.DepthStencilReadOnlyOptimal:
+                                       aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil;
+                                       if (usage.HasFlag (VkImageUsageFlags.ColorAttachment))
+                                               usage |= VkImageUsageFlags.InputAttachment;
+                                       else
+                                               usage |= VkImageUsageFlags.Sampled;
+                                       break;
+                               case VkImageLayout.ShaderReadOnlyOptimal:
+                                       aspectFlags |= VkImageAspectFlags.Color;
+                                       usage |= VkImageUsageFlags.Sampled;
+                                       break;
+                               case VkImageLayout.TransferSrcOptimal:
+                                       usage |= VkImageUsageFlags.TransferSrc;
+                                       break;
+                               case VkImageLayout.TransferDstOptimal:
+                                       usage |= VkImageUsageFlags.TransferDst;
+                                       break;
+                               case VkImageLayout.DepthReadOnlyStencilAttachmentOptimalKHR:
+                               case VkImageLayout.DepthAttachmentStencilReadOnlyOptimalKHR:
+                                       aspectFlags |= VkImageAspectFlags.Depth | VkImageAspectFlags.Stencil;
+                                       usage |= VkImageUsageFlags.Sampled | VkImageUsageFlags.DepthStencilAttachment;
+                                       break;
+                       }
+               }
+    }
+}
diff --git a/vke/src/Version.cs b/vke/src/Version.cs
new file mode 100644 (file)
index 0000000..ed71d20
--- /dev/null
@@ -0,0 +1,23 @@
+namespace Vulkan 
+{
+    public struct Version
+    {
+        private readonly uint value;
+
+        public Version(uint major, uint minor, uint patch)
+        {
+            value = major << 22 | minor << 12 | patch;
+        }
+
+        public uint Major => value >> 22;
+
+        public uint Minor => (value >> 12) & 0x3ff;
+
+        public uint Patch => (value >> 22) & 0xfff;
+
+        public static implicit operator uint(Version version)
+        {
+            return version.value;
+        }
+    }
+}
\ No newline at end of file
diff --git a/vke/src/VertexAttribute.cs b/vke/src/VertexAttribute.cs
new file mode 100644 (file)
index 0000000..75bf5df
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+
+namespace VK {
+       public enum VertexAttributeType {
+               Position,
+               Normal,
+               UVs,
+               Tangent,
+       }
+       [Flags]
+       public enum AttachmentType : UInt32 {
+               None,
+               Color = 0x1,
+               Normal = 0x2,
+               AmbientOcclusion = 0x4,
+               Metal = 0x8,
+               Roughness = 0x10,
+               PhysicalProps = 0x20,
+               Emissive = 0x40,
+       };
+
+       public class VertexAttributeAttribute : Attribute {
+               public VkFormat Format;
+               public VertexAttributeType Type;
+
+               public VertexAttributeAttribute (VertexAttributeType type, VkFormat format)  {
+                       Format = format;
+                       Type = type;
+               }
+       }
+       public class AttachmentAttribute : Attribute {
+               public AttachmentType AttachmentType;
+               public Type DataType;
+               public AttachmentAttribute (AttachmentType attachmentType, Type dataType) {
+                       AttachmentType = attachmentType;
+                       DataType = dataType;
+               }
+       }
+}
diff --git a/vke/src/VkFormatSize.cs b/vke/src/VkFormatSize.cs
new file mode 100644 (file)
index 0000000..75e815d
--- /dev/null
@@ -0,0 +1,548 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+
+namespace VK {
+       [Flags]
+       public enum VkFormatSizeFlag {
+               SizePacked                              = 0x00000001,
+               SizeCompressed                  = 0x00000002,
+               SizePalettized                  = 0x00000004,
+               SizeDepth                               = 0x00000008,
+               SizeStencil                             = 0x00000010,
+       };
+       
+       public struct VkFormatSize {
+               public VkFormatSizeFlag         flags;
+               public uint             paletteSizeInBits;
+               public uint             blockSizeInBits;
+               public uint             blockWidth;                     // in texels
+               public uint             blockHeight;            // in texels
+               public uint             blockDepth;                     // in texels
+       };
+
+
+    public static partial class Utils {        
+               public static void vkGetFormatSize(VkFormat format, out VkFormatSize pFormatSize )
+               {
+                   switch ( format )
+                   {
+                       case VkFormat.R4g4UnormPack8:
+                           pFormatSize.flags = VkFormatSizeFlag.SizePacked;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 1 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R4g4b4a4UnormPack16:
+                       case VkFormat.B4g4r4a4UnormPack16:
+                       case VkFormat.R5g6b5UnormPack16:
+                       case VkFormat.B5g6r5UnormPack16:
+                       case VkFormat.R5g5b5a1UnormPack16:
+                       case VkFormat.B5g5r5a1UnormPack16:
+                       case VkFormat.A1r5g5b5UnormPack16:
+                           pFormatSize.flags = VkFormatSizeFlag.SizePacked;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 2 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R8Unorm:
+                       case VkFormat.R8Snorm:
+                       case VkFormat.R8Uscaled:
+                       case VkFormat.R8Sscaled:
+                       case VkFormat.R8Uint:
+                       case VkFormat.R8Sint:
+                       case VkFormat.R8Srgb:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 1 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R8g8Unorm:
+                       case VkFormat.R8g8Snorm:
+                       case VkFormat.R8g8Uscaled:
+                       case VkFormat.R8g8Sscaled:
+                       case VkFormat.R8g8Uint:
+                       case VkFormat.R8g8Sint:
+                       case VkFormat.R8g8Srgb:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 2 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R8g8b8Unorm:
+                       case VkFormat.R8g8b8Snorm:
+                       case VkFormat.R8g8b8Uscaled:
+                       case VkFormat.R8g8b8Sscaled:
+                       case VkFormat.R8g8b8Uint:
+                       case VkFormat.R8g8b8Sint:
+                       case VkFormat.R8g8b8Srgb:
+                       case VkFormat.B8g8r8Unorm:
+                       case VkFormat.B8g8r8Snorm:
+                       case VkFormat.B8g8r8Uscaled:
+                       case VkFormat.B8g8r8Sscaled:
+                       case VkFormat.B8g8r8Uint:
+                       case VkFormat.B8g8r8Sint:
+                       case VkFormat.B8g8r8Srgb:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 3 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R8g8b8a8Unorm:
+                       case VkFormat.R8g8b8a8Snorm:
+                       case VkFormat.R8g8b8a8Uscaled:
+                       case VkFormat.R8g8b8a8Sscaled:
+                       case VkFormat.R8g8b8a8Uint:
+                       case VkFormat.R8g8b8a8Sint:
+                       case VkFormat.R8g8b8a8Srgb:
+                       case VkFormat.B8g8r8a8Unorm:
+                       case VkFormat.B8g8r8a8Snorm:
+                       case VkFormat.B8g8r8a8Uscaled:
+                       case VkFormat.B8g8r8a8Sscaled:
+                       case VkFormat.B8g8r8a8Uint:
+                       case VkFormat.B8g8r8a8Sint:
+                       case VkFormat.B8g8r8a8Srgb:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.A8b8g8r8UnormPack32:
+                       case VkFormat.A8b8g8r8SnormPack32:
+                       case VkFormat.A8b8g8r8UscaledPack32:
+                       case VkFormat.A8b8g8r8SscaledPack32:
+                       case VkFormat.A8b8g8r8UintPack32:
+                       case VkFormat.A8b8g8r8SintPack32:
+                       case VkFormat.A8b8g8r8SrgbPack32:
+                           pFormatSize.flags = VkFormatSizeFlag.SizePacked;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.A2r10g10b10UnormPack32:
+                       case VkFormat.A2r10g10b10SnormPack32:
+                       case VkFormat.A2r10g10b10UscaledPack32:
+                       case VkFormat.A2r10g10b10SscaledPack32:
+                       case VkFormat.A2r10g10b10UintPack32:
+                       case VkFormat.A2r10g10b10SintPack32:
+                       case VkFormat.A2b10g10r10UnormPack32:
+                       case VkFormat.A2b10g10r10SnormPack32:
+                       case VkFormat.A2b10g10r10UscaledPack32:
+                       case VkFormat.A2b10g10r10SscaledPack32:
+                       case VkFormat.A2b10g10r10UintPack32:
+                       case VkFormat.A2b10g10r10SintPack32:
+                           pFormatSize.flags = VkFormatSizeFlag.SizePacked;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R16Unorm:
+                       case VkFormat.R16Snorm:
+                       case VkFormat.R16Uscaled:
+                       case VkFormat.R16Sscaled:
+                       case VkFormat.R16Uint:
+                       case VkFormat.R16Sint:
+                       case VkFormat.R16Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 2 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R16g16Unorm:
+                       case VkFormat.R16g16Snorm:
+                       case VkFormat.R16g16Uscaled:
+                       case VkFormat.R16g16Sscaled:
+                       case VkFormat.R16g16Uint:
+                       case VkFormat.R16g16Sint:
+                       case VkFormat.R16g16Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R16g16b16Unorm:
+                       case VkFormat.R16g16b16Snorm:
+                       case VkFormat.R16g16b16Uscaled:
+                       case VkFormat.R16g16b16Sscaled:
+                       case VkFormat.R16g16b16Uint:
+                       case VkFormat.R16g16b16Sint:
+                       case VkFormat.R16g16b16Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 6 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R16g16b16a16Unorm:
+                       case VkFormat.R16g16b16a16Snorm:
+                       case VkFormat.R16g16b16a16Uscaled:
+                       case VkFormat.R16g16b16a16Sscaled:
+                       case VkFormat.R16g16b16a16Uint:
+                       case VkFormat.R16g16b16a16Sint:
+                       case VkFormat.R16g16b16a16Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 8 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R32Uint:
+                       case VkFormat.R32Sint:
+                       case VkFormat.R32Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R32g32Uint:
+                       case VkFormat.R32g32Sint:
+                       case VkFormat.R32g32Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 8 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R32g32b32Uint:
+                       case VkFormat.R32g32b32Sint:
+                       case VkFormat.R32g32b32Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 12 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R32g32b32a32Uint:
+                       case VkFormat.R32g32b32a32Sint:
+                       case VkFormat.R32g32b32a32Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R64Uint:
+                       case VkFormat.R64Sint:
+                       case VkFormat.R64Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 8 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R64g64Uint:
+                       case VkFormat.R64g64Sint:
+                       case VkFormat.R64g64Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R64g64b64Uint:
+                       case VkFormat.R64g64b64Sint:
+                       case VkFormat.R64g64b64Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 24 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.R64g64b64a64Uint:
+                       case VkFormat.R64g64b64a64Sint:
+                       case VkFormat.R64g64b64a64Sfloat:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 32 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.B10g11r11UfloatPack32:
+                       case VkFormat.E5b9g9r9UfloatPack32:
+                           pFormatSize.flags = VkFormatSizeFlag.SizePacked;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.D16Unorm:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeDepth;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 2 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.X8D24UnormPack32:
+                           pFormatSize.flags = VkFormatSizeFlag.SizePacked | VkFormatSizeFlag.SizeDepth;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.D32Sfloat:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeDepth;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.S8Uint:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeStencil;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 1 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.D16UnormS8Uint:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeDepth | VkFormatSizeFlag.SizeStencil;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 3 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.D24UnormS8Uint:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeDepth | VkFormatSizeFlag.SizeStencil;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 4 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.D32SfloatS8Uint:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeDepth | VkFormatSizeFlag.SizeStencil;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 8 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Bc1RgbUnormBlock:
+                       case VkFormat.Bc1RgbSrgbBlock:
+                       case VkFormat.Bc1RgbaUnormBlock:
+                       case VkFormat.Bc1RgbaSrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 8 * 8;
+                           pFormatSize.blockWidth = 4;
+                           pFormatSize.blockHeight = 4;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Bc2UnormBlock:
+                       case VkFormat.Bc2SrgbBlock:
+                       case VkFormat.Bc3UnormBlock:
+                       case VkFormat.Bc3SrgbBlock:
+                       case VkFormat.Bc4UnormBlock:
+                       case VkFormat.Bc4SnormBlock:
+                       case VkFormat.Bc5UnormBlock:
+                       case VkFormat.Bc5SnormBlock:
+                       case VkFormat.Bc6hUfloatBlock:
+                       case VkFormat.Bc6hSfloatBlock:
+                       case VkFormat.Bc7UnormBlock:
+                       case VkFormat.Bc7SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 4;
+                           pFormatSize.blockHeight = 4;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Etc2R8g8b8UnormBlock:
+                       case VkFormat.Etc2R8g8b8SrgbBlock:
+                       case VkFormat.Etc2R8g8b8a1UnormBlock:
+                       case VkFormat.Etc2R8g8b8a1SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 8 * 8;
+                           pFormatSize.blockWidth = 4;
+                           pFormatSize.blockHeight = 4;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Etc2R8g8b8a8UnormBlock:
+                       case VkFormat.Etc2R8g8b8a8SrgbBlock:
+                       case VkFormat.EacR11UnormBlock:
+                       case VkFormat.EacR11SnormBlock:
+                       case VkFormat.EacR11g11UnormBlock:
+                       case VkFormat.EacR11g11SnormBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 4;
+                           pFormatSize.blockHeight = 4;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc4x4UnormBlock:
+                       case VkFormat.Astc4x4SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 4;
+                           pFormatSize.blockHeight = 4;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc5x4UnormBlock:
+                       case VkFormat.Astc5x4SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 5;
+                           pFormatSize.blockHeight = 4;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc5x5UnormBlock:
+                       case VkFormat.Astc5x5SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 5;
+                           pFormatSize.blockHeight = 5;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc6x5UnormBlock:
+                       case VkFormat.Astc6x5SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 6;
+                           pFormatSize.blockHeight = 5;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc6x6UnormBlock:
+                       case VkFormat.Astc6x6SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 6;
+                           pFormatSize.blockHeight = 6;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc8x5UnormBlock:
+                       case VkFormat.Astc8x5SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 8;
+                           pFormatSize.blockHeight = 5;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc8x6UnormBlock:
+                       case VkFormat.Astc8x6SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 8;
+                           pFormatSize.blockHeight = 6;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc8x8UnormBlock:
+                       case VkFormat.Astc8x8SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 8;
+                           pFormatSize.blockHeight = 8;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc10x5UnormBlock:
+                       case VkFormat.Astc10x5SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 10;
+                           pFormatSize.blockHeight = 5;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc10x6UnormBlock:
+                       case VkFormat.Astc10x6SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 10;
+                           pFormatSize.blockHeight = 6;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc10x8UnormBlock:
+                       case VkFormat.Astc10x8SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 10;
+                           pFormatSize.blockHeight = 8;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc10x10UnormBlock:
+                       case VkFormat.Astc10x10SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 10;
+                           pFormatSize.blockHeight = 10;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc12x10UnormBlock:
+                       case VkFormat.Astc12x10SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 12;
+                           pFormatSize.blockHeight = 10;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       case VkFormat.Astc12x12UnormBlock:
+                       case VkFormat.Astc12x12SrgbBlock:
+                           pFormatSize.flags = VkFormatSizeFlag.SizeCompressed;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 16 * 8;
+                           pFormatSize.blockWidth = 12;
+                           pFormatSize.blockHeight = 12;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                       default:
+                           pFormatSize.flags = 0;
+                           pFormatSize.paletteSizeInBits = 0;
+                           pFormatSize.blockSizeInBits = 0 * 8;
+                           pFormatSize.blockWidth = 1;
+                           pFormatSize.blockHeight = 1;
+                           pFormatSize.blockDepth = 1;
+                           break;
+                   }
+               }
+    }
+}
diff --git a/vke/src/VkWindow.cs b/vke/src/VkWindow.cs
new file mode 100644 (file)
index 0000000..6f5d9e4
--- /dev/null
@@ -0,0 +1,348 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Diagnostics;
+using System.Linq;
+using Glfw;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Base class to build vulkan application.
+       /// Provide default swapchain with its command pool and buffers per image and the main present queue
+       /// </summary>
+       public abstract class VkWindow : IDisposable {
+               static VkWindow currentWindow;
+
+               IntPtr hWin;
+
+               protected VkSurfaceKHR hSurf;
+               protected Instance instance;
+               protected PhysicalDevice phy;
+               protected Device dev;
+               protected PresentQueue presentQueue;
+               protected SwapChain swapChain;
+               protected CommandPool cmdPool;
+               protected CommandBuffer[] cmds;
+               protected VkSemaphore[] drawComplete;
+
+               DebugReport dbgRepport;
+
+               protected uint fps;
+               protected bool updateViewRequested = true;
+               protected double lastMouseX, lastMouseY;
+               protected bool[] MouseButton => buttons;
+
+               /// <summary>
+               /// default camera
+               /// </summary>
+               protected Camera camera = new Camera (Utils.DegreesToRadians (45f), 1f);
+
+               uint width, height;
+               bool[] buttons = new bool[10];
+               public Modifier KeyModifiers = 0;
+
+               uint frameCount;
+               Stopwatch frameChrono;
+
+               /// <summary>
+               /// Override this property to change the list of enabled extensions
+               /// </summary>
+               public virtual string[] EnabledDeviceExtensions => new string[] { Ext.D.VK_KHR_swapchain };
+
+               /// <summary>
+               /// Frequency in millisecond of the call to the Update method
+               /// </summary>
+               public long UpdateFrequency = 200;
+
+               public uint Width => width;
+               public uint Height => height;
+
+               public VkWindow (string name = "VkWindow", uint _width = 800, uint _height = 600, bool vSync = false) {
+                       currentWindow = this;
+
+                       width = _width;
+                       height = _height;
+
+                       Glfw3.Init ();
+
+                       Glfw3.WindowHint (WindowAttribute.ClientApi, 0);
+                       Glfw3.WindowHint (WindowAttribute.Resizable, 1);
+
+                       hWin = Glfw3.CreateWindow ((int)width, (int)height, name, MonitorHandle.Zero, IntPtr.Zero);
+
+                       if (hWin == IntPtr.Zero)
+                               throw new Exception ("[GLFW3] Unable to create vulkan Window");
+
+                       Glfw3.SetKeyCallback (hWin, HandleKeyDelegate);
+                       Glfw3.SetMouseButtonPosCallback (hWin, HandleMouseButtonDelegate);
+                       Glfw3.SetCursorPosCallback (hWin, HandleCursorPosDelegate);
+                       Glfw3.SetWindowSizeCallback (hWin, HandleWindowSizeDelegate);
+                       Glfw3.SetScrollCallback (hWin, HandleScrollDelegate);
+                       Glfw3.SetCharCallback (hWin, HandleCharDelegate);
+
+                       initVulkan (vSync);
+               }
+               IntPtr currentCursor;
+               public void SetCursor (CursorShape cursor) {
+                       if (currentCursor != IntPtr.Zero)
+                               Glfw3.DestroyCursor (currentCursor);
+                       currentCursor = Glfw3.CreateStandardCursor (cursor);
+                       Glfw3.SetCursor (hWin, currentCursor);
+               }
+
+               void initVulkan (bool vSync) {
+                       instance = new Instance ();
+
+                       if (Instance.DEBUG_UTILS) {
+                               //dbgmsg = new CVKL.DebugUtils.Messenger (instance);
+                               dbgRepport = new DebugReport (instance,
+                                       VkDebugReportFlagsEXT.ErrorEXT
+                                       | VkDebugReportFlagsEXT.WarningEXT
+                                       | VkDebugReportFlagsEXT.PerformanceWarningEXT
+                               );
+                       }
+
+                       hSurf = instance.CreateSurface (hWin);
+
+                       phy = instance.GetAvailablePhysicalDevice ().Where (p => p.HasSwapChainSupport).FirstOrDefault ();
+
+                       VkPhysicalDeviceFeatures enabledFeatures = default (VkPhysicalDeviceFeatures);
+                       configureEnabledFeatures (phy.Features, ref enabledFeatures);
+
+                       //First create the c# device class
+                       dev = new Device (phy);
+                       //create queue class
+                       createQueues ();
+
+                       //activate the device to have effective queues created accordingly to what's available
+                       dev.Activate (enabledFeatures, EnabledDeviceExtensions);
+
+                       swapChain = new SwapChain (presentQueue as PresentQueue, width, height, SwapChain.PREFERED_FORMAT,
+                               vSync ? VkPresentModeKHR.FifoKHR : VkPresentModeKHR.MailboxKHR);
+                       swapChain.Create ();
+
+                       cmdPool = new CommandPool (dev, presentQueue.qFamIndex);
+
+                       cmds = new CommandBuffer[swapChain.ImageCount];
+                       drawComplete = new VkSemaphore[swapChain.ImageCount];
+
+                       for (int i = 0; i < swapChain.ImageCount; i++)
+                               drawComplete[i] = dev.CreateSemaphore ();
+
+                       cmdPool.SetName ("main CmdPool");
+                       for (int i = 0; i < swapChain.ImageCount; i++)
+                               drawComplete[i].SetDebugMarkerName (dev, "Semaphore DrawComplete" + i);
+               }
+               /// <summary>
+               /// override this method to modify enabled features before device creation
+               /// </summary>
+               /// <param name="enabled_features">Features.</param>
+               protected virtual void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) {
+               }
+               /// <summary>
+               /// override this method to create additional queue. Dedicated queue of the requested type will be selected first, created queues may excess
+               /// available physical queues.
+               /// </summary>
+               protected virtual void createQueues () {
+                       presentQueue = new PresentQueue (dev, VkQueueFlags.Graphics, hSurf);
+               }
+
+               /// <summary>
+               /// Main render method called each frame. get next swapchain image, process resize if needed, submit and present to the presentQueue.
+               /// Wait QueueIdle after presenting.
+               /// </summary>
+               protected virtual void render () {
+                       int idx = swapChain.GetNextImage ();
+                       if (idx < 0) {
+                               OnResize ();
+                               return;
+                       }
+
+                       if (cmds[idx] == null)
+                               return;
+
+                       presentQueue.Submit (cmds[idx], swapChain.presentComplete, drawComplete[idx]);
+                       presentQueue.Present (swapChain, drawComplete[idx]);
+
+                       presentQueue.WaitIdle ();
+               }
+
+               protected virtual void onScroll (double xOffset, double yOffset) { }
+               protected virtual void onMouseMove (double xPos, double yPos) {
+                       double diffX = lastMouseX - xPos;
+                       double diffY = lastMouseY - yPos;
+                       if (MouseButton[0]) {
+                               camera.Rotate ((float)-diffX, (float)-diffY);
+                       } else if (MouseButton[1]) {
+                               camera.Move (0, 0, (float)diffY);
+                       }
+
+                       updateViewRequested = true;
+               }
+               protected virtual void onMouseButtonDown (Glfw.MouseButton button) { }
+               protected virtual void onMouseButtonUp (Glfw.MouseButton button) { }
+               protected virtual void onKeyDown (Key key, int scanCode, Modifier modifiers) {
+                       switch (key) {
+                               case Key.F4:
+                                       if (modifiers == Modifier.Alt)
+                                               Glfw3.SetWindowShouldClose (currentWindow.hWin, 1);
+                                       break;
+                               case Key.Escape:
+                                       Glfw3.SetWindowShouldClose (currentWindow.hWin, 1);
+                                       break;
+                               case Key.Up:
+                                       camera.Move (0, 0, 1);
+                                       break;
+                               case Key.Down:
+                                       camera.Move (0, 0, -1);
+                                       break;
+                               case Key.Left:
+                                       camera.Move (1, 0, 0);
+                                       break;
+                               case Key.Right:
+                                       camera.Move (-1, 0, 0);
+                                       break;
+                               case Key.PageUp:
+                                       camera.Move (0, 1, 0);
+                                       break;
+                               case Key.PageDown:
+                                       camera.Move (0, -1, 0);
+                                       break;
+                               case Key.F3:
+                                       if (camera.Type == Camera.CamType.FirstPerson)
+                                               camera.Type = Camera.CamType.LookAt;
+                                       else
+                                               camera.Type = Camera.CamType.FirstPerson;
+                                       break;
+                       }
+                       updateViewRequested = true;
+               }
+               protected virtual void onKeyUp (Key key, int scanCode, Modifier modifiers) { }
+               protected virtual void onChar (CodePoint cp) { }
+
+               #region events delegates
+               static void HandleWindowSizeDelegate (IntPtr window, int width, int height) { }
+               static void HandleCursorPosDelegate (IntPtr window, double xPosition, double yPosition) {
+                       currentWindow.onMouseMove (xPosition, yPosition);
+                       currentWindow.lastMouseX = xPosition;
+                       currentWindow.lastMouseY = yPosition;
+               }
+               static void HandleMouseButtonDelegate (IntPtr window, Glfw.MouseButton button, InputAction action, Modifier mods) {
+                       if (action == InputAction.Press) {
+                               currentWindow.buttons[(int)button] = true;
+                               currentWindow.onMouseButtonDown (button);
+                       } else {
+                               currentWindow.buttons[(int)button] = false;
+                               currentWindow.onMouseButtonUp (button);
+                       }
+               }
+               static void HandleScrollDelegate (IntPtr window, double xOffset, double yOffset) {
+                       currentWindow.onScroll (xOffset, yOffset);
+               }
+               static void HandleKeyDelegate (IntPtr window, Key key, int scanCode, InputAction action, Modifier modifiers) {
+                       currentWindow.KeyModifiers = modifiers;
+                       if (action == InputAction.Press || action == InputAction.Repeat) {
+                               currentWindow.onKeyDown (key, scanCode, modifiers);
+                       } else {
+                               currentWindow.onKeyUp (key, scanCode, modifiers);
+                       }
+               }
+               static void HandleCharDelegate (IntPtr window, CodePoint codepoint) {
+                       currentWindow.onChar (codepoint);
+               }
+               #endregion
+
+               /// <summary>
+               /// main window loop, exits on GLFW3 exit event
+               /// </summary>
+               public virtual void Run () {
+                       OnResize ();
+                       UpdateView ();
+
+                       frameChrono = Stopwatch.StartNew ();
+                       long totTime = 0;
+
+                       while (!Glfw3.WindowShouldClose (hWin)) {
+                               render ();
+
+                               if (updateViewRequested)
+                                       UpdateView ();
+
+                               frameCount++;
+
+                               if (frameChrono.ElapsedMilliseconds > UpdateFrequency) {
+                                       Update ();
+
+                                       frameChrono.Stop ();
+                                       totTime += frameChrono.ElapsedMilliseconds;
+                                       fps = (uint)((double)frameCount / (double)totTime * 1000.0);
+                                       Glfw3.SetWindowTitle (hWin, "FPS: " + fps.ToString ());                    
+                                       if (totTime > 2000) {
+                                               frameCount = 0;
+                                               totTime = 0;
+                                       }
+                                       frameChrono.Restart ();
+                               }
+                               Glfw3.PollEvents ();
+                       }
+               }
+               public virtual void UpdateView () { }
+               /// <summary>
+               /// custom update method called at UpdateFrequency
+               /// </summary>
+               public virtual void Update () { }
+
+               /// <summary>
+               /// called when swapchain has been resized, override this method to resize your framebuffers coupled to the swapchain
+               /// </summary>
+               protected virtual void OnResize () { }
+
+
+               #region IDisposable Support
+               protected bool isDisposed;
+
+               protected virtual void Dispose (bool disposing) {
+                       if (!isDisposed) {
+                               dev.WaitIdle ();
+
+                               for (int i = 0; i < swapChain.ImageCount; i++) {
+                                       dev.DestroySemaphore (drawComplete[i]);
+                                       cmds[i].Free ();
+                               }
+
+                               swapChain.Dispose ();
+
+                               vkDestroySurfaceKHR (instance.Handle, hSurf, IntPtr.Zero);
+
+                               if (disposing) {
+                                       cmdPool.Dispose ();
+                                       dev.Dispose ();
+                                       dbgRepport?.Dispose ();
+                                       instance.Dispose ();
+                               } else
+                                       Debug.WriteLine ("a VkWindow has not been correctly disposed");
+
+                               if (currentCursor != IntPtr.Zero)
+                                       Glfw3.DestroyCursor (currentCursor);
+
+                               Glfw3.DestroyWindow (hWin);
+                               Glfw3.Terminate ();
+
+
+                               isDisposed = true;
+                       }
+               }
+
+               ~VkWindow () {
+                       Dispose (false);
+               }
+               public void Dispose () {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/base/Activable.cs b/vke/src/base/Activable.cs
new file mode 100644 (file)
index 0000000..b77443c
--- /dev/null
@@ -0,0 +1,134 @@
+//
+// Activable.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Tristate status of activables, reflecting vulkan openrations
+       /// </summary>
+       public enum ActivableState {
+               /// <summary>
+               /// Class has been instanced, but no vulkan handle was created.
+               /// </summary>
+               Init,
+               /// <summary>
+               /// On the first activation, vulkan handle is created and ref count is one. Further activations will increment the reference count by one.
+               /// </summary>
+               Activated,
+               /// <summary>
+               /// Reference count is zero, handles have been destroyed. Such object may be reactivated.
+               /// </summary>
+               Disposed
+       };
+       /// <summary>
+       /// Base class for most of the vulkan device's objects following the IDispose pattern. Each time an activable is used, it's reference count is incremented, and
+       /// each time it is disposed, the count is decremented. When the count reach zero, the handle is destroyed and the finalizizer is unregistered. Once disposed, the
+       /// objecte may still be reactivated.
+       /// </summary>
+       /// <remarks>
+       /// Some of the activation trigger the first activation on creation, those activables have to be explicitly dispose at the end of the application.
+       /// The activables that trigger activation only on usage does not require an additional dispose at the end.
+       /// </remarks>
+       public abstract class Activable : IDisposable {
+               //count number of activation, only the first one will create a handle 
+               protected uint references;
+               //keep track of the current state of activation.
+               protected ActivableState state;
+               //With the debug marker extension, setting name to vulkan's object ease the debugging.
+               protected string name;
+               /// <summary>
+               /// This property has to be implemented in every vulkan object. It should return the correct debug marker info.
+               /// </summary>
+               /// <value>The debug marker info.</value>
+               protected abstract VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo { get; }
+               /// <summary>
+               /// Vulkan logical device this activable is bound to.
+               /// </summary>
+               public Device Dev { get; private set; }
+
+               #region CTOR
+               protected Activable (Device dev) {
+                       this.Dev = dev;
+                       this.name = GetType ().Name;
+               }
+               protected Activable (Device dev, string name) {
+                       this.Dev = dev;
+                       this.name = name;
+               }
+               #endregion
+
+               /// <summary>
+               /// if debug marker extension is activated, this will set the name for debuggers
+               /// </summary>
+               public void SetName (string name) {
+                       this.name = name;
+
+                       if (!Dev.debugMarkersEnabled)
+                               return;
+
+                       VkDebugMarkerObjectNameInfoEXT dmo = DebugMarkerInfo;
+                       dmo.pObjectName = name.Pin();
+                       Utils.CheckResult (vkDebugMarkerSetObjectNameEXT (Dev.VkDev, ref dmo));
+                       name.Unpin ();                  
+               }
+               /// <summary>
+               /// Activation of the object, the reference count is incremented.
+               /// </summary>
+               public virtual void Activate () {
+                       references++;
+                       if (state == ActivableState.Activated)
+                               return;
+                       if (state == ActivableState.Disposed) 
+                               GC.ReRegisterForFinalize (this);
+                       state = ActivableState.Activated;
+                       SetName (name);
+               }
+
+               public override string ToString () {
+                       return name;
+               }
+
+               #region IDisposable Support
+               protected virtual void Dispose (bool disposing) {
+                       state = ActivableState.Disposed;
+               }
+
+               ~Activable() {
+                       Dispose(false);
+               }
+               public void Dispose () {
+                       if (references>0)
+                               references--;
+                       if (references>0)
+                               return;
+                       Dispose (true);
+                       GC.SuppressFinalize(this);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/base/Buffer.cs b/vke/src/base/Buffer.cs
new file mode 100644 (file)
index 0000000..254d547
--- /dev/null
@@ -0,0 +1,111 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+
+    /// <summary>
+    /// Base class for HostBuffer and GPUBuffer
+    /// </summary>
+    public class Buffer : Resource {
+        internal VkBuffer handle;
+               protected VkBufferCreateInfo createInfo = VkBufferCreateInfo.New ();
+
+               public VkDescriptorBufferInfo Descriptor;
+               public VkBuffer Handle => handle;
+               public VkBufferCreateInfo Infos => createInfo;
+        
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.BufferEXT, handle.Handle);
+
+               #region CTORS
+               public Buffer (Device device, VkBufferUsageFlags usage, VkMemoryPropertyFlags _memoryPropertyFlags, UInt64 size)
+        : base (device, _memoryPropertyFlags) {
+
+            createInfo.size = size;
+            createInfo.usage = usage;
+            createInfo.sharingMode = VkSharingMode.Exclusive;
+
+            Activate ();//DONT OVERRIDE Activate in derived classes!!!!
+        }
+               #endregion
+
+        public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               Utils.CheckResult (vkCreateBuffer (Dev.VkDev, ref createInfo, IntPtr.Zero, out handle));
+#if MEMORY_POOLS
+                               Dev.resourceManager.Add (this);
+#else
+                               updateMemoryRequirements ();
+                               allocateMemory ();
+                               bindMemory ();
+#endif
+                               SetupDescriptor ();
+                       }
+                       base.Activate ();
+        }
+
+               internal override void updateMemoryRequirements () {
+                       vkGetBufferMemoryRequirements (Dev.VkDev, handle, out memReqs);
+               }
+
+               internal override void bindMemory () {
+#if MEMORY_POOLS
+                       Utils.CheckResult (vkBindBufferMemory (Dev.VkDev, handle, memoryPool.vkMemory, poolOffset));
+#else
+                       Utils.CheckResult (vkBindBufferMemory (Dev.VkDev, handle, vkMemory, 0));
+#endif
+               }
+
+               public void SetupDescriptor (ulong size = WholeSize, ulong offset = 0) {
+            Descriptor.buffer = handle;
+            Descriptor.range = size;
+            Descriptor.offset = offset;
+        }
+
+        public void CopyTo (CommandBuffer cmd, Image img, VkImageLayout finalLayout = VkImageLayout.ShaderReadOnlyOptimal) {
+            img.SetLayout (cmd, VkImageAspectFlags.Color,
+                VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal,
+                VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.Transfer);
+
+            VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy {
+                imageExtent = img.CreateInfo.extent,
+                imageSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color)
+            };
+
+            vkCmdCopyBufferToImage (cmd.Handle, handle, img.handle, VkImageLayout.TransferDstOptimal, 1, ref bufferCopyRegion);
+
+            img.SetLayout (cmd, VkImageAspectFlags.Color,
+                VkImageLayout.TransferDstOptimal, finalLayout,
+                VkPipelineStageFlags.Transfer, VkPipelineStageFlags.Transfer);
+        }
+        public void CopyTo (CommandBuffer cmd, Buffer buff, ulong size = 0, ulong srcOffset = 0, ulong dstOffset = 0) {
+            VkBufferCopy bufferCopy = new VkBufferCopy {
+                size = (size == 0) ? AllocatedDeviceMemorySize : size,
+                srcOffset = srcOffset,
+                dstOffset = dstOffset
+            };
+            vkCmdCopyBuffer (cmd.Handle, handle, buff.handle, 1, ref bufferCopy);
+        }
+               public void Fill (CommandBuffer cmd, uint data, ulong size = 0, ulong offset = 0) {
+                       vkCmdFillBuffer (cmd.Handle, handle, offset, (size == 0) ? AllocatedDeviceMemorySize : size, data);
+               }
+
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+
+#region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (state == ActivableState.Activated) {
+                               base.Dispose (disposing);
+                               vkDestroyBuffer (Dev.VkDev, handle, IntPtr.Zero);
+                       }
+                       state = ActivableState.Disposed;
+        }
+#endregion
+       }
+}
diff --git a/vke/src/base/CommandBuffer.cs b/vke/src/base/CommandBuffer.cs
new file mode 100644 (file)
index 0000000..0c381a7
--- /dev/null
@@ -0,0 +1,188 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Runtime.InteropServices;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Command buffers are objects used to record commands which can be subsequently submitted to a device queue for execution.
+       /// There are two levels of command buffers 
+       /// - primary command buffers, which can execute secondary command buffers, and which are submitted to queues
+       /// - secondary command buffers, which can be executed by primary command buffers, and which are not directly submitted to queues.
+       /// Command buffer are not derived from activable, because their state is retained by the pool which create them.
+       /// </summary>
+       public class CommandBuffer {
+               public enum States { Init, Record, Executable, Pending, Invalid };
+
+        CommandPool pool;
+        VkCommandBuffer handle;
+
+        public VkCommandBuffer Handle => handle;
+               public Device Device => pool?.Dev;//this help
+               //public States State { get; internal set; }
+
+        internal CommandBuffer (VkDevice _dev, CommandPool _pool, VkCommandBuffer _buff)
+        {
+            pool = _pool;
+            handle = _buff;
+
+                       //State = States.Init;
+        }
+
+
+        public void Submit (VkQueue queue, VkSemaphore wait = default(VkSemaphore), VkSemaphore signal = default (VkSemaphore), VkFence fence = default(VkFence)) {
+            VkSubmitInfo submit_info = VkSubmitInfo.New();
+
+                       IntPtr dstStageMask = Marshal.AllocHGlobal (sizeof(uint));
+                       Marshal.WriteInt32 (dstStageMask, (int)VkPipelineStageFlags.ColorAttachmentOutput);
+
+            submit_info.pWaitDstStageMask = dstStageMask;
+            if (signal != VkSemaphore.Null) {
+                submit_info.signalSemaphoreCount = 1;
+                submit_info.pSignalSemaphores = signal.Pin();
+            }
+            if (wait != VkSemaphore.Null) {
+                submit_info.waitSemaphoreCount = 1;
+                submit_info.pWaitSemaphores = wait.Pin();
+            }
+
+            submit_info.commandBufferCount = 1;
+            submit_info.pCommandBuffers = handle.Pin();
+
+            Utils.CheckResult (vkQueueSubmit (queue, 1, ref submit_info, fence));
+
+                       if (signal != VkSemaphore.Null)
+                               signal.Unpin ();
+                       if (wait != VkSemaphore.Null)
+                               wait.Unpin ();
+                       handle.Unpin ();
+
+                       Marshal.FreeHGlobal (dstStageMask);
+        }
+        public void Start (VkCommandBufferUsageFlags usage = 0) {
+            VkCommandBufferBeginInfo cmdBufInfo = new VkCommandBufferBeginInfo (usage);
+            Utils.CheckResult (vkBeginCommandBuffer (handle, ref cmdBufInfo));
+        }
+        public void End () {
+            Utils.CheckResult (vkEndCommandBuffer (handle));
+        }
+        /// <summary>
+        /// Update dynamic viewport state
+        /// </summary>
+        public void SetViewport (float width, float height, float x = 0f, float y = 0f, float minDepth = 0.0f, float maxDepth = 1.0f) {
+            VkViewport viewport = new VkViewport {
+                               x = x,
+                               y = y,
+                height = height,
+                width = width,
+                minDepth = minDepth,
+                maxDepth = maxDepth,
+            };
+            vkCmdSetViewport (handle, 0, 1, ref viewport);
+        }
+        /// <summary>
+        /// Update dynamic scissor state
+        /// </summary>
+        public void SetScissor (uint width, uint height, int offsetX = 0, int offsetY = 0) {
+            VkRect2D scissor = new VkRect2D (offsetX, offsetY, width, height);
+            vkCmdSetScissor (handle, 0, 1, ref scissor);
+        }
+        public void BindPipeline (Pipeline pipeline, VkPipelineBindPoint bindPoint) {
+            vkCmdBindPipeline (handle, bindPoint, pipeline.Handle);
+        }
+               public void Dispatch (uint groupCountX, uint groupCountY = 1, uint groupCountZ = 1) {
+                       vkCmdDispatch (handle, groupCountX, groupCountY, groupCountZ);
+               }
+               public void BindPipeline (Pipeline pl) {
+                       pl.Bind (this);
+               }
+               /// <summary>
+               /// bind pipeline and descriptor set with default pipeline layout
+               /// </summary>
+               /// <param name="pl">pipeline to bind</param>
+               /// <param name="ds">descriptor set</param>
+               /// <param name="firstset">first set to bind</param>
+               public void BindPipeline (Pipeline pl, DescriptorSet ds, uint firstset = 0) {
+                       pl.Bind (this);
+                       pl.BindDescriptorSet (this, ds, firstset);
+               }
+               public void BindDescriptorSet (PipelineLayout pipelineLayout, DescriptorSet descriptorSet, uint firstSet = 0) {
+            vkCmdBindDescriptorSets (handle, VkPipelineBindPoint.Graphics, pipelineLayout.handle, firstSet, 1, ref descriptorSet.handle, 0, IntPtr.Zero);
+        }
+               public void BindDescriptorSet (VkPipelineBindPoint bindPoint, PipelineLayout pipelineLayout, DescriptorSet descriptorSet, uint firstSet = 0) {
+                       vkCmdBindDescriptorSets (handle, bindPoint, pipelineLayout.handle, firstSet, 1, ref descriptorSet.handle, 0, IntPtr.Zero);
+               }
+               public void BindVertexBuffer (Buffer vertices, uint binding = 0, ulong offset = 0) {
+            vkCmdBindVertexBuffers (handle, binding, 1, ref vertices.handle, ref offset);
+        }
+        public void BindIndexBuffer (Buffer indices, VkIndexType indexType = VkIndexType.Uint32, ulong offset = 0) {
+            vkCmdBindIndexBuffer (handle, indices.handle, offset, indexType);
+        }
+        public void DrawIndexed (uint indexCount, uint instanceCount = 1, uint firstIndex = 0, int vertexOffset = 0, uint firstInstance = 0) {
+            vkCmdDrawIndexed (Handle, indexCount, instanceCount, firstIndex, vertexOffset, firstInstance);
+        }
+        public void Draw (uint vertexCount, uint instanceCount = 1, uint firstVertex = 0, uint firstInstance = 0) {
+            vkCmdDraw (Handle, vertexCount, instanceCount, firstVertex, firstInstance);
+        }
+               public void PushConstant (PipelineLayout pipelineLayout, VkShaderStageFlags stageFlags, Object data, uint offset = 0) {
+                       vkCmdPushConstants (handle, pipelineLayout.handle, stageFlags, offset, (uint)Marshal.SizeOf (data), data.Pin ());
+                       data.Unpin ();
+               }
+               public void PushConstant (Pipeline pipeline, object obj, int rangeIndex = 0, uint offset = 0) {
+                       PushConstant (pipeline.Layout, pipeline.Layout.PushConstantRanges[rangeIndex].stageFlags, obj, offset);
+               }
+               public void PushConstant (PipelineLayout pipelineLayout, VkShaderStageFlags stageFlags, uint size, Object data, uint offset = 0) {
+                       vkCmdPushConstants (handle, pipelineLayout.handle, stageFlags, offset, size, data.Pin ());
+                       data.Unpin ();
+               }
+               public void SetMemoryBarrier (VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
+                       VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkDependencyFlags dependencyFlags = VkDependencyFlags.ByRegion) {
+                       VkMemoryBarrier memoryBarrier = VkMemoryBarrier.New ();
+                       memoryBarrier.srcAccessMask = srcAccessMask;
+                       memoryBarrier.dstAccessMask = dstAccessMask;
+                       Vk.vkCmdPipelineBarrier (Handle, srcStageMask, dstStageMask,
+                               dependencyFlags, 1, ref memoryBarrier, 0, IntPtr.Zero, 0, IntPtr.Zero);
+               }
+               public void BeginRegion (string name, float r = 1f, float g = 0.1f, float b=0.1f, float a = 1f) {
+                       if (!Device.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerMarkerInfoEXT info = VkDebugMarkerMarkerInfoEXT.New();
+                       info.pMarkerName = name.Pin ();
+                       unsafe {
+                               info.color[0] = r;
+                               info.color[1] = g;
+                               info.color[2] = b;
+                               info.color[3] = a;
+                       }
+                       vkCmdDebugMarkerBeginEXT (Handle, ref info);
+                       name.Unpin ();
+               }
+               public void InsertDebugMarker (string name, float r = 1f, float g = 0.1f, float b=0.1f, float a = 1f) {
+                       if (!Device.debugMarkersEnabled)
+                               return;
+                       VkDebugMarkerMarkerInfoEXT info = VkDebugMarkerMarkerInfoEXT.New();
+                       info.pMarkerName = name.Pin ();
+                       unsafe {
+                               info.color[0] = r;
+                               info.color[1] = g;
+                               info.color[2] = b;
+                               info.color[3] = a;
+                       }
+                       vkCmdDebugMarkerInsertEXT (Handle, ref info);
+                       name.Unpin ();
+               }
+               public void EndRegion () {
+                       if (Device.debugMarkersEnabled)
+                               vkCmdDebugMarkerEndEXT (Handle);
+               }
+
+               public void Free () {
+            pool.FreeCommandBuffers (this);
+        }
+    }
+}
diff --git a/vke/src/base/CommandPool.cs b/vke/src/base/CommandPool.cs
new file mode 100644 (file)
index 0000000..3317733
--- /dev/null
@@ -0,0 +1,147 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Runtime.InteropServices;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Command pools are opaque objects that command buffer memory is allocated from, and which allow the implementation
+       /// to amortize the cost of resource creation across multiple command buffers.
+       /// </summary>
+       public sealed class CommandPool : Activable {
+        public readonly uint QFamIndex;
+        VkCommandPool handle;
+
+               #region CTORS
+               /// <summary>
+               /// Create and activate a new Command Pool.
+               /// </summary>
+               /// <param name="device">Vulkan Device.</param>
+               /// <param name="qFamIdx">Queue family index.</param>
+               public CommandPool (Device device, uint qFamIdx) : base(device)
+        {            
+            QFamIndex = qFamIdx;
+
+                       Activate ();
+        }
+               /// <summary>
+               /// Initializes a new instance of the [[CommandPool]]"/> class.
+               /// </summary>
+               /// <param name="queue">Device Queue of the queue family to create the pool for.</param>
+               public CommandPool (Queue queue) : this(queue.dev, queue.qFamIndex) {}
+               #endregion
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.CommandPoolEXT, handle.Handle);
+
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {            
+                   VkCommandPoolCreateInfo infos = VkCommandPoolCreateInfo.New();
+               infos.queueFamilyIndex = QFamIndex;
+                   Utils.CheckResult (vkCreateCommandPool (Dev.VkDev, ref infos, IntPtr.Zero, out handle));
+                       }
+                       base.Activate ();
+               }
+               /// <summary>
+               /// Allocates single command buffer.
+               /// When command buffers are first allocated, they are in the initial state.
+               /// </summary>
+               /// <returns>The command buffer in the Init state.</returns>
+               /// <param name="level">Command Buffer Level.</param>
+               public CommandBuffer AllocateCommandBuffer (VkCommandBufferLevel level = VkCommandBufferLevel.Primary) {
+            VkCommandBuffer buff;
+            VkCommandBufferAllocateInfo infos = VkCommandBufferAllocateInfo.New();
+            infos.commandPool = handle;
+            infos.level = level;
+            infos.commandBufferCount = 1;
+
+            Utils.CheckResult (vkAllocateCommandBuffers (Dev.VkDev, ref infos, out buff));
+
+            return new CommandBuffer (Dev.VkDev, this, buff);
+        }
+               /// <summary>
+               /// Allocates multiple command buffer.
+               /// </summary>
+               /// <returns>An array of command buffers alloocated from this pool.</returns>
+               /// <param name="count">Buffer count to create.</param>
+               /// <param name="level">Command Buffer Level.</param>
+               public CommandBuffer[] AllocateCommandBuffer (uint count, VkCommandBufferLevel level = VkCommandBufferLevel.Primary) {
+                       VkCommandBufferAllocateInfo infos = VkCommandBufferAllocateInfo.New ();
+                       infos.commandPool = handle;
+                       infos.level = level;
+                       infos.commandBufferCount = count;
+                       VkCommandBuffer[] buffs = new VkCommandBuffer[count];
+                       Utils.CheckResult (vkAllocateCommandBuffers (Dev.VkDev, ref infos, buffs.Pin()));
+                       buffs.Unpin ();
+                       CommandBuffer[] cmds = new CommandBuffer[count];
+                       for (int i = 0; i < count; i++) 
+                               cmds[i] = new CommandBuffer (Dev.VkDev, this, buffs[i]);
+
+                       return cmds;
+               }
+               /// <summary>
+               /// Resetting a command pool recycles all of the resources from all of the command buffers allocated from the command
+               /// pool back to the command pool. All command buffers that have been allocated from the command pool are put in the initial state. 
+               /// Any primary command buffer allocated from another VkCommandPool that is in the recording or executable state and has a secondary
+               /// command buffer allocated from commandPool recorded into it, becomes invalid.
+               /// </summary>
+               /// <param name="flags">Set `ReleaseResources` flag to recycles all of the resources from the command pool back to the system.</param>
+               public void Reset (VkCommandPoolResetFlags flags = 0) {
+                       Vk.vkResetCommandPool (Dev.VkDev, handle, flags);
+               }
+               /// <summary>
+               /// Allocates a new command buffer and automatically start it.
+               /// </summary>
+               /// <returns>New command buffer in the recording state.</returns>
+               /// <param name="usage">Usage.</param>
+               /// <param name="level">Command buffer level, primary or secondary.</param>
+               public CommandBuffer AllocateAndStart (VkCommandBufferUsageFlags usage = 0, VkCommandBufferLevel level = VkCommandBufferLevel.Primary) {
+                       CommandBuffer cmd = AllocateCommandBuffer (level);
+                       cmd.Start (usage);
+                       return cmd;
+               }
+               /// <summary>
+               /// Any primary command buffer that is in the recording or executable state and has any element of the command buffer list recorded into it, becomes invalid.
+               /// </summary>
+               /// <param name="cmds">Command buffer list to free.</param>
+               public void FreeCommandBuffers (params CommandBuffer[] cmds) {
+            if (cmds.Length == 1) {
+                VkCommandBuffer hnd = cmds[0].Handle;
+                vkFreeCommandBuffers (Dev.VkDev, handle, 1, ref hnd);
+                return;
+            }
+                       int sizeElt = Marshal.SizeOf<IntPtr> ();
+                       IntPtr cmdsPtr = Marshal.AllocHGlobal (cmds.Length * sizeElt);
+                       int count = 0;
+                       for (int i = 0; i < cmds.Length; i++) {
+                               if (cmds[i] == null)
+                                       continue;
+                               Marshal.WriteIntPtr (cmdsPtr + count * sizeElt, cmds[i].Handle.Handle);
+                               count++;
+                       }
+                       if (count > 0)
+                               vkFreeCommandBuffers (Dev.VkDev, handle, (uint)count, cmdsPtr);
+
+                       Marshal.FreeHGlobal (cmdsPtr);
+        }
+
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+
+               #region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (!disposing)
+                               System.Diagnostics.Debug.WriteLine ("VKE CommandPool disposed by finalizer");
+                       if (state == ActivableState.Activated)
+                               vkDestroyCommandPool (Dev.VkDev, handle, IntPtr.Zero);
+                       base.Dispose (disposing);
+               }
+               #endregion
+
+       }
+}
diff --git a/vke/src/base/ComputePipeline.cs b/vke/src/base/ComputePipeline.cs
new file mode 100644 (file)
index 0000000..e57a7d0
--- /dev/null
@@ -0,0 +1,81 @@
+//
+// ComputePipeline.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+    public sealed class ComputePipeline : Pipeline {
+
+               public string SpirVPath;
+    
+               #region CTORS
+               public ComputePipeline (Device dev, PipelineCache cache = null, string name = "compute pipeline") : base (dev, cache, name) { 
+               }
+               /// <summary>
+               /// Create a new Pipeline with supplied PipelineLayout
+               /// </summary>
+               public ComputePipeline (PipelineLayout layout, string spirvPath, PipelineCache cache = null, string name = "pipeline") : base(layout.Dev, cache, name)
+               {
+                       SpirVPath = spirvPath;
+                       this.layout = layout;
+
+                       Activate ();
+               }
+               #endregion
+
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               layout.Activate ();
+                               Cache?.Activate ();
+
+                               using (ShaderInfo shader = new ShaderInfo (VkShaderStageFlags.Compute, SpirVPath)) {
+                                       VkComputePipelineCreateInfo info = VkComputePipelineCreateInfo.New ();
+                                       info.layout = layout.Handle;
+                                       info.stage = shader.GetStageCreateInfo (Dev);
+                                       info.basePipelineHandle = 0;
+                                       info.basePipelineIndex = 0;
+
+                                       Utils.CheckResult (Vk.vkCreateComputePipelines (Dev.VkDev, Cache == null ? VkPipelineCache.Null : Cache.handle, 1, ref info, IntPtr.Zero, out handle));
+
+                                       Dev.DestroyShaderModule (info.stage.module);
+                               }
+                       }
+                       base.Activate ();
+               }
+
+               public override void Bind (CommandBuffer cmd) {
+            vkCmdBindPipeline (cmd.Handle, VkPipelineBindPoint.Compute, handle);
+        }
+               public override void BindDescriptorSet (CommandBuffer cmd, DescriptorSet dset, uint firstSet = 0) {
+                       cmd.BindDescriptorSet (VkPipelineBindPoint.Compute, layout, dset, firstSet);
+               }
+               public void BindAndDispatch (CommandBuffer cmd, uint groupCountX, uint groupCountY = 1, uint groupCountZ = 1) {
+                       vkCmdBindPipeline (cmd.Handle, VkPipelineBindPoint.Compute, handle);
+                       vkCmdDispatch (cmd.Handle, groupCountX, groupCountY, groupCountZ);
+               }
+       }
+}
diff --git a/vke/src/base/DebuDrawPipeline.cs b/vke/src/base/DebuDrawPipeline.cs
new file mode 100644 (file)
index 0000000..c974416
--- /dev/null
@@ -0,0 +1,83 @@
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL {
+       public class DebugDrawPipeline : GraphicPipeline {
+               public HostBuffer Vertices;
+               uint vertexCount;
+        uint vboLength;
+
+               public DebugDrawPipeline (RenderPass renderPass, uint maxVertices = 100,
+            VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1, PipelineCache pipelineCache = null) :
+                       base (renderPass, pipelineCache, "Debug draw pipeline") {
+
+            vboLength = 100 * 6 * sizeof(float);
+
+            GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.LineList, samples);
+                       cfg.rasterizationState.lineWidth = 1.0f;
+                       cfg.RenderPass = RenderPass;
+                       cfg.Layout = new PipelineLayout(Dev, new VkPushConstantRange(VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf<Matrix4x4>() * 2));
+                       cfg.AddVertexBinding (0, 6 * sizeof(float));
+                       cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat);
+                       cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true);
+
+                       cfg.AddShader (VkShaderStageFlags.Vertex, "#CVKL.debug.vert.spv");
+                       cfg.AddShader (VkShaderStageFlags.Fragment, "#CVKL.debug.frag.spv");
+
+                       layout = cfg.Layout;
+
+                       init (cfg);
+
+                       Vertices = new HostBuffer (Dev, VkBufferUsageFlags.VertexBuffer, vboLength);
+                       Vertices.Map ();
+               }
+
+               public void AddLine (Vector3 start, Vector3 end, float r, float g, float b) {
+                       float[] data = {
+                               start.X, start.Y, start.Z,
+                               r, g, b,
+                               end.X, end.Y, end.Z,
+                               r, g, b
+                       };
+                       Vertices.Update (data, 12 * sizeof (float), vertexCount * 6 * sizeof (float));
+                       vertexCount+=2;
+               }
+               public void UpdateLine (uint lineNum, Vector3 start, Vector3 end, float r, float g, float b) {
+                       float[] data = {
+                               start.X, start.Y, start.Z,
+                               r, g, b,
+                               end.X, end.Y, end.Z,
+                               r, g, b
+                       };
+                       Vertices.Update (data, 12 * sizeof (float), (lineNum-1) * 2 * 6 * sizeof (float));
+               }
+
+               public void RecordDraw (CommandBuffer cmd, Framebuffer fb, Matrix4x4 projection, Matrix4x4 view) {
+
+            //cmd.SetViewport (fb.Width/ratio, fb.Height/ratio, (ratio-1) * (int)fb.Width / ratio, (ratio-1) * (int)fb.Height / ratio);
+            //cmd.SetViewport (200, 200,100,100,-10,10);//, 4 * (int)fb.Width / 5, 4 * (int)fb.Height / 5);
+            //cmd.SetScissor (fb.Width / ratio, fb.Height / ratio, (ratio-1) * (int)fb.Width / ratio, (ratio-1) * (int)fb.Height / ratio);
+            //cmd.SetScissor (200, 200,100,100);
+
+            Bind(cmd);
+
+            cmd.PushConstant (layout, VkShaderStageFlags.Vertex, projection);
+            cmd.PushConstant (layout, VkShaderStageFlags.Vertex, view, (uint)Marshal.SizeOf<Matrix4x4>());
+
+                       cmd.BindVertexBuffer (Vertices);
+                       cmd.Draw (vertexCount);
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               Vertices.Unmap ();
+                               Vertices.Dispose ();
+                       }
+
+                       base.Dispose (disposing);
+               }
+       }
+
+}
diff --git a/vke/src/base/DebugReport.cs b/vke/src/base/DebugReport.cs
new file mode 100644 (file)
index 0000000..3bda80b
--- /dev/null
@@ -0,0 +1,114 @@
+//
+// DebugReport.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Runtime.InteropServices;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+
+    public class DebugReport : IDisposable {        
+        VkDebugReportCallbackEXT handle;
+               Instance inst;
+
+        PFN_vkDebugReportCallbackEXT debugCallbackDelegate = new PFN_vkDebugReportCallbackEXT (debugCallback);
+
+        static VkBool32 debugCallback (VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, ulong obj,
+            UIntPtr location, int messageCode, IntPtr pLayerPrefix, IntPtr pMessage, IntPtr pUserData) {
+            string prefix = "";
+            switch (flags) {
+                case 0:
+                    prefix = "?";
+                    break;
+                case VkDebugReportFlagsEXT.InformationEXT:
+                                       Console.ForegroundColor = ConsoleColor.Gray;
+                                       prefix = "INFO";
+                    break;
+                case VkDebugReportFlagsEXT.WarningEXT:
+                                       Console.ForegroundColor = ConsoleColor.DarkYellow;
+                                       prefix = "WARN";
+                    break;
+                case VkDebugReportFlagsEXT.PerformanceWarningEXT:
+                                       Console.ForegroundColor = ConsoleColor.Yellow;
+                                       prefix = "PERF";
+                    break;
+                case VkDebugReportFlagsEXT.ErrorEXT:
+                                       Console.ForegroundColor = ConsoleColor.DarkRed;
+                                       prefix = "EROR";
+                                       break;
+                case VkDebugReportFlagsEXT.DebugEXT:
+                                       Console.ForegroundColor = ConsoleColor.Red;
+                                       prefix = "DBUG";
+                    break;
+            }
+                       try {
+                               string msg = Marshal.PtrToStringAnsi (pMessage);
+                               string[] tmp = msg.Split ('|');
+                               Console.WriteLine ($"{prefix}:{tmp[1]} |{Marshal.PtrToStringAnsi (pLayerPrefix)}({messageCode}){objectType}:{tmp[0]}");
+                       } catch (Exception ex) {
+                               Console.WriteLine ("error parsing debug message: " + ex);
+                       }
+                       Console.ForegroundColor = ConsoleColor.White;
+            return VkBool32.False;
+        }
+        
+        public DebugReport (Instance instance, VkDebugReportFlagsEXT flags = VkDebugReportFlagsEXT.ErrorEXT | VkDebugReportFlagsEXT.WarningEXT) {
+                       inst = instance;
+
+            VkDebugReportCallbackCreateInfoEXT dbgInfo = new VkDebugReportCallbackCreateInfoEXT {
+                sType = VkStructureType.DebugReportCallbackCreateInfoEXT,
+                flags = flags,
+                pfnCallback = Marshal.GetFunctionPointerForDelegate (debugCallbackDelegate)
+            };
+
+            Utils.CheckResult (vkCreateDebugReportCallbackEXT (inst.Handle, ref dbgInfo, IntPtr.Zero, out handle));
+        }
+
+               #region IDisposable Support
+               private bool disposedValue = false;
+
+               protected virtual void Dispose (bool disposing) {
+                       if (!disposedValue) {
+                               if (disposing) {
+                                       // TODO: supprimer l'état managé (objets managés).
+                               }
+
+                               vkDestroyDebugReportCallbackEXT (inst.Handle, handle, IntPtr.Zero);
+
+                               disposedValue = true;
+                       }
+               }
+
+               ~DebugReport () {
+                       Dispose (false);
+               }
+               public void Dispose () {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/base/DebugUtilsMessenger.cs b/vke/src/base/DebugUtilsMessenger.cs
new file mode 100644 (file)
index 0000000..b048c36
--- /dev/null
@@ -0,0 +1,95 @@
+using System;
+using System.Runtime.InteropServices;
+using VK;
+using static VK.Vk;
+
+namespace CVKL.DebugUtils {
+
+    public class Messenger : IDisposable {
+               Instance inst;
+               VkDebugUtilsMessengerEXT handle;
+               PFN_vkDebugUtilsMessengerCallbackEXT onMessage = new PFN_vkDebugUtilsMessengerCallbackEXT(HandlePFN_vkDebugUtilsMessengerCallbackEXT);
+
+               static VkBool32 HandlePFN_vkDebugUtilsMessengerCallbackEXT (VkDebugUtilsMessageSeverityFlagsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, IntPtr pCallbackData, IntPtr pUserData) {
+                       //Console.WriteLine ("{0} {1}: {2}",messageSeverity, messageTypes, Marshal.PtrToStringAnsi(pUserData));
+                       Console.WriteLine ("MESSAGE RECEIVED");
+                       return false;
+               }
+
+
+
+   //     PFN_vkDebugReportCallbackEXT debugCallbackDelegate = new PFN_vkDebugReportCallbackEXT (debugCallback);
+
+   //     static VkBool32 debugCallback (VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, ulong obj,
+   //         UIntPtr location, int messageCode, IntPtr pLayerPrefix, IntPtr pMessage, IntPtr pUserData) {
+   //         string prefix = "";
+   //         switch (flags) {
+   //             case 0:
+   //                 prefix = "?";
+   //                 break;
+   //             case VkDebugReportFlagsEXT.InformationEXT:
+                       //              Console.ForegroundColor = ConsoleColor.Gray;
+                       //              prefix = "INFO";
+   //                 break;
+   //             case VkDebugReportFlagsEXT.WarningEXT:
+                       //              Console.ForegroundColor = ConsoleColor.DarkYellow;
+                       //              prefix = "WARN";
+   //                 break;
+   //             case VkDebugReportFlagsEXT.PerformanceWarningEXT:
+                       //              Console.ForegroundColor = ConsoleColor.Yellow;
+                       //              prefix = "PERF";
+   //                 break;
+   //             case VkDebugReportFlagsEXT.ErrorEXT:
+                       //              Console.ForegroundColor = ConsoleColor.DarkRed;
+                       //              prefix = "EROR";
+                       //              break;
+   //             case VkDebugReportFlagsEXT.DebugEXT:
+                       //              Console.ForegroundColor = ConsoleColor.Red;
+                       //              prefix = "DBUG";
+   //                 break;
+   //         }
+
+   //         Console.WriteLine ("{0} {1}: {2}",prefix, messageCode, Marshal.PtrToStringAnsi(pMessage));
+                       //Console.ForegroundColor = ConsoleColor.White;
+        //    return VkBool32.False;
+        //}
+        
+        public Messenger (Instance instance,
+               VkDebugUtilsMessageTypeFlagsEXT typeMask =  VkDebugUtilsMessageTypeFlagsEXT.ValidationEXT,
+                       VkDebugUtilsMessageSeverityFlagsEXT severityMask = VkDebugUtilsMessageSeverityFlagsEXT.ErrorEXT | VkDebugUtilsMessageSeverityFlagsEXT.WarningEXT) {
+
+                       inst = instance;
+                       VkDebugUtilsMessengerCreateInfoEXT info = VkDebugUtilsMessengerCreateInfoEXT.New ();
+                       info.messageType = typeMask;
+                       info.messageSeverity = severityMask;
+                       info.pfnUserCallback = Marshal.GetFunctionPointerForDelegate (onMessage);
+                       info.pUserData = IntPtr.Zero;
+
+                       Utils.CheckResult (vkCreateDebugUtilsMessengerEXT (inst.VkInstance, ref info, IntPtr.Zero, out handle));
+        }
+
+               #region IDisposable Support
+               private bool disposedValue;
+
+               protected virtual void Dispose (bool disposing) {
+                       if (!disposedValue) {
+                               if (disposing) {
+                                       // TODO: supprimer l'état managé (objets managés).
+                               }
+
+                               vkDestroyDebugUtilsMessengerEXT (inst.Handle, handle, IntPtr.Zero);
+
+                               disposedValue = true;
+                       }
+               }
+
+               ~Messenger () {
+                       Dispose (false);
+               }
+               public void Dispose () {
+                       Dispose (true);
+                       GC.SuppressFinalize (this);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/base/DescriptorPool.cs b/vke/src/base/DescriptorPool.cs
new file mode 100644 (file)
index 0000000..6c9f9c4
--- /dev/null
@@ -0,0 +1,90 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Collections.Generic;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+    public sealed class DescriptorPool : Activable {
+        internal VkDescriptorPool handle;        
+        public readonly uint MaxSets;
+
+        public List<VkDescriptorPoolSize> PoolSizes = new List<VkDescriptorPoolSize> ();
+        
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.DescriptorPoolEXT, handle.Handle);
+
+               #region CTORS
+               public DescriptorPool (Device device, uint maxSets = 1) : base (device) {            
+            MaxSets = maxSets;
+        }
+        public DescriptorPool (Device device, uint maxSets = 1, params VkDescriptorPoolSize[] poolSizes)
+            : this (device, maxSets) {
+
+                       PoolSizes.AddRange (poolSizes);
+
+            Activate ();            
+        }
+               #endregion
+
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {            
+                               VkDescriptorPoolCreateInfo info = VkDescriptorPoolCreateInfo.New();
+                   info.poolSizeCount = (uint)PoolSizes.Count;
+                   info.pPoolSizes = PoolSizes.Pin ();
+                   info.maxSets = MaxSets;
+
+                   Utils.CheckResult (vkCreateDescriptorPool (Dev.VkDev, ref info, IntPtr.Zero, out handle));
+                               PoolSizes.Unpin ();
+                       }
+                       base.Activate ();
+               }
+
+        /// <summary>
+        /// Create and allocate a new DescriptorSet
+        /// </summary>
+        public DescriptorSet Allocate (params DescriptorSetLayout[] layouts) {
+            DescriptorSet ds = new DescriptorSet (this, layouts);
+            Allocate (ds);
+            return ds;
+        }
+        public void Allocate (DescriptorSet descriptorSet) {
+            VkDescriptorSetAllocateInfo allocInfo = VkDescriptorSetAllocateInfo.New();
+            allocInfo.descriptorPool = handle;
+            allocInfo.descriptorSetCount = (uint)descriptorSet.descriptorSetLayouts.Count;
+            allocInfo.pSetLayouts = descriptorSet.descriptorSetLayouts.Pin();
+
+            Utils.CheckResult (vkAllocateDescriptorSets (Dev.VkDev, ref allocInfo, out descriptorSet.handle));
+
+                       descriptorSet.descriptorSetLayouts.Unpin ();
+        }
+        public void FreeDescriptorSet (params DescriptorSet[] descriptorSets) {
+            if (descriptorSets.Length == 1) {
+                Utils.CheckResult (vkFreeDescriptorSets (Dev.VkDev, handle, 1, ref descriptorSets[0].handle));
+                return;
+            }
+            Utils.CheckResult (vkFreeDescriptorSets (Dev.VkDev, handle, (uint)descriptorSets.Length, descriptorSets.Pin()));
+                       descriptorSets.Unpin ();
+        }
+        public void Reset () {
+            Utils.CheckResult (vkResetDescriptorPool (Dev.VkDev, handle, 0));
+        }
+
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+
+               #region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (!disposing)
+                               System.Diagnostics.Debug.WriteLine ($"CVKL DescriptorPool '{name}' disposed by finalizer");
+                       if (state == ActivableState.Activated)
+                               vkDestroyDescriptorPool (Dev.VkDev, handle, IntPtr.Zero);
+                       base.Dispose (disposing);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/base/DescriptorSet.cs b/vke/src/base/DescriptorSet.cs
new file mode 100644 (file)
index 0000000..19b231c
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System.Collections.Generic;
+using VK;
+
+namespace CVKL {
+       public class DescriptorSet {
+        internal VkDescriptorSet handle;
+               public VkDescriptorSet Handle => handle;
+        DescriptorPool pool;
+        internal List<VkDescriptorSetLayout> descriptorSetLayouts = new List<VkDescriptorSetLayout> ();
+
+        public DescriptorSet (DescriptorPool descriptorPool) {
+            pool = descriptorPool;
+        }
+        public DescriptorSet (DescriptorPool descriptorPool, params DescriptorSetLayout[] layouts) 
+            : this (descriptorPool) {
+
+            foreach (DescriptorSetLayout layout in layouts)
+                descriptorSetLayouts.Add (layout.handle);
+        }        
+
+               public void Free () {
+                       pool.FreeDescriptorSet (this);
+               }
+    }
+}
diff --git a/vke/src/base/DescriptorSetLayout.cs b/vke/src/base/DescriptorSetLayout.cs
new file mode 100644 (file)
index 0000000..f63e5e2
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright (c) 2019  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Descriptor set layout, automatically activated when used in pipeline creation, so don't allocate descriptor sets using
+       /// this layout unless it has been activated.
+       /// </summary>
+    public sealed class DescriptorSetLayout : Activable {
+        internal VkDescriptorSetLayout handle;
+        
+               public VkDescriptorSetLayoutCreateFlags Flags { get; private set; } = 0;
+        public List<VkDescriptorSetLayoutBinding> Bindings { get; private set; } = new List<VkDescriptorSetLayoutBinding> ();
+        
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo 
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.DescriptorSetLayoutEXT, handle.Handle);
+
+               #region CTORS
+               public DescriptorSetLayout (Device device, VkDescriptorSetLayoutCreateFlags flags) : base (device) {            
+                       Flags = flags;
+        }
+               public DescriptorSetLayout (Device device, params VkDescriptorSetLayoutBinding[] bindings)
+        : this (device, 0, bindings) {
+        }
+        public DescriptorSetLayout (Device device, VkDescriptorSetLayoutCreateFlags flags, params VkDescriptorSetLayoutBinding[] bindings)
+        : this (device, flags) {
+            foreach (VkDescriptorSetLayoutBinding b in bindings) 
+                Bindings.Add (b);            
+        }
+               #endregion
+
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               VkDescriptorSetLayoutCreateInfo info = new VkDescriptorSetLayoutCreateInfo (Flags, (uint)Bindings.Count, Bindings.Pin());
+                   Utils.CheckResult (vkCreateDescriptorSetLayout (Dev.VkDev, ref info, IntPtr.Zero, out handle));
+                               Bindings.Unpin ();
+                       }
+                       base.Activate ();
+               }
+
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+
+               #region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (!disposing)
+                               System.Diagnostics.Debug.WriteLine ("VKE DescriptorSetLayout disposed by finalizer");
+                       if (state == ActivableState.Activated)
+                               vkDestroyDescriptorSetLayout (Dev.VkDev, handle, IntPtr.Zero);
+                       base.Dispose (disposing);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/base/DescriptorSetWrites.cs b/vke/src/base/DescriptorSetWrites.cs
new file mode 100644 (file)
index 0000000..b47d7d0
--- /dev/null
@@ -0,0 +1,227 @@
+//
+// DescriptorSetWrites.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Descriptor set writes is defined once, then update affect descriptors to write array
+       /// </summary>
+       public class DescriptorSetWrites {
+               VkDescriptorSet? dstSetOverride = null;//when set, override target descriptors to update in each write
+               public List<VkWriteDescriptorSet> WriteDescriptorSets = new List<VkWriteDescriptorSet> ();
+
+               #region CTORS
+               public DescriptorSetWrites () { }
+               public DescriptorSetWrites (VkDescriptorSetLayoutBinding binding) {
+                       AddWriteInfo (binding);
+               }
+               public DescriptorSetWrites (DescriptorSet destSet, VkDescriptorSetLayoutBinding binding) {
+                       AddWriteInfo (destSet, binding);
+               }
+               public DescriptorSetWrites (params VkDescriptorSetLayoutBinding[] bindings) {
+                       AddWriteInfo (bindings);
+               }
+               public DescriptorSetWrites (DescriptorSet destSet, params VkDescriptorSetLayoutBinding[] bindings) {
+                       AddWriteInfo (destSet, bindings);
+               }
+               /// <summary>
+               /// Configure the Write to update the full layout at once
+               /// </summary>
+               public DescriptorSetWrites (DescriptorSet destSet, DescriptorSetLayout layout) {
+                       foreach (VkDescriptorSetLayoutBinding binding in layout.Bindings) {
+                               AddWriteInfo (destSet, binding);
+                       }
+               }
+               /// <summary>
+               /// Configure the Write to update the full layout at once
+               /// </summary>
+               public DescriptorSetWrites (DescriptorSetLayout layout) {
+                       foreach (VkDescriptorSetLayoutBinding binding in layout.Bindings) {
+                               AddWriteInfo (binding);
+                       }
+               }
+               #endregion
+
+               /// <summary>
+               /// Adds write info with a destination descriptor set, it could be overriden by calling Write
+               /// with another descriptorSet in parametters
+               /// </summary>
+               public void AddWriteInfo (DescriptorSet destSet, params VkDescriptorSetLayoutBinding[] bindings) {
+                       foreach (VkDescriptorSetLayoutBinding binding in bindings)
+                               AddWriteInfo (destSet, binding);
+        }
+               /// <summary>
+               /// Adds write info with a destination descriptor set, it could be overriden by calling Write
+               /// with another descriptorSet in parametters
+               /// </summary>
+               public void AddWriteInfo (DescriptorSet destSet, VkDescriptorSetLayoutBinding binding) {
+                       VkWriteDescriptorSet wds = VkWriteDescriptorSet.New();
+                       wds.descriptorType = binding.descriptorType;
+                       wds.descriptorCount = binding.descriptorCount;
+                       wds.dstBinding = binding.binding;
+                       wds.dstSet = destSet.handle;
+                       WriteDescriptorSets.Add (wds);
+        }
+               /// <summary>
+               /// Adds write info without specifying a destination descriptor set, this imply that on calling Write, you MUST
+               /// provide a desDescriptor!
+               /// </summary>
+               public void AddWriteInfo (VkDescriptorSetLayoutBinding[] bindings) {
+                       foreach (VkDescriptorSetLayoutBinding binding in bindings)
+                               AddWriteInfo (binding);
+               }
+               /// <summary>
+               /// Adds write info without specifying a destination descriptor set, this imply that on calling Write, you MUST
+               /// provide a desDescriptor!
+               /// </summary>
+               public void AddWriteInfo (VkDescriptorSetLayoutBinding binding) {
+            VkWriteDescriptorSet wds = VkWriteDescriptorSet.New();
+            wds.descriptorType = binding.descriptorType;
+            wds.descriptorCount = binding.descriptorCount;
+            wds.dstBinding = binding.binding;
+            WriteDescriptorSets.Add (wds); 
+               }
+
+               /// <summary>
+               /// execute the descriptors writes providing a target descriptorSet
+               /// </summary>
+               public void Write (Device dev, DescriptorSet set, params object[] descriptors) {
+                       dstSetOverride = set.handle;
+                       Write (dev, descriptors);
+               }
+
+               /// <summary>
+               /// execute the descriptors writes targeting descriptorSets setted on AddWriteInfo call
+               /// </summary>
+               public void Write (Device dev, params object[] descriptors) {
+                       //if (descriptors.Length != WriteDescriptorSets.Count)
+                       //      throw new Exception ("descriptors count must equal the WriteInfo count.");
+                       List<object> descriptorsLists = new List<object> ();//strore temp arrays of pDesc for unpinning
+                                                                                                                               //if descriptorCount>1
+                       int i = 0;
+                       int wdsPtr = 0;
+                       while (i < descriptors.Length) {
+                               int firstDescriptor = i;
+                               VkWriteDescriptorSet wds = WriteDescriptorSets[wdsPtr];
+                               if (dstSetOverride != null)
+                                       wds.dstSet = dstSetOverride.Value.Handle;
+                               IntPtr pDescriptors = IntPtr.Zero;
+
+                               if (wds.descriptorCount > 1) {
+                                       List<IntPtr> descPtrArray = new List<IntPtr> ();
+                                       for (int d = 0; d < wds.descriptorCount; d++) {
+                                               descPtrArray.Add (descriptors[i].Pin ());
+                                               i++;
+                                       }
+                                       descriptorsLists.Add (descPtrArray);
+                                       pDescriptors = descPtrArray.Pin ();
+                               } else {
+                                       pDescriptors = descriptors[i].Pin ();
+                                       i++;
+                               }
+                               if (descriptors[firstDescriptor] is VkDescriptorBufferInfo)
+                                       wds.pBufferInfo = pDescriptors;
+                               else if (descriptors[firstDescriptor] is VkDescriptorImageInfo)
+                                       wds.pImageInfo = pDescriptors;
+
+                               WriteDescriptorSets[wdsPtr] = wds;
+                               wdsPtr++;
+                       }
+                       vkUpdateDescriptorSets (dev.VkDev, (uint)WriteDescriptorSets.Count, WriteDescriptorSets.Pin (), 0, IntPtr.Zero);
+                       WriteDescriptorSets.Unpin ();
+                       foreach (object descArray in descriptorsLists) 
+                               descArray.Unpin ();
+                       for (i = 0; i < descriptors.Length; i++) 
+                               descriptors[i].Unpin ();                        
+               }
+       }
+       /// <summary>
+       /// Descriptor set writes include descriptor in write addition with IDisposable model
+       /// </summary>
+       [Obsolete]
+    public class DescriptorSetWrites2 : IDisposable {
+        Device dev;
+        List<VkWriteDescriptorSet> WriteDescriptorSets = new List<VkWriteDescriptorSet> ();
+               List<object> descriptors = new List<object> ();
+        
+               public DescriptorSetWrites2 (Device device) {
+            dev = device;
+        }
+        public void AddWriteInfo (DescriptorSet destSet, VkDescriptorSetLayoutBinding binding, VkDescriptorBufferInfo descriptor) {
+                       if (!descriptors.Contains (descriptor)) 
+                               descriptors.Add (descriptor);
+            VkWriteDescriptorSet wds = VkWriteDescriptorSet.New();
+            wds.descriptorType = binding.descriptorType;
+            wds.descriptorCount = binding.descriptorCount;
+            wds.dstBinding = binding.binding;
+            wds.dstSet = destSet.handle;            
+            wds.pBufferInfo = descriptor.Pin ();            
+            
+                       WriteDescriptorSets.Add (wds);            
+        }
+        public void AddWriteInfo (DescriptorSet destSet, VkDescriptorSetLayoutBinding binding, VkDescriptorImageInfo descriptor) {
+                       if (!descriptors.Contains (descriptor)) 
+                               descriptors.Add (descriptor);
+            VkWriteDescriptorSet wds = VkWriteDescriptorSet.New();
+            wds.descriptorType = binding.descriptorType;
+            wds.descriptorCount = binding.descriptorCount;
+            wds.dstBinding = binding.binding;
+            wds.dstSet = destSet.handle;            
+            wds.pImageInfo = descriptor.Pin ();
+            
+            WriteDescriptorSets.Add (wds);            
+        }
+
+        public void Update () {
+            vkUpdateDescriptorSets (dev.VkDev, (uint)WriteDescriptorSets.Count, WriteDescriptorSets.Pin (), 0, IntPtr.Zero);
+                       WriteDescriptorSets.Unpin ();
+        }
+
+        #region IDisposable Support
+        private bool disposedValue = false; // Pour détecter les appels redondants
+
+        protected virtual void Dispose (bool disposing) {
+            if (!disposedValue) {
+                foreach (object descriptor in descriptors)
+                    descriptor.Unpin ();
+                disposedValue = true;
+            }
+        }
+        ~DescriptorSetWrites2() {
+            Dispose(false);
+        }
+        // Ce code est ajouté pour implémenter correctement le modèle supprimable.
+        public void Dispose () {
+            Dispose (true);
+            GC.SuppressFinalize(this);
+        }
+        #endregion
+    }
+}
diff --git a/vke/src/base/Device.cs b/vke/src/base/Device.cs
new file mode 100644 (file)
index 0000000..1bb9e09
--- /dev/null
@@ -0,0 +1,323 @@
+//
+// Device.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using VK;
+using static VK.Vk;
+
+
+namespace CVKL {
+       /// <summary>
+       /// Logical device encapsulating vulkan logical device handle. Implements only IDisposable an do not derive from
+       /// Activable, so it may be activated only once and no reference counting on it is handled, and no reactivation is posible
+       /// after being disposed.
+       /// </summary>
+       public class Device : IDisposable {
+               public readonly PhysicalDevice phy;
+
+               VkDevice dev;
+               public VkDevice VkDev => dev;
+               public IntPtr Handle => dev.Handle;
+
+               internal List<Queue> queues = new List<Queue> ();
+               internal bool debugMarkersEnabled;
+
+#if MEMORY_POOLS
+               public ResourceManager resourceManager;
+#endif
+
+               public Device (PhysicalDevice _phy) {
+                       phy = _phy;
+               }
+
+               public void Activate (VkPhysicalDeviceFeatures enabledFeatures, params string[] extensions) {
+                       List<VkDeviceQueueCreateInfo> qInfos = new List<VkDeviceQueueCreateInfo> ();
+                       List<List<float>> prioritiesLists = new List<List<float>> ();//store pinned lists for later unpin
+
+                       foreach (IGrouping<uint, Queue> qfams in queues.GroupBy (q => q.qFamIndex)) {
+                               int qTot = qfams.Count ();
+                               uint qIndex = 0;
+                               List<float> priorities = new List<float> ();
+                               bool qCountReached = false;//true when queue count of that family is reached
+
+                               foreach (Queue q in qfams) {
+                                       if (!qCountReached)
+                                               priorities.Add (q.priority);
+                                       q.index = qIndex++;
+                                       if (qIndex == phy.QueueFamilies[qfams.Key].queueCount) {
+                                               qIndex = 0;
+                                               qCountReached = true;
+                                       }
+                               }
+
+                               qInfos.Add (new VkDeviceQueueCreateInfo {
+                                       sType = VkStructureType.DeviceQueueCreateInfo,
+                                       queueCount = qCountReached ? phy.QueueFamilies[qfams.Key].queueCount : qIndex,
+                                       queueFamilyIndex = qfams.Key,
+                                       pQueuePriorities = priorities.Pin ()
+                               });
+                               prioritiesLists.Add (priorities);//add for unpined
+                       }
+
+                       //enable only supported exceptions
+                       List<IntPtr> deviceExtensions = new List<IntPtr> ();
+                       for (int i = 0; i < extensions.Length; i++) {
+                               if (phy.GetDeviceExtensionSupported (extensions[i])) {
+                                       deviceExtensions.Add (new FixedUtf8String (extensions[i]));
+                                       //store in a bool to prevent frequent string test for debug marker ext presence
+                                       if (extensions[i] == Ext.D.VK_EXT_debug_marker)
+                                               debugMarkersEnabled = true;
+                               }
+                       }
+
+                       VkDeviceCreateInfo deviceCreateInfo = VkDeviceCreateInfo.New ();
+                       deviceCreateInfo.queueCreateInfoCount = (uint)qInfos.Count;
+                       deviceCreateInfo.pQueueCreateInfos = qInfos.Pin ();
+                       deviceCreateInfo.pEnabledFeatures = enabledFeatures.Pin ();
+
+                       if (deviceExtensions.Count > 0) {
+                               deviceCreateInfo.enabledExtensionCount = (uint)deviceExtensions.Count;
+                               deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.Pin ();
+                       }
+
+                       Utils.CheckResult (vkCreateDevice (phy.Handle, ref deviceCreateInfo, IntPtr.Zero, out dev));
+                       qInfos.Unpin ();
+                       enabledFeatures.Unpin ();
+                       foreach (List<float> fa in prioritiesLists)
+                               fa.Unpin ();
+
+                       deviceExtensions.Unpin ();
+
+                       //Vk.LoadDeviceFunctionPointers (dev);
+
+                       foreach (Queue q in queues)
+                               q.updateHandle ();
+
+#if MEMORY_POOLS
+                       resourceManager = new ResourceManager (this);
+#endif
+               }
+
+               public VkSemaphore CreateSemaphore () {
+                       VkSemaphore tmp;
+                       VkSemaphoreCreateInfo info = VkSemaphoreCreateInfo.New ();
+                       Utils.CheckResult (vkCreateSemaphore (dev, ref info, IntPtr.Zero, out tmp));
+                       return tmp;
+               }
+               public void DestroySemaphore (VkSemaphore semaphore) {
+                       vkDestroySemaphore (dev, semaphore, IntPtr.Zero);
+               }
+               public VkFence CreateFence (bool signaled = false) {
+                       VkFence tmp;
+                       VkFenceCreateInfo info = VkFenceCreateInfo.New ();
+                       info.flags = signaled ? VkFenceCreateFlags.Signaled : 0;
+                       Utils.CheckResult (vkCreateFence (dev, ref info, IntPtr.Zero, out tmp));
+                       return tmp;
+               }
+               public void DestroyFence (VkFence fence) {
+                       vkDestroyFence (dev, fence, IntPtr.Zero);
+               }
+               public void WaitForFence (VkFence fence, ulong timeOut = UInt64.MaxValue) {
+                       vkWaitForFences (dev, 1, ref fence, 1, timeOut);
+               }
+               public void ResetFence (VkFence fence) {
+                       vkResetFences (dev, 1, ref fence);
+               }
+               public void WaitForFences (VkFence[] fences, ulong timeOut = UInt64.MaxValue) {
+                       vkWaitForFences (dev, (uint)fences.Length, fences.Pin(), 1, timeOut);
+                       fences.Unpin ();
+               }
+               public void ResetFences (params VkFence[] fences) {
+                       vkResetFences (dev, (uint)fences.Length, fences.Pin());
+                       fences.Unpin ();
+               }
+
+               public void DestroyShaderModule (VkShaderModule module) {
+                       vkDestroyShaderModule (VkDev, module, IntPtr.Zero);
+               }
+               public void WaitIdle () {
+                       Utils.CheckResult (vkDeviceWaitIdle (dev));
+               }
+               public VkRenderPass CreateRenderPass (VkRenderPassCreateInfo info) {
+                       VkRenderPass renderPass;
+                       Utils.CheckResult (vkCreateRenderPass (dev, ref info, IntPtr.Zero, out renderPass));
+                       return renderPass;
+               }
+               internal VkSwapchainKHR CreateSwapChain (VkSwapchainCreateInfoKHR infos) {
+                       VkSwapchainKHR newSwapChain;
+                       Utils.CheckResult (vkCreateSwapchainKHR (dev, ref infos, IntPtr.Zero, out newSwapChain));
+                       return newSwapChain;
+               }
+               internal void DestroySwapChain (VkSwapchainKHR swapChain) {
+                       vkDestroySwapchainKHR (dev, swapChain, IntPtr.Zero);
+               }
+               unsafe public VkImage[] GetSwapChainImages (VkSwapchainKHR swapchain) {
+                       uint imageCount = 0;
+                       Utils.CheckResult (vkGetSwapchainImagesKHR (dev, swapchain, out imageCount, IntPtr.Zero));
+                       if (imageCount == 0)
+                               throw new Exception ("Swapchain image count is 0.");
+                       VkImage[] imgs = new VkImage[imageCount];
+
+                       Utils.CheckResult (vkGetSwapchainImagesKHR (dev, swapchain, out imageCount, imgs.Pin ()));
+                       imgs.Unpin ();
+
+                       return imgs;
+               }
+               unsafe public VkImageView CreateImageView (VkImage image, VkFormat format, VkImageViewType viewType = VkImageViewType.ImageView2D, VkImageAspectFlags aspectFlags = VkImageAspectFlags.Color) {
+                       VkImageView view;
+                       VkImageViewCreateInfo infos = VkImageViewCreateInfo.New ();
+                       infos.image = image;
+                       infos.viewType = viewType;
+                       infos.format = format;
+                       infos.components = new VkComponentMapping { r = VkComponentSwizzle.R, g = VkComponentSwizzle.G, b = VkComponentSwizzle.B, a = VkComponentSwizzle.A };
+                       infos.subresourceRange = new VkImageSubresourceRange (aspectFlags);
+
+                       Utils.CheckResult (vkCreateImageView (dev, ref infos, IntPtr.Zero, out view));
+                       return view;
+               }
+               public void DestroyImageView (VkImageView view) {
+                       vkDestroyImageView (dev, view, IntPtr.Zero);
+               }
+               public void DestroySampler (VkSampler sampler) {
+                       vkDestroySampler (dev, sampler, IntPtr.Zero);
+               }
+               public void DestroyImage (VkImage img) {
+                       vkDestroyImage (dev, img, IntPtr.Zero);
+               }
+               public void DestroyFramebuffer (VkFramebuffer fb) {
+                       vkDestroyFramebuffer (dev, fb, IntPtr.Zero);
+               }
+               public void DestroyRenderPass (VkRenderPass rp) {
+                       vkDestroyRenderPass (dev, rp, IntPtr.Zero);
+               }
+               // This function is used to request a Device memory type that supports all the property flags we request (e.g. Device local, host visibile)
+               // Upon success it will return the index of the memory type that fits our requestes memory properties
+               // This is necessary as implementations can offer an arbitrary number of memory types with different
+               // memory properties. 
+               // You can check http://vulkan.gpuinfo.org/ for details on different memory configurations
+               internal uint GetMemoryTypeIndex (uint typeBits, VkMemoryPropertyFlags properties) {
+            // Iterate over all memory types available for the Device used in this example
+            for (uint i = 0; i < phy.memoryProperties.memoryTypeCount; i++) {
+                if ((typeBits & 1) == 1) {
+                    if ((phy.memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) {
+                        return i;
+                    }
+                }
+                typeBits >>= 1;
+            }
+
+            throw new InvalidOperationException ("Could not find a suitable memory type!");
+        }
+        public VkFormat GetSuitableDepthFormat () {
+            VkFormat[] formats = new VkFormat[] { VkFormat.D32SfloatS8Uint, VkFormat.D32Sfloat, VkFormat.D24UnormS8Uint, VkFormat.D16UnormS8Uint, VkFormat.D16Unorm };
+            foreach (VkFormat f in formats) {
+                if (phy.GetFormatProperties (f).optimalTilingFeatures.HasFlag(VkFormatFeatureFlags.DepthStencilAttachment))
+                    return f;
+            }
+            throw new InvalidOperationException ("No suitable depth format found.");
+        }
+
+        public VkShaderModule LoadSPIRVShader (string filename) {
+                       VkShaderModule shaderModule;
+                       using (Stream stream = StaticGetStreamFromPath (filename)) {
+                               using (BinaryReader br = new BinaryReader (stream)) {
+                                       byte[] shaderCode = br.ReadBytes ((int)stream.Length);
+                                       ulong shaderSize = (ulong)shaderCode.Length;
+
+                                       // Create a new shader module that will be used for Pipeline creation
+                                       VkShaderModuleCreateInfo moduleCreateInfo = VkShaderModuleCreateInfo.New ();
+                                       moduleCreateInfo.codeSize = new UIntPtr (shaderSize);
+                                       moduleCreateInfo.pCode = shaderCode.Pin ();
+
+                                       Utils.CheckResult (vkCreateShaderModule (VkDev, ref moduleCreateInfo, IntPtr.Zero, out shaderModule));
+
+                                       shaderCode.Unpin ();
+                               }
+
+                       }
+                       return shaderModule;            
+
+        }
+
+               public static Stream StaticGetStreamFromPath (string path) {
+                       Stream stream = null;
+
+                       if (path.StartsWith ("#", StringComparison.Ordinal)) {
+                               string resId = path.Substring (1);
+                               //first search entry assembly
+                               stream = Assembly.GetEntryAssembly ().GetManifestResourceStream (resId);
+                               if (stream != null)
+                                       return stream;
+                               //if not found, search assembly named with the 1st element of the resId
+                               string assemblyName = resId.Split ('.')[0];
+                               Assembly a = AppDomain.CurrentDomain.GetAssemblies ().FirstOrDefault (aa => aa.GetName ().Name == assemblyName);
+                               if (a == null)
+                                       throw new Exception ($"Assembly '{assemblyName}' not found for ressource '{path}'.");
+                               stream = a.GetManifestResourceStream (resId);
+                               if (stream == null)
+                                       throw new Exception ("Resource not found: " + path);
+                       } else {
+                               if (!File.Exists (path))
+                                       throw new FileNotFoundException ("File not found: ", path);
+                               stream = new FileStream (path, FileMode.Open, FileAccess.Read);
+                       }
+                       return stream;
+               }
+
+#region IDisposable Support
+               private bool disposedValue = false; // Pour détecter les appels redondants
+
+        protected virtual void Dispose (bool disposing) {
+            if (!disposedValue) {
+                               if (disposing) {
+#if MEMORY_POOLS
+                                       resourceManager.Dispose ();
+#endif
+                               } else
+                                       System.Diagnostics.Debug.WriteLine ("Device disposed by Finalizer.");
+
+                vkDestroyDevice (dev, IntPtr.Zero);
+
+                disposedValue = true;
+            }
+        }
+
+        ~Device() {
+           Dispose(false);
+        }
+
+        // Ce code est ajouté pour implémenter correctement le modèle supprimable.
+        public void Dispose () {
+            Dispose (true);
+            GC.SuppressFinalize(this);
+        }
+#endregion
+    }
+}
diff --git a/vke/src/base/FrameBuffer.cs b/vke/src/base/FrameBuffer.cs
new file mode 100644 (file)
index 0000000..2f668e0
--- /dev/null
@@ -0,0 +1,120 @@
+//
+// FrameBuffer.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+
+    public class Framebuffer : Activable {
+        internal VkFramebuffer handle;
+        RenderPass renderPass;
+        
+               public List<Image> attachments = new List<Image> ();
+        VkFramebufferCreateInfo createInfo = VkFramebufferCreateInfo.New();
+
+        public uint Width => createInfo.width;
+        public uint Height => createInfo.height;
+        public uint Layers => createInfo.layers;
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo 
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.FramebufferEXT, handle.Handle);
+
+               #region CTORS
+               public Framebuffer (RenderPass _renderPass, uint _width, uint _height, uint _layers = 1) : base(_renderPass.Dev) {
+            renderPass = _renderPass;
+            createInfo.width = _width;
+            createInfo.height = _height;
+            createInfo.layers = _layers;
+            createInfo.renderPass = renderPass.handle;
+        }
+
+               public Framebuffer (RenderPass _renderPass, uint _width, uint _height, params Image[] views)
+        : this (_renderPass, _width, _height) {
+                       for (int i = 0; i < views.Length; i++) {
+                               Image v = views[i];
+                               if (v == null) {
+                                       //automatically create attachment if not in unused state in the renderpass
+                                       VkAttachmentDescription ad = renderPass.attachments[i];
+                                       VkImageUsageFlags usage = 0;
+                                       VkImageAspectFlags aspectFlags = 0;
+
+                                       Utils.QueryLayoutRequirements (ad.initialLayout, ref usage, ref aspectFlags);
+                                       Utils.QueryLayoutRequirements (ad.finalLayout, ref usage, ref aspectFlags);
+                                       foreach (SubPass sp in renderPass.subpasses) {
+                                               //TODO:check subpass usage
+                                       }
+
+                                       v = new Image (renderPass.Dev, ad.format, usage, VkMemoryPropertyFlags.DeviceLocal,
+                                               _width, _height, VkImageType.Image2D, ad.samples, VkImageTiling.Optimal, 1, createInfo.layers);
+                                       v.SetName ($"fbImg{i}");
+                                       v.CreateView (VkImageViewType.ImageView2D, aspectFlags);
+                               } else
+                                       v.Activate ();//increase ref and create handle if not already activated
+
+                attachments.Add (v);
+                       }
+            Activate ();
+               }
+               #endregion
+
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               VkImageView[] views = attachments.Select (a => a.Descriptor.imageView).ToArray ();
+                               createInfo.attachmentCount = (uint)views.Length;
+                               createInfo.pAttachments = views.Pin ();
+
+                               Utils.CheckResult (vkCreateFramebuffer (renderPass.Dev.VkDev, ref createInfo, IntPtr.Zero, out handle));
+
+                               views.Unpin ();
+                       }
+                       base.Activate ();
+        }
+
+
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+
+#region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (state == ActivableState.Activated)
+                               Dev.DestroyFramebuffer (handle);
+                       if (disposing) {
+                               foreach (Image img in attachments) 
+                                       img.Dispose();
+                       }else
+                               System.Diagnostics.Debug.WriteLine ("VKE Activable object disposed by finalizer");
+                               
+                       base.Dispose (disposing);
+               }
+#endregion
+
+       }
+}
diff --git a/vke/src/base/GPUBuffer.cs b/vke/src/base/GPUBuffer.cs
new file mode 100644 (file)
index 0000000..61504fd
--- /dev/null
@@ -0,0 +1,68 @@
+//
+// GPUBuffer.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Runtime.InteropServices;
+using VK;
+
+namespace CVKL {
+
+    /// <summary>
+    /// Device local Buffer
+    /// </summary>
+    public class GPUBuffer : Buffer {
+        public GPUBuffer (Device device, VkBufferUsageFlags usage, UInt64 size) 
+        : base (device, usage, VkMemoryPropertyFlags.DeviceLocal, size){ 
+        }
+    }
+       /// <summary>
+       /// Device local Buffer
+       /// </summary>
+       public class GPUBuffer<T> : GPUBuffer {
+
+               public int ElementCount { get; private set; }
+
+               public GPUBuffer (Device device, VkBufferUsageFlags usage, int elementCount)
+            : base (device, usage, (ulong)(Marshal.SizeOf<T> () * elementCount)) {
+                       ElementCount = elementCount;
+        }
+        public GPUBuffer (Queue staggingQ, CommandPool staggingCmdPool, VkBufferUsageFlags usage, T[] elements)
+            : base (staggingQ.Dev, usage | VkBufferUsageFlags.TransferDst, (ulong)(Marshal.SizeOf<T> () * elements.Length)) {
+                       using (HostBuffer<T> stagging = new HostBuffer<T> (Dev, VkBufferUsageFlags.TransferSrc, elements)) { 
+                               CommandBuffer cmd = staggingCmdPool.AllocateCommandBuffer ();
+                               cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                               stagging.CopyTo (cmd, this);
+
+                               cmd.End ();
+
+                               staggingQ.Submit (cmd);
+                               staggingQ.WaitIdle ();
+
+                               cmd.Free ();
+                       }
+        }
+    }
+}
diff --git a/vke/src/base/GraphicPipeline.cs b/vke/src/base/GraphicPipeline.cs
new file mode 100644 (file)
index 0000000..562e4fa
--- /dev/null
@@ -0,0 +1,135 @@
+//
+// FrameBuffer.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+    public class GraphicPipeline : Pipeline {
+
+               public readonly RenderPass RenderPass;
+               public VkSampleCountFlags Samples => RenderPass.Samples;
+
+               #region CTORS
+               protected GraphicPipeline (RenderPass renderPass, PipelineCache cache = null, string name = "graphic pipeline") : base(renderPass.Dev, cache, name) { 
+                       RenderPass = renderPass;
+                       handle.SetDebugMarkerName (Dev, name);
+               }
+               /// <summary>
+               /// Create a new Pipeline with supplied RenderPass
+               /// </summary>
+               public GraphicPipeline (GraphicPipelineConfig cfg, string name = "graphic pipeline") : this (cfg.RenderPass, cfg.Cache, name)
+               {
+                       layout = cfg.Layout;
+
+                       init (cfg);
+               }
+
+               #endregion
+
+               public override void Activate () => throw new NotSupportedException ("Please initialize graphic pipeline through the init method");
+
+               protected void init (GraphicPipelineConfig cfg) {
+                       if (state != ActivableState.Activated) {
+                               Layout.Activate ();
+                               RenderPass.Activate ();
+                               Cache?.Activate ();
+
+                               List<VkPipelineShaderStageCreateInfo> shaderStages = new List<VkPipelineShaderStageCreateInfo> ();
+                               foreach (ShaderInfo shader in cfg.shaders)
+                                       shaderStages.Add (shader.GetStageCreateInfo (Dev));
+
+                               VkPipelineColorBlendStateCreateInfo colorBlendInfo = VkPipelineColorBlendStateCreateInfo.New ();
+                               colorBlendInfo.attachmentCount = (uint)cfg.blendAttachments.Count;
+                               colorBlendInfo.pAttachments = cfg.blendAttachments.Pin ();
+
+                               VkPipelineDynamicStateCreateInfo dynStatesInfo = VkPipelineDynamicStateCreateInfo.New ();
+                               dynStatesInfo.dynamicStateCount = (uint)cfg.dynamicStates.Count;
+                               dynStatesInfo.pDynamicStates = cfg.dynamicStates.Pin ();
+
+                               VkPipelineVertexInputStateCreateInfo vertInputInfo = VkPipelineVertexInputStateCreateInfo.New ();
+                               vertInputInfo.vertexBindingDescriptionCount = (uint)cfg.vertexBindings.Count;
+                               vertInputInfo.pVertexBindingDescriptions = cfg.vertexBindings.Pin ();
+                               vertInputInfo.vertexAttributeDescriptionCount = (uint)cfg.vertexAttributes.Count;
+                               vertInputInfo.pVertexAttributeDescriptions = cfg.vertexAttributes.Pin ();
+
+                               VkGraphicsPipelineCreateInfo info = VkGraphicsPipelineCreateInfo.New ();
+                               info.renderPass = RenderPass.handle;
+                               info.layout = Layout.handle;
+                               info.pVertexInputState = vertInputInfo.Pin ();
+                               info.pInputAssemblyState = cfg.inputAssemblyState.Pin ();
+                               info.pRasterizationState = cfg.rasterizationState.Pin ();
+                               info.pColorBlendState = colorBlendInfo.Pin ();
+                               info.pMultisampleState = cfg.multisampleState.Pin ();
+                               info.pViewportState = cfg.viewportState.Pin ();
+                               info.pDepthStencilState = cfg.depthStencilState.Pin ();
+                               info.pDynamicState = dynStatesInfo.Pin ();
+                               info.stageCount = (uint)cfg.shaders.Count;
+                               info.pStages = shaderStages.Pin ();
+                               info.subpass = cfg.SubpassIndex;
+
+                               Utils.CheckResult (vkCreateGraphicsPipelines (Dev.VkDev, Cache == null ? VkPipelineCache.Null : Cache.handle, 1, ref info, IntPtr.Zero, out handle));
+
+                               for (int i = 0; i < cfg.shaders.Count; i++)
+                                       Dev.DestroyShaderModule (shaderStages[i].module);
+
+                               vertInputInfo.Unpin ();
+                               cfg.inputAssemblyState.Unpin ();
+                               cfg.rasterizationState.Unpin ();
+                               colorBlendInfo.Unpin ();
+                               cfg.multisampleState.Unpin ();
+                               cfg.viewportState.Unpin ();
+                               cfg.depthStencilState.Unpin ();
+                               dynStatesInfo.Unpin ();
+                               shaderStages.Unpin ();
+
+                               cfg.vertexAttributes.Unpin ();
+                               cfg.vertexBindings.Unpin ();
+                               cfg.dynamicStates.Unpin ();
+                               cfg.blendAttachments.Unpin ();
+                       }
+                       base.Activate ();
+               }
+
+               public override void Bind (CommandBuffer cmd) {
+                       vkCmdBindPipeline (cmd.Handle, VkPipelineBindPoint.Graphics, handle);
+               }
+               public override void BindDescriptorSet (CommandBuffer cmd, DescriptorSet dset, uint firstSet = 0) {
+                       cmd.BindDescriptorSet (VkPipelineBindPoint.Graphics, layout, dset, firstSet);
+               }
+
+               protected override void Dispose (bool disposing) {
+                       if (disposing) {
+                               if (state == ActivableState.Activated) 
+                                       RenderPass.Dispose ();
+                       }else
+                               System.Diagnostics.Debug.WriteLine ("GraphicPipeline disposed by finalizer");
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/vke/src/base/GraphicPipelineConfig.cs b/vke/src/base/GraphicPipelineConfig.cs
new file mode 100644 (file)
index 0000000..56b74ca
--- /dev/null
@@ -0,0 +1,143 @@
+//
+// PipelineConfig.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+    public class GraphicPipelineConfig {
+               public uint SubpassIndex;
+        public PipelineLayout Layout;
+               public RenderPass RenderPass;
+               public PipelineCache Cache;
+               public VkPipelineBindPoint bindPoint = VkPipelineBindPoint.Graphics;
+        public VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = VkPipelineInputAssemblyStateCreateInfo.New();
+        public VkPipelineRasterizationStateCreateInfo rasterizationState = VkPipelineRasterizationStateCreateInfo.New();
+        public VkPipelineViewportStateCreateInfo viewportState = VkPipelineViewportStateCreateInfo.New();
+        public VkPipelineDepthStencilStateCreateInfo depthStencilState = VkPipelineDepthStencilStateCreateInfo.New();
+        public VkPipelineMultisampleStateCreateInfo multisampleState = VkPipelineMultisampleStateCreateInfo.New();
+        public List<VkPipelineColorBlendAttachmentState> blendAttachments = new List<VkPipelineColorBlendAttachmentState>();
+        public List<VkDynamicState> dynamicStates = new List<VkDynamicState> ();
+        public List<VkVertexInputBindingDescription> vertexBindings = new List<VkVertexInputBindingDescription> ();
+        public List<VkVertexInputAttributeDescription> vertexAttributes = new List<VkVertexInputAttributeDescription> ();
+        public readonly List<ShaderInfo> shaders = new List<ShaderInfo>();
+        
+               public VkSampleCountFlags Samples {
+                       get { return multisampleState.rasterizationSamples; }
+               }
+
+               public GraphicPipelineConfig () {
+
+               }
+
+               /// <summary>
+               /// Create a default pipeline configuration with viewport and scissor as dynamic states. One blend attachment is
+               /// added automatically with blending disabled. (cfg.blendAttachments[0])
+               /// </summary>
+               public static GraphicPipelineConfig CreateDefault (VkPrimitiveTopology topology = VkPrimitiveTopology.TriangleList,
+                       VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1, bool depthTestEnabled = true)
+               {
+                       GraphicPipelineConfig cfg = new GraphicPipelineConfig ();
+
+                       cfg.inputAssemblyState.topology = topology;
+            cfg.multisampleState.rasterizationSamples = samples;
+
+            cfg.rasterizationState.polygonMode = VkPolygonMode.Fill;
+            cfg.rasterizationState.cullMode = (uint)VkCullModeFlags.None;
+            cfg.rasterizationState.frontFace = VkFrontFace.CounterClockwise;
+            cfg.rasterizationState.depthClampEnable = False;
+            cfg.rasterizationState.rasterizerDiscardEnable = False;
+            cfg.rasterizationState.depthBiasEnable = False;
+            cfg.rasterizationState.lineWidth = 1.0f;
+
+            cfg.viewportState.viewportCount = 1;
+            cfg.viewportState.scissorCount = 1;
+
+            cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false));
+
+            cfg.dynamicStates.Add (VkDynamicState.Viewport);
+            cfg.dynamicStates.Add (VkDynamicState.Scissor);
+
+                       if (depthTestEnabled) {
+                               cfg.depthStencilState.depthTestEnable = True;
+                               cfg.depthStencilState.depthWriteEnable = True;
+                               cfg.depthStencilState.depthCompareOp = VkCompareOp.LessOrEqual;
+                               cfg.depthStencilState.depthBoundsTestEnable = False;
+                               cfg.depthStencilState.back.failOp = VkStencilOp.Keep;
+                               cfg.depthStencilState.back.passOp = VkStencilOp.Keep;
+                               cfg.depthStencilState.back.compareOp = VkCompareOp.Always;
+                               cfg.depthStencilState.stencilTestEnable = False;
+                               cfg.depthStencilState.front = cfg.depthStencilState.back;
+                       }
+
+                       return cfg;
+               }
+
+        uint currentAttributeIndex = 0;
+        public void AddVertexAttributes (uint binding, params VkFormat[] attribsDesc) {
+            uint currentAttributeoffset = 0;
+            for (uint i = 0; i < attribsDesc.Length; i++) {
+                               vertexAttributes.Add (new VkVertexInputAttributeDescription (binding, i + currentAttributeIndex, attribsDesc[i], currentAttributeoffset));
+                               VkFormatSize fs;
+                               Utils.vkGetFormatSize (attribsDesc[i], out fs);
+                               currentAttributeoffset += fs.blockSizeInBits/8;
+                       }
+            currentAttributeIndex += (uint)attribsDesc.Length;
+               }
+               public void AddVertexBinding (uint binding, uint stride, VkVertexInputRate inputRate = VkVertexInputRate.Vertex) { 
+                       vertexBindings.Add (new VkVertexInputBindingDescription (binding, stride, inputRate));
+               }
+               public void AddVertexBinding<T> (uint binding = 0, VkVertexInputRate inputRate = VkVertexInputRate.Vertex) { 
+                       vertexBindings.Add (new VkVertexInputBindingDescription (binding, (uint)Marshal.SizeOf<T> (), inputRate));
+               }
+               /// <summary>
+               /// Automatically configure Attribute for that binding.
+               /// </summary>
+               public void AddVertex<T> (uint binding = 0, VkVertexInputRate inputRate = VkVertexInputRate.Vertex) {
+                       vertexBindings.Add (new VkVertexInputBindingDescription (binding, (uint)Marshal.SizeOf<T> (), inputRate));
+                       FieldInfo[] fields = typeof (T).GetFields ();
+                       VkFormat[] attribs = new VkFormat[fields.Length];
+                       for (int i = 0; i < fields.Length; i++) 
+                               attribs[i] = fields[i].GetCustomAttribute<VertexAttributeAttribute> ().Format;
+                       AddVertexAttributes (binding, attribs);
+               }
+               public void AddShader (VkShaderStageFlags _stageFlags, string _spirvPath, SpecializationInfo specializationInfo = null, string _entryPoint = "main") {
+                       shaders.Add (new ShaderInfo (_stageFlags, _spirvPath, specializationInfo, _entryPoint));
+               }
+
+               public void ResetShadersAndVerticesInfos () {
+                       foreach (ShaderInfo shader in shaders) 
+                               shader.Dispose ();
+            currentAttributeIndex = 0;
+            vertexBindings.Clear ();
+                       vertexAttributes.Clear ();
+                       shaders.Clear ();
+               }
+       }
+}
diff --git a/vke/src/base/HostBuffer.cs b/vke/src/base/HostBuffer.cs
new file mode 100644 (file)
index 0000000..20734c8
--- /dev/null
@@ -0,0 +1,109 @@
+//
+// HostBuffer.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       
+       public class HostBuffer<T> : HostBuffer {
+        int TSize;
+
+               public HostBuffer (Device device, VkBufferUsageFlags usage, uint arrayElementCount, bool keepMapped = false, bool coherentMem = true)
+            : base (device, usage, (ulong)(Marshal.SizeOf<T> () * arrayElementCount), keepMapped, coherentMem) {
+            TSize = Marshal.SizeOf<T>();
+        }
+               public HostBuffer (Device device, VkBufferUsageFlags usage, IList<T> data, bool keepMapped = false, bool coherentMem = true)
+            : base (device, usage, (ulong)(Marshal.SizeOf<T> () * data.Count), keepMapped, coherentMem) {
+            TSize = Marshal.SizeOf<T>();
+            Map();
+            Update (data, createInfo.size);
+                       if (!keepMapped)
+               Unmap ();
+        }
+        public HostBuffer (Device device, VkBufferUsageFlags usage, T[] data, bool keepMapped = false, bool coherentMem = true)
+            : base (device, usage, (ulong)(Marshal.SizeOf<T> () * data.Length), keepMapped, coherentMem) {
+            TSize = Marshal.SizeOf<T>();
+            Map();
+            Update (data, createInfo.size);
+                       if (!keepMapped)
+               Unmap ();
+        }
+               public void Update (T[] data) {
+                       Update (data, (ulong)(TSize * data.Length));
+               }
+        public void Update (uint index, T data) {
+            GCHandle ptr = GCHandle.Alloc(data, GCHandleType.Pinned);
+            unsafe {
+                System.Buffer.MemoryCopy(ptr.AddrOfPinnedObject().ToPointer(), (mappedData + (int)(TSize*index)).ToPointer(), TSize, TSize);
+            }
+            ptr.Free();
+        }
+        public void Flush (uint startIndex, uint endIndex) {
+            VkMappedMemoryRange mr = new VkMappedMemoryRange
+            {
+                sType = VkStructureType.MappedMemoryRange,
+#if MEMORY_POOLS
+                               memory = memoryPool.vkMemory,
+                offset = poolOffset + (ulong)(startIndex * TSize),
+#else
+                               memory = vkMemory,
+                               offset = (ulong)(startIndex * TSize),
+#endif
+                size = (ulong)((endIndex - startIndex) * TSize)
+            };
+            vkFlushMappedMemoryRanges(Dev.VkDev, 1, ref mr);
+        }
+    }
+       /// <summary>
+       /// Mappable Buffer with HostVisble and HostCoherent memory flags
+       /// </summary>
+       public class HostBuffer : Buffer {
+        public HostBuffer (Device device, VkBufferUsageFlags usage, UInt64 size, bool keepMapped = false, bool coherentMem = true)
+                    : base (device, usage, coherentMem ? VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent : VkMemoryPropertyFlags.HostVisible, size) {
+            if (keepMapped)
+                Map();
+        }
+        public HostBuffer (Device device, VkBufferUsageFlags usage, object data, bool keepMapped = false, bool coherentMem = true)
+            : base (device, usage, coherentMem ? VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent : VkMemoryPropertyFlags.HostVisible, (ulong)Marshal.SizeOf(data)) {
+            Map ();
+            Update (data, createInfo.size);
+                       if (!keepMapped)
+               Unmap ();
+        }
+        public HostBuffer (Device device, VkBufferUsageFlags usage, UInt64 size, IntPtr data, bool keepMapped = false, bool coherentMem = true)
+            : base (device, usage, coherentMem ? VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent : VkMemoryPropertyFlags.HostVisible, size) {
+            Map ();
+            unsafe {
+                System.Buffer.MemoryCopy (data.ToPointer (), mappedData.ToPointer (), size, size);
+            }
+                       if (!keepMapped)
+               Unmap ();
+        }
+    }  
+}
diff --git a/vke/src/base/Image.cs b/vke/src/base/Image.cs
new file mode 100644 (file)
index 0000000..3787e3c
--- /dev/null
@@ -0,0 +1,643 @@
+//
+// Buffer.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Diagnostics;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Combined Image/Descriptor class. Optional Sampler and View are disposed with the vkImage. If multiple view/sampler have to be
+       /// created for the same vkImage, you may call the constructor accepting a vkImage as parameter to import an existing one. vkImage handle of
+       /// such imported image will not be disposed with the sampler and the view.
+       /// </summary>
+       public class Image : Resource {
+               public static VkFormat DefaultTextureFormat = VkFormat.R8g8b8a8Unorm;
+
+               internal VkImage handle; 
+        VkImageCreateInfo info = VkImageCreateInfo.New();
+
+               /// <summary>
+               /// if true, the vkImage handle will not be destroyed on dispose, useful to create image for swapchain
+               /// </summary>
+        bool imported;
+
+        public VkDescriptorImageInfo Descriptor;
+        public VkImageCreateInfo CreateInfo => info;
+        public VkExtent3D Extent => info.extent;
+        public VkFormat Format => info.format;
+               public VkImage Handle => handle;
+               public uint Width => CreateInfo.extent.width;
+               public uint Height => CreateInfo.extent.height;
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.ImageEXT, handle.Handle);
+
+               #region CTORS
+        public Image (Device device, VkFormat format, VkImageUsageFlags usage, VkMemoryPropertyFlags _memoryPropertyFlags,
+            uint width, uint height,
+            VkImageType type = VkImageType.Image2D, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1,
+            VkImageTiling tiling = VkImageTiling.Optimal, uint mipsLevels = 1, uint layers = 1, uint depth = 1,
+                       VkImageCreateFlags createFlags = 0)
+            : base (device, _memoryPropertyFlags) {
+
+            info.imageType = type;
+            info.format = format;
+            info.extent.width = width;
+            info.extent.height = height;
+            info.extent.depth = depth;
+            info.mipLevels = mipsLevels;
+            info.arrayLayers = layers;
+            info.samples = samples;
+            info.tiling = tiling;
+            info.usage = usage;
+            info.initialLayout = (tiling == VkImageTiling.Optimal) ? VkImageLayout.Undefined : VkImageLayout.Preinitialized;
+            info.sharingMode = VkSharingMode.Exclusive;
+                       info.flags = createFlags;
+
+            Activate ();//DONT OVERRIDE Activate in derived classes!!!!
+        }
+
+               /// <summary>
+               /// Import vkImage handle into a new Image class, handle will be preserve on destruction.
+               /// </summary>
+               public Image (Device device, VkImage vkHandle, VkFormat format, VkImageUsageFlags usage, uint width, uint height)
+               : base (device, VkMemoryPropertyFlags.DeviceLocal) {
+                       info.imageType = VkImageType.Image2D;
+                       info.format = format;
+                       info.extent.width = width;
+                       info.extent.height = height;
+                       info.extent.depth = 1;
+                       info.mipLevels = 1;
+                       info.arrayLayers = 1;
+                       info.samples = VkSampleCountFlags.SampleCount1;
+                       info.tiling = VkImageTiling.Optimal;
+                       info.usage = usage;
+
+                       handle = vkHandle;
+                       imported = true;
+
+                       state = ActivableState.Activated;
+                       references++;//increment ref because it is bound to swapchain
+               }
+               #endregion
+
+               public static uint ComputeMipLevels(uint size) => (uint)Math.Floor (Math.Log (size)) + 1;
+               public static uint ComputeMipLevels (int width, int height) =>  (uint)Math.Floor (Math.Log (Math.Max (width, height))) + 1;
+
+               public static bool CheckFormatIsSupported (VkImageUsageFlags usage, VkFormatFeatureFlags phyFormatSupport) {
+                       if (usage.HasFlag (VkImageUsageFlags.TransferSrc) & !phyFormatSupport.HasFlag (VkFormatFeatureFlags.TransferSrc))
+                               return false;
+                       if (usage.HasFlag (VkImageUsageFlags.TransferDst) & !phyFormatSupport.HasFlag (VkFormatFeatureFlags.TransferSrc))
+                               return false;
+                       if (usage.HasFlag (VkImageUsageFlags.Sampled) & !phyFormatSupport.HasFlag (VkFormatFeatureFlags.SampledImage))
+                               return false;
+                       if (usage.HasFlag (VkImageUsageFlags.Storage) & !phyFormatSupport.HasFlag (VkFormatFeatureFlags.StorageImage))
+                               return false;
+                       if (usage.HasFlag (VkImageUsageFlags.ColorAttachment) & !phyFormatSupport.HasFlag (VkFormatFeatureFlags.ColorAttachment))
+                               return false;
+                       if (usage.HasFlag (VkImageUsageFlags.DepthStencilAttachment) & !phyFormatSupport.HasFlag (VkFormatFeatureFlags.DepthStencilAttachment))
+                               return false;
+                       /*if (usage.HasFlag (VkImageUsageFlags.TransientAttachment) ^ phyFormatSupport.HasFlag (VkFormatFeatureFlags.))
+                               return false;*/
+                       if (usage.HasFlag (VkImageUsageFlags.InputAttachment) & !phyFormatSupport.HasFlag (VkFormatFeatureFlags.SampledImage))
+                               return false;
+                       /*if (usage.HasFlag (VkImageUsageFlags.ShadingRateImageNV) ^ phyFormatSupport.HasFlag (VkFormatFeatureFlags.TransferSrc))
+                               return false;
+                       if (usage.HasFlag (VkImageUsageFlags.FragmentDensityMapEXT) ^ phyFormatSupport.HasFlag (VkFormatFeatureFlags.TransferSrc))
+                               return false;*/
+                       return true;
+               }
+               /// <summary>
+               /// Load image from byte array containing full image file (jpg, png,...)
+               /// </summary>
+               public static Image Load (Device dev, Queue staggingQ, CommandPool staggingCmdPool,
+                       byte[] bitmap, VkFormat format = VkFormat.Undefined,
+                       VkMemoryPropertyFlags memoryProps = VkMemoryPropertyFlags.DeviceLocal,
+                       VkImageTiling tiling = VkImageTiling.Optimal, bool generateMipmaps = true,
+                       VkImageType imageType = VkImageType.Image2D,
+                       VkImageUsageFlags usage = VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst) {
+
+                       Image img = Load (dev, staggingQ, staggingCmdPool, bitmap.Pin (), (ulong)bitmap.Length, format, memoryProps, tiling, generateMipmaps,
+                               imageType, usage);
+                       bitmap.Unpin ();
+                       return img;
+               }
+
+               #region bitmap loading
+               /// <summary>
+               /// Load image from data pointed by IntPtr pointer containing full image file (jpg, png,...)
+               /// </summary>
+               public static Image Load (Device dev, Queue staggingQ, CommandPool staggingCmdPool,
+                       IntPtr bitmap, ulong bitmapByteCount, VkFormat format = VkFormat.Undefined,
+                       VkMemoryPropertyFlags memoryProps = VkMemoryPropertyFlags.DeviceLocal,
+                       VkImageTiling tiling = VkImageTiling.Optimal, bool generateMipmaps = true,
+                       VkImageType imageType = VkImageType.Image2D,
+                       VkImageUsageFlags usage = VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst) {
+
+                       if (format == VkFormat.Undefined)
+                               format = DefaultTextureFormat;
+                       if (tiling == VkImageTiling.Optimal)
+                               usage |= VkImageUsageFlags.TransferDst;
+                       if (generateMipmaps)
+                               usage |= (VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst);
+
+                       using (StbImage stbi = new StbImage (bitmap, bitmapByteCount)) {
+                               uint mipLevels = generateMipmaps ? ComputeMipLevels (stbi.Width, stbi.Height) : 1;
+
+                               Image img = new Image (dev, format, usage, memoryProps, (uint)stbi.Width, (uint)stbi.Height, imageType,
+                                       VkSampleCountFlags.SampleCount1, tiling, mipLevels);
+
+                               img.load (staggingQ, staggingCmdPool, stbi.Handle, generateMipmaps);
+
+                               return img;
+                       }
+               }
+
+               /// <summary>
+               /// Load bitmap into Image with stagging and mipmap generation if necessary
+               /// and usage.
+               /// </summary>
+               public static Image Load (Device dev, Queue staggingQ, CommandPool staggingCmdPool,
+                       string path, VkFormat format = VkFormat.Undefined,
+                       VkMemoryPropertyFlags memoryProps = VkMemoryPropertyFlags.DeviceLocal,
+                       VkImageTiling tiling = VkImageTiling.Optimal, bool generateMipmaps = true,
+                       VkImageType imageType = VkImageType.Image2D,
+                       VkImageUsageFlags usage = VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst) {
+
+                       if (format == VkFormat.Undefined)
+                               format = DefaultTextureFormat;
+                       if (tiling == VkImageTiling.Optimal)
+                               usage |= VkImageUsageFlags.TransferDst;
+                       if (generateMipmaps)
+                               usage |= (VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst);
+
+                       using (StbImage stbi = new StbImage (path)) {
+                               uint mipLevels = generateMipmaps ? ComputeMipLevels (stbi.Width, stbi.Height) : 1;
+
+                               Image img = new Image (dev, format, usage, memoryProps, (uint)stbi.Width, (uint)stbi.Height, imageType,
+                                       VkSampleCountFlags.SampleCount1, tiling, mipLevels);
+
+                               img.load (staggingQ, staggingCmdPool, stbi.Handle, generateMipmaps);
+
+                               return img;
+                       }
+               }
+
+               /// <summary>
+               /// create host visible linear image without command from path
+               /// </summary>
+               public static Image Load (Device dev,
+                       string path, VkImageUsageFlags usage = VkImageUsageFlags.Sampled, bool reserveSpaceForMipmaps = true, VkFormat format = VkFormat.Undefined,
+                       VkMemoryPropertyFlags memoryProps = VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent,
+                       VkImageTiling tiling = VkImageTiling.Linear, VkImageType imageType = VkImageType.Image2D) {
+
+                       if (format == VkFormat.Undefined)
+                               format = DefaultTextureFormat;
+
+                       using (StbImage stbi = new StbImage (path)) {
+                               uint mipLevels = reserveSpaceForMipmaps ? ComputeMipLevels (stbi.Width, stbi.Height) : 1;
+
+                               Image img = new Image (dev, format, usage, memoryProps, (uint)stbi.Width, (uint)stbi.Height, imageType,
+                                       VkSampleCountFlags.SampleCount1, tiling, mipLevels);
+
+                               img.Map ();
+                               stbi.CoptyTo (img.MappedData);
+                               img.Unmap ();
+
+                               return img;
+                       }
+        }
+               /// <summary>
+               /// create host visible linear image without command from byte array
+               /// </summary>
+               public static Image Load (Device dev,
+                       byte[] bitmap, VkImageUsageFlags usage = VkImageUsageFlags.TransferSrc,
+                       VkFormat format = VkFormat.Undefined,
+                       VkMemoryPropertyFlags memoryProps = VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent,
+                       VkImageTiling tiling = VkImageTiling.Linear, bool generateMipmaps = false,
+                       VkImageType imageType = VkImageType.Image2D) {
+
+                       Image img = Load (dev, bitmap.Pin (), (ulong)bitmap.Length, usage, format, memoryProps, tiling, generateMipmaps,
+                               imageType);
+                       bitmap.Unpin ();
+                       return img;
+               }
+               /// <summary>
+               /// create host visible linear image without command from data pointed by IntPtr pointer containing full image file (jpg, png,...)
+               /// </summary>
+               public static Image Load (Device dev,
+                       IntPtr bitmap, ulong bitmapByteCount, VkImageUsageFlags usage = VkImageUsageFlags.TransferSrc,
+                       VkFormat format = VkFormat.Undefined,
+                       VkMemoryPropertyFlags memoryProps = VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent,
+                       VkImageTiling tiling = VkImageTiling.Linear, bool generateMipmaps = false,
+                       VkImageType imageType = VkImageType.Image2D) {
+
+                       if (format == VkFormat.Undefined)
+                               format = DefaultTextureFormat;
+                       if (generateMipmaps)
+                               usage |= (VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst);
+
+                       using (StbImage stbi = new StbImage (bitmap, bitmapByteCount)) {
+                               uint mipLevels = generateMipmaps ? ComputeMipLevels (stbi.Width, stbi.Height) : 1;
+
+                               Image img = new Image (dev, format, usage, memoryProps, (uint)stbi.Width, (uint)stbi.Height, imageType,
+                                       VkSampleCountFlags.SampleCount1, tiling, mipLevels);
+
+                               img.Map ();
+                               stbi.CoptyTo (img.MappedData);
+                               img.Unmap ();
+
+                               return img;
+                       }
+               }
+
+               /// <summary>
+               /// load bitmap from pointer
+               /// </summary>
+               void load (Queue staggingQ, CommandPool staggingCmdPool, IntPtr bitmap, bool generateMipmaps = true) {
+                       long size = info.extent.width * info.extent.height * 4 * info.extent.depth;
+
+                       if (MemoryFlags.HasFlag (VkMemoryPropertyFlags.HostVisible)) {
+                               Map ();
+                               unsafe {
+                                       System.Buffer.MemoryCopy (bitmap.ToPointer (), MappedData.ToPointer (), size, size);
+                               }
+                               Unmap ();
+
+                               if (generateMipmaps)
+                                       BuildMipmaps (staggingQ, staggingCmdPool);
+                       } else {
+                               using (HostBuffer stagging = new HostBuffer (Dev, VkBufferUsageFlags.TransferSrc, (UInt64)size, bitmap)) {
+
+                                       CommandBuffer cmd = staggingCmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+
+                                       stagging.CopyTo (cmd, this);
+                                       if (generateMipmaps)
+                                               BuildMipmaps (cmd);
+
+                                       cmd.End ();
+                                       staggingQ.Submit (cmd);
+                                       staggingQ.WaitIdle ();
+                                       cmd.Free ();
+                               }
+                       }
+               }
+
+               #endregion
+
+               internal override void updateMemoryRequirements () {            
+            vkGetImageMemoryRequirements (Dev.VkDev, handle, out memReqs);            
+        }
+               internal override void bindMemory () {
+#if MEMORY_POOLS
+                       Utils.CheckResult (vkBindImageMemory (Dev.VkDev, handle, memoryPool.vkMemory, poolOffset));
+#else
+                       Utils.CheckResult (vkBindImageMemory (Dev.VkDev, handle, vkMemory, 0));
+#endif
+               }
+        public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               Utils.CheckResult (vkCreateImage (Dev.VkDev, ref info, IntPtr.Zero, out handle));
+#if MEMORY_POOLS
+                               Dev.resourceManager.Add (this);
+#else
+                               updateMemoryRequirements ();
+                               allocateMemory ();
+                               bindMemory ();
+#endif
+
+                       }
+            base.Activate ();
+        }
+
+               public void CreateView (VkImageViewType type = VkImageViewType.ImageView2D, VkImageAspectFlags aspectFlags = VkImageAspectFlags.Color,
+                       uint layerCount = 1,
+            uint baseMipLevel = 0, int levelCount = -1, uint baseArrayLayer = 0,
+                       VkComponentSwizzle r = VkComponentSwizzle.R,
+                       VkComponentSwizzle g = VkComponentSwizzle.G,
+                       VkComponentSwizzle b = VkComponentSwizzle.B,
+                       VkComponentSwizzle a = VkComponentSwizzle.A) {
+
+            VkImageView view = default(VkImageView);
+            VkImageViewCreateInfo viewInfo = VkImageViewCreateInfo.New();
+            viewInfo.image = handle;
+            viewInfo.viewType = type;
+            viewInfo.format = Format;
+            viewInfo.components.r = r;
+            viewInfo.components.g = g;
+            viewInfo.components.b = b;
+            viewInfo.components.a = a;
+            viewInfo.subresourceRange.aspectMask = aspectFlags;
+                       viewInfo.subresourceRange.baseMipLevel = baseMipLevel;
+                       viewInfo.subresourceRange.levelCount = levelCount < 0 ? info.mipLevels : (uint)levelCount;
+                       viewInfo.subresourceRange.baseArrayLayer = baseArrayLayer;
+                       viewInfo.subresourceRange.layerCount = layerCount;
+
+            Utils.CheckResult (vkCreateImageView (Dev.VkDev, ref viewInfo, IntPtr.Zero, out view));
+
+            if (Descriptor.imageView.Handle != 0)
+                Dev.DestroyImageView (Descriptor.imageView);
+            Descriptor.imageView = view;
+        }
+
+               public void CreateSampler (VkSamplerAddressMode addressMode, VkFilter minFilter = VkFilter.Linear,
+                               VkFilter magFilter = VkFilter.Linear, VkSamplerMipmapMode mipmapMode = VkSamplerMipmapMode.Linear,
+                               float maxAnisotropy = 1.0f, float minLod = 0.0f, float maxLod = -1f) {
+                       CreateSampler (minFilter, magFilter, mipmapMode, addressMode, maxAnisotropy, minLod, maxLod);
+               }
+
+               public void CreateSampler (VkFilter minFilter = VkFilter.Linear, VkFilter magFilter = VkFilter.Linear,
+                               VkSamplerMipmapMode mipmapMode = VkSamplerMipmapMode.Linear, VkSamplerAddressMode addressMode = VkSamplerAddressMode.Repeat,
+            float maxAnisotropy = 1.0f, float minLod = 0.0f, float maxLod = -1f) {
+            VkSampler sampler;
+            VkSamplerCreateInfo sampInfo = VkSamplerCreateInfo.New();
+            sampInfo.maxAnisotropy = maxAnisotropy;
+                       sampInfo.maxAnisotropy = 1.0f;// device->enabledFeatures.samplerAnisotropy ? device->properties.limits.maxSamplerAnisotropy : 1.0f;
+                       //samplerInfo.anisotropyEnable = device->enabledFeatures.samplerAnisotropy;
+                       sampInfo.addressModeU = addressMode;
+            sampInfo.addressModeV = addressMode;
+            sampInfo.addressModeW = addressMode;
+            sampInfo.magFilter = magFilter;
+            sampInfo.minFilter = minFilter;
+            sampInfo.mipmapMode = mipmapMode;
+            sampInfo.minLod = minLod;
+            sampInfo.maxLod = maxLod < 0f ? info.mipLevels > 1 ? info.mipLevels : 0 : maxLod;
+                       sampInfo.compareOp = VkCompareOp.Never;
+                       sampInfo.borderColor = VkBorderColor.FloatOpaqueWhite;
+
+            Utils.CheckResult (vkCreateSampler (Dev.VkDev, ref sampInfo, IntPtr.Zero, out sampler));
+
+            if (Descriptor.sampler.Handle != 0)
+                Dev.DestroySampler (Descriptor.sampler);
+            Descriptor.sampler = sampler;
+        }
+
+        public void SetLayout (
+            CommandBuffer cmdbuffer,
+            VkImageAspectFlags aspectMask,
+            VkImageLayout oldImageLayout,
+            VkImageLayout newImageLayout,
+            VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands,
+            VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) {
+            VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange {
+                aspectMask = aspectMask,
+                baseMipLevel = 0,
+                levelCount = CreateInfo.mipLevels,
+                layerCount = CreateInfo.arrayLayers,
+            };
+            SetLayout (cmdbuffer, oldImageLayout, newImageLayout, subresourceRange, srcStageMask, dstStageMask);
+        }
+               public void SetLayout (
+                       CommandBuffer cmdbuffer,
+                       VkImageAspectFlags aspectMask,
+                       VkAccessFlags srcAccessMask,
+                       VkAccessFlags dstAccessMask,
+                       VkImageLayout oldImageLayout,
+                       VkImageLayout newImageLayout,
+                       VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands,
+                       VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) {
+                       VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange {
+                               aspectMask = aspectMask,
+                               baseMipLevel = 0,
+                               levelCount = CreateInfo.mipLevels,
+                               layerCount = CreateInfo.arrayLayers,
+                       };
+                       SetLayout (cmdbuffer, srcAccessMask, dstAccessMask, oldImageLayout, newImageLayout, subresourceRange, srcStageMask, dstStageMask);
+               }
+               public void SetLayout (
+                       CommandBuffer cmdbuffer,
+                       VkAccessFlags srcAccessMask,
+                       VkAccessFlags dstAccessMask,
+                       VkImageLayout oldImageLayout,
+                       VkImageLayout newImageLayout,
+                       VkImageSubresourceRange subresourceRange,
+                       VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands,
+                       VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) {
+
+                       VkImageMemoryBarrier imageMemoryBarrier = VkImageMemoryBarrier.New ();
+                       imageMemoryBarrier.srcQueueFamilyIndex = Vk.QueueFamilyIgnored;
+                       imageMemoryBarrier.dstQueueFamilyIndex = Vk.QueueFamilyIgnored;
+                       imageMemoryBarrier.oldLayout = oldImageLayout;
+                       imageMemoryBarrier.newLayout = newImageLayout;
+                       imageMemoryBarrier.image = handle;
+                       imageMemoryBarrier.subresourceRange = subresourceRange;
+                       imageMemoryBarrier.srcAccessMask = srcAccessMask;
+                       imageMemoryBarrier.dstAccessMask = dstAccessMask;
+
+                       Vk.vkCmdPipelineBarrier (
+                               cmdbuffer.Handle,
+                               srcStageMask,
+                               dstStageMask,
+                               0,
+                               0, IntPtr.Zero,
+                               0, IntPtr.Zero,
+                               1, ref imageMemoryBarrier);
+
+               }
+               // Create an image memory barrier for changing the layout of
+               // an image and put it into an active command buffer
+               // See chapter 11.4 "Image Layout" for details
+               public void SetLayout (
+            CommandBuffer cmdbuffer,            
+            VkImageLayout oldImageLayout,
+            VkImageLayout newImageLayout,
+            VkImageSubresourceRange subresourceRange,
+            VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands,
+            VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) {
+            // Create an image barrier object
+            VkImageMemoryBarrier imageMemoryBarrier = VkImageMemoryBarrier.New();
+            imageMemoryBarrier.srcQueueFamilyIndex = Vk.QueueFamilyIgnored;
+            imageMemoryBarrier.dstQueueFamilyIndex = Vk.QueueFamilyIgnored;
+            imageMemoryBarrier.oldLayout = oldImageLayout;
+            imageMemoryBarrier.newLayout = newImageLayout;
+            imageMemoryBarrier.image = handle;
+            imageMemoryBarrier.subresourceRange = subresourceRange;
+
+            // Source layouts (old)
+            // Source access mask controls actions that have to be finished on the old layout
+            // before it will be transitioned to the new layout
+            switch (oldImageLayout) {
+                case VkImageLayout.Undefined:
+                    // Image layout is undefined (or does not matter)
+                    // Only valid as initial layout
+                    // No flags required, listed only for completeness
+                    imageMemoryBarrier.srcAccessMask = 0;
+                    break;
+
+                case VkImageLayout.Preinitialized:
+                    // Image is preinitialized
+                    // Only valid as initial layout for linear images, preserves memory contents
+                    // Make sure host writes have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite;
+                    break;
+
+                case VkImageLayout.ColorAttachmentOptimal:
+                    // Image is a color attachment
+                    // Make sure any writes to the color buffer have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.ColorAttachmentWrite;
+                    break;
+
+                case VkImageLayout.DepthStencilAttachmentOptimal:
+                    // Image is a depth/stencil attachment
+                    // Make sure any writes to the depth/stencil buffer have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.DepthStencilAttachmentWrite;
+                    break;
+
+                case VkImageLayout.TransferSrcOptimal:
+                    // Image is a transfer source 
+                    // Make sure any reads from the image have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead;
+                    break;
+
+                case VkImageLayout.TransferDstOptimal:
+                    // Image is a transfer destination
+                    // Make sure any writes to the image have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferWrite;
+                    break;
+
+                case VkImageLayout.ShaderReadOnlyOptimal:
+                    // Image is read by a shader
+                    // Make sure any shader reads from the image have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.ShaderRead;
+                    break;
+            }
+
+            // Target layouts (new)
+            // Destination access mask controls the dependency for the new image layout
+            switch (newImageLayout) {
+                case VkImageLayout.TransferDstOptimal:
+                    // Image will be used as a transfer destination
+                    // Make sure any writes to the image have been finished
+                    imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferWrite;
+                    break;
+
+                case VkImageLayout.TransferSrcOptimal:
+                    // Image will be used as a transfer source
+                    // Make sure any reads from and writes to the image have been finished
+                    imageMemoryBarrier.srcAccessMask = imageMemoryBarrier.srcAccessMask | VkAccessFlags.TransferRead;
+                    imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferRead;
+                    break;
+
+                case VkImageLayout.ColorAttachmentOptimal:
+                    // Image will be used as a color attachment
+                    // Make sure any writes to the color buffer have been finished
+                    imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead;
+                    imageMemoryBarrier.dstAccessMask = VkAccessFlags.ColorAttachmentWrite;
+                    break;
+
+                case VkImageLayout.DepthStencilAttachmentOptimal:
+                    // Image layout will be used as a depth/stencil attachment
+                    // Make sure any writes to depth/stencil buffer have been finished
+                    imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VkAccessFlags.DepthStencilAttachmentWrite;
+                    break;
+
+                case VkImageLayout.ShaderReadOnlyOptimal:
+                    // Image will be read in a shader (sampler, input attachment)
+                    // Make sure any writes to the image have been finished
+                    if (imageMemoryBarrier.srcAccessMask == 0) {
+                        imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite | VkAccessFlags.TransferWrite;
+                    }
+                    imageMemoryBarrier.dstAccessMask = VkAccessFlags.ShaderRead;
+                    break;
+            }
+
+            // Put barrier inside setup command buffer
+            Vk.vkCmdPipelineBarrier (
+                cmdbuffer.Handle,
+                srcStageMask,
+                dstStageMask,
+                0,
+                0,IntPtr.Zero,
+                0, IntPtr.Zero,
+                1, ref imageMemoryBarrier);
+        }
+
+               public void BuildMipmaps (Queue copyQ, CommandPool copyCmdPool) {
+                       if (info.mipLevels == 1) {
+                               Debug.WriteLine ("Invoking BuildMipmaps on image that has only one mipLevel");
+                               return;
+                       }
+                       CommandBuffer cmd = copyCmdPool.AllocateCommandBuffer ();
+
+                       cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit);
+                       BuildMipmaps (cmd);
+                       cmd.End ();
+
+                       copyQ.Submit (cmd);
+                       copyQ.WaitIdle ();
+
+                       cmd.Free ();
+               }
+               public void BuildMipmaps (CommandBuffer cmd) {
+                       VkImageSubresourceRange mipSubRange = new VkImageSubresourceRange (VkImageAspectFlags.Color, 0, 1, 0, info.arrayLayers);
+
+                       for (int i = 1; i < info.mipLevels; i++) {
+                               VkImageBlit imageBlit = new VkImageBlit {
+                                       srcSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, info.arrayLayers, (uint)i - 1),
+                                       srcOffsets_1 = new VkOffset3D ((int)info.extent.width >> (i - 1), (int)info.extent.height >> (i - 1), 1),
+                                       dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, info.arrayLayers, (uint)i),
+                                       dstOffsets_1 = new VkOffset3D ((int)info.extent.width >> i, (int)info.extent.height >> i, 1)
+                               };
+
+                               SetLayout (cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.TransferSrcOptimal, mipSubRange,
+                                       VkPipelineStageFlags.Transfer, VkPipelineStageFlags.Transfer);
+                               vkCmdBlitImage (cmd.Handle, handle, VkImageLayout.TransferSrcOptimal, handle, VkImageLayout.TransferDstOptimal, 1, ref imageBlit, VkFilter.Linear);
+                               mipSubRange.baseMipLevel = (uint)i;
+                       }
+                       SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.TransferSrcOptimal, VkImageLayout.ShaderReadOnlyOptimal,
+                                       VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader);
+               }
+               public void BlitTo (CommandBuffer cmd, Image dest) {
+                       VkImageBlit imageBlit = new VkImageBlit {
+                               srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, info.arrayLayers, 0),
+                               srcOffsets_1 = new VkOffset3D ((int)info.extent.width, (int)info.extent.height, 1),
+                               dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, info.arrayLayers, 0),
+                               dstOffsets_1 = new VkOffset3D ((int)dest.info.extent.width, (int)dest.info.extent.height, 1)
+                       };
+                       vkCmdBlitImage (cmd.Handle, handle, VkImageLayout.TransferSrcOptimal, dest.handle, VkImageLayout.TransferDstOptimal, 1, ref imageBlit, VkFilter.Linear);
+               }
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+               #region IDisposable Support
+        protected override void Dispose (bool disposing) {
+                       if (state == ActivableState.Activated) {
+                               if (Descriptor.sampler.Handle != 0)
+                    Dev.DestroySampler (Descriptor.sampler);
+                if (Descriptor.imageView.Handle != 0)
+                    Dev.DestroyImageView (Descriptor.imageView);
+                               if (!imported) {
+                                       base.Dispose (disposing);
+                                       Dev.DestroyImage (handle);
+                               }
+                       }
+                       state = ActivableState.Disposed;
+        }
+               #endregion
+       }
+}
diff --git a/vke/src/base/Instance.cs b/vke/src/base/Instance.cs
new file mode 100644 (file)
index 0000000..d8a06d3
--- /dev/null
@@ -0,0 +1,192 @@
+//
+// Instance.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Vulkan Instance disposable class
+       /// </summary>
+    public class Instance : IDisposable {
+               /// <summary>If true, the VK_LAYER_KHRONOS_validation layer is loaded at startup; </summary>
+               public static bool VALIDATION;
+               /// <summary>If true, the VK_EXT_debug_utils and VK_EXT_debug_report instance extensions are enabled</summary>
+               public static bool DEBUG_UTILS;
+               /// <summary>If true, the VK_LAYER_RENDERDOC_Capture layer is loaded at startup; </summary>
+               public static bool RENDER_DOC_CAPTURE;
+
+        VkInstance inst;
+
+               public IntPtr Handle => inst.Handle;
+               public VkInstance VkInstance => inst;
+
+
+               static class Strings {
+            public static FixedUtf8String Name = "VKENGINE";
+            public static FixedUtf8String VK_KHR_SURFACE_EXTENSION_NAME = "VK_KHR_surface";
+            public static FixedUtf8String VK_KHR_WIN32_SURFACE_EXTENSION_NAME = "VK_KHR_win32_surface";
+            public static FixedUtf8String VK_KHR_XCB_SURFACE_EXTENSION_NAME = "VK_KHR_xcb_surface";
+            public static FixedUtf8String VK_KHR_XLIB_SURFACE_EXTENSION_NAME = "VK_KHR_xlib_surface";
+            public static FixedUtf8String VK_KHR_SWAPCHAIN_EXTENSION_NAME = "VK_KHR_swapchain";
+            public static FixedUtf8String VK_EXT_DEBUG_REPORT_EXTENSION_NAME = "VK_EXT_debug_report";
+                       public static FixedUtf8String VK_EXT_DEBUG_UTILS_EXTENSION_NAME = "VK_EXT_debug_utils";
+                       public static FixedUtf8String LayerValidation = "VK_LAYER_KHRONOS_validation";
+                       public static FixedUtf8String LayerValidation_old = "VK_LAYER_LUNARG_standard_validation"; 
+                       public static FixedUtf8String LayerMonitor = "VK_LAYER_LUNARG_monitor";
+                       public static FixedUtf8String VkTraceLayeName = "VK_LAYER_LUNARG_vktrace";
+            public static FixedUtf8String RenderdocCaptureLayerName = "VK_LAYER_RENDERDOC_Capture";
+            public static FixedUtf8String main = "main";
+        }        
+
+        public Instance () {
+            init ();
+        }
+
+               unsafe bool ExtensionIsSupported (FixedUtf8String layer, string extName) {
+                       uint count;
+                       Utils.CheckResult (vkEnumerateInstanceExtensionProperties ((IntPtr)layer, out count, IntPtr.Zero));
+                       VkExtensionProperties[] tmp = new VkExtensionProperties[count];
+                       Utils.CheckResult (vkEnumerateInstanceExtensionProperties (layer, out count, tmp.Pin ()));
+                       tmp.Unpin ();
+
+                       fixed (VkExtensionProperties* ep = tmp) {
+                               for (int i = 0; i < tmp.Length; i++) {
+                                       IntPtr n = (IntPtr)ep[i].extensionName;
+                                       if (Marshal.PtrToStringAnsi (n) == extName)
+                                                       return true;
+                               }
+                       }
+                       return false;
+               }
+
+
+
+               void init () {
+                       List<IntPtr> instanceExtensions = new List<IntPtr> ();
+                       List<IntPtr> enabledLayerNames = new List<IntPtr> ();
+
+                       instanceExtensions.Add (Strings.VK_KHR_SURFACE_EXTENSION_NAME);
+                       if (RuntimeInformation.IsOSPlatform (OSPlatform.Windows)) {
+                               instanceExtensions.Add (Strings.VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
+                       } else if (RuntimeInformation.IsOSPlatform (OSPlatform.Linux)) {
+                               instanceExtensions.Add (Strings.VK_KHR_XCB_SURFACE_EXTENSION_NAME);
+                       } else {
+                               throw new PlatformNotSupportedException ();
+                       }
+
+                       if (VALIDATION) {
+                               enabledLayerNames.Add (Strings.LayerValidation);
+                               //enabledLayerNames.Add (Strings.LayerValidation_old);
+
+                               if (DEBUG_UTILS) {
+                                       //if (ExtensionIsSupported(Strings.LayerValidation, Strings.VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
+                                       //instanceExtensions.Add (Strings.VK_EXT_DEBUG_UTILS_EXTENSION_NAME);                                           
+                                       instanceExtensions.Add (Strings.VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
+                               }
+
+                       }
+                       if (RENDER_DOC_CAPTURE)
+                               enabledLayerNames.Add (Strings.RenderdocCaptureLayerName);
+
+
+                       VkApplicationInfo appInfo = new VkApplicationInfo () {
+                               sType = VkStructureType.ApplicationInfo,
+                               apiVersion = new Vulkan.Version (1, 0, 0),
+                               pApplicationName = Strings.Name,
+                               pEngineName = Strings.Name,
+                       };
+
+                       VkInstanceCreateInfo instanceCreateInfo = VkInstanceCreateInfo.New ();
+                       instanceCreateInfo.pApplicationInfo = appInfo.Pin ();
+
+                       if (instanceExtensions.Count > 0) {
+                               instanceCreateInfo.enabledExtensionCount = (uint)instanceExtensions.Count;
+                               instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions.Pin ();
+                       }
+                       if (enabledLayerNames.Count > 0) {
+                               instanceCreateInfo.enabledLayerCount = (uint)enabledLayerNames.Count;
+                               instanceCreateInfo.ppEnabledLayerNames = enabledLayerNames.Pin ();
+                       }
+
+                       VkResult result = vkCreateInstance (ref instanceCreateInfo, IntPtr.Zero, out inst);
+                       if (result != VkResult.Success)
+                               throw new InvalidOperationException ("Could not create Vulkan instance. Error: " + result);
+
+                       Vk.LoadInstanceFunctionPointers (inst);
+
+                       appInfo.Unpin ();
+
+                       if (instanceExtensions.Count > 0)
+                               instanceExtensions.Unpin ();
+                       if (enabledLayerNames.Count > 0)
+                               enabledLayerNames.Unpin ();
+               }
+
+               public PhysicalDeviceCollection GetAvailablePhysicalDevice () => new PhysicalDeviceCollection (inst);
+               /// <summary>
+               /// Create a new vulkan surface from native window pointer
+               /// </summary>
+               public VkSurfaceKHR CreateSurface (IntPtr hWindow) {
+                       ulong surf;
+                       Utils.CheckResult ((VkResult)Glfw.Glfw3.CreateWindowSurface (inst.Handle, hWindow, IntPtr.Zero, out surf), "Create Surface Failed.");
+                       return surf;
+               }
+               public void GetDelegate<T> (string name, out T del) {
+            using (FixedUtf8String n = new FixedUtf8String (name)) {
+                del = Marshal.GetDelegateForFunctionPointer<T> (vkGetInstanceProcAddr (Handle, (IntPtr)n));
+            }
+        }
+
+        #region IDisposable Support
+        private bool disposedValue = false;
+
+        protected virtual void Dispose (bool disposing) {
+            if (!disposedValue) {
+                               if (disposing) {
+                                       // TODO: supprimer l'état managé (objets managés).
+                               } else
+                                       System.Diagnostics.Debug.WriteLine ("Instance disposed by Finalizer");
+                
+                vkDestroyInstance (inst, IntPtr.Zero);
+
+                disposedValue = true;
+            }
+        }
+
+        ~Instance () {
+           Dispose(false);
+        }
+
+        public void Dispose () {
+            Dispose (true);
+            GC.SuppressFinalize(this);
+        }
+        #endregion
+    }
+}
diff --git a/vke/src/base/PhysicalDevice.cs b/vke/src/base/PhysicalDevice.cs
new file mode 100644 (file)
index 0000000..265c6fa
--- /dev/null
@@ -0,0 +1,203 @@
+//
+// PhysicalDevice.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using VK;
+using static VK.Vk;
+using static VK.Utils;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace CVKL {
+    public class PhysicalDeviceCollection : IEnumerable<PhysicalDevice> {
+        VkInstance inst;
+        PhysicalDevice[] phys;
+
+        public PhysicalDeviceCollection (VkInstance instance) {
+            inst = instance;
+            init ();
+        }
+
+        public PhysicalDevice this[int i] {
+            get {
+                return phys[i];
+            }
+        }
+
+        public IEnumerator<PhysicalDevice> GetEnumerator () {
+            return ((IEnumerable<PhysicalDevice>)phys).GetEnumerator ();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator () {
+            return ((IEnumerable<PhysicalDevice>)phys).GetEnumerator ();
+        }
+
+        void init () {
+            uint gpuCount = 0;
+            CheckResult (vkEnumeratePhysicalDevices (inst, out gpuCount, IntPtr.Zero));
+            if (gpuCount <= 0)
+                throw new Exception ("No GPU found");
+
+                       IntPtr gpus = Marshal.AllocHGlobal (Marshal.SizeOf<IntPtr> ()* (int)gpuCount); 
+            CheckResult (vkEnumeratePhysicalDevices (inst, out gpuCount, gpus), "Could not enumerate physical devices.");
+            
+            phys = new PhysicalDevice[gpuCount];
+
+            for (int i = 0; i < gpuCount; i++)
+                phys[i] = new PhysicalDevice (Marshal.ReadIntPtr(gpus + i * Marshal.SizeOf<IntPtr>()));
+
+                       Marshal.FreeHGlobal (gpus);
+        }
+    }
+    public class PhysicalDevice {
+        IntPtr phy;
+
+        public VkPhysicalDeviceMemoryProperties memoryProperties { get; private set; }
+        public VkQueueFamilyProperties[] QueueFamilies { get; private set; }
+               public VkPhysicalDeviceProperties Properties {
+                       get {
+                               VkPhysicalDeviceProperties pdp;
+                               vkGetPhysicalDeviceProperties (phy, out pdp);
+                               return pdp;
+                       }
+               }
+               public VkPhysicalDeviceFeatures Features {
+                       get {
+                               VkPhysicalDeviceFeatures df;
+                               vkGetPhysicalDeviceFeatures (phy, out df);
+                               return df;
+                       }
+               }
+               public VkPhysicalDeviceLimits Limits => Properties.limits;
+
+        public bool HasSwapChainSupport { get; private set; }
+        public IntPtr Handle => phy;
+
+        public PhysicalDevice (IntPtr vkPhy) {
+            phy = vkPhy;
+            init ();
+        }
+
+        unsafe void init () {
+            // Gather physical Device memory properties
+            IntPtr tmp = Marshal.AllocHGlobal (Marshal.SizeOf<VkPhysicalDeviceMemoryProperties>());
+            vkGetPhysicalDeviceMemoryProperties (phy, tmp);
+            memoryProperties = Marshal.PtrToStructure<VkPhysicalDeviceMemoryProperties> (tmp);
+
+            uint queueFamilyCount = 0;
+            vkGetPhysicalDeviceQueueFamilyProperties (phy, out queueFamilyCount, IntPtr.Zero);
+            QueueFamilies = new VkQueueFamilyProperties[queueFamilyCount];
+
+            if (queueFamilyCount <= 0)
+                throw new Exception ("No queues found for physical device");
+
+                       vkGetPhysicalDeviceQueueFamilyProperties (phy, out queueFamilyCount, QueueFamilies.Pin ());
+                       QueueFamilies.Unpin ();
+
+            uint propCount = 0;
+
+            vkEnumerateDeviceExtensionProperties (phy, IntPtr.Zero, out propCount, IntPtr.Zero);
+
+            VkExtensionProperties[] extProps = new VkExtensionProperties[propCount];
+
+                       vkEnumerateDeviceExtensionProperties (phy, IntPtr.Zero, out propCount, extProps.Pin ());
+                       extProps.Unpin ();
+
+            for (int i = 0; i < extProps.Length; i++)
+            {
+                fixed (VkExtensionProperties* ep = extProps) {
+                    IntPtr n = (IntPtr)ep[i].extensionName;
+                    switch (Marshal.PtrToStringAnsi(n))
+                    {
+                        case "VK_KHR_swapchain":
+                            HasSwapChainSupport = true;
+                            break;
+                    }
+                }
+                       }               
+        }
+
+               public unsafe bool GetDeviceExtensionSupported (string extName) {
+                       uint propCount = 0;
+
+                       vkEnumerateDeviceExtensionProperties (phy, IntPtr.Zero, out propCount, IntPtr.Zero);
+
+                       VkExtensionProperties[] extProps = new VkExtensionProperties[propCount];
+
+                       vkEnumerateDeviceExtensionProperties (phy, IntPtr.Zero, out propCount, extProps.Pin ());
+                       extProps.Unpin ();
+
+                       for (int i = 0; i < extProps.Length; i++) {
+                               fixed (VkExtensionProperties* ep = extProps) {
+                                       IntPtr n = (IntPtr)ep[i].extensionName;
+                                       if (Marshal.PtrToStringAnsi (n) == extName)
+                                                       return true;
+                               }
+                       }
+                       Console.WriteLine ($"INFO: unsuported device extension: {extName}");
+                       return false;
+               }
+
+
+        public bool GetPresentIsSupported (uint qFamilyIndex, VkSurfaceKHR surf) {
+            VkBool32 isSupported = false;
+            vkGetPhysicalDeviceSurfaceSupportKHR (phy, qFamilyIndex, surf, out isSupported);
+            return isSupported;
+        }
+
+        public VkSurfaceCapabilitiesKHR GetSurfaceCapabilities (VkSurfaceKHR surf) {
+            VkSurfaceCapabilitiesKHR caps;
+            vkGetPhysicalDeviceSurfaceCapabilitiesKHR (phy, surf, out caps);
+            return caps;
+        }
+
+        unsafe public VkSurfaceFormatKHR[] GetSurfaceFormats (VkSurfaceKHR surf) {
+            uint count = 0;
+            vkGetPhysicalDeviceSurfaceFormatsKHR (phy, surf, out count, IntPtr.Zero);
+            VkSurfaceFormatKHR[] formats = new VkSurfaceFormatKHR[count];
+            
+            vkGetPhysicalDeviceSurfaceFormatsKHR (phy, surf, out count, formats.Pin());
+                       formats.Unpin ();
+            
+            return formats;
+        }
+        unsafe public VkPresentModeKHR[] GetSurfacePresentModes (VkSurfaceKHR surf) {
+            uint count = 0;
+            vkGetPhysicalDeviceSurfacePresentModesKHR (phy, surf, out count, IntPtr.Zero);
+            VkPresentModeKHR[] modes = new VkPresentModeKHR[count];
+            
+            vkGetPhysicalDeviceSurfacePresentModesKHR (phy, surf, out count, modes.Pin());
+                       modes.Unpin ();
+            
+            return modes;
+        }
+        public VkFormatProperties GetFormatProperties (VkFormat format) {
+            VkFormatProperties properties;
+            vkGetPhysicalDeviceFormatProperties (phy, format, out properties);
+            return properties;
+        }
+    }
+}
diff --git a/vke/src/base/Pipeline.cs b/vke/src/base/Pipeline.cs
new file mode 100644 (file)
index 0000000..3e6eac3
--- /dev/null
@@ -0,0 +1,72 @@
+//
+// Pipeline.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       public abstract class Pipeline : Activable {
+        protected VkPipeline handle;
+               protected PipelineLayout layout;
+               protected PipelineCache Cache;
+
+               public VkPipeline Handle => handle;
+               public PipelineLayout Layout => layout;
+
+               protected readonly VkPipelineBindPoint bindPoint;
+
+               #region CTORS
+               protected Pipeline (Device dev, PipelineCache cache = null, string name = "custom pipeline") : base(dev, name) {
+                       this.Cache = cache;
+               }
+               #endregion
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.PipelineEXT, handle.Handle);
+
+               public abstract void Bind (CommandBuffer cmd);
+               public abstract void BindDescriptorSet (CommandBuffer cmd, DescriptorSet dset, uint firstSet = 0);
+               public void PushConstant (CommandBuffer cmd, object obj, int rangeIndex = 0, uint offset = 0) {
+                       cmd.PushConstant (layout, layout.PushConstantRanges[rangeIndex].stageFlags, obj, offset);
+               }
+
+
+               protected override void Dispose (bool disposing) {
+                       if (state == ActivableState.Activated) {
+                               if (disposing) {
+                                       layout.Dispose ();
+                                       Cache?.Dispose ();
+                               } else
+                                       System.Diagnostics.Debug.WriteLine ($"Pipeline '{name}' disposed by finalizer");
+
+                               vkDestroyPipeline (Dev.VkDev, handle, IntPtr.Zero);
+                       } else if (disposing)
+                               System.Diagnostics.Debug.WriteLine ($"Calling dispose on unactive Pipeline: {name}");
+
+                       base.Dispose (disposing);
+               }
+       }
+}
diff --git a/vke/src/base/PipelineCache.cs b/vke/src/base/PipelineCache.cs
new file mode 100644 (file)
index 0000000..c139696
--- /dev/null
@@ -0,0 +1,142 @@
+//
+// SpecializationConstant.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+       /// <summary>
+       /// Activable holding the pipeline cache handle. Activation is triggered by usage, so disposing pipelines that use this
+       /// cache is enough to have the cache disposed correctly. 
+       /// </summary>
+       /// <remarks>
+       /// Restore and Saving of the cache may be controled through the two static members:
+       ///     - `SaveOnDispose`
+       ///     - `LoadOnActivation`
+       /// </remarks>
+       public sealed class PipelineCache : Activable {
+               /// <summary>
+               /// If true, cache will be saved on dispose
+               /// </summary>
+               public static bool SaveOnDispose;
+               /// <summary>
+               /// If true, cache will be restore on activation
+               /// </summary>
+               public static bool LoadOnActivation;
+
+               internal VkPipelineCache handle;
+               readonly string globalConfigPath;
+               readonly string cacheFile;
+
+               #region CTOR
+               public PipelineCache (Device dev, string cacheFile = "pipelines.bin", string name = "pipeline cache") : base(dev, name) {
+                       string configRoot = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), ".config");
+                       string appName = Assembly.GetEntryAssembly ().GetName ().Name;
+                       globalConfigPath = Path.Combine (configRoot, appName);
+
+                       if (!Directory.Exists (globalConfigPath))
+                               Directory.CreateDirectory (globalConfigPath);
+
+                       this.cacheFile = cacheFile;
+        }
+               #endregion
+
+               public override void Activate () {
+                       string path = Path.Combine (globalConfigPath, cacheFile);
+
+                       if (state != ActivableState.Activated) {
+                               VkPipelineCacheCreateInfo info = VkPipelineCacheCreateInfo.New ();                                                      
+
+                               if (File.Exists (path) && LoadOnActivation) {
+                                       using (FileStream fs = File.Open (path, FileMode.Open)) {
+                                               using (BinaryReader br = new BinaryReader (fs)) {
+                                                       int length = (int)br.BaseStream.Length;
+                                                       info.pInitialData = Marshal.AllocHGlobal (length);
+                                                       info.initialDataSize = (UIntPtr)br.BaseStream.Length;
+                                                       Marshal.Copy(br.ReadBytes (length),0, info.pInitialData, length);
+                                               }
+                                       }
+                               }
+
+                               Utils.CheckResult (vkCreatePipelineCache (Dev.VkDev, ref info, IntPtr.Zero, out handle));
+
+                               if (info.pInitialData != IntPtr.Zero)
+                                       Marshal.FreeHGlobal (info.pInitialData);
+                       }
+                       base.Activate ();
+               }
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.PipelineCacheEXT, handle.Handle);
+
+
+               public void Delete () {
+                       string path = Path.Combine (globalConfigPath, cacheFile);
+                       if (File.Exists (path))
+                               File.Delete (path);
+               }
+
+               public void Save () {
+                       if (state != ActivableState.Activated)
+                               return;
+
+                       string path = Path.Combine (globalConfigPath, cacheFile);
+
+                       if (File.Exists (path))
+                               File.Delete (path);
+
+                       UIntPtr dataSize;
+                       Utils.CheckResult (vkGetPipelineCacheData (Dev.VkDev, handle, out dataSize, IntPtr.Zero));
+                       byte[] pData = new byte[(int)dataSize];
+                       Utils.CheckResult (vkGetPipelineCacheData (Dev.VkDev, handle, out dataSize, pData.Pin ()));
+                       pData.Unpin ();
+
+                       using (FileStream fs = File.Open (path, FileMode.CreateNew)) 
+                               using (BinaryWriter br = new BinaryWriter (fs)) 
+                                       br.Write (pData, 0, (int)dataSize);                     
+               }
+
+
+               #region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (!disposing)
+                               System.Diagnostics.Debug.WriteLine ($"CVKL PipelineCache '{name}' disposed by finalizer");
+                       if (state == ActivableState.Activated) {
+                               if (SaveOnDispose)
+                                       Save ();
+                               vkDestroyPipelineCache (Dev.VkDev, handle, IntPtr.Zero);
+                       }
+                       base.Dispose (disposing);
+               }
+               #endregion
+
+       }
+}
diff --git a/vke/src/base/PipelineLayout.cs b/vke/src/base/PipelineLayout.cs
new file mode 100644 (file)
index 0000000..1c59f90
--- /dev/null
@@ -0,0 +1,113 @@
+//
+// PipelineLayout.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       public sealed class PipelineLayout : Activable {
+        internal VkPipelineLayout handle;
+               public VkPipelineLayout Handle => handle;
+
+               public List<DescriptorSetLayout> DescriptorSetLayouts = new List<DescriptorSetLayout> ();
+               public List<VkPushConstantRange> PushConstantRanges = new List<VkPushConstantRange> ();
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.PipelineLayoutEXT, handle.Handle);
+
+               #region CTORS
+               public PipelineLayout (Device device) : base (device) { }
+               public PipelineLayout (Device device, VkPushConstantRange pushConstantRange, params DescriptorSetLayout[] descriptorSetLayouts) 
+               : this (device, descriptorSetLayouts) {
+                       PushConstantRanges.Add (pushConstantRange);
+               }
+               public PipelineLayout (Device device, VkPushConstantRange[] pushConstantRanges, params DescriptorSetLayout[] descriptorSetLayouts)
+               : this (device, descriptorSetLayouts) {
+                       foreach (VkPushConstantRange pcr in pushConstantRanges)
+                               PushConstantRanges.Add (pcr);
+               }
+               public PipelineLayout (Device device, params DescriptorSetLayout[] descriptorSetLayouts)
+                       :this (device) {
+            
+                       if (descriptorSetLayouts.Length > 0)
+                               DescriptorSetLayouts.AddRange (descriptorSetLayouts);
+        }
+               #endregion
+
+               public void AddPushConstants (params VkPushConstantRange[] pushConstantRanges) { 
+                       foreach (VkPushConstantRange pcr in pushConstantRanges)
+                               PushConstantRanges.Add (pcr);
+               }
+
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               foreach (DescriptorSetLayout dsl in DescriptorSetLayouts) 
+                                       dsl.Activate ();
+                               VkPipelineLayoutCreateInfo info = VkPipelineLayoutCreateInfo.New();
+                               VkDescriptorSetLayout[] dsls = DescriptorSetLayouts.Select (dsl => dsl.handle).ToArray ();
+
+                               if (dsls.Length > 0) {
+                                       info.setLayoutCount = (uint)dsls.Length;
+                                       info.pSetLayouts = dsls.Pin ();
+                               }
+                               if (PushConstantRanges.Count > 0) {
+                                       info.pushConstantRangeCount = (uint)PushConstantRanges.Count;
+                                       info.pPushConstantRanges = PushConstantRanges.Pin();
+                               }
+                               Utils.CheckResult (vkCreatePipelineLayout (Dev.VkDev, ref info, IntPtr.Zero, out handle));
+
+                               if (dsls.Length > 0)
+                                       dsls.Unpin ();
+                               if (PushConstantRanges.Count > 0)
+                                       PushConstantRanges.Unpin ();
+                       }
+                       base.Activate ();
+               }
+
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+
+               #region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (state == ActivableState.Activated) {
+                               if (disposing) {
+                                       foreach (DescriptorSetLayout dsl in DescriptorSetLayouts) 
+                                               dsl.Dispose ();                                 
+                               } else
+                                       System.Diagnostics.Debug.WriteLine ("VKE Activable PipelineLayout disposed by finalizer");
+
+                               vkDestroyPipelineLayout (Dev.VkDev, handle, IntPtr.Zero);
+                       }else if (disposing)
+                               System.Diagnostics.Debug.WriteLine ("Calling dispose on unactive PipelineLayout");
+
+                       base.Dispose (disposing);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/base/QueryPool.cs b/vke/src/base/QueryPool.cs
new file mode 100644 (file)
index 0000000..1d95174
--- /dev/null
@@ -0,0 +1,164 @@
+//
+// CommandPool.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+       public class TimestampQueryPool : QueryPool {
+               public readonly float Period;
+
+               #region CTORS
+               public TimestampQueryPool (Device device, uint count = 2)
+               : base (device, VkQueryType.Timestamp, 0, count)
+               {
+                       Period = Dev.phy.Limits.timestampPeriod;
+
+                       resultLength = 1;
+
+                       Activate ();
+               }
+               #endregion
+
+               public void Write (CommandBuffer cmd, uint query, VkPipelineStageFlags stageFlags = VkPipelineStageFlags.BottomOfPipe) {
+                       vkCmdWriteTimestamp (cmd.Handle, stageFlags, handle, query);
+               }
+               public void Start (CommandBuffer cmd, VkPipelineStageFlags stageFlags = VkPipelineStageFlags.BottomOfPipe) {
+                       vkCmdWriteTimestamp (cmd.Handle, stageFlags, handle, 0);
+               }
+               public void End (CommandBuffer cmd, VkPipelineStageFlags stageFlags = VkPipelineStageFlags.BottomOfPipe) {
+                       vkCmdWriteTimestamp (cmd.Handle, stageFlags, handle, 1);
+               }
+               public float ElapsedMiliseconds {
+                       get {
+                               ulong[] res = GetResults ();
+                               return (res[1] - res[0]) * Period / 1000000f;
+                       }
+               }
+       }
+       public class PipelineStatisticsQueryPool : QueryPool {
+
+               public readonly VkQueryPipelineStatisticFlags[] RequestedStats;
+
+               #region CTORS
+               public PipelineStatisticsQueryPool (Device device, VkQueryPipelineStatisticFlags statisticFlags, uint count = 1)
+               : base (device, VkQueryType.PipelineStatistics, statisticFlags, count)
+               {
+                       List<VkQueryPipelineStatisticFlags> requests = new List<VkQueryPipelineStatisticFlags> ();
+
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.InputAssemblyVertices))
+                               requests.Add (VkQueryPipelineStatisticFlags.InputAssemblyVertices);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.InputAssemblyPrimitives))
+                               requests.Add (VkQueryPipelineStatisticFlags.InputAssemblyPrimitives);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.VertexShaderInvocations))
+                               requests.Add (VkQueryPipelineStatisticFlags.VertexShaderInvocations);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.GeometryShaderInvocations))
+                               requests.Add (VkQueryPipelineStatisticFlags.GeometryShaderInvocations);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.GeometryShaderPrimitives))
+                               requests.Add (VkQueryPipelineStatisticFlags.GeometryShaderPrimitives);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.ClippingInvocations))
+                               requests.Add (VkQueryPipelineStatisticFlags.ClippingInvocations);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.ClippingPrimitives))
+                               requests.Add (VkQueryPipelineStatisticFlags.ClippingPrimitives);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.FragmentShaderInvocations))
+                               requests.Add (VkQueryPipelineStatisticFlags.FragmentShaderInvocations);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.TessellationControlShaderPatches))
+                               requests.Add (VkQueryPipelineStatisticFlags.TessellationControlShaderPatches);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.TessellationEvaluationShaderInvocations))
+                               requests.Add (VkQueryPipelineStatisticFlags.TessellationEvaluationShaderInvocations);
+                       if (statisticFlags.HasFlag (VkQueryPipelineStatisticFlags.ComputeShaderInvocations))
+                               requests.Add (VkQueryPipelineStatisticFlags.ComputeShaderInvocations);
+
+                       RequestedStats = requests.ToArray ();
+
+                       resultLength = (uint)requests.Count;
+
+                       Activate ();
+               }
+               #endregion
+
+               public void Begin (CommandBuffer cmd, uint query = 0) {
+                       vkCmdBeginQuery (cmd.Handle, handle, query, VkQueryControlFlags.Precise);
+               }
+               public void End (CommandBuffer cmd, uint query = 0) {
+                       vkCmdEndQuery (cmd.Handle, handle, query);
+               }
+       }
+
+       public abstract class QueryPool : Activable {        
+        protected VkQueryPool handle;
+               protected readonly VkQueryPoolCreateInfo createInfos;
+               public readonly VkQueryType QueryType;
+               protected uint resultLength;
+
+               #region CTORS
+               protected QueryPool (Device device, VkQueryType queryType, VkQueryPipelineStatisticFlags statisticFlags, uint count = 1)
+               : base(device)
+        {
+                       createInfos = VkQueryPoolCreateInfo.New (queryType, statisticFlags, count);
+
+                       //Activate ();
+        }
+
+               #endregion
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.QueryPoolEXT, handle.Handle);
+
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               VkQueryPoolCreateInfo infos = createInfos;              
+                   Utils.CheckResult (vkCreateQueryPool (Dev.VkDev, ref infos, IntPtr.Zero, out handle));
+                       }
+                       base.Activate ();
+               }
+
+               public ulong[] GetResults () {
+                       ulong[] results = new ulong[resultLength * createInfos.queryCount];
+                       IntPtr ptr = results.Pin ();
+                       vkGetQueryPoolResults (Dev.VkDev, handle, 0, createInfos.queryCount, (UIntPtr)(resultLength * createInfos.queryCount* sizeof (ulong)), ptr, sizeof (ulong), VkQueryResultFlags.QueryResult64);
+                       results.Unpin ();
+                       return results;
+               }
+
+                   
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+
+               #region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (!disposing)
+                               System.Diagnostics.Debug.WriteLine ("VKE QueryPool disposed by finalizer");
+                       if (state == ActivableState.Activated)
+                               vkDestroyQueryPool (Dev.VkDev, handle, IntPtr.Zero);
+                       base.Dispose (disposing);
+               }
+               #endregion
+
+       }
+}
diff --git a/vke/src/base/Queue.cs b/vke/src/base/Queue.cs
new file mode 100644 (file)
index 0000000..b463ba6
--- /dev/null
@@ -0,0 +1,136 @@
+//
+// Queue.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+
+    public class PresentQueue : Queue {
+        public readonly VkSurfaceKHR Surface;
+
+        public PresentQueue (Device _dev, VkQueueFlags requestedFlags, VkSurfaceKHR _surface, float _priority = 0.0f) {        
+            dev = _dev;
+            priority = _priority;
+            Surface = _surface;
+
+            qFamIndex = searchQFamily (requestedFlags);
+            dev.queues.Add (this);
+        }
+        
+        uint searchQFamily (VkQueueFlags requestedFlags) {
+            //search for dedicated Q
+            for (uint i = 0; i < dev.phy.QueueFamilies.Length; i++) {
+                if (dev.phy.QueueFamilies[i].queueFlags == requestedFlags && dev.phy.GetPresentIsSupported (i, Surface)) 
+                    return i;
+            }
+            //search Q having flags
+            for (uint i = 0; i < dev.phy.QueueFamilies.Length; i++) {
+                if ((dev.phy.QueueFamilies[i].queueFlags & requestedFlags) == requestedFlags && dev.phy.GetPresentIsSupported (i, Surface)) 
+                    return i;
+            }
+
+            throw new Exception (string.Format ("No Queue with flags {0} found", requestedFlags));
+        }
+
+        public void Present (VkPresentInfoKHR present) {
+            Utils.CheckResult (vkQueuePresentKHR (handle, ref present));
+        }
+        public void Present (SwapChain swapChain, VkSemaphore wait) {
+            VkPresentInfoKHR present = VkPresentInfoKHR.New();
+
+            uint idx = swapChain.currentImageIndex;
+            VkSwapchainKHR sc = swapChain.handle;
+            present.swapchainCount = 1;
+            present.pSwapchains = sc.Pin();
+            present.waitSemaphoreCount = 1;
+            present.pWaitSemaphores = wait.Pin();
+            present.pImageIndices = idx.Pin();
+
+            vkQueuePresentKHR (handle, ref present);
+
+                       sc.Unpin ();
+                       wait.Unpin ();
+                       idx.Unpin ();
+        }
+    }
+
+    public class Queue {
+
+        internal VkQueue handle;
+        internal Device dev;
+               public Device Dev => dev;
+
+        VkQueueFlags flags => dev.phy.QueueFamilies[qFamIndex].queueFlags;
+        public uint qFamIndex;
+        public uint index;//index in queue family
+        public float priority;
+
+        protected Queue () { }
+        public Queue (Device _dev, VkQueueFlags requestedFlags, float _priority = 0.0f) {
+            dev = _dev;
+            priority = _priority;
+
+            qFamIndex = searchQFamily (requestedFlags);
+            dev.queues.Add (this);
+        }
+               /// <summary>
+               /// End command recording, submit, and wait queue idle
+               /// </summary>
+               public void EndSubmitAndWait (CommandBuffer cmd) {
+                       cmd.End ();
+                       Submit (cmd);
+                       WaitIdle ();
+               }
+        public void Submit (CommandBuffer cmd, VkSemaphore wait = default(VkSemaphore), VkSemaphore signal = default (VkSemaphore), VkFence fence = default (VkFence)) {
+            cmd.Submit (handle, wait, signal, fence);
+        }
+        public void WaitIdle () {
+            Utils.CheckResult (vkQueueWaitIdle (handle));
+        }
+
+        uint searchQFamily (VkQueueFlags requestedFlags) {
+            //search for dedicated Q
+            for (uint i = 0; i < dev.phy.QueueFamilies.Length; i++) {
+                if (dev.phy.QueueFamilies[i].queueFlags == requestedFlags)
+                    return i;
+            }
+            //search Q having flags
+            for (uint i = 0; i < dev.phy.QueueFamilies.Length; i++) {
+                if ((dev.phy.QueueFamilies[i].queueFlags & requestedFlags) == requestedFlags)
+                    return i;
+            }
+
+            throw new Exception (string.Format ("No Queue with flags {0} found", requestedFlags));
+        }
+
+        internal void updateHandle () {
+            vkGetDeviceQueue (dev.VkDev, qFamIndex, index, out handle);
+        }
+    }
+}
diff --git a/vke/src/base/RenderPass.cs b/vke/src/base/RenderPass.cs
new file mode 100644 (file)
index 0000000..1609f56
--- /dev/null
@@ -0,0 +1,260 @@
+//
+// RenderPass.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+    public class RenderPass : Activable {
+        internal VkRenderPass handle;        
+
+               public readonly VkSampleCountFlags Samples;
+
+        internal List<VkAttachmentDescription> attachments = new List<VkAttachmentDescription> ();
+        public List<VkClearValue> ClearValues = new List<VkClearValue> ();
+        internal List<SubPass> subpasses = new List<SubPass> ();
+        List<VkSubpassDependency> dependencies = new List<VkSubpassDependency> ();
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT(VkDebugReportObjectTypeEXT.RenderPassEXT, handle.Handle);
+
+               #region CTORS
+
+               public RenderPass (Device device, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1) : base(device) {
+                       Samples = samples;
+               }
+
+               /// <summary>
+               /// Create renderpass with a single color attachment and a resolve one if needed
+               /// </summary>
+               public RenderPass (Device device, VkFormat colorFormat, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1, VkAttachmentLoadOp loadOp = VkAttachmentLoadOp.Clear)
+                       : this (device) { 
+                       Samples = samples;
+
+                       AddAttachment (colorFormat, (samples == VkSampleCountFlags.SampleCount1) ? VkImageLayout.PresentSrcKHR : VkImageLayout.ColorAttachmentOptimal, samples,
+                               loadOp, VkAttachmentStoreOp.Store, VkImageLayout.Undefined);
+            ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+
+                       SubPass subpass0 = new SubPass ();
+                       subpass0.AddColorReference (0, VkImageLayout.ColorAttachmentOptimal);
+
+                       if (samples != VkSampleCountFlags.SampleCount1) {
+                               AddAttachment (colorFormat, VkImageLayout.PresentSrcKHR, VkSampleCountFlags.SampleCount1);
+                               ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+                               subpass0.AddResolveReference (1, VkImageLayout.ColorAttachmentOptimal);
+                       }
+
+            AddSubpass (subpass0);
+
+            AddDependency (Vk.SubpassExternal, 0,
+                VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput,
+                VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite);
+            AddDependency (0, Vk.SubpassExternal,
+                VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe,
+                VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead);
+
+               }
+        /// <summary>
+        /// Create default renderpass with one color and one depth attachments.
+        /// </summary>
+        public RenderPass (Device device, VkFormat colorFormat, VkFormat depthFormat, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1)
+            : this (device){
+
+                       Samples = samples;
+
+                       AddAttachment (colorFormat, (samples == VkSampleCountFlags.SampleCount1) ? VkImageLayout.PresentSrcKHR : VkImageLayout.ColorAttachmentOptimal, samples);
+                       AddAttachment (depthFormat, VkImageLayout.DepthStencilAttachmentOptimal, samples);
+
+            ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) });
+            ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) });
+
+                       SubPass subpass0 = new SubPass ();
+
+                       subpass0.AddColorReference (0, VkImageLayout.ColorAttachmentOptimal);
+                       subpass0.SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal);
+
+                       if (samples != VkSampleCountFlags.SampleCount1) {
+                               AddAttachment (colorFormat, VkImageLayout.PresentSrcKHR, VkSampleCountFlags.SampleCount1);
+                               ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.2f) });
+                               subpass0.AddResolveReference (2, VkImageLayout.ColorAttachmentOptimal);
+                       }
+
+            AddSubpass (subpass0);
+
+            AddDependency (Vk.SubpassExternal, 0,
+                VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput,
+                VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite);
+            AddDependency (0, Vk.SubpassExternal,
+                VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe,
+                VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead);
+        }
+               #endregion
+
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               List<VkSubpassDescription> spDescs = new List<VkSubpassDescription> ();
+                               foreach (SubPass sp in subpasses)
+                                       spDescs.Add (sp.SubpassDescription);
+
+                               VkRenderPassCreateInfo renderPassInfo = VkRenderPassCreateInfo.New();
+                               renderPassInfo.attachmentCount = (uint)attachments.Count;
+                               renderPassInfo.pAttachments = attachments.Pin ();
+                               renderPassInfo.subpassCount = (uint)spDescs.Count;
+                               renderPassInfo.pSubpasses = spDescs.Pin ();
+                               renderPassInfo.dependencyCount = (uint)dependencies.Count;
+                               renderPassInfo.pDependencies = dependencies.Pin ();
+
+                               handle = Dev.CreateRenderPass (renderPassInfo);
+
+                               foreach (SubPass sp in subpasses)
+                                       sp.UnpinLists ();
+
+                               attachments.Unpin ();
+                               spDescs.Unpin ();
+                               dependencies.Unpin ();
+                       }
+                       base.Activate ();
+        }
+
+
+        public void AddAttachment (VkFormat format,
+            VkImageLayout finalLayout, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1,
+            VkAttachmentLoadOp loadOp = VkAttachmentLoadOp.Clear,
+            VkAttachmentStoreOp storeOp = VkAttachmentStoreOp.Store,
+            VkImageLayout initialLayout = VkImageLayout.Undefined) {
+            attachments.Add (new VkAttachmentDescription {
+                format = format,
+                samples = samples,
+                loadOp = loadOp,
+                storeOp = storeOp,
+                stencilLoadOp = VkAttachmentLoadOp.DontCare,
+                stencilStoreOp = VkAttachmentStoreOp.DontCare,
+                initialLayout = initialLayout,
+                finalLayout = finalLayout,
+            }); 
+        }
+        public void AddAttachment (VkFormat format, VkImageLayout finalLayout,
+            VkAttachmentLoadOp stencilLoadOp,
+            VkAttachmentStoreOp stencilStoreOp,
+            VkAttachmentLoadOp loadOp = VkAttachmentLoadOp.DontCare,
+            VkAttachmentStoreOp storeOp = VkAttachmentStoreOp.DontCare,
+            VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1,
+            VkImageLayout initialLayout = VkImageLayout.Undefined) {
+            attachments.Add (new VkAttachmentDescription {
+                format = format,
+                samples = samples,
+                loadOp = loadOp,
+                storeOp = storeOp,
+                stencilLoadOp = stencilLoadOp,
+                stencilStoreOp = stencilStoreOp,
+                initialLayout = initialLayout,
+                finalLayout = finalLayout,
+            });
+        }
+               //public void AddDependency (SubPass srcSubpass, SubPass dstSubpass,
+               //      VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
+               //      VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
+               //      VkDependencyFlags dependencyFlags = VkDependencyFlags.ByRegion) {
+
+               //      AddDependency (srcSubpass.Index, dstSubpass.Index, srcStageMask, dstStageMask,
+               //              srcAccessMask, dstAccessMask, dependencyFlags);
+               //}
+
+               public void AddDependency (uint srcSubpass, uint dstSubpass,
+            VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask,
+            VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask,
+            VkDependencyFlags dependencyFlags = VkDependencyFlags.ByRegion) {
+            dependencies.Add (new VkSubpassDependency {
+                srcSubpass = srcSubpass,
+                dstSubpass = dstSubpass,
+                srcStageMask = srcStageMask,
+                dstStageMask = dstStageMask,
+                srcAccessMask = srcAccessMask,
+                dstAccessMask = dstAccessMask,
+                dependencyFlags = dependencyFlags
+            });
+        }
+        public void AddSubpass (params SubPass[] subPass) {
+                       for (uint i = 0; i < subPass.Length; i++) {
+                               subPass[i].Index = (uint)subpasses.Count + i;
+                               subpasses.Add (subPass[i]);
+                       }
+        }
+        /// <summary>
+        /// Begin Render pass with framebuffer extent dimensions
+        /// </summary>
+        public void Begin (CommandBuffer cmd, Framebuffer frameBuffer) {
+            Begin (cmd, frameBuffer, frameBuffer.Width, frameBuffer.Height);
+        }
+        /// <summary>
+        /// Begin Render pass with custom render area
+        /// </summary>
+        public void Begin (CommandBuffer cmd, Framebuffer frameBuffer, uint width, uint height) {
+
+            VkRenderPassBeginInfo info = VkRenderPassBeginInfo.New();
+            info.renderPass = handle;
+            info.renderArea.extent.width = width;
+            info.renderArea.extent.height = height;
+            info.clearValueCount = (uint)ClearValues.Count;
+            info.pClearValues = ClearValues.Pin ();
+            info.framebuffer = frameBuffer.handle;
+
+                       vkCmdBeginRenderPass (cmd.Handle, ref info, VkSubpassContents.Inline);
+
+                       ClearValues.Unpin ();
+        }
+               /// <summary>
+               /// Switch to next subpass
+               /// </summary>
+               public void BeginSubPass (CommandBuffer cmd, VkSubpassContents subpassContents = VkSubpassContents.Inline) {
+                       vkCmdNextSubpass (cmd.Handle, subpassContents);
+               }
+        public void End (CommandBuffer cmd) {
+            vkCmdEndRenderPass (cmd.Handle);
+        }
+
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+               }
+        
+               #region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (state == ActivableState.Activated) {
+                               if (disposing) {
+                               } else
+                                       System.Diagnostics.Debug.WriteLine ("VKE Activable RenderPass disposed by finalizer");
+
+                               Dev.DestroyRenderPass (handle);
+                       }else if (disposing)
+                               System.Diagnostics.Debug.WriteLine ("Calling dispose on unactive RenderPass");
+
+                       base.Dispose (disposing);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/base/Resource.cs b/vke/src/base/Resource.cs
new file mode 100644 (file)
index 0000000..f5b4c87
--- /dev/null
@@ -0,0 +1,108 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using VK;
+
+using static VK.Vk;
+
+namespace CVKL {
+       [DebuggerDisplay ("{previous.name} <- {name} -> {next.name}")]
+       public abstract class Resource : Activable {
+               protected VkMemoryRequirements memReqs;
+#if MEMORY_POOLS
+               internal MemoryPool memoryPool;
+               public ulong poolOffset;
+#else
+               protected VkDeviceMemory vkMemory;
+#endif
+
+               /// <summary> double linked list in memory pool </summary>
+               internal Resource previous;
+               public Resource next;
+
+               public ulong AllocatedDeviceMemorySize => memReqs.size;
+               public uint TypeBits => memReqs.memoryTypeBits;
+               public ulong MemoryAlignment => memReqs.alignment;
+
+               protected IntPtr mappedData;
+               public IntPtr MappedData => mappedData;
+
+               public readonly VkMemoryPropertyFlags MemoryFlags;
+
+        protected Resource (Device device, VkMemoryPropertyFlags memoryFlags) : base (device) {            
+            MemoryFlags = memoryFlags;
+        }
+
+        internal abstract void updateMemoryRequirements ();
+
+               internal abstract void bindMemory ();
+
+               internal VkMappedMemoryRange MapRange => new VkMappedMemoryRange {
+                               sType = VkStructureType.MappedMemoryRange,
+#if MEMORY_POOLS
+                               memory = memoryPool.vkMemory,
+                               offset = poolOffset,
+#else
+                               memory = vkMemory,
+                               offset = 0,
+#endif
+                               size = AllocatedDeviceMemorySize
+                       };
+#if !MEMORY_POOLS
+               protected void allocateMemory () {
+                       VkMemoryAllocateInfo memInfo = VkMemoryAllocateInfo.New ();
+                       memInfo.allocationSize = memReqs.size;
+                       memInfo.memoryTypeIndex = Dev.GetMemoryTypeIndex (memReqs.memoryTypeBits, MemoryFlags);
+                       Utils.CheckResult (vkAllocateMemory (Dev.VkDev, ref memInfo, IntPtr.Zero, out vkMemory));
+               }
+#endif
+
+
+               public void Map (ulong offset = 0) {
+#if MEMORY_POOLS
+                       if (!memoryPool.IsMapped)
+                               memoryPool.Map ();
+                       mappedData = new IntPtr(memoryPool.MappedData.ToInt64() + (long)(poolOffset + offset));
+#else
+                       Utils.CheckResult (vkMapMemory (Dev.VkDev, vkMemory, offset, AllocatedDeviceMemorySize, 0, ref mappedData));
+#endif
+               }
+               public void Unmap () {
+#if MEMORY_POOLS
+#else
+                       vkUnmapMemory (Dev.VkDev, vkMemory);
+                       mappedData = IntPtr.Zero;
+#endif
+               }
+               public void Update (object data, ulong size, ulong offset = 0) {
+            GCHandle ptr = GCHandle.Alloc (data, GCHandleType.Pinned);
+            unsafe {
+                System.Buffer.MemoryCopy (ptr.AddrOfPinnedObject ().ToPointer (), (mappedData + (int)offset).ToPointer (), size, size);
+            }
+            ptr.Free ();
+        }
+        public void Flush () {
+                       VkMappedMemoryRange range = MapRange;
+            vkFlushMappedMemoryRanges (Dev.VkDev, 1, ref range);
+        }
+
+#region IDisposable Support        
+        protected override void Dispose (bool disposing) {
+                       if (!disposing)
+                               System.Diagnostics.Debug.WriteLine ("VKE Activable object disposed by finalizer");
+                       if (state == ActivableState.Activated) {
+                               if (mappedData != IntPtr.Zero)
+                                       Unmap ();
+#if MEMORY_POOLS
+                               memoryPool.Remove (this);
+#else
+                               vkFreeMemory (Dev.VkDev, vkMemory, IntPtr.Zero);
+#endif
+
+                       }
+                       base.Dispose (disposing);
+        }
+#endregion
+       }
+}
\ No newline at end of file
diff --git a/vke/src/base/SubPass.cs b/vke/src/base/SubPass.cs
new file mode 100644 (file)
index 0000000..2396cdb
--- /dev/null
@@ -0,0 +1,120 @@
+//
+// SubPass.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System.Collections.Generic;
+using VK;
+
+namespace CVKL {
+       public class SubPass {
+               public uint Index { get; internal set; }
+               List<VkAttachmentReference> colorRefs = new List<VkAttachmentReference>();
+        List<VkAttachmentReference> inputRefs = new List<VkAttachmentReference>();
+        MarshaledObject<VkAttachmentReference> depthRef;
+        List<VkAttachmentReference> resolveRefs = new List<VkAttachmentReference>();
+        List<uint> preservedRefs = new List<uint>();
+
+        public SubPass () {
+        }
+               public SubPass (params VkImageLayout[] layouts) {
+                       for (uint i = 0; i < layouts.Length; i++) 
+                               AddColorReference (i, layouts[i]);
+               }
+
+               public void AddColorReference (uint attachment, VkImageLayout layout = VkImageLayout.DepthStencilAttachmentOptimal) {
+            AddColorReference (new VkAttachmentReference { attachment = attachment, layout = layout });
+        }
+        public void AddColorReference (params VkAttachmentReference[] refs) {
+            if (colorRefs == null)
+                colorRefs = new List<VkAttachmentReference> ();
+            for (int i = 0; i < refs.Length; i++)
+                colorRefs.Add (refs[i]);
+        }
+        public void AddInputReference (params VkAttachmentReference[] refs) {
+               inputRefs.AddRange (refs);
+        }
+        public void AddPreservedReference (params uint[] refs) {
+            preservedRefs.AddRange (refs);
+        }
+        public void SetDepthReference (uint attachment, VkImageLayout layout = VkImageLayout.DepthStencilAttachmentOptimal) {
+            DepthReference = new VkAttachmentReference { attachment = attachment, layout = layout };
+        }
+               public void AddResolveReference (params VkAttachmentReference[] refs) {
+                       resolveRefs.AddRange (refs);
+               }
+               public void AddResolveReference (uint attachment, VkImageLayout layout = VkImageLayout.ColorAttachmentOptimal) {
+                       AddResolveReference (new VkAttachmentReference { attachment = attachment, layout = layout });
+               }
+               public VkAttachmentReference DepthReference {
+            set {
+                if (depthRef != null)
+                    depthRef.Dispose ();
+                depthRef = new MarshaledObject<VkAttachmentReference> (value);
+            }
+        }
+
+               /// <summary>
+               /// after having fetched the vkSubpassDescription structure, the lists of attachment are pinned,
+               /// so it is mandatory to call the UnpinLists methods after.
+               /// </summary>
+               public VkSubpassDescription SubpassDescription {
+            get {
+                VkSubpassDescription subpassDescription = new VkSubpassDescription ();
+                subpassDescription.pipelineBindPoint = VkPipelineBindPoint.Graphics;
+                if (colorRefs.Count > 0) {
+                    subpassDescription.colorAttachmentCount = (uint)colorRefs.Count;
+                    subpassDescription.pColorAttachments = colorRefs.Pin(); ; 
+                }
+                if (inputRefs.Count > 0) {
+                    subpassDescription.inputAttachmentCount = (uint)inputRefs.Count;
+                    subpassDescription.pInputAttachments = inputRefs.Pin (); ;
+                }
+                if (preservedRefs.Count > 0) {
+                    subpassDescription.preserveAttachmentCount = (uint)preservedRefs.Count;
+                    subpassDescription.pPreserveAttachments = preservedRefs.Pin (); ;
+                }
+                               if (resolveRefs.Count > 0)
+                                       subpassDescription.pResolveAttachments = resolveRefs.Pin ();
+
+                               if (depthRef != null)
+                    subpassDescription.pDepthStencilAttachment = depthRef.Pointer;
+
+                return subpassDescription;
+            }        
+        }
+
+               public void UnpinLists () {
+                       if (colorRefs.Count > 0) 
+                               colorRefs.Unpin (); 
+                       if (inputRefs.Count > 0)                        
+                               inputRefs.Unpin ();
+                       if (preservedRefs.Count > 0) 
+                               preservedRefs.Unpin ();
+                       if (resolveRefs.Count > 0)
+                               resolveRefs.Unpin ();
+               }
+
+               public static implicit operator uint(SubPass sp) => sp.Index;
+       }
+}
diff --git a/vke/src/base/SwapChain.cs b/vke/src/base/SwapChain.cs
new file mode 100644 (file)
index 0000000..0ebdb0d
--- /dev/null
@@ -0,0 +1,183 @@
+//
+// SwapChain.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using VK;
+using static VK.Vk;
+
+namespace CVKL {
+    public class SwapChain : Activable {
+               /// <summary>
+               /// Set the default swapchain image format.
+               /// </summary>
+               public static VkFormat PREFERED_FORMAT = VkFormat.B8g8r8a8Srgb;
+               /// <summary>
+               /// Set additional usage flags for the swapchain images such as TransferDst.
+               /// </summary>
+               public static VkImageUsageFlags IMAGES_USAGE = VkImageUsageFlags.ColorAttachment;
+
+               internal VkSwapchainKHR handle;
+
+               internal uint currentImageIndex;
+               VkSwapchainCreateInfoKHR createInfos;
+               PresentQueue presentQueue;
+
+               public VkSemaphore presentComplete;
+        public Image[] images;
+
+               protected override VkDebugMarkerObjectNameInfoEXT DebugMarkerInfo
+                       => new VkDebugMarkerObjectNameInfoEXT (VkDebugReportObjectTypeEXT.SwapchainKhrEXT, handle.Handle);
+
+
+               public uint ImageCount => (uint)images?.Length;
+        public uint Width => createInfos.imageExtent.width;
+        public uint Height => createInfos.imageExtent.height;
+        public VkFormat ColorFormat => createInfos.imageFormat;
+        public VkImageUsageFlags ImageUsage => createInfos.imageUsage;
+
+        public SwapChain (PresentQueue _presentableQueue, uint width = 800, uint height = 600, VkFormat format = VkFormat.B8g8r8a8Unorm,
+               VkPresentModeKHR presentMode = VkPresentModeKHR.FifoKHR)
+        : base (_presentableQueue.dev){
+
+            presentQueue = _presentableQueue;            
+            createInfos = VkSwapchainCreateInfoKHR.New();
+
+            VkSurfaceFormatKHR[] formats = Dev.phy.GetSurfaceFormats (presentQueue.Surface);
+            for (int i = 0; i < formats.Length; i++) {
+                if (formats[i].format == format) {
+                    createInfos.imageFormat = format;
+                    createInfos.imageColorSpace = formats[i].colorSpace;
+                    break;
+                }
+            }
+            if (createInfos.imageFormat == VkFormat.Undefined) 
+                throw new Exception ("Invalid format for swapchain: " + format);
+
+            VkPresentModeKHR[] presentModes = Dev.phy.GetSurfacePresentModes (presentQueue.Surface);
+            for (int i = 0; i < presentModes.Length; i++) {
+                if (presentModes[i] == presentMode) {
+                    createInfos.presentMode = presentMode;
+                    break;
+                }
+            }
+            if (createInfos.presentMode != presentMode)
+                throw new Exception ("Invalid presentMode for swapchain: " + presentMode);
+
+            createInfos.surface = presentQueue.Surface;
+            createInfos.imageExtent = new VkExtent2D (width, height);
+            createInfos.imageArrayLayers = 1;
+            createInfos.imageUsage = IMAGES_USAGE;
+            createInfos.imageSharingMode = VkSharingMode.Exclusive;
+            createInfos.compositeAlpha = VkCompositeAlphaFlagsKHR.OpaqueKHR;
+            createInfos.presentMode = presentMode;
+            createInfos.clipped = 1;            
+        }
+               public override void Activate () {
+                       if (state != ActivableState.Activated) {
+                               presentComplete = Dev.CreateSemaphore ();
+                               presentComplete.SetDebugMarkerName (Dev, "Semaphore PresentComplete");
+                       }
+                       base.Activate ();
+               }
+
+               public void Create () {
+                       if (state != ActivableState.Activated)
+                               Activate ();
+
+                       Dev.WaitIdle ();
+
+            VkSurfaceCapabilitiesKHR capabilities = Dev.phy.GetSurfaceCapabilities (presentQueue.Surface);
+
+            createInfos.minImageCount = capabilities.minImageCount;
+            createInfos.preTransform = capabilities.currentTransform;
+            createInfos.oldSwapchain = handle;
+
+            if (capabilities.currentExtent.width == 0xFFFFFFFF) {
+                if (createInfos.imageExtent.width < capabilities.minImageExtent.width)
+                    createInfos.imageExtent.width = capabilities.minImageExtent.width;
+                else if (createInfos.imageExtent.width > capabilities.maxImageExtent.width)
+                    createInfos.imageExtent.width = capabilities.maxImageExtent.width;
+
+                if (createInfos.imageExtent.height < capabilities.minImageExtent.height)
+                    createInfos.imageExtent.height = capabilities.minImageExtent.height;
+                else if (createInfos.imageExtent.height > capabilities.maxImageExtent.height)
+                    createInfos.imageExtent.height = capabilities.maxImageExtent.height;
+            } else 
+                createInfos.imageExtent = capabilities.currentExtent;
+
+            VkSwapchainKHR newSwapChain = Dev.CreateSwapChain (createInfos);
+            if (handle.Handle != 0)
+                _destroy ();
+            handle = newSwapChain;
+
+            VkImage[] tmp = Dev.GetSwapChainImages (handle);
+            images = new Image[tmp.Length];
+            for (int i = 0; i < tmp.Length; i++) {
+                images[i] = new Image (Dev, tmp[i], ColorFormat, ImageUsage, Width, Height);
+                images[i].CreateView ();
+                               images[i].SetName ("SwapChain Img" + i);
+                               images[i].Descriptor.imageView.SetDebugMarkerName (Dev, "SwapChain Img" + i + " view");
+            }
+        }
+
+        public int GetNextImage () {
+            VkResult res = vkAcquireNextImageKHR (Dev.VkDev, handle, UInt64.MaxValue, presentComplete, VkFence.Null, out currentImageIndex);
+            if (res == VkResult.ErrorOutOfDateKHR || res == VkResult.SuboptimalKHR) {
+                Create ();
+                return -1;
+            }
+            Utils.CheckResult (res);
+            return (int)currentImageIndex;
+        }
+
+        void _destroy () {
+            for (int i = 0; i < ImageCount; i++) 
+                images[i].Dispose ();
+
+            Dev.DestroySwapChain (handle);
+        }
+
+               public override string ToString () {
+                       return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString ("x")}]");
+               }
+
+               #region IDisposable Support
+               protected override void Dispose (bool disposing) {
+                       if (state == ActivableState.Activated) {
+                               if (disposing) {
+                               } else
+                                       System.Diagnostics.Debug.WriteLine ("VKE Swapchain disposed by finalizer");
+
+                               Dev.DestroySemaphore (presentComplete);
+                               _destroy ();
+
+                       } else if (disposing)
+                               System.Diagnostics.Debug.WriteLine ("Calling dispose on unactive Swapchain");
+
+                       base.Dispose (disposing);
+               }
+               #endregion
+       }
+}
diff --git a/vke/src/gl.cs b/vke/src/gl.cs
new file mode 100644 (file)
index 0000000..89de221
--- /dev/null
@@ -0,0 +1,982 @@
+// C# port of the work from J.M.P. van Waveren by jp Bruyère
+//
+// Original licence is http://www.apache.org/licenses/LICENSE-2.0
+// C# port is under MIT licencing.
+//
+// Author:
+//                      J.M.P. van Waveren 
+//           JP Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 Jean-Philippe Bruyère
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// Description :       Vulkan format properties and conversion from OpenGL.
+// Author              :       J.M.P. van Waveren
+// Date                :       07/17/2016
+// Language    :       C99
+// Format              :       Real tabs with the tab size equal to 4 spaces.
+// Copyright   :       Copyright (c) 2016 Oculus VR, LLC. All Rights reserved.
+// LICENSE
+// =======
+//
+// Copyright (c) 2016 Oculus VR, LLC.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+using System.Diagnostics;
+using VK;
+
+namespace KTX
+{
+       public static class GLHelper {
+
+               #region GL constants
+               const uint GL_R8                                        = 0x8229;
+               const uint GL_R8_SNORM                                  = 0x8F94;
+               const uint GL_RG8_SNORM                                 = 0x8F95;
+               const uint GL_RGB8_SNORM                                = 0x8F96;
+               const uint GL_RGBA8_SNORM                               = 0x8F97;
+               const uint GL_R8UI                                      = 0x8232;
+               const uint GL_RG8UI                                     = 0x8238;
+               const uint GL_RGB8UI                                    = 0x8D7D;
+               const uint GL_RGBA8UI                                   = 0x8D7C;
+               const uint GL_R8I                                       = 0x8231;
+               const uint GL_RG8I                                      = 0x8237;
+               const uint GL_RGB8I                                     = 0x8D8F;
+               const uint GL_RGBA8I                                    = 0x8D8E;
+               const uint GL_SR8                                       = 0x8FBD;
+               const uint GL_SRG8                                      = 0x8FBE;
+               const uint GL_SRGB8                                     = 0x8C41;
+               const uint GL_SRGB8_ALPHA8                              = 0x8C43;
+               const uint GL_R16                                       = 0x822A;
+               const uint GL_RG16                                      = 0x822C;
+               const uint GL_RGB16                                     = 0x8054;
+               const uint GL_RGBA16                                    = 0x805B;
+               const uint GL_R16_SNORM                                 = 0x8F98;
+               const uint GL_RG16_SNORM                                = 0x8F99;
+               const uint GL_RGB16_SNORM                               = 0x8F9A;
+               const uint GL_RGBA16_SNORM                              = 0x8F9B;
+               const uint GL_R16UI                                     = 0x8234;
+               const uint GL_RG16UI                                    = 0x823A;
+               const uint GL_RGB16UI                                   = 0x8D77;
+               const uint GL_RGBA16UI                                  = 0x8D76;
+               const uint GL_R16I                                      = 0x8233;
+               const uint GL_RG16I                                     = 0x8239;
+               const uint GL_RGB16I                                    = 0x8D89;
+               const uint GL_RGBA16I                                   = 0x8D88;
+               const uint GL_R16F                                      = 0x822D;
+               const uint GL_RG16F                                     = 0x822F;
+               const uint GL_RGB16F                                    = 0x881B;
+               const uint GL_RGBA16F                                   = 0x881A;
+               const uint GL_R32UI                                     = 0x8236;
+               const uint GL_RG32UI                                    = 0x823C;
+               const uint GL_RGB32UI                                   = 0x8D71;
+               const uint GL_RGBA32UI                                  = 0x8D70;
+               const uint GL_R32I                                      = 0x8235;
+               const uint GL_RG32I                                     = 0x823B;
+               const uint GL_RGB32I                                    = 0x8D83;
+               const uint GL_RGBA32I                                   = 0x8D82;
+               const uint GL_R32F                                      = 0x822E;
+               const uint GL_RG32F                                     = 0x8230;
+               const uint GL_RGB32F                                    = 0x8815;
+               const uint GL_RGBA32F                                   = 0x8814;
+               const uint GL_R3_G3_B2                                  = 0x2A10;
+               const uint GL_RGB4                                      = 0x804F;
+               const uint GL_RGB5                                      = 0x8050;
+               const uint GL_RGB565                                    = 0x8D62;
+               const uint GL_RGB10                                     = 0x8052;
+               const uint GL_RGB12                                     = 0x8053;
+               const uint GL_RGBA2                                     = 0x8055;
+               const uint GL_RGBA4                                     = 0x8056;
+               const uint GL_RGBA12                                    = 0x805A;
+               const uint GL_RGB5_A1                                   = 0x8057;
+               const uint GL_RGB10_A2                                  = 0x8059;
+               const uint GL_RGB10_A2UI                                = 0x906F;
+               const uint GL_R11F_G11F_B10F                            = 0x8C3A;
+               const uint GL_RGB9_E5                                   = 0x8C3D;
+               const uint GL_COMPRESSED_RGB_S3TC_DXT1_EXT              = 0x83F0;
+               const uint GL_COMPRESSED_RGBA_S3TC_DXT1_EXT             = 0x83F1;
+               const uint GL_COMPRESSED_RGBA_S3TC_DXT5_EXT             = 0x83F3;
+               const uint GL_COMPRESSED_RGBA_S3TC_DXT3_EXT             = 0x83F2;
+               const uint GL_COMPRESSED_SRGB_S3TC_DXT1_EXT             = 0x8C4C;
+               const uint GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT       = 0x8C4D;
+               const uint GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT       = 0x8C4E;
+               const uint GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT       = 0x8C4F;
+               const uint GL_COMPRESSED_LUMINANCE_LATC1_EXT            = 0x8C70;
+               const uint GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT      = 0x8C72;
+               const uint GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT     = 0x8C71;
+               const uint GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT = 0x8C73;
+               const uint GL_COMPRESSED_RED_RGTC1                      = 0x8DBB;
+               const uint GL_COMPRESSED_RG_RGTC2                       = 0x8DBD;
+               const uint GL_COMPRESSED_SIGNED_RED_RGTC1               = 0x8DBC;
+               const uint GL_COMPRESSED_SIGNED_RG_RGTC2                = 0x8DBE;
+               const uint GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT        = 0x8E8F;
+               const uint GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT          = 0x8E8E;
+               const uint GL_COMPRESSED_RGBA_BPTC_UNORM                = 0x8E8C;
+               const uint GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM          = 0x8E8D;
+               const uint GL_ETC1_RGB8_OES                             = 0x8D64;
+               const uint GL_COMPRESSED_RGB8_ETC2                      = 0x9274;
+               const uint GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2  = 0x9276;
+               const uint GL_COMPRESSED_RGBA8_ETC2_EAC                 = 0x9278;
+               const uint GL_COMPRESSED_SRGB8_ETC2                     = 0x9275;
+               const uint GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC          = 0x9279;
+               const uint GL_COMPRESSED_R11_EAC                        = 0x9270;
+               const uint GL_COMPRESSED_RG11_EAC                       = 0x9272;
+               const uint GL_COMPRESSED_SIGNED_R11_EAC                 = 0x9271;
+               const uint GL_COMPRESSED_SIGNED_RG11_EAC                = 0x9273;
+               const uint GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG           = 0x8C01;
+               const uint GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG           = 0x8C00;
+               const uint GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG          = 0x8C03;
+               const uint GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG          = 0x8C02;
+               const uint GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG          = 0x9137;
+               const uint GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG          = 0x9138;
+               const uint GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT          = 0x8A54;
+               const uint GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT          = 0x8A55;
+               const uint GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT    = 0x8A56;
+               const uint GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT    = 0x8A57;
+               const uint GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG    = 0x93F0;
+               const uint GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG    = 0x93F1;
+               const uint GL_COMPRESSED_RGBA_ASTC_4x4_KHR              = 0x93B0;
+               const uint GL_COMPRESSED_RGBA_ASTC_5x4_KHR              = 0x93B1;
+               const uint GL_COMPRESSED_RGBA_ASTC_5x5_KHR              = 0x93B2;
+               const uint GL_COMPRESSED_RGBA_ASTC_6x5_KHR              = 0x93B3;
+               const uint GL_COMPRESSED_RGBA_ASTC_6x6_KHR              = 0x93B4;
+               const uint GL_COMPRESSED_RGBA_ASTC_8x5_KHR              = 0x93B5;
+               const uint GL_COMPRESSED_RGBA_ASTC_8x6_KHR              = 0x93B6;
+               const uint GL_COMPRESSED_RGBA_ASTC_8x8_KHR              = 0x93B7;
+               const uint GL_COMPRESSED_RGBA_ASTC_10x5_KHR             = 0x93B8;
+               const uint GL_COMPRESSED_RGBA_ASTC_10x6_KHR             = 0x93B9;
+               const uint GL_COMPRESSED_RGBA_ASTC_10x8_KHR             = 0x93BA;
+               const uint GL_COMPRESSED_RGBA_ASTC_10x10_KHR            = 0x93BB;
+               const uint GL_COMPRESSED_RGBA_ASTC_12x10_KHR            = 0x93BC;
+               const uint GL_COMPRESSED_RGBA_ASTC_12x12_KHR            = 0x93BD;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR      = 0x93D0;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR      = 0x93D1;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR      = 0x93D2;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR      = 0x93D3;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR      = 0x93D4;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR      = 0x93D5;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR      = 0x93D6;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR      = 0x93D7;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR     = 0x93D8;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR     = 0x93D9;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR     = 0x93DA;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR    = 0x93DB;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR    = 0x93DC;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR    = 0x93DD;
+               const uint GL_COMPRESSED_RGBA_ASTC_3x3x3_OES            = 0x93C0;
+               const uint GL_COMPRESSED_RGBA_ASTC_4x3x3_OES            = 0x93C1;
+               const uint GL_COMPRESSED_RGBA_ASTC_4x4x3_OES            = 0x93C2;
+               const uint GL_COMPRESSED_RGBA_ASTC_4x4x4_OES            = 0x93C3;
+               const uint GL_COMPRESSED_RGBA_ASTC_5x4x4_OES            = 0x93C4;
+               const uint GL_COMPRESSED_RGBA_ASTC_5x5x4_OES            = 0x93C5;
+               const uint GL_COMPRESSED_RGBA_ASTC_5x5x5_OES            = 0x93C6;
+               const uint GL_COMPRESSED_RGBA_ASTC_6x5x5_OES            = 0x93C7;
+               const uint GL_COMPRESSED_RGBA_ASTC_6x6x5_OES            = 0x93C8;
+               const uint GL_COMPRESSED_RGBA_ASTC_6x6x6_OES            = 0x93C9;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES    = 0x93E0;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES    = 0x93E1;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES    = 0x93E2;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES    = 0x93E3;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES    = 0x93E4;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES    = 0x93E5;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES    = 0x93E6;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES    = 0x93E7;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES    = 0x93E8;
+               const uint GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES    = 0x93E9;
+               const uint GL_ATC_RGB_AMD                               = 0x8C92;
+               const uint GL_ATC_RGBA_EXPLICIT_ALPHA_AMD               = 0x8C93;
+               const uint GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD           = 0x87EE;
+               const uint GL_PALETTE4_RGB8_OES                         = 0x8B90;
+               const uint GL_PALETTE4_RGBA8_OES                        = 0x8B91;
+               const uint GL_PALETTE4_R5_G6_B5_OES                     = 0x8B92;
+               const uint GL_PALETTE4_RGBA4_OES                        = 0x8B93;
+               const uint GL_PALETTE4_RGB5_A1_OES                      = 0x8B94;
+               const uint GL_PALETTE8_RGB8_OES                         = 0x8B95;
+               const uint GL_PALETTE8_RGBA8_OES                        = 0x8B96;
+               const uint GL_PALETTE8_R5_G6_B5_OES                     = 0x8B97;
+               const uint GL_PALETTE8_RGBA4_OES                        = 0x8B98;
+               const uint GL_PALETTE8_RGB5_A1_OES                      = 0x8B99;
+               const uint GL_DEPTH_COMPONENT16                         = 0x81A5;
+               const uint GL_DEPTH_COMPONENT24                         = 0x81A6;
+               const uint GL_DEPTH_COMPONENT32                         = 0x81A7;
+               const uint GL_DEPTH_COMPONENT32F                        = 0x8CAC;
+               const uint GL_DEPTH_COMPONENT32F_NV                     = 0x8DAB;
+               const uint GL_STENCIL_INDEX1                            = 0x8D46;
+               const uint GL_STENCIL_INDEX4                            = 0x8D47;
+               const uint GL_STENCIL_INDEX8                            = 0x8D48;
+               const uint GL_STENCIL_INDEX16                           = 0x8D49;
+               const uint GL_DEPTH24_STENCIL8                          = 0x88F0;
+               const uint GL_DEPTH32F_STENCIL8                         = 0x8CAD;
+               const uint GL_DEPTH32F_STENCIL8_NV                      = 0x8DAC;
+
+               const uint GL_UNSIGNED_BYTE                             = 0x1401;
+               const uint GL_RED                                       = 0x1903;
+               const uint GL_RG                                        = 0x8227;
+               const uint GL_RGB                                       = 0x1907;
+               const uint GL_BGR                                       = 0x80E0;
+               const uint GL_RGBA                                      = 0x1908;
+               const uint GL_BGRA                                      = 0x80E1;
+               const uint GL_RED_INTEGER                               = 0x8D94;
+               const uint GL_RG_INTEGER                                = 0x8228;
+               const uint GL_RGB_INTEGER                               = 0x8D98;
+               const uint GL_BGR_INTEGER                               = 0x8D9A;
+               const uint GL_RGBA_INTEGER                              = 0x8D99;
+               const uint GL_BGRA_INTEGER                              = 0x8D9B;
+               const uint GL_STENCIL_INDEX                             = 0x1901;
+               const uint GL_DEPTH_COMPONENT                           = 0x1902;
+               const uint GL_DEPTH_STENCIL                             = 0x84F9;
+               const uint GL_BYTE                                      = 0x1400;
+               const uint GL_UNSIGNED_SHORT                            = 0x1403;
+               const uint GL_SHORT                                     = 0x1402;
+               const uint GL_HALF_FLOAT                                = 0x140B;
+               const uint GL_HALF_FLOAT_OES                            = 0x8D61;
+               const uint GL_UNSIGNED_INT                              = 0x1405;
+               const uint GL_INT                                       = 0x1404;
+               const uint GL_FLOAT                                     = 0x1406;
+               const uint GL_UNSIGNED_INT64                                                                      = 0x8BC2;
+               const uint GL_INT64                                                                                                       = 0x140E;
+               const uint GL_DOUBLE                                    = 0x140A;
+               const uint GL_UNSIGNED_BYTE_3_3_2                       = 0x8032;
+               const uint GL_UNSIGNED_BYTE_2_3_3_REV                   = 0x8362;
+               const uint GL_UNSIGNED_SHORT_5_6_5                      = 0x8363;
+               const uint GL_UNSIGNED_SHORT_5_6_5_REV                  = 0x8364;
+               const uint GL_UNSIGNED_SHORT_4_4_4_4                    = 0x8033;
+               const uint GL_UNSIGNED_SHORT_4_4_4_4_REV                = 0x8365;
+               const uint GL_UNSIGNED_SHORT_5_5_5_1                    = 0x8034;
+               const uint GL_UNSIGNED_SHORT_1_5_5_5_REV                = 0x8366;
+               const uint GL_UNSIGNED_INT_8_8_8_8                      = 0x8035;
+               const uint GL_UNSIGNED_INT_8_8_8_8_REV                  = 0x8367;
+               const uint GL_UNSIGNED_INT_10_10_10_2                   = 0x8036;
+               const uint GL_UNSIGNED_INT_2_10_10_10_REV               = 0x8368;
+               const uint GL_UNSIGNED_INT_10F_11F_11F_REV              = 0x8C3B;
+               const uint GL_UNSIGNED_INT_5_9_9_9_REV                  = 0x8C3E;
+               const uint GL_UNSIGNED_INT_24_8                         = 0x84FA;
+               const uint GL_FLOAT_32_UNSIGNED_INT_24_8_REV            = 0x8DAD;
+               #endregion
+
+               public static VkFormat vkGetFormatFromOpenGLInternalFormat(uint internalFormat)
+               {
+
+                       switch (internalFormat)
+                       {
+                               //
+                               // 8 bits per component
+                               //
+                               case GL_R8:                                                                                      return VkFormat.R8Unorm;                   // 1-component, 8-bit unsigned normalized
+                               case GL_R8_SNORM:                                    return VkFormat.R8Snorm;                   // 1-component, 8-bit signed normalized
+                               case GL_RG8_SNORM:                                   return VkFormat.R8g8Snorm;                 // 2-component, 8-bit signed normalized
+                               case GL_RGB8_SNORM:                                  return VkFormat.R8g8b8Snorm;               // 3-component, 8-bit signed normalized
+                               case GL_RGBA8_SNORM:                                 return VkFormat.R8g8b8a8Snorm;             // 4-component, 8-bit signed normalized
+                               
+                               case GL_R8UI:                                        return VkFormat.R8Uint;                    // 1-component, 8-bit unsigned integer
+                               case GL_RG8UI:                                       return VkFormat.R8g8Uint;                  // 2-component, 8-bit unsigned integer
+                               case GL_RGB8UI:                                      return VkFormat.R8g8b8Uint;                // 3-component, 8-bit unsigned integer
+                               case GL_RGBA8UI:                                     return VkFormat.R8g8b8a8Uint;              // 4-component, 8-bit unsigned integer
+                               
+                               case GL_R8I:                                         return VkFormat.R8Sint;                    // 1-component, 8-bit signed integer
+                               case GL_RG8I:                                        return VkFormat.R8g8Sint;                  // 2-component, 8-bit signed integer
+                               case GL_RGB8I:                                       return VkFormat.R8g8b8Sint;                // 3-component, 8-bit signed integer
+                               case GL_RGBA8I:                                      return VkFormat.R8g8b8a8Sint;              // 4-component, 8-bit signed integer
+                               
+                               case GL_SR8:                                         return VkFormat.R8Srgb;                    // 1-component, 8-bit sRGB
+                               case GL_SRG8:                                        return VkFormat.R8g8Srgb;                  // 2-component, 8-bit sRGB
+                               case GL_SRGB8:                                       return VkFormat.R8g8b8Srgb;                // 3-component, 8-bit sRGB
+                               case GL_SRGB8_ALPHA8:                                return VkFormat.R8g8b8a8Srgb;              // 4-component, 8-bit sRGB
+                               
+                               //
+                               // 16 bits per component
+                               //
+                               case GL_R16:                                         return VkFormat.R16Unorm;                  // 1-component, 16-bit unsigned normalized
+                               case GL_RG16:                                        return VkFormat.R16g16Unorm;               // 2-component, 16-bit unsigned normalized
+                               case GL_RGB16:                                       return VkFormat.R16g16b16Unorm;            // 3-component, 16-bit unsigned normalized
+                               case GL_RGBA16:                                      return VkFormat.R16g16b16a16Unorm;         // 4-component, 16-bit unsigned normalized
+                               
+                               case GL_R16_SNORM:                                   return VkFormat.R16Snorm;                  // 1-component, 16-bit signed normalized
+                               case GL_RG16_SNORM:                                  return VkFormat.R16g16Snorm;               // 2-component, 16-bit signed normalized
+                               case GL_RGB16_SNORM:                                 return VkFormat.R16g16b16Snorm;            // 3-component, 16-bit signed normalized
+                               case GL_RGBA16_SNORM:                                return VkFormat.R16g16b16a16Snorm;         // 4-component, 16-bit signed normalized
+                               
+                               case GL_R16UI:                                       return VkFormat.R16Uint;                   // 1-component, 16-bit unsigned integer
+                               case GL_RG16UI:                                      return VkFormat.R16g16Uint;                // 2-component, 16-bit unsigned integer
+                               case GL_RGB16UI:                                     return VkFormat.R16g16b16Uint;             // 3-component, 16-bit unsigned integer
+                               case GL_RGBA16UI:                                    return VkFormat.R16g16b16a16Uint;          // 4-component, 16-bit unsigned integer
+                               
+                               case GL_R16I:                                        return VkFormat.R16Sint;                   // 1-component, 16-bit signed integer
+                               case GL_RG16I:                                       return VkFormat.R16g16Sint;                // 2-component, 16-bit signed integer
+                               case GL_RGB16I:                                      return VkFormat.R16g16b16Sint;             // 3-component, 16-bit signed integer
+                               case GL_RGBA16I:                                     return VkFormat.R16g16b16a16Sint;          // 4-component, 16-bit signed integer
+                               
+                               case GL_R16F:                                        return VkFormat.R16Sfloat;                 // 1-component, 16-bit floating-point
+                               case GL_RG16F:                                       return VkFormat.R16g16Sfloat;              // 2-component, 16-bit floating-point
+                               case GL_RGB16F:                                      return VkFormat.R16g16b16Sfloat;           // 3-component, 16-bit floating-point
+                               case GL_RGBA16F:                                     return VkFormat.R16g16b16a16Sfloat;        // 4-component, 16-bit floating-point
+                               
+                               //
+                               // 32 bits per component
+                               //
+                               case GL_R32UI:                                       return VkFormat.R32Uint;                   // 1-component, 32-bit unsigned integer
+                               case GL_RG32UI:                                      return VkFormat.R32g32Uint;                // 2-component, 32-bit unsigned integer
+                               case GL_RGB32UI:                                     return VkFormat.R32g32b32Uint;             // 3-component, 32-bit unsigned integer
+                               case GL_RGBA32UI:                                    return VkFormat.R32g32b32a32Uint;          // 4-component, 32-bit unsigned integer
+                               
+                               case GL_R32I:                                        return VkFormat.R32Sint;                   // 1-component, 32-bit signed integer
+                               case GL_RG32I:                                       return VkFormat.R32g32Sint;                // 2-component, 32-bit signed integer
+                               case GL_RGB32I:                                      return VkFormat.R32g32b32Sint;             // 3-component, 32-bit signed integer
+                               case GL_RGBA32I:                                     return VkFormat.R32g32b32a32Sint;          // 4-component, 32-bit signed integer
+                               
+                               case GL_R32F:                                        return VkFormat.R32Sfloat;                 // 1-component, 32-bit floating-point
+                               case GL_RG32F:                                       return VkFormat.R32g32Sfloat;              // 2-component, 32-bit floating-point
+                               case GL_RGB32F:                                      return VkFormat.R32g32b32Sfloat;           // 3-component, 32-bit floating-point
+                               case GL_RGBA32F:                                     return VkFormat.R32g32b32a32Sfloat;        // 4-component, 32-bit floating-point
+                               
+                               //
+                               // Packed
+                               //
+                               case GL_R3_G3_B2:                                    return VkFormat.Undefined;                 // 3-component 3:3:2, unsigned normalized
+                               case GL_RGB4:                                        return VkFormat.Undefined;                 // 3-component 4:4:4, unsigned normalized
+                               case GL_RGB5:                                        return VkFormat.R5g5b5a1UnormPack16;       // 3-component 5:5:5, unsigned normalized
+                               case GL_RGB565:                                      return VkFormat.R5g6b5UnormPack16;         // 3-component 5:6:5, unsigned normalized
+                               case GL_RGB10:                                       return VkFormat.A2r10g10b10UnormPack32;    // 3-component 10:10:10, unsigned normalized
+                               case GL_RGB12:                                       return VkFormat.Undefined;                 // 3-component 12:12:12, unsigned normalized
+                               case GL_RGBA2:                                       return VkFormat.Undefined;                 // 4-component 2:2:2:2, unsigned normalized
+                               case GL_RGBA4:                                       return VkFormat.R4g4b4a4UnormPack16;       // 4-component 4:4:4:4, unsigned normalized
+                               case GL_RGBA12:                                      return VkFormat.Undefined;                 // 4-component 12:12:12:12, unsigned normalized
+                               case GL_RGB5_A1:                                     return VkFormat.A1r5g5b5UnormPack16;       // 4-component 5:5:5:1, unsigned normalized
+                               case GL_RGB10_A2:                                    return VkFormat.A2r10g10b10UnormPack32;    // 4-component 10:10:10:2, unsigned normalized
+                               case GL_RGB10_A2UI:                                  return VkFormat.A2r10g10b10UintPack32;     // 4-component 10:10:10:2, unsigned integer
+                               case GL_R11F_G11F_B10F:                              return VkFormat.B10g11r11UfloatPack32;     // 3-component 11:11:10, floating-point
+                               case GL_RGB9_E5:                                     return VkFormat.E5b9g9r9UfloatPack32;      // 3-component/exp 9:9:9/5, floating-point
+                               
+                               //
+                               // S3TC/DXT/BC
+                               //
+                               
+                               case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:                return VkFormat.Bc1RgbUnormBlock;          // line through 3D space, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:               return VkFormat.Bc1RgbaUnormBlock;         // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:               return VkFormat.Bc2UnormBlock;             // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:               return VkFormat.Bc3UnormBlock;             // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized
+                               
+                               case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:               return VkFormat.Bc1RgbSrgbBlock;           // line through 3D space, 4x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:         return VkFormat.Bc1RgbaSrgbBlock;          // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:         return VkFormat.Bc2SrgbBlock;              // line through 3D space plus line through 1D space, 4x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:         return VkFormat.Bc3SrgbBlock;              // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB
+                               
+                               case GL_COMPRESSED_LUMINANCE_LATC1_EXT:              return VkFormat.Bc4UnormBlock;             // line through 1D space, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:        return VkFormat.Bc5UnormBlock;             // two lines through 1D space, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:       return VkFormat.Bc4SnormBlock;             // line through 1D space, 4x4 blocks, signed normalized
+                               case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: return VkFormat.Bc5SnormBlock;             // two lines through 1D space, 4x4 blocks, signed normalized
+                               
+                               case GL_COMPRESSED_RED_RGTC1:                        return VkFormat.Bc4UnormBlock;             // line through 1D space, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RG_RGTC2:                         return VkFormat.Bc5UnormBlock;             // two lines through 1D space, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_SIGNED_RED_RGTC1:                 return VkFormat.Bc4SnormBlock;             // line through 1D space, 4x4 blocks, signed normalized
+                               case GL_COMPRESSED_SIGNED_RG_RGTC2:                  return VkFormat.Bc5SnormBlock;             // two lines through 1D space, 4x4 blocks, signed normalized
+                               
+                               case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:          return VkFormat.Bc6hUfloatBlock;           // 3-component, 4x4 blocks, unsigned floating-point
+                               case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:            return VkFormat.Bc6hSfloatBlock;           // 3-component, 4x4 blocks, signed floating-point
+                               case GL_COMPRESSED_RGBA_BPTC_UNORM:                  return VkFormat.Bc7UnormBlock;             // 4-component, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:            return VkFormat.Bc7SrgbBlock;              // 4-component, 4x4 blocks, sRGB
+                               
+                               //
+                               // ETC
+                               //
+                               case GL_ETC1_RGB8_OES:                               return VkFormat.Etc2R8g8b8UnormBlock;      // 3-component ETC1, 4x4 blocks, unsigned normalized
+                               
+                               case GL_COMPRESSED_RGB8_ETC2:                        return VkFormat.Etc2R8g8b8UnormBlock;      // 3-component ETC2, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:    return VkFormat.Etc2R8g8b8a1UnormBlock;    // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA8_ETC2_EAC:                   return VkFormat.Etc2R8g8b8a8UnormBlock;    // 4-component ETC2, 4x4 blocks, unsigned normalized
+                               
+                               case GL_COMPRESSED_SRGB8_ETC2:                       return VkFormat.Etc2R8g8b8SrgbBlock;       // 3-component ETC2, 4x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:   return VkFormat.Etc2R8g8b8a1SrgbBlock;     // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:            return VkFormat.Etc2R8g8b8a8SrgbBlock;     // 4-component ETC2, 4x4 blocks, sRGB
+                               
+                               case GL_COMPRESSED_R11_EAC:                          return VkFormat.EacR11UnormBlock;          // 1-component ETC, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RG11_EAC:                         return VkFormat.EacR11g11UnormBlock;       // 2-component ETC, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_SIGNED_R11_EAC:                   return VkFormat.EacR11SnormBlock;          // 1-component ETC, 4x4 blocks, signed normalized
+                               case GL_COMPRESSED_SIGNED_RG11_EAC:                  return VkFormat.EacR11g11SnormBlock;       // 2-component ETC, 4x4 blocks, signed normalized
+                               
+                               //
+                               // PVRTC
+                               //
+                               case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:             return VkFormat.Undefined;                 // 3-component PVRTC, 16x8 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:             return VkFormat.Undefined;                 // 3-component PVRTC, 8x8 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:            return VkFormat.Undefined;                 // 4-component PVRTC, 16x8 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:            return VkFormat.Undefined;                 // 4-component PVRTC, 8x8 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG:            return VkFormat.Undefined;                 // 4-component PVRTC, 8x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG:            return VkFormat.Undefined;                 // 4-component PVRTC, 4x4 blocks, unsigned normalized
+                               
+                               case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT:            return VkFormat.Undefined;                 // 3-component PVRTC, 16x8 blocks, sRGB
+                               case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT:            return VkFormat.Undefined;                 // 3-component PVRTC, 8x8 blocks, sRGB
+                               case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT:      return VkFormat.Undefined;                 // 4-component PVRTC, 16x8 blocks, sRGB
+                               case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT:      return VkFormat.Undefined;                 // 4-component PVRTC, 8x8 blocks, sRGB
+                               case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG:      return VkFormat.Undefined;                 // 4-component PVRTC, 8x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG:      return VkFormat.Undefined;                 // 4-component PVRTC, 4x4 blocks, sRGB
+                               
+                               //
+                               // ASTC
+                               //
+                               case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:                return VkFormat.Astc4x4UnormBlock;         // 4-component ASTC, 4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:                return VkFormat.Astc5x4UnormBlock;         // 4-component ASTC, 5x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:                return VkFormat.Astc5x5UnormBlock;         // 4-component ASTC, 5x5 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:                return VkFormat.Astc6x5UnormBlock;         // 4-component ASTC, 6x5 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:                return VkFormat.Astc6x6UnormBlock;         // 4-component ASTC, 6x6 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:                return VkFormat.Astc8x5UnormBlock;         // 4-component ASTC, 8x5 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:                return VkFormat.Astc8x6UnormBlock;         // 4-component ASTC, 8x6 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:                return VkFormat.Astc8x8UnormBlock;         // 4-component ASTC, 8x8 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:               return VkFormat.Astc10x5UnormBlock;        // 4-component ASTC, 10x5 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:               return VkFormat.Astc10x6UnormBlock;        // 4-component ASTC, 10x6 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:               return VkFormat.Astc10x8UnormBlock;        // 4-component ASTC, 10x8 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:              return VkFormat.Astc10x10UnormBlock;       // 4-component ASTC, 10x10 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:              return VkFormat.Astc12x10UnormBlock;       // 4-component ASTC, 12x10 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:              return VkFormat.Astc12x12UnormBlock;       // 4-component ASTC, 12x12 blocks, unsigned normalized
+                               
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:        return VkFormat.Astc4x4SrgbBlock;          // 4-component ASTC, 4x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:        return VkFormat.Astc5x4SrgbBlock;          // 4-component ASTC, 5x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:        return VkFormat.Astc5x5SrgbBlock;          // 4-component ASTC, 5x5 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:        return VkFormat.Astc6x5SrgbBlock;          // 4-component ASTC, 6x5 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:        return VkFormat.Astc6x6SrgbBlock;          // 4-component ASTC, 6x6 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:        return VkFormat.Astc8x5SrgbBlock;          // 4-component ASTC, 8x5 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:        return VkFormat.Astc8x6SrgbBlock;          // 4-component ASTC, 8x6 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:        return VkFormat.Astc8x8SrgbBlock;          // 4-component ASTC, 8x8 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:       return VkFormat.Astc10x5SrgbBlock;         // 4-component ASTC, 10x5 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:       return VkFormat.Astc10x6SrgbBlock;         // 4-component ASTC, 10x6 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:       return VkFormat.Astc10x8SrgbBlock;         // 4-component ASTC, 10x8 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:      return VkFormat.Astc10x10SrgbBlock;        // 4-component ASTC, 10x10 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:      return VkFormat.Astc12x10SrgbBlock;        // 4-component ASTC, 12x10 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:      return VkFormat.Astc12x12SrgbBlock;        // 4-component ASTC, 12x12 blocks, sRGB
+                               
+                               case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 3x3x3 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 4x3x3 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 4x4x3 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 4x4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 5x4x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 5x5x4 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 5x5x5 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 6x5x5 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 6x6x5 blocks, unsigned normalized
+                               case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES:              return VkFormat.Undefined;                 // 4-component ASTC, 6x6x6 blocks, unsigned normalized
+                               
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 3x3x3 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 4x3x3 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 4x4x3 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 4x4x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 5x4x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 5x5x4 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 5x5x5 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 6x5x5 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 6x6x5 blocks, sRGB
+                               case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES:      return VkFormat.Undefined;                 // 4-component ASTC, 6x6x6 blocks, sRGB
+                               
+                               //
+                               // ATC
+                               //
+                               case GL_ATC_RGB_AMD:                                 return VkFormat.Undefined;                 // 3-component, 4x4 blocks, unsigned normalized
+                               case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:                 return VkFormat.Undefined;                 // 4-component, 4x4 blocks, unsigned normalized
+                               case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:             return VkFormat.Undefined;                 // 4-component, 4x4 blocks, unsigned normalized
+                               
+                               //
+                               // Palletized
+                               //
+                               case GL_PALETTE4_RGB8_OES:                           return VkFormat.Undefined;                 // 3-component 8:8:8, 4-bit palette, unsigned normalized
+                               case GL_PALETTE4_RGBA8_OES:                          return VkFormat.Undefined;                 // 4-component 8:8:8:8, 4-bit palette, unsigned normalized
+                               case GL_PALETTE4_R5_G6_B5_OES:                       return VkFormat.Undefined;                 // 3-component 5:6:5, 4-bit palette, unsigned normalized
+                               case GL_PALETTE4_RGBA4_OES:                          return VkFormat.Undefined;                 // 4-component 4:4:4:4, 4-bit palette, unsigned normalized
+                               case GL_PALETTE4_RGB5_A1_OES:                        return VkFormat.Undefined;                 // 4-component 5:5:5:1, 4-bit palette, unsigned normalized
+                               case GL_PALETTE8_RGB8_OES:                           return VkFormat.Undefined;                 // 3-component 8:8:8, 8-bit palette, unsigned normalized
+                               case GL_PALETTE8_RGBA8_OES:                          return VkFormat.Undefined;                 // 4-component 8:8:8:8, 8-bit palette, unsigned normalized
+                               case GL_PALETTE8_R5_G6_B5_OES:                       return VkFormat.Undefined;                 // 3-component 5:6:5, 8-bit palette, unsigned normalized
+                               case GL_PALETTE8_RGBA4_OES:                          return VkFormat.Undefined;                 // 4-component 4:4:4:4, 8-bit palette, unsigned normalized
+                               case GL_PALETTE8_RGB5_A1_OES:                        return VkFormat.Undefined;                 // 4-component 5:5:5:1, 8-bit palette, unsigned normalized
+                               
+                               //
+                               // Depth/stencil
+                               //
+                               case GL_DEPTH_COMPONENT16:                           return VkFormat.D16Unorm;
+                               case GL_DEPTH_COMPONENT24:                           return VkFormat.X8D24UnormPack32;
+                               case GL_DEPTH_COMPONENT32:                           return VkFormat.Undefined;
+                               case GL_DEPTH_COMPONENT32F:                          return VkFormat.D32Sfloat;
+                               case GL_DEPTH_COMPONENT32F_NV:                       return VkFormat.D32Sfloat;
+                               case GL_STENCIL_INDEX1:                              return VkFormat.Undefined;
+                               case GL_STENCIL_INDEX4:                              return VkFormat.Undefined;
+                               case GL_STENCIL_INDEX8:                              return VkFormat.S8Uint;
+                               case GL_STENCIL_INDEX16:                             return VkFormat.Undefined;
+                               case GL_DEPTH24_STENCIL8:                            return VkFormat.D24UnormS8Uint;
+                               case GL_DEPTH32F_STENCIL8:                           return VkFormat.D32SfloatS8Uint;
+                               case GL_DEPTH32F_STENCIL8_NV:                        return VkFormat.D32SfloatS8Uint;
+                                       
+                               default:                                             return VkFormat.Undefined;
+                       }
+               }
+
+               public static VkFormat vkGetFormatFromOpenGLFormat (uint format, uint type) {
+                       switch (type) {
+                               //
+                               // 8 bits per component
+                               //
+                               case GL_UNSIGNED_BYTE: {
+                                               switch (format) {
+                                                       case GL_RED:                                                                                            return VkFormat.R8Unorm;
+                                                       case GL_RG:                                                                                             return VkFormat.R8g8Unorm;
+                                                       case GL_RGB:                                                                                            return VkFormat.R8g8b8Unorm;
+                                                       case GL_BGR:                                                                                            return VkFormat.B8g8r8Unorm;
+                                                       case GL_RGBA:                                                                                           return VkFormat.R8g8b8a8Unorm;
+                                                       case GL_BGRA:                                                                                           return VkFormat.B8g8r8a8Unorm;
+                                                       case GL_RED_INTEGER:                                                                    return VkFormat.R8Uint;
+                                                       case GL_RG_INTEGER:                                                                     return VkFormat.R8g8Uint;
+                                                       case GL_RGB_INTEGER:                                                                    return VkFormat.R8g8b8Uint;
+                                                       case GL_BGR_INTEGER:                                                                    return VkFormat.B8g8r8Uint;
+                                                       case GL_RGBA_INTEGER:                                                                   return VkFormat.R8g8b8a8Uint;
+                                                       case GL_BGRA_INTEGER:                                                                   return VkFormat.B8g8r8a8Uint;
+                                                       case GL_STENCIL_INDEX:                                                          return VkFormat.S8Uint;
+                                                       case GL_DEPTH_COMPONENT:                                                                return VkFormat.Undefined;
+                                                       case GL_DEPTH_STENCIL:                                                          return VkFormat.Undefined;
+                                               }
+                                               break;
+                                       }
+                               case GL_BYTE: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R8Snorm;
+                                                       case GL_RG: return VkFormat.R8g8Snorm;
+                                                       case GL_RGB: return VkFormat.R8g8b8Snorm;
+                                                       case GL_BGR: return VkFormat.B8g8r8Snorm;
+                                                       case GL_RGBA: return VkFormat.R8g8b8a8Snorm;
+                                                       case GL_BGRA: return VkFormat.B8g8r8a8Snorm;
+                                                       case GL_RED_INTEGER: return VkFormat.R8Sint;
+                                                       case GL_RG_INTEGER: return VkFormat.R8g8Sint;
+                                                       case GL_RGB_INTEGER: return VkFormat.R8g8b8Sint;
+                                                       case GL_BGR_INTEGER: return VkFormat.B8g8r8Sint;
+                                                       case GL_RGBA_INTEGER: return VkFormat.R8g8b8a8Sint;
+                                                       case GL_BGRA_INTEGER: return VkFormat.B8g8r8a8Sint;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.Undefined;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.Undefined;
+                                               }
+                                               break;
+                                       }
+
+                               //
+                               // 16 bits per component
+                               //
+                               case GL_UNSIGNED_SHORT: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R16Unorm;
+                                                       case GL_RG: return VkFormat.R16g16Unorm;
+                                                       case GL_RGB: return VkFormat.R16g16b16Unorm;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R16g16b16a16Unorm;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.R16Uint;
+                                                       case GL_RG_INTEGER: return VkFormat.R16g16Uint;
+                                                       case GL_RGB_INTEGER: return VkFormat.R16g16b16Uint;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.R16g16b16a16Uint;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.D16Unorm;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.D16UnormS8Uint;
+                                               }
+                                               break;
+                                       }
+                               case GL_SHORT: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R16Snorm;
+                                                       case GL_RG: return VkFormat.R16g16Snorm;
+                                                       case GL_RGB: return VkFormat.R16g16b16Snorm;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R16g16b16a16Snorm;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.R16Sint;
+                                                       case GL_RG_INTEGER: return VkFormat.R16g16Sint;
+                                                       case GL_RGB_INTEGER: return VkFormat.R16g16b16Sint;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.R16g16b16a16Sint;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.Undefined;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.Undefined;
+                                               }
+                                               break;
+                                       }
+                               case GL_HALF_FLOAT:
+                               case GL_HALF_FLOAT_OES: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R16Sfloat;
+                                                       case GL_RG: return VkFormat.R16g16Sfloat;
+                                                       case GL_RGB: return VkFormat.R16g16b16Sfloat;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R16g16b16a16Sfloat;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RG_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGB_INTEGER: return VkFormat.Undefined;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.Undefined;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.Undefined;
+                                               }
+                                               break;
+                                       }
+
+                               //
+                               // 32 bits per component
+                               //
+                               case GL_UNSIGNED_INT: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R32Uint;
+                                                       case GL_RG: return VkFormat.R32g32Uint;
+                                                       case GL_RGB: return VkFormat.R32g32b32Uint;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R32g32b32a32Uint;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.R32Uint;
+                                                       case GL_RG_INTEGER: return VkFormat.R32g32Uint;
+                                                       case GL_RGB_INTEGER: return VkFormat.R32g32b32Uint;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.R32g32b32a32Uint;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.X8D24UnormPack32;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.D24UnormS8Uint;
+                                               }
+                                               break;
+                                       }
+                               case GL_INT: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R32Sint;
+                                                       case GL_RG: return VkFormat.R32g32Sint;
+                                                       case GL_RGB: return VkFormat.R32g32b32Sint;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R32g32b32a32Sint;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.R32Sint;
+                                                       case GL_RG_INTEGER: return VkFormat.R32g32Sint;
+                                                       case GL_RGB_INTEGER: return VkFormat.R32g32b32Sint;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.R32g32b32a32Sint;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.Undefined;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.Undefined;
+                                               }
+                                               break;
+                                       }
+                               case GL_FLOAT: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R32Sfloat;
+                                                       case GL_RG: return VkFormat.R32g32Sfloat;
+                                                       case GL_RGB: return VkFormat.R32g32b32Sfloat;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R32g32b32a32Sfloat;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RG_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGB_INTEGER: return VkFormat.Undefined;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.D32Sfloat;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.D32SfloatS8Uint;
+                                               }
+                                               break;
+                                       }
+
+                               //
+                               // 64 bits per component
+                               //
+                               case GL_UNSIGNED_INT64: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R64Uint;
+                                                       case GL_RG: return VkFormat.R64g64Uint;
+                                                       case GL_RGB: return VkFormat.R64g64b64Uint;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R64g64b64a64Uint;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RG_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGB_INTEGER: return VkFormat.Undefined;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.Undefined;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.Undefined;
+                                               }
+                                               break;
+                                       }
+                               case GL_INT64: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R64Sint;
+                                                       case GL_RG: return VkFormat.R64g64Sint;
+                                                       case GL_RGB: return VkFormat.R64g64b64Sint;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R64g64b64a64Sint;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.R64Sint;
+                                                       case GL_RG_INTEGER: return VkFormat.R64g64Sint;
+                                                       case GL_RGB_INTEGER: return VkFormat.R64g64b64Sint;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.R64g64b64a64Sint;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.Undefined;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.Undefined;
+                                               }
+                                               break;
+                                       }
+                               case GL_DOUBLE: {
+                                               switch (format) {
+                                                       case GL_RED: return VkFormat.R64Sfloat;
+                                                       case GL_RG: return VkFormat.R64g64Sfloat;
+                                                       case GL_RGB: return VkFormat.R64g64b64Sfloat;
+                                                       case GL_BGR: return VkFormat.Undefined;
+                                                       case GL_RGBA: return VkFormat.R64g64b64a64Sfloat;
+                                                       case GL_BGRA: return VkFormat.Undefined;
+                                                       case GL_RED_INTEGER: return VkFormat.R64Sfloat;
+                                                       case GL_RG_INTEGER: return VkFormat.R64g64Sfloat;
+                                                       case GL_RGB_INTEGER: return VkFormat.R64g64b64Sfloat;
+                                                       case GL_BGR_INTEGER: return VkFormat.Undefined;
+                                                       case GL_RGBA_INTEGER: return VkFormat.R64g64b64a64Sfloat;
+                                                       case GL_BGRA_INTEGER: return VkFormat.Undefined;
+                                                       case GL_STENCIL_INDEX: return VkFormat.Undefined;
+                                                       case GL_DEPTH_COMPONENT: return VkFormat.Undefined;
+                                                       case GL_DEPTH_STENCIL: return VkFormat.Undefined;
+                                               }
+                                               break;
+                                       }
+
+                               //
+                               // Packed
+                               //
+
+                               case GL_UNSIGNED_BYTE_3_3_2:
+                                       Debug.Assert (format == GL_RGB || format == GL_RGB_INTEGER);
+                                       return VkFormat.Undefined;
+                               case GL_UNSIGNED_BYTE_2_3_3_REV:
+                                       Debug.Assert (format == GL_BGR || format == GL_BGR_INTEGER);
+                                       return VkFormat.Undefined;
+                               case GL_UNSIGNED_SHORT_5_6_5:
+                                       Debug.Assert (format == GL_RGB || format == GL_RGB_INTEGER);
+                                       return VkFormat.R5g6b5UnormPack16;
+                               case GL_UNSIGNED_SHORT_5_6_5_REV:
+                                       Debug.Assert (format == GL_BGR || format == GL_BGR_INTEGER);
+                                       return VkFormat.B5g6r5UnormPack16;
+                               case GL_UNSIGNED_SHORT_4_4_4_4:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER);
+                                       return VkFormat.R4g4b4a4UnormPack16;
+                               case GL_UNSIGNED_SHORT_4_4_4_4_REV:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER);
+                                       return VkFormat.B4g4r4a4UnormPack16;
+                               case GL_UNSIGNED_SHORT_5_5_5_1:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER);
+                                       return VkFormat.R5g5b5a1UnormPack16;
+                               case GL_UNSIGNED_SHORT_1_5_5_5_REV:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER);
+                                       return VkFormat.A1r5g5b5UnormPack16;
+                               case GL_UNSIGNED_INT_8_8_8_8:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER);
+                                       return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) ? VkFormat.R8g8b8a8Uint : VkFormat.R8g8b8a8Unorm;
+                               case GL_UNSIGNED_INT_8_8_8_8_REV:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER);
+                                       return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) ? VkFormat.A8b8g8r8UintPack32 : VkFormat.A8b8g8r8UnormPack32;
+                               case GL_UNSIGNED_INT_10_10_10_2:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER);
+                                       return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) ? VkFormat.A2r10g10b10UintPack32 : VkFormat.A2r10g10b10UnormPack32;
+                               case GL_UNSIGNED_INT_2_10_10_10_REV:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER);
+                                       return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) ? VkFormat.A2b10g10r10UintPack32 : VkFormat.A2b10g10r10UnormPack32;
+                               case GL_UNSIGNED_INT_10F_11F_11F_REV:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGR);
+                                       return VkFormat.B10g11r11UfloatPack32;
+                               case GL_UNSIGNED_INT_5_9_9_9_REV:
+                                       Debug.Assert (format == GL_RGB || format == GL_BGR);
+                                       return VkFormat.E5b9g9r9UfloatPack32;
+                               case GL_UNSIGNED_INT_24_8:
+                                       Debug.Assert (format == GL_DEPTH_STENCIL);
+                                       return VkFormat.D24UnormS8Uint;
+                               case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
+                                       Debug.Assert (format == GL_DEPTH_STENCIL);
+                                       return VkFormat.D32SfloatS8Uint;
+
+
+                       }
+
+                       return VkFormat.Undefined;
+               }
+
+               public static VkFormat vkGetFormatFromOpenGLType( uint type, uint numComponents, bool normalized )
+               {
+                       switch (type) {
+                               //
+                               // 8 bits per component
+                               //
+                               case GL_UNSIGNED_BYTE: {
+                                               switch (numComponents) {
+                                                       case 1: return normalized ? VkFormat.R8Unorm : VkFormat.R8Uint;
+                                                       case 2: return normalized ? VkFormat.R8g8Unorm : VkFormat.R8g8Uint;
+                                                       case 3: return normalized ? VkFormat.R8g8b8Unorm : VkFormat.R8g8b8Uint;
+                                                       case 4: return normalized ? VkFormat.R8g8b8a8Unorm : VkFormat.R8g8b8a8Uint;
+                                               }
+                                               break;
+                                       }
+                               case GL_BYTE: {
+                                               switch (numComponents) {
+                                                       case 1: return normalized ? VkFormat.R8Snorm : VkFormat.R8Sint;
+                                                       case 2: return normalized ? VkFormat.R8g8Snorm : VkFormat.R8g8Sint;
+                                                       case 3: return normalized ? VkFormat.R8g8b8Snorm : VkFormat.R8g8b8Sint;
+                                                       case 4: return normalized ? VkFormat.R8g8b8a8Snorm : VkFormat.R8g8b8a8Sint;
+                                               }
+                                               break;
+                                       }
+
+                               //
+                               // 16 bits per component
+                               //
+                               case GL_UNSIGNED_SHORT: {
+                                               switch (numComponents) {
+                                                       case 1: return normalized ? VkFormat.R16Unorm : VkFormat.R16Uint;
+                                                       case 2: return normalized ? VkFormat.R16g16Unorm : VkFormat.R16g16Uint;
+                                                       case 3: return normalized ? VkFormat.R16g16b16Unorm : VkFormat.R16g16b16Uint;
+                                                       case 4: return normalized ? VkFormat.R16g16b16a16Unorm : VkFormat.R16g16b16a16Uint;
+                                               }
+                                               break;
+                                       }
+                               case GL_SHORT: {
+                                               switch (numComponents) {
+                                                       case 1: return normalized ? VkFormat.R16Snorm : VkFormat.R16Sint;
+                                                       case 2: return normalized ? VkFormat.R16g16Snorm : VkFormat.R16g16Sint;
+                                                       case 3: return normalized ? VkFormat.R16g16b16Snorm : VkFormat.R16g16b16Sint;
+                                                       case 4: return normalized ? VkFormat.R16g16b16a16Snorm : VkFormat.R16g16b16a16Sint;
+                                               }
+                                               break;
+                                       }
+                               case GL_HALF_FLOAT:
+                               case GL_HALF_FLOAT_OES: {
+                                               switch (numComponents) {
+                                                       case 1: return VkFormat.R16Sfloat;
+                                                       case 2: return VkFormat.R16g16Sfloat;
+                                                       case 3: return VkFormat.R16g16b16Sfloat;
+                                                       case 4: return VkFormat.R16g16b16a16Sfloat;
+                                               }
+                                               break;
+                                       }
+
+                               //
+                               // 32 bits per component
+                               //
+                               case GL_UNSIGNED_INT: {
+                                               switch (numComponents) {
+                                                       case 1: return VkFormat.R32Uint;
+                                                       case 2: return VkFormat.R32g32Uint;
+                                                       case 3: return VkFormat.R32g32b32Uint;
+                                                       case 4: return VkFormat.R32g32b32a32Uint;
+                                               }
+                                               break;
+                                       }
+                               case GL_INT: {
+                                               switch (numComponents) {
+                                                       case 1: return VkFormat.R32Sint;
+                                                       case 2: return VkFormat.R32g32Sint;
+                                                       case 3: return VkFormat.R32g32b32Sint;
+                                                       case 4: return VkFormat.R32g32b32a32Sint;
+                                               }
+                                               break;
+                                       }
+                               case GL_FLOAT: {
+                                               switch (numComponents) {
+                                                       case 1: return VkFormat.R32Sfloat;
+                                                       case 2: return VkFormat.R32g32Sfloat;
+                                                       case 3: return VkFormat.R32g32b32Sfloat;
+                                                       case 4: return VkFormat.R32g32b32a32Sfloat;
+                                               }
+                                               break;
+                                       }
+
+                               //
+                               // 64 bits per component
+                               //
+                               case GL_UNSIGNED_INT64: {
+                                               switch (numComponents) {
+                                                       case 1: return VkFormat.R64Uint;
+                                                       case 2: return VkFormat.R64g64Uint;
+                                                       case 3: return VkFormat.R64g64b64Uint;
+                                                       case 4: return VkFormat.R64g64b64a64Uint;
+                                               }
+                                               break;
+                                       }
+                               case GL_INT64: {
+                                               switch (numComponents) {
+                                                       case 1: return VkFormat.R64Sint;
+                                                       case 2: return VkFormat.R64g64Sint;
+                                                       case 3: return VkFormat.R64g64b64Sint;
+                                                       case 4: return VkFormat.R64g64b64a64Sint;
+                                               }
+                                               break;
+                                       }
+                               case GL_DOUBLE: {
+                                               switch (numComponents) {
+                                                       case 1: return VkFormat.R64Sfloat;
+                                                       case 2: return VkFormat.R64g64Sfloat;
+                                                       case 3: return VkFormat.R64g64b64Sfloat;
+                                                       case 4: return VkFormat.R64g64b64a64Sfloat;
+                                               }
+                                               break;
+                                       }
+
+                               //
+                               // Packed
+                               //
+                               case GL_UNSIGNED_BYTE_3_3_2: return VkFormat.Undefined;
+                               case GL_UNSIGNED_BYTE_2_3_3_REV: return VkFormat.Undefined;
+                               case GL_UNSIGNED_SHORT_5_6_5: return VkFormat.R5g6b5UnormPack16;
+                               case GL_UNSIGNED_SHORT_5_6_5_REV: return VkFormat.B5g6r5UnormPack16;
+                               case GL_UNSIGNED_SHORT_4_4_4_4: return VkFormat.R4g4b4a4UnormPack16;
+                               case GL_UNSIGNED_SHORT_4_4_4_4_REV: return VkFormat.B4g4r4a4UnormPack16;
+                               case GL_UNSIGNED_SHORT_5_5_5_1: return VkFormat.R5g5b5a1UnormPack16;
+                               case GL_UNSIGNED_SHORT_1_5_5_5_REV: return VkFormat.A1r5g5b5UnormPack16;
+                               case GL_UNSIGNED_INT_8_8_8_8: return normalized ? VkFormat.R8g8b8a8Unorm : VkFormat.R8g8b8a8Uint;
+                               case GL_UNSIGNED_INT_8_8_8_8_REV: return normalized ? VkFormat.A8b8g8r8UnormPack32 : VkFormat.A8b8g8r8UintPack32;
+                               case GL_UNSIGNED_INT_10_10_10_2: return normalized ? VkFormat.A2r10g10b10UnormPack32 : VkFormat.A2r10g10b10UintPack32;
+                               case GL_UNSIGNED_INT_2_10_10_10_REV: return normalized ? VkFormat.A2b10g10r10UnormPack32 : VkFormat.A2b10g10r10UintPack32;
+                               case GL_UNSIGNED_INT_10F_11F_11F_REV: return VkFormat.B10g11r11UfloatPack32;
+                               case GL_UNSIGNED_INT_5_9_9_9_REV: return VkFormat.E5b9g9r9UfloatPack32;
+                               case GL_UNSIGNED_INT_24_8: return VkFormat.D24UnormS8Uint;
+                               case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: return VkFormat.D32SfloatS8Uint;
+                       }
+
+                       return VkFormat.Undefined;
+               }
+               
+
+
+       }
+}
diff --git a/vke/src/glfw/CodePoint.cs b/vke/src/glfw/CodePoint.cs
new file mode 100644 (file)
index 0000000..142dbc7
--- /dev/null
@@ -0,0 +1,43 @@
+using System.Text;
+
+namespace Glfw
+{
+    /// <summary>
+    /// Represents a native UTF32 codepoint.
+    /// </summary>
+    public struct CodePoint
+    {
+        /// <summary>
+        /// The numeric value of the codepoint.
+        /// </summary>
+        public readonly uint Value;
+
+        /// <summary>
+        /// Casts the codepoint to System.Char.
+        /// </summary>
+        /// <returns>
+        /// The character representation of the codepoint.
+        /// </returns>
+        public unsafe char ToChar()
+        {
+            uint value = this.Value;
+
+            char result;
+
+            Encoding.UTF32.GetChars((byte*)&value, 4, &result, 1);
+
+            return result;
+        }
+
+        /// <summary>
+        /// Converts the value of this instance to its equivalent string representation.
+        /// </summary>
+        /// <returns>
+        /// A string containing the character representation of the codepoint.
+        /// </returns>
+        public override string ToString()
+        {
+            return this.ToChar().ToString();
+        }
+    }
+}
diff --git a/vke/src/glfw/Delegates.cs b/vke/src/glfw/Delegates.cs
new file mode 100644 (file)
index 0000000..2f2acb6
--- /dev/null
@@ -0,0 +1,123 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Glfw {
+    /// <summary>
+    /// The function signature for keyboard key callback functions.
+    /// </summary>
+    /// <param name="window">
+    /// The window that received the event.
+    /// </param>
+    /// <param name="key">
+    /// The keyboard key that was pressed or released.
+    /// </param>
+    /// <param name="scanCode">
+    /// The system-specific scancode of the key.
+    /// </param>
+    /// <param name="action">
+    /// The input action that occured.
+    /// </param>
+    /// <param name="modifiers">
+    /// Bit field describing which modifier keys were held down.
+    /// </param>
+    public delegate void KeyDelegate (IntPtr window, Key key, int scanCode, InputAction action, Modifier modifiers);
+    /// <summary>
+    /// A delegate representing character events on a WindowHandle.
+    /// </summary>
+    /// <param name="window">
+    /// The window raising the event.
+    /// </param>
+    /// <param name="codepoint">
+    /// The Unicode codepoint of the character.
+    /// </param>
+    public delegate void CharDelegate (IntPtr window, CodePoint codepoint);
+    /// <summary>
+    /// A delegate representing character events with modifiers on a WindowHandle.
+    /// </summary>
+    /// <param name="window">
+    /// The window raising the event.
+    /// </param>
+    /// <param name="codepoint">
+    /// The Unicode codepoint of the character.
+    /// </param>
+    /// <param name="modifiers">
+    /// The modifiers applied to the character.
+    /// </param>
+    public delegate void CharModsDelegate (IntPtr window, CodePoint codepoint, Modifier modifiers);
+    /// <summary>
+    /// The function signature for cursor position callback functions.
+    /// </summary>
+    /// <param name="window">
+    /// The window that received the event.
+    /// </param>
+    /// <param name="xPosition">
+    /// The new cursor x-coordinate, relative to the left edge of the client area.
+    /// </param>
+    /// <param name="yPosition">
+    /// The new cursor y-coordinate, relative to the top edge of the client area.
+    /// </param>
+    public delegate void CursorPosDelegate (IntPtr window, double xPosition, double yPosition);
+    /// <summary>
+    /// The function signature for error callbacks.
+    /// </summary>
+    /// <param name="error">An error code giving the general category of the error.</param>
+    /// <param name="description">A string description of the error.</param>
+    [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+    public delegate void ErrorDelegate (ErrorCode error, [MarshalAs (UnmanagedType.LPStr)] string description);
+    /// <summary>
+    /// The function signature for monitor configuration callback functions.
+    /// </summary>
+    /// <param name="monitor">
+    /// The monitor that was connected or disconnected.
+    /// </param>
+    /// <param name="eventStatus">
+    /// The event that was raised.
+    /// </param>
+    [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+    public delegate void MonitorEventDelegate (MonitorHandle monitor, MonitorEvent eventStatus);
+    /// <summary>
+    /// The function signature for mouse button callback functions.
+    /// </summary>
+    /// <param name="window">
+    /// The window that received the event.
+    /// </param>
+    /// <param name="button">
+    /// The mouse button that was pressed or released.
+    /// </param>
+    /// <param name="action">
+    /// One of <see cref="InputAction.Press"/> or <see cref="InputAction.Release"/>.
+    /// </param>
+    /// <param name="mods">
+    /// Bit field describing which modifier keys were held down.
+    /// </param>
+    public delegate void MouseButtonDelegate (IntPtr window, MouseButton button, InputAction action, Modifier mods);
+    /// <summary>
+    /// The function signature for scroll callback functions.
+    /// </summary>
+    /// <param name="window">
+    /// The window that received the event.
+    /// </param>
+    /// <param name="xOffset">
+    /// The scroll offset along the x-axis.
+    /// </param>
+    /// <param name="yOffset">
+    /// The scroll offset along the y-axis.
+    /// </param>
+    public delegate void ScrollDelegate (IntPtr window, double xOffset, double yOffset);
+    /// <summary>
+    /// The function signature for window size callback functions.
+    /// </summary>
+    /// <param name="window">
+    /// The window that was resized.
+    /// </param>
+    /// <param name="width">
+    /// The new width, in screen coordinates, of the window.
+    /// </param>
+    /// <param name="height">
+    /// The new height, in screen coordinates, of the window.
+    /// </param>
+    [UnmanagedFunctionPointer (CallingConvention.Cdecl)]
+    public delegate void WindowSizeDelegate (IntPtr window, int width, int height);
+
+}
+
diff --git a/vke/src/glfw/ErrorCode.cs b/vke/src/glfw/ErrorCode.cs
new file mode 100644 (file)
index 0000000..c757488
--- /dev/null
@@ -0,0 +1,49 @@
+namespace Glfw
+{
+    /// <summary>
+    /// Indicates the general category of an error.
+    /// </summary>
+    public enum ErrorCode
+    {
+        /// <summary>
+        /// GLFW has not been initialized.
+        /// </summary>
+        NotInitialised = 0x00010001,
+        /// <summary>
+        /// No context is current for this thread.
+        /// </summary>
+        NoCurrentContext,
+        /// <summary>
+        /// One of the arguments to the function was an invalid enum value.
+        /// </summary>
+        InvalidEnum,
+        /// <summary>
+        /// One of the arguments to the function was an invalid value.
+        /// </summary>
+        InvalidValue,
+        /// <summary>
+        /// A memory allocation failed.
+        /// </summary>
+        OutOfMemory,
+        /// <summary>
+        /// GLFW could not find support for the requested API on the system.
+        /// </summary>
+        ApiUnavailable,
+        /// <summary>
+        /// The requested OpenGL or OpenGL ES version is not available.
+        /// </summary>
+        VersionUnavailable,
+        /// <summary>
+        /// A platform-specific error occurred that does not match any of the more specific categories.
+        /// </summary>
+        PlatformError,
+        /// <summary>
+        /// The requested format is not supported or available.
+        /// </summary>
+        FormatUnavailable,
+        /// <summary>
+        /// The specified window does not have an OpenGL or OpenGL ES context.
+        /// </summary>
+        NoWindowContext
+    }
+}
diff --git a/vke/src/glfw/Glfw3.cs b/vke/src/glfw/Glfw3.cs
new file mode 100644 (file)
index 0000000..27e7497
--- /dev/null
@@ -0,0 +1,534 @@
+using System;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace Glfw
+{
+       public enum CursorShape
+       {
+               Arrow           = 0x00036001,
+               IBeam           = 0x00036002,
+               Crosshair       = 0x00036003,
+               Hand            = 0x00036004,
+               HResize         = 0x00036005,
+               VResize         = 0x00036006
+       }
+       /// <summary>
+       /// Interop functions for the GLFW3 API.
+       /// </summary>
+       public unsafe static class Glfw3
+    {
+        /// <summary>
+        /// The base name for the GLFW3 library.
+        /// </summary>
+        public const string GlfwDll = "glfw";
+
+        /// <summary>
+        /// Initializes the GLFW library.
+        /// </summary>
+        /// <returns>
+        /// True if successful, otherwise false.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwInit")]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        public static extern bool Init();
+
+        /// <summary>
+        /// This function destroys all remaining windows and cursors, restores
+        /// any modified gamma ramps and frees any other allocated resources.
+        /// </summary>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwTerminate")]
+        public static extern void Terminate();
+
+        /// <summary>
+        /// This function retrieves the major, minor and revision numbers of
+        /// the GLFW library.
+        /// </summary>
+        /// <param name="major">
+        /// The major version number.
+        /// </param>
+        /// <param name="minor">
+        /// The minor version number.
+        /// </param>
+        /// <param name="rev">
+        /// The revision number.
+        /// </param>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetVersion")]
+        public static extern void GetVersion(out int major, out int minor, out int rev);
+
+
+        /// <summary>
+        /// Returns the compile-time generated version string of the GLFW
+        /// library binary. It describes the version, platform, compiler and
+        /// any platform-specific compile-time options.
+        /// </summary>
+        /// <returns>
+        /// The compile-time generated version string of the GLFW library
+        /// binary.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetVersionString")]
+        public static extern NativeString GetVersionString();
+
+        /// <summary>
+        /// Creates a window and its associated OpenGL or OpenGL ES context.
+        /// Most of the options controlling how the window and its context
+        /// should be created are specified with window hints.
+        /// </summary>
+        /// <param name="width">
+        /// The desired width, in screen coordinates, of the window. This must
+        /// be greater than zero.
+        /// </param>
+        /// <param name="height">
+        /// The desired height, in screen coordinates, of the window. This must
+        /// be greater than zero.
+        /// </param>
+        /// <param name="title">
+        /// The initial window title.
+        /// </param>
+        /// <param name="monitor">
+        /// The monitor to use for full screen mode, or Null for windowed mode.
+        /// </param>
+        /// <param name="share">
+        /// The window whose context to share resources with, or Null to not share resources.
+        /// </param>
+        /// <returns>
+        /// The handle of the created window, or Null if an error occurred.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwCreateWindow")]
+        public static extern IntPtr CreateWindow(int width, int height, [MarshalAs(UnmanagedType.LPStr)] string title, MonitorHandle monitor, IntPtr share);
+
+        /// <summary>
+        /// Destroys the specified window and its context. On calling this
+        /// function, no further callbacks will be called for that window.
+        /// </summary>
+        /// <param name="window">
+        /// The window to destroy.
+        /// </param>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwDestroyWindow")]
+        public static extern void DestroyWindow(IntPtr window);
+
+        /// <summary>
+        /// Processes events in the event queue and then returns immediately.
+        /// Processing events will cause the window and input callbacks
+        /// associated with those events to be called.
+        /// </summary>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwPollEvents")]
+        public static extern void PollEvents();
+
+        /// <summary>
+        /// Sets hints for the next call to CreateWindow. The hints, once set,
+        /// retain their values until changed by a call to WindowHint or
+        /// DefaultWindowHints, or until the library is terminated.
+        /// </summary>
+        /// <param name="hint">
+        /// The window hint to set.
+        /// </param>
+        /// <param name="value">
+        /// The new value of the window hint.
+        /// </param>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwWindowHint")]
+        public static extern void WindowHint(WindowAttribute hint, int value);
+
+        /// <summary>
+        /// Returns the value of the close flag of the specified window.
+        /// </summary>
+        /// <param name="window">
+        /// The window to query.
+        /// </param>
+        /// <returns>
+        /// The value of the close flag.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwWindowShouldClose")]
+        public static extern bool WindowShouldClose(IntPtr window);
+
+        [DllImport (GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetWindowShouldClose")]
+        public static extern void SetWindowShouldClose (IntPtr window, int value);
+
+        [DllImport (GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetWindowTitle")]
+        public static extern void SetWindowTitle (IntPtr window, [MarshalAs (UnmanagedType.LPStr)] string title);
+
+        /// <summary>
+        /// Creates a Vulkan surface for the specified window.
+        /// </summary>
+        /// <param name="instance">
+        /// The Vulkan instance to create the surface in.
+        /// </param>
+        /// <param name="window">
+        /// The window to create the surface for.
+        /// </param>
+        /// <param name="pAllocator">
+        /// The allocator to use, or NULL to use the default allocator.
+        /// </param>
+        /// <param name="surface">
+        /// Where to store the handle of the surface. This is set to
+        /// VK_NULL_HANDLE if an error occurred.
+        /// </param>
+        /// <returns>
+        /// Result.Success if successful, or a Vulkan error code if an error
+        /// occurred.
+        /// </returns>
+        [DllImport (GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwCreateWindowSurface")]
+        public static extern int CreateWindowSurface(IntPtr instance, IntPtr window, IntPtr pAllocator, out ulong surface);
+
+        /// <summary>
+        /// Returns an array of names of Vulkan instance extensions required by
+        /// GLFW for creating Vulkan surfaces for GLFW windows. If successful,
+        /// the list will always contains VK_KHR_surface, so if you don't
+        /// require any additional extensions you can pass this list directly
+        /// to the InstanceCreateInfo struct.
+        /// </summary>
+        /// <param name="count">
+        /// Where to store the number of extensions in the returned array. This
+        /// is set to zero if an error occurred.
+        /// </param>
+        /// <returns>
+        /// An array of extension names, or Null if an error occurred.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetRequiredInstanceExtensions")]
+        public static extern byte** GetRequiredInstanceExtensions(out int count);
+
+        /// <summary>
+        /// Sets the size callback of the specified window, which is called
+        /// when the window is resized. The callback is provided with the size,
+        /// in screen coordinates, of the client area of the window.
+        /// </summary>
+        /// <param name="window">
+        /// The window whose callback to set.
+        /// </param>
+        /// <param name="callback">
+        /// The new callback, or Null to remove the currently set callback.
+        /// </param>
+        /// <returns></returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetWindowSizeCallback")]
+        public static extern WindowSizeDelegate SetWindowSizeCallback(IntPtr window, WindowSizeDelegate callback);
+
+        /// <summary>
+        /// Sets the error callback, which is called with an error code and a
+        /// human-readable description each time a GLFW error occurs.
+        /// </summary>
+        /// <param name="callback">
+        /// The new callback, or Null to remove the currently set callback.
+        /// </param>
+        /// <returns>
+        /// The previously set callback, or Null if no callback was set.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetErrorCallback")]
+        public static extern ErrorDelegate SetErrorCallback(ErrorDelegate callback);
+
+        /// <summary>
+        /// Returns an array of handles for all currently connected monitors.
+        /// The primary monitor is always first in the returned array. If no
+        /// monitors were found, this function returns Null.
+        /// </summary>
+        /// <param name="count">
+        /// Where to store the number of monitors in the returned array. This
+        /// is set to zero if an error occurred.
+        /// </param>
+        /// <returns>
+        /// An array of monitor handles, or Null if no monitors were found or
+        /// if an error occurred.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetMonitors")]
+        public static extern MonitorHandle* GetMonitors(out int count);
+
+        /// <summary>
+        /// Returns the primary monitor. This is usually the monitor where
+        /// elements like the task bar or global menu bar are located.
+        /// </summary>
+        /// <returns>
+        /// The primary monitor, or Null if no monitors were found or if an
+        /// error occurred.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetPrimaryMonitor")]
+        public static extern MonitorHandle GetPrimaryMonitor();
+
+        /// <summary>
+        /// Returns the position, in screen coordinates, of the upper-left
+        /// corner of the specified monitor.
+        /// </summary>
+        /// <param name="monitor">
+        /// The monitor to query.
+        /// </param>
+        /// <param name="xPos">
+        /// Returns the monitor x-coordinate.
+        /// </param>
+        /// <param name="yPos">
+        /// Returns the monitor y-coordinate.
+        /// </param>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetMonitorPos")]
+        public static extern void GetMonitorPos(MonitorHandle monitor, out int xPos, out int yPos);
+
+        /// <summary>
+        /// Returns the size, in millimetres, of the display area of the
+        /// specified monitor.
+        /// </summary>
+        /// <param name="monitor">
+        /// The monitor to query.
+        /// </param>
+        /// <param name="widthMm">
+        /// The width, in millimetres, of the monitor's display area.
+        /// </param>
+        /// <param name="heightMm">
+        /// The width, in millimetres, of the monitor's display area.
+        /// </param>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetMonitorPhysicalSize")]
+        public static extern void GetMonitorPhysicalSize(MonitorHandle monitor, out int widthMm, out int heightMm);
+
+        /// <summary>
+        /// Returns a human-readable name, of the specified monitor. The name
+        /// typically reflects the make and model of the monitor and is not
+        /// guaranteed to be unique among the connected monitors.
+        /// </summary>
+        /// <param name="monitor">
+        /// The monitor to query.
+        /// </param>
+        /// <returns>
+        /// The name of the monitor, or Null if an error occurred.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetMonitorName")]
+        public static extern NativeString GetMonitorName(MonitorHandle monitor);
+
+        /// <summary>
+        /// Sets the monitor configuration callback, or removes the currently
+        /// set callback. This is called when a monitor is connected to or
+        /// disconnected from the system.
+        /// </summary>
+        /// <param name="callback">
+        /// The new callback, or Null to remove the currently set callback.
+        /// </param>
+        /// <returns>
+        /// The previously set callback, or NULL if no callback was set or the
+        /// library had not been initialized.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetMonitorCallback")]
+        public static extern MonitorEventDelegate SetMonitorCallback(MonitorEventDelegate callback);
+
+        /// <summary>
+        /// Returns an array of all video modes supported by the specified
+        /// monitor. The returned array is sorted in ascending order, first by
+        /// color bit depth (the sum of all channel depths) and then by
+        /// resolution area (the product of width and height).
+        /// </summary>
+        /// <param name="monitor">
+        /// The monitor to query.
+        /// </param>
+        /// <param name="count">
+        /// Tthe number of video modes in the returned array. This is set to
+        /// zero if an error occurred.
+        /// </param>
+        /// <returns>
+        /// An array of video modes, or Null if an error occurred.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetVideoModes")]
+        public static extern VideoMode* GetVideoModes(MonitorHandle monitor, out int count);
+
+        /// <summary>
+        /// Returns the current video mode of the specified monitor. If you
+        /// have created a full screen window for that monitor, the return
+        /// value will depend on whether that window is iconified.
+        /// </summary>
+        /// <param name="monitor">
+        /// The monitor to query.
+        /// </param>
+        /// <returns>
+        /// A wrapped pointer to the current mode of the monitor, or Null if
+        /// an error occurred.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetVideoMode")]
+        public static extern VideoModePointer GetVideoMode(MonitorHandle monitor);
+
+        /// <summary>
+        /// Generates a 256-element gamma ramp from the specified exponent and
+        /// then calls glfwSetGammaRamp with it. The value must be a finite
+        /// number greater than zero.
+        /// </summary>
+        /// <param name="monitor">
+        /// The monitor whose gamma ramp to set.
+        /// </param>
+        /// <param name="gamma">
+        /// The desired exponent.
+        /// </param>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetGamma")]
+        public static extern void SetGamma(MonitorHandle monitor, float gamma);
+
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetInputMode")]
+        public static extern int GetInputMode(IntPtr window, int mode);
+
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetInputMode")]
+        public static extern void SetInputMode(IntPtr window, int mode, int value);
+
+        /// <summary>
+        /// Returns the localized name of the specified printable key. This is
+        /// intended for displaying key bindings to the user.
+        /// </summary>
+        /// <param name="key">
+        /// The key to query, or Key.Unknown.
+        /// </param>
+        /// <param name="scancode">
+        /// The scancode of the key to query, if key is Key.Unknown.
+        /// </param>
+        /// <returns>
+        /// The localized name of the key, or Null.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetKeyName")]
+        public static extern NativeString GetKeyName(Key key, int scancode);
+        
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetKey")]
+        public static extern InputAction GetKey(IntPtr window, Key key);
+
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetMouseButton")]
+        public static extern InputAction GetMouseButton(IntPtr window, MouseButton button);
+
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwGetCursorPos")]
+        public static extern void GetCursorPosition(IntPtr window, out double xPosition, out double yPosition);
+
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetCursorPos")]
+        public static extern void SetCursorPosition(IntPtr window, double xPosition, double yPosition);
+
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetKeyCallback")]
+        public static extern KeyDelegate SetKeyCallback(IntPtr window, KeyDelegate callback);
+
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetCharCallback")]
+        public static extern KeyDelegate SetCharCallback(IntPtr window, CharDelegate callback);
+
+        /// <summary>
+        /// <para>Sets a callback for Mouse movement events. Use this for full
+        /// mouse path resolution between PollEvents() calls.</para>
+        /// <para>From GLFW Documentation: The callback functions receives the
+        /// cursor position, measured in screen coordinates but relative to the
+        /// top-left corner of the window client area. On platforms that
+        /// provide it, the full sub-pixel cursor position is passed on.</para>
+        /// </summary>
+        /// <returns>
+        /// The previously set callback, or NULL if no callback was set or the
+        /// library had not been initialized.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetCursorPosCallback")]
+        public static extern CursorPosDelegate SetCursorPosCallback(IntPtr window, CursorPosDelegate callback);
+
+        /// <summary>
+        /// <para>Sets a Callback for Button Events (i.e. clicks). This also
+        /// detects mouse press and release events done between PollEvents()
+        /// calls.</para>
+        /// <para>From GLFW Documentation: Whenever you poll state, you risk
+        /// missing the state change you are looking for. If a pressed mouse
+        /// button is released again before you poll its state, you will have
+        /// missed the button press. The recommended solution for this is to
+        /// use a mouse button callback, but there is also the
+        /// GLFW_STICKY_MOUSE_BUTTONS input mode.</para>
+        /// </summary>
+        /// <returns>
+        /// The previously set callback, or NULL if no callback was set or the
+        /// library had not been initialized.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetMouseButtonCallback")]
+        public static extern MouseButtonDelegate SetMouseButtonPosCallback(IntPtr window, MouseButtonDelegate callback);
+
+        /// <summary>
+        /// Sets a Callback for Mouse Scrolling Events. (i.e. scroll wheel)
+        /// There is no polling support for this, so if youre interested in the wheel, you have to set this callback
+        /// NOTE: your normal desktop mouse variant likely only reports Y-Coordinate
+        /// </summary>
+        /// <returns>
+        /// The previously set callback, or NULL if no callback was set or the
+        /// library had not been initialized.
+        /// </returns>
+        [DllImport(GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetScrollCallback")]
+        public static extern ScrollDelegate SetScrollCallback(IntPtr window, ScrollDelegate callback);
+
+        /// <summary>
+        /// Returns an array of names of Vulkan instance extensions required by
+        /// GLFW for creating Vulkan surfaces for GLFW windows. If successful,
+        /// the list will always contains VK_KHR_surface, so if you don't
+        /// require any additional extensions you can pass this list directly
+        /// to the InstanceCreateInfo struct.
+        /// </summary>
+        /// <returns>
+        /// An array of extension names, or Null if an error occurred.
+        /// </returns>
+        public static string[] GetRequiredInstanceExtensions()
+        {
+            byte** namePointer = GetRequiredInstanceExtensions(out int count);
+
+            var result = new string[count];
+
+            for (int nameIndex = 0; nameIndex < count; nameIndex++)
+            {
+                result[nameIndex] = Marshal.PtrToStringAnsi(new System.IntPtr (namePointer[nameIndex]));
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// Returns an array of handles for all currently connected monitors.
+        /// The primary monitor is always first in the returned array. If no
+        /// monitors were found, this function returns Null.
+        /// </summary>
+        /// <returns>
+        /// An array of monitor handles, or Null if no monitors were found or
+        /// if an error occurred.
+        /// </returns>
+        public static MonitorHandle[] GetMonitors()
+        {
+            MonitorHandle* monitorPointer = GetMonitors(out int count);
+
+            var result = new MonitorHandle[count];
+
+            for (int i = 0; i < count; i++)
+            {
+                result[i] = monitorPointer[i];
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// Returns an array of all video modes supported by the specified
+        /// monitor. The returned array is sorted in ascending order, first by
+        /// color bit depth (the sum of all channel depths) and then by
+        /// resolution area (the product of width and height).
+        /// </summary>
+        /// <param name="monitor">
+        /// The monitor to query.
+        /// </param>
+        /// <returns>
+        /// An array of video modes, or Null if an error occurred.
+        /// </returns>
+        public static VideoMode[] GetVideoModes(MonitorHandle monitor)
+        {
+            VideoMode* videoModePointer = GetVideoModes(monitor, out int count);
+
+            var result = new VideoMode[count];
+
+            for (int i = 0; i < count; i++)
+            {
+                result[i] = videoModePointer[i];
+            }
+
+            return result;
+        }
+
+        /// <summary>
+        /// This function retrieves the version number of the GLFW library.
+        /// </summary>
+        /// <returns>
+        /// The version number of the GLFW library.
+        /// </returns>
+        public static Version GetVersion()
+        {
+            GetVersion(out int major, out int minor, out int revision);
+
+            return new Version(major, minor, revision);
+        }
+
+               [DllImport (GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwCreateStandardCursor")]
+               public static extern IntPtr CreateStandardCursor (CursorShape shape);
+
+               [DllImport (GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwDestroyCursor")]
+               public static extern void DestroyCursor (IntPtr cursor);
+
+               [DllImport (GlfwDll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "glfwSetCursor")]
+               public static extern void SetCursor (IntPtr window, IntPtr cursor);
+       }
+}
diff --git a/vke/src/glfw/InputAction.cs b/vke/src/glfw/InputAction.cs
new file mode 100644 (file)
index 0000000..a0f7ee3
--- /dev/null
@@ -0,0 +1,21 @@
+namespace Glfw
+{
+    /// <summary>
+    /// Represents the action of an input key event or the state of a key.
+    /// </summary>
+    public enum InputAction
+    {
+        /// <summary>
+        /// The key was released or is not pressed.
+        /// </summary>
+        Release = 0,
+        /// <summary>
+        /// The key was or is pressed.
+        /// </summary>
+        Press = 1,
+        /// <summary>
+        /// The key has been held down long enough to repeat.
+        /// </summary>
+        Repeat = 2
+    }
+}
diff --git a/vke/src/glfw/Key.cs b/vke/src/glfw/Key.cs
new file mode 100644 (file)
index 0000000..0cc2a64
--- /dev/null
@@ -0,0 +1,130 @@
+namespace Glfw
+{
+    /// <summary>
+    /// Represents a key on a keyboard.
+    /// </summary>
+    public enum Key
+    {
+        Unknown = -1,
+        Space = 32,
+        Apostrophe = 39,
+        Comma = 44,
+        Minus = 45,
+        Period = 46,
+        Slash = 47,
+        Num0 = 48,
+        Num1 = 49,
+        Num2 = 50,
+        Num3 = 51,
+        Num4 = 52,
+        Num5 = 53,
+        Num6 = 54,
+        Num7 = 55,
+        Num8 = 56,
+        Num9 = 57,
+        Semicolon = 59,
+        Equal = 61,
+        A = 65,
+        B = 66,
+        C = 67,
+        D = 68,
+        E = 69,
+        F = 70,
+        G = 71,
+        H = 72,
+        I = 73,
+        J = 74,
+        K = 75,
+        L = 76,
+        M = 77,
+        N = 78,
+        O = 79,
+        P = 80,
+        Q = 81,
+        R = 82,
+        S = 83,
+        T = 84,
+        U = 85,
+        V = 86,
+        W = 87,
+        X = 88,
+        Y = 89,
+        Z = 90,
+        LeftBracket = 91,
+        Backslash = 92,
+        RightBracket = 93,
+        GraveAccent = 96,
+        World1 = 161,
+        World2 = 162,
+        Escape = 256,
+        Enter = 257,
+        Tab = 258,
+        Backspace = 259,
+        Insert = 260,
+        Delete = 261,
+        Right = 262,
+        Left = 263,
+        Down = 264,
+        Up = 265,
+        PageUp = 266,
+        PageDown = 267,
+        Home = 268,
+        End = 269,
+        CapsLock = 280,
+        ScrollLock = 281,
+        NumLock = 282,
+        PrintScreen = 283,
+        Pause = 284,
+        F1 = 290,
+        F2 = 291,
+        F3 = 292,
+        F4 = 293,
+        F5 = 294,
+        F6 = 295,
+        F7 = 296,
+        F8 = 297,
+        F9 = 298,
+        F10 = 299,
+        F11 = 300,
+        F12 = 301,
+        F13 = 302,
+        F14 = 303,
+        F15 = 304,
+        F16 = 305,
+        F17 = 306,
+        F18 = 307,
+        F19 = 308,
+        F20 = 309,
+        F21 = 310,
+        F22 = 311,
+        F23 = 312,
+        F24 = 313,
+        F25 = 314,
+        Keypad0 = 320,
+        Keypad1 = 321,
+        Keypad2 = 322,
+        Keypad3 = 323,
+        Keypad4 = 324,
+        Keypad5 = 325,
+        Keypad6 = 326,
+        Keypad7 = 327,
+        Keypad8 = 328,
+        Keypad9 = 329,
+        KeypadDecimal = 330,
+        KeypadDivide = 331,
+        KeypadMultiply = 332,
+        KeypadSubtract = 333,
+        KeypadAdd = 334,
+        KeypadEnter = 335,
+        KeypadEqual = 336,
+        LeftShift = 340,
+        LeftControl = 341,
+        LeftAlt = 342,
+        LeftSuper = 343,
+        RightShift = 344,
+        RightControl = 345,
+        RightAlt = 346,
+        RightSuper = 347,
+        Menu = 348,
+    }
+}
diff --git a/vke/src/glfw/Modifier.cs b/vke/src/glfw/Modifier.cs
new file mode 100644 (file)
index 0000000..55da485
--- /dev/null
@@ -0,0 +1,16 @@
+using System;
+
+namespace Glfw
+{
+    /// <summary>
+    /// Bitmask indicating modifer keys.
+    /// </summary>
+    [Flags]
+    public enum Modifier
+    {
+        Shift = 0x1,
+        Control = 0x2,
+        Alt = 0x4,
+        Super = 0x8,
+    }
+}
\ No newline at end of file
diff --git a/vke/src/glfw/MonitorEvent.cs b/vke/src/glfw/MonitorEvent.cs
new file mode 100644 (file)
index 0000000..340ba6e
--- /dev/null
@@ -0,0 +1,17 @@
+namespace Glfw
+{
+    /// <summary>
+    /// Events that may be raised from a monitor callback.
+    /// </summary>
+    public enum  MonitorEvent
+    {
+        /// <summary>
+        /// The monitor was connected.
+        /// </summary>
+        Connected = 0x00040001,
+        /// <summary>
+        /// The monitor was disconnected.
+        /// </summary>
+        Disconnected = 0x00040002
+    }
+}
diff --git a/vke/src/glfw/MonitorHandle.cs b/vke/src/glfw/MonitorHandle.cs
new file mode 100644 (file)
index 0000000..16b8a71
--- /dev/null
@@ -0,0 +1,34 @@
+using System;
+
+namespace Glfw
+{
+    /// <summary>
+    /// Opaque monitor handle.
+    /// </summary>
+    public struct MonitorHandle
+    {
+        internal MonitorHandle(System.IntPtr handle)
+        {
+            this.handle = handle;
+        }
+
+        private System.IntPtr handle;
+
+        /// <summary>
+        /// Gets the underlying native pointer to the monitor object.
+        /// </summary>
+        public IntPtr RawHandle
+        {
+            get
+            {
+                return this.handle;
+            }
+        }
+
+        /// <summary>
+        /// A read-only field that represents a MonitorHandle that has been
+        /// inititalised to zero.
+        /// </summary>
+        public static readonly MonitorHandle Zero = new MonitorHandle (System.IntPtr.Zero);
+    }
+}
diff --git a/vke/src/glfw/MouseButton.cs b/vke/src/glfw/MouseButton.cs
new file mode 100644 (file)
index 0000000..12425ad
--- /dev/null
@@ -0,0 +1,57 @@
+namespace Glfw
+{
+    /// <summary>
+    /// The index and name of mouse buttons for callbacks.
+    /// </summary>
+    public enum MouseButton
+    {
+        /// <summary>
+        /// The first mouse button.
+        /// </summary>
+        Button1 = 0,
+        /// <summary>
+        /// The second mouse button.
+        /// </summary>
+        Button2 = 1,
+        /// <summary>
+        /// The third mouse button.
+        /// </summary>
+        Button3 = 2,
+        /// <summary>
+        /// The fourth mouse button.
+        /// </summary>
+        Button4 = 3,
+        /// <summary>
+        /// The fifth mouse button.
+        /// </summary>
+        Button5 = 4,
+        /// <summary>
+        /// The sixth mouse button.
+        /// </summary>
+        Button6 = 5,
+        /// <summary>
+        /// The seven mouse button.
+        /// </summary>
+        Button7 = 6,
+        /// <summary>
+        /// The eighth mouse button.
+        /// </summary>
+        Button8 = 7,
+        /// <summary>
+        /// The left mouse button.
+        /// </summary>
+        Left = Button1,
+        /// <summary>
+        /// The right mouse button.
+        /// </summary>
+        Right = Button2,
+        /// <summary>
+        /// The middle mouse button.
+        /// </summary>
+        Middle = Button3,
+        /// <summary>
+        /// The highest-indexed mouse button.
+        /// </summary>
+        Last = Button8
+    }
+}
diff --git a/vke/src/glfw/NativeString.cs b/vke/src/glfw/NativeString.cs
new file mode 100644 (file)
index 0000000..f191e33
--- /dev/null
@@ -0,0 +1,50 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Glfw
+{
+    /// <summary>
+    /// Wraps a pointer to a native, null-terminated ANSI string.
+    /// </summary>
+    public struct NativeString
+    {
+        internal NativeString(System.IntPtr pointer)
+        {
+            this.pointer = pointer;
+        }
+
+        private System.IntPtr pointer;
+
+        /// <summary>
+        /// Gets the marshalled string value for this native string.
+        /// </summary>
+        public string Value
+        {
+            get
+            {
+                if (this.IsNull)
+                {
+                    throw new NullReferenceException();
+                }
+
+                return Marshal.PtrToStringAnsi(this.pointer);
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether the pointer wrapped by this
+        /// instance is null.
+        /// </summary>
+        public bool IsNull => this.pointer == System.IntPtr.Zero;
+
+        /// <summary>
+        /// The underlying pointer wrapped by this instance.
+        /// </summary>
+        public IntPtr RawPointer => this.pointer;
+
+        public override string ToString()
+        {
+            return this.Value;
+        }
+    }
+}
diff --git a/vke/src/glfw/VideoMode.cs b/vke/src/glfw/VideoMode.cs
new file mode 100644 (file)
index 0000000..8a60269
--- /dev/null
@@ -0,0 +1,47 @@
+using System.Runtime.InteropServices;
+
+namespace Glfw
+{
+    /// <summary>
+    /// Represents a single video mode.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential)]
+    public struct VideoMode
+    {
+        private int width;
+
+        private int height;
+
+        private int redBits;
+
+        private int greenBits;
+
+        private int blueBits;
+
+        /// <summary>
+        /// The resolution, in screen coordinates, of the video mode.
+        /// </summary>
+        public (int Width, int Height) Resolution => (this.width, this.height);
+
+        /// <summary>
+        /// The bit depth of the red channel of the video mode.
+        /// </summary>
+        public (int Red, int Green, int Blue) Bits => (this.redBits, this.greenBits, this.blueBits);
+
+        /// <summary>
+        /// The refresh rate, in Hz, of the video mode.
+        /// </summary>
+        public int RefreshRate;
+
+        /// <summary>
+        /// Returns a string representation of this video mode.
+        /// </summary>
+        /// <returns>
+        /// A string representation of this video mode.
+        /// </returns>
+        public override string ToString()
+        {
+            return $"[Resolution: {this.Resolution}, Bit Depth: {this.Bits}, Refresh Rate: {this.RefreshRate}Hz]";
+        }
+    }
+}
diff --git a/vke/src/glfw/VideoModePointer.cs b/vke/src/glfw/VideoModePointer.cs
new file mode 100644 (file)
index 0000000..e740772
--- /dev/null
@@ -0,0 +1,34 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Glfw
+{
+    /// <summary>
+    /// Wraps a pointer to a VideoMode struct.
+    /// </summary>
+    public struct VideoModePointer
+    {
+        internal VideoModePointer(System.IntPtr pointer)
+        {
+            this.pointer = pointer;
+        }
+
+        private System.IntPtr pointer;
+        
+        /// <summary>
+        /// Gets the VideoMode value at the referenced memory location.
+        /// </summary>
+        public VideoMode Value => this.IsNull ? throw new NullReferenceException() : Marshal.PtrToStructure<VideoMode>(this.pointer);
+
+        /// <summary>
+        /// Gets a value indicating whether the pointer wrapped by this
+        /// instance is null.
+        /// </summary>
+        public bool IsNull => this.pointer == System.IntPtr.Zero;
+
+        /// <summary>
+        /// The underlying pointer wrapped by this instance.
+        /// </summary>
+        public IntPtr RawPointer => this.pointer;
+    }
+}
diff --git a/vke/src/glfw/WindowAttribute.cs b/vke/src/glfw/WindowAttribute.cs
new file mode 100644 (file)
index 0000000..5d862af
--- /dev/null
@@ -0,0 +1,180 @@
+namespace Glfw
+{
+    /// <summary>
+    /// Attributes of a window or its framebuffer or context, that can be
+    /// configured/queried at runtime or hinted at creation.
+    /// </summary>
+    public enum WindowAttribute
+    {
+        /// <summary>
+        /// Whether the specified window has input focus. This hint is ignored
+        /// for full screen and initially hidden windows.
+        /// </summary>
+        Focused = 0x00020001,
+        /// <summary>
+        /// Whether the specified window is iconified (minimized).
+        /// </summary>
+        Iconified = 0x00020002,
+        /// <summary>
+        /// Whether the specified window is resizable by the user. This is set
+        /// on creation.
+        /// </summary>
+        Resizable = 0x00020003,
+        /// <summary>
+        /// Whether the specified window is visible.
+        /// </summary>
+        Visible = 0x00020004,
+        /// <summary>
+        /// Whether the specified window has decorations such as a border, a
+        /// close widget, etc. This is set on creation.
+        /// </summary>
+        Decorated = 0x00020005,
+        /// <summary>
+        /// Whether the full screen window will automatically iconify and
+        /// restore the previous default framebuffer on input focus loss. This hint is
+        /// ignored for windowed mode windows.
+        /// </summary>
+        AutoIconify = 0x00020006,
+        /// <summary>
+        /// Whether the windowed mode window will be floating above other
+        /// regular windows, also called topmost or always-on-top. This is
+        /// intended primarily for debugging purposes and cannot be used to
+        /// implement proper full screen windows. This hint is ignored for full
+        /// screen windows.
+        /// </summary>
+        Floating = 0x00020007,
+        /// <summary>
+        /// Whether the specified window is maximized.
+        /// </summary>
+        Maximized = 0x00020008,
+        /// <summary>
+        /// The bit depth of the red channel of the default framebuffer.
+        /// </summary>
+        RedBits = 0x00021001,
+        /// <summary>
+        /// The bit depth of the green channel of the default framebuffer.
+        /// </summary>
+        GreenBits = 0x00021002,
+        /// <summary>
+        /// The bit depth of the blue channel of the default framebuffer.
+        /// </summary>
+        BlueBits = 0x00021003,
+        /// <summary>
+        /// The bit depth of the alpha channel of the default framebuffer.
+        /// </summary>
+        AlphaBits = 0x00021004,
+        /// <summary>
+        /// The bit depth of the depth channel of the default framebuffer.
+        /// </summary>
+        DepthBits = 0x00021005,
+        /// <summary>
+        /// The bit depth of the stencil channel of the default framebuffer.
+        /// </summary>
+        StencilBits = 0x00021006,
+        /// <summary>
+        /// The desired bit depth of the red channel of the accumulation
+        /// buffer. Accumulation buffers are a legacy OpenGL feature and
+        /// should not be used in new code.
+        /// </summary>
+        AccumRedBits = 0x00021007,
+        /// <summary>
+        /// The desired bit depth of the green channel of the accumulation
+        /// buffer. Accumulation buffers are a legacy OpenGL feature and
+        /// should not be used in new code.
+        /// </summary>
+        AccumGreenBits = 0x00021008,
+        /// <summary>
+        /// The desired bit depth of the blue channel of the accumulation
+        /// buffer. Accumulation buffers are a legacy OpenGL feature and
+        /// should not be used in new code.
+        /// </summary>
+        AccumBlueBits = 0x00021009,
+        /// <summary>
+        /// The desired bit depth of the alpha channel of the accumulation
+        /// buffer. Accumulation buffers are a legacy OpenGL feature and
+        /// should not be used in new code.
+        /// </summary>
+        AccumAlphaBits = 0x0002100A,
+        /// <summary>
+        ///  The desired number of auxiliary buffers. Auxiliary buffers are a
+        ///  legacy OpenGL feature and should not be used in new code.
+        /// </summary>
+        AuxBuffers = 0x0002100B,
+        /// <summary>
+        /// Whether to use stereoscopic rendering. 
+        /// </summary>
+        Stereo = 0x0002100C,
+        /// <summary>
+        /// The desired number of samples to use for multisampling. Zero
+        /// disables multisampling.
+        /// </summary>
+        Samples = 0x0002100D,
+        /// <summary>
+        /// Whether the framebuffer should be sRGB capable.
+        /// </summary>
+        SrgbCapable = 0x0002100E,
+        /// <summary>
+        /// The desired refresh rate for full screen windows. This hint is
+        /// ignored for windowed mode windows.
+        /// </summary>
+        RefreshRate = 0x0002100F,
+        /// <summary>
+        /// Whether the framebuffer should be double buffered.
+        /// </summary>
+        DoubleBuffer = 0x00021010,
+        /// <summary>
+        /// Specifies which client API to create the context for.
+        /// </summary>
+        ClientApi = 0x00022001,
+        /// <summary>
+        /// Specifies the major component of the client API version of the
+        /// window's context.
+        /// </summary>
+        ContextVersionMajor = 0x00022002,
+        /// <summary>
+        /// Specifies the minor component of the client API version of the
+        /// window's context.
+        /// </summary>
+        ContextVersionMinor = 0x00022003,
+        /// <summary>
+        /// Specifies the revision component of the client API version of the
+        /// window's context.
+        /// </summary>
+        ContextRevision = 0x00022004,
+        /// <summary>
+        /// The robustness strategy used by the context.
+        /// </summary>
+        ContextRobustness = 0x00022005,
+        /// <summary>
+        /// Specifies whether the OpenGL context should be forward-compatible,
+        /// i.e. one where all functionality deprecated in the requested
+        /// version of OpenGL is removed. If OpenGL ES is requested, this hint
+        /// is ignored.
+        /// </summary>
+        OpenGlForwardCompat = 0x00022006,
+        /// <summary>
+        /// Specifies whether to create a debug OpenGL context, which may have
+        /// additional error and performance issue reporting functionality. If
+        /// OpenGL ES is requested, this hint is ignored.
+        /// </summary>
+        OpenGlDebugContext = 0x00022007,
+        /// <summary>
+        /// The OpenGL profile used by the context.
+        /// </summary>
+        OpenGlProfile = 0x00022008,
+        /// <summary>
+        /// Specifies the release behavior to be used by the context.
+        /// </summary>
+        ContextReleaseBehavior = 0x00022009,
+        /// <summary>
+        /// Specifies whether errors should be generated by the context. If
+        /// enabled, situations that would have generated errors instead cause
+        /// undefined behavior.
+        /// </summary>
+        ContextNoError = 0x0002200A,
+        /// <summary>
+        /// The context creation API used to create the window's context.
+        /// </summary>
+        ContextCreationApi = 0x0002200B
+    }
+}
diff --git a/vke/src/ktx.cs b/vke/src/ktx.cs
new file mode 100644 (file)
index 0000000..cc68854
--- /dev/null
@@ -0,0 +1,209 @@
+//
+// ktx.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 Jean-Philippe Bruyère
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.IO;
+
+using VK;
+using CVKL;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+
+namespace KTX {
+
+       public class KtxException : Exception {
+               public KtxException (string message) : base (message) { }
+       }
+
+       public class KTX {
+               static byte[] ktxSignature = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+
+               public static Image Load (Queue staggingQ, CommandPool staggingCmdPool, string ktxPath, VkImageUsageFlags usage = VkImageUsageFlags.Sampled,
+                       VkMemoryPropertyFlags memoryProperty = VkMemoryPropertyFlags.DeviceLocal, bool generateMipmaps = true,
+                       VkImageTiling tiling = VkImageTiling.Optimal) {
+                       Image img = null;
+
+                       using (Stream ktxStream = File.Open (ktxPath, FileMode.Open, FileAccess.Read)) {
+                               using (BinaryReader br = new BinaryReader (ktxStream)) {
+                                       if (!br.ReadBytes (12).AreEquals (ktxSignature))
+                                               throw new KtxException ("Not a ktx file: " + ktxPath);
+
+                                       UInt32 endianness = br.ReadUInt32 ();
+                                       UInt32 glType = br.ReadUInt32 ();
+                                       UInt32 glTypeSize = br.ReadUInt32 ();
+                                       UInt32 glFormat = br.ReadUInt32 ();
+                                       UInt32 glInternalFormat = br.ReadUInt32 ();
+                                       UInt32 glBaseInternalFormat = br.ReadUInt32 ();
+                                       UInt32 pixelWidth = br.ReadUInt32 ();
+                                       UInt32 pixelHeight = br.ReadUInt32 ();
+                                       UInt32 pixelDepth = Math.Max (1, br.ReadUInt32 ());
+                                       UInt32 numberOfArrayElements = br.ReadUInt32 ();//only for array text, else 0
+                                       UInt32 numberOfFaces = br.ReadUInt32 ();//only for cube map, else 1
+                                       UInt32 numberOfMipmapLevels = Math.Max (1, br.ReadUInt32 ());
+                                       UInt32 bytesOfKeyValueData = br.ReadUInt32 ();
+                                                                                       
+                                       VkFormat vkFormat = GLHelper.vkGetFormatFromOpenGLInternalFormat (glInternalFormat);
+                                       if (vkFormat == VkFormat.Undefined) {
+                                               vkFormat = GLHelper.vkGetFormatFromOpenGLFormat (glFormat, glType);
+                                               if (vkFormat == VkFormat.Undefined)
+                                                       throw new KtxException ("Undefined format: " + ktxPath);
+                                       }
+                                       VkFormatProperties formatProperties = staggingQ.Dev.phy.GetFormatProperties (vkFormat);
+                                       VkFormatFeatureFlags phyFormatSupport = (tiling == VkImageTiling.Linear) ?
+                                               formatProperties.linearTilingFeatures :
+                                               formatProperties.optimalTilingFeatures;
+
+                                       uint requestedMipsLevels = numberOfMipmapLevels;
+                                       if (numberOfMipmapLevels == 1)
+                                               requestedMipsLevels = (generateMipmaps && phyFormatSupport.HasFlag (VkFormatFeatureFlags.BlitSrc | VkFormatFeatureFlags.BlitDst)) ?
+                                                       (uint)Math.Floor (Math.Log (Math.Max (pixelWidth, pixelHeight))) + 1 : 1 ;
+                                                       
+                                       if (tiling == VkImageTiling.Optimal)
+                                               usage |= VkImageUsageFlags.TransferDst;
+                                       if (generateMipmaps)
+                                               usage |= (VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst);
+
+                                       VkImageCreateFlags createFlags = 0;
+
+                                       VkImageType imgType =
+                                               (pixelWidth == 0) ? throw new KtxException ("pixelWidth must be > 0") :
+                                               (pixelHeight == 0) ? imgType = VkImageType.Image1D :
+                                               (pixelDepth == 1) ? imgType = VkImageType.Image2D : imgType = VkImageType.Image3D;
+                                               
+
+                                       VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1;
+
+                                       if (numberOfFaces > 1) {
+                                               if (imgType != VkImageType.Image2D)
+                                                       throw new KtxException ("cubemap faces must be 2D textures");
+                                               createFlags = VkImageCreateFlags.CubeCompatible;
+                                               samples = VkSampleCountFlags.SampleCount1;
+                                               numberOfArrayElements = numberOfFaces;
+                                       } else {
+                                               numberOfFaces = 1;
+                                               if (numberOfArrayElements == 0)
+                                                       numberOfArrayElements = 1;
+                                       }
+
+                                       if (!Image.CheckFormatIsSupported (usage, phyFormatSupport))
+                                               throw new Exception ($"Unsupported image format: {vkFormat}, {tiling}, {usage}");
+
+                                       img = new Image (staggingQ.Dev, vkFormat, usage, memoryProperty, pixelWidth, pixelHeight, imgType, samples,
+                                               tiling, requestedMipsLevels, numberOfArrayElements, pixelDepth, createFlags);
+                                               
+                                       byte[] keyValueDatas = br.ReadBytes ((int)bytesOfKeyValueData);
+
+
+                                       if (memoryProperty.HasFlag (VkMemoryPropertyFlags.DeviceLocal)) {
+                                               ulong staggingSize = (ulong)ktxStream.Length;//img.AllocatedDeviceMemorySize;
+
+                                               using (HostBuffer stagging = new HostBuffer (staggingQ.Dev, VkBufferUsageFlags.TransferSrc, staggingSize)) {
+                                                       stagging.Map ();
+
+                                                       CommandBuffer cmd = staggingCmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit);
+                                                       img.SetLayout (cmd, VkImageAspectFlags.Color,
+                                                               VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal,
+                                                               VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.Transfer);
+
+                                                       List<VkBufferImageCopy> buffCopies = new List<VkBufferImageCopy> ();
+
+                                                       VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy {
+                                                               imageExtent = img.CreateInfo.extent,
+                                                               imageSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, img.CreateInfo.arrayLayers, 0)
+                                                       };
+
+                                                       ulong bufferOffset = 0;
+                                                       uint imgWidth = img.CreateInfo.extent.width;
+                                                       uint imgHeight = img.CreateInfo.extent.height;
+
+                                                       for (int mips = 0; mips < numberOfMipmapLevels; mips++) {
+                                                               UInt32 imgSize = br.ReadUInt32 ();
+
+                                                               bufferCopyRegion.bufferImageHeight = imgHeight;
+                                                               bufferCopyRegion.bufferRowLength = imgWidth;
+                                                               bufferCopyRegion.bufferOffset = bufferOffset;
+                                                               bufferCopyRegion.imageSubresource.mipLevel = (uint)mips;
+                                                               bufferCopyRegion.imageExtent.width = imgWidth;
+                                                               bufferCopyRegion.imageExtent.height = imgHeight;
+
+                                                               if (createFlags.HasFlag (VkImageCreateFlags.CubeCompatible)) {
+                                                                       for (uint face = 0; face < numberOfFaces; face++) {
+                                                                               Marshal.Copy (br.ReadBytes ((int)imgSize), 0, stagging.MappedData + (int)bufferOffset, (int)imgSize);
+                                                                               uint faceOffset = imgSize + (imgSize % 4);//cube padding                                                                                                                                                                  
+                                                                               bufferOffset += faceOffset;
+                                                                       }
+                                                                       buffCopies.Add (bufferCopyRegion);
+                                                                       bufferCopyRegion.bufferOffset = bufferOffset;
+                                                               } else {
+                                                                       Marshal.Copy (br.ReadBytes ((int)imgSize), 0, stagging.MappedData + (int)bufferOffset, (int)imgSize);
+                                                                       buffCopies.Add (bufferCopyRegion);
+                                                                       bufferOffset += imgSize;
+                                                               }
+
+                                                               imgWidth /= 2;
+                                                               imgHeight /= 2;
+                                                       }
+                                                       stagging.Unmap ();
+
+                                                       Vk.vkCmdCopyBufferToImage (cmd.Handle, stagging.handle, img.handle, VkImageLayout.TransferDstOptimal,
+                                                               (uint)buffCopies.Count, buffCopies.Pin());
+                                                       buffCopies.Unpin ();
+
+                                                       if (requestedMipsLevels > numberOfMipmapLevels)
+                                                               img.BuildMipmaps (cmd);                                                         
+                                                       else
+                                                               img.SetLayout (cmd, VkImageAspectFlags.Color,
+                                                                       VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal,
+                                                                       VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader);
+
+                                                       cmd.End ();
+
+                                                       staggingQ.Submit (cmd);
+                                                       staggingQ.WaitIdle ();
+
+                                                       cmd.Free ();
+
+                                               }
+                                       } else { 
+                                       }
+                               }
+                       }
+
+                       return img;
+               }
+       }
+}
+
+
+/*VkFormatFeatureFlags phyFormatSupport = (tiling == VkImageTiling.Linear) ?
+       dev.phy.GetFormatProperties (vkFormat).linearTilingFeatures :
+       dev.phy.GetFormatProperties (vkFormat).optimalTilingFeatures;
+
+VkFormatFeatureFlags requiredFlags = VkFormatFeatureFlags.None;
+if (usage.HasFlag (VkImageUsageFlags.ColorAttachment))
+       requiredFlags |= VkFormatFeatureFlags.ColorAttachment;
+
+if (!phyFormatSupport.HasFlag (requiredFlags))
+       throw new KtxException ("Unsupported format: " + ktxPath);*/
\ No newline at end of file
diff --git a/vke/src/model/BoundingBox.cs b/vke/src/model/BoundingBox.cs
new file mode 100644 (file)
index 0000000..07747c8
--- /dev/null
@@ -0,0 +1,78 @@
+//
+// BoundingBox
+//
+// Authors:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//              https://github.com/mellinoe
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System.Numerics;
+/* code derived from https://github.com/mellinoe*/
+namespace CVKL {
+       public struct BoundingBox {
+               public Vector3 min;
+               public Vector3 max;
+               public bool isValid;
+               public BoundingBox (Vector3 min, Vector3 max, bool isValid = false) {
+                       this.min = min;
+                       this.max = max;
+                       this.isValid = isValid;
+               }
+               public BoundingBox getAABB (Matrix4x4 m) {
+                       if (!isValid)
+                               return default (BoundingBox);
+                       Vector3 mini = new Vector3 (m.M41, m.M42, m.M43);
+                       Vector3 maxi = mini;
+                       Vector3 v0, v1;
+
+                       Vector3 right = new Vector3 (m.M11, m.M12, m.M13);
+                       v0 = right * this.min.X;
+                       v1 = right * this.max.X;
+                       mini += Vector3.Min (v0, v1);
+                       maxi += Vector3.Max (v0, v1);
+
+                       Vector3 up = new Vector3 (m.M21, m.M22, m.M23);
+                       v0 = up * this.min.Y;
+                       v1 = up * this.max.Y;
+                       mini += Vector3.Min (v0, v1);
+                       maxi += Vector3.Max (v0, v1);
+
+                       Vector3 back = new Vector3 (m.M31, m.M32, m.M33);
+                       v0 = back * this.min.Z;
+                       v1 = back * this.max.Z;
+                       mini += Vector3.Min (v0, v1);
+                       maxi += Vector3.Max (v0, v1);
+
+                       return new BoundingBox (mini, maxi, true);
+               }
+
+               public float Width => max.X - min.X;
+               public float Height => max.Y - min.Y;
+               public float Depth => max.Z - min.Z;
+
+               public Vector3 Center => new Vector3 (Width / 2f + min.X, Height / 2f + min.Y, Depth / 2f + min.Z);
+
+               public static BoundingBox operator +(BoundingBox bb1, BoundingBox bb2) {
+                       return bb1.isValid ? bb2.isValid ? new BoundingBox (Vector3.Min (bb1.min, bb2.min), Vector3.Min (bb1.max, bb2.max),true) : bb1 : bb2.isValid ? bb2 : default(BoundingBox);
+               }
+               public override string ToString () => isValid ? string.Format ($" {min}->{max}") : "Invalid";
+       }
+}
diff --git a/vke/src/model/Model.cs b/vke/src/model/Model.cs
new file mode 100644 (file)
index 0000000..0bc56e8
--- /dev/null
@@ -0,0 +1,171 @@
+//
+// Model.cs
+//
+// Author:
+//       Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// Copyright (c) 2019 jp
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using VK;
+
+namespace CVKL {
+
+       public enum AlphaMode : UInt32 {
+               Opaque,
+               Mask,
+               Blend
+       };
+       /// <summary>
+       /// Model base class, does not implement Vulkan objects (vbo, ibo, descSets,...)
+       /// </summary>
+       public class Model {
+               #region nested structs and classes
+               public struct Vertex {
+                       [VertexAttribute (VertexAttributeType.Position, VkFormat.R32g32b32Sfloat)]
+                       public Vector3 pos;
+                       [VertexAttribute (VertexAttributeType.Normal, VkFormat.R32g32b32Sfloat)]
+                       public Vector3 normal;
+                       [VertexAttribute (VertexAttributeType.UVs, VkFormat.R32g32Sfloat)]
+                       public Vector2 uv;
+                       public override string ToString () {
+                               return pos.ToString () + ";" + normal.ToString () + ";" + uv.ToString ();
+                       }
+               };
+
+               public struct Dimensions {
+                       public Vector3 min;
+                       public Vector3 max;
+
+                       public Dimensions (Vector3 _min, Vector3 _max) {
+                               min = _min;
+                               max = _max;
+                       }
+               }
+        
+        public class InstancedCmd
+        {
+            public int meshIdx;
+            public uint count;
+        }
+        
+        public class Primitive {
+                       public string name;
+                       public UInt32 indexBase;
+                       public Int32 vertexBase;
+                       public UInt32 vertexCount;
+                       public UInt32 indexCount;
+                       public UInt32 material;
+                       public BoundingBox bb;
+               }
+
+               public class Mesh {
+                       public string Name;
+                       public List<Primitive> Primitives = new List<Primitive> ();
+                       public BoundingBox bb;
+
+                       /// <summary>
+                       /// add primitive and update mesh bounding box
+                       /// </summary>
+                       public void AddPrimitive (Primitive p) {
+                               if (Primitives.Count == 0)
+                                       bb = p.bb;
+                               else
+                                       bb += p.bb;
+                               Primitives.Add (p);
+                       }
+               }
+
+               public class Scene {
+                       public string Name;
+                       public Node Root;
+                       public List<Node> GetNodes () => Root?.Children;
+            public Node FindNode(string name) => Root == null ? null : Root.FindNode (name);
+
+            public BoundingBox AABB => Root.GetAABB (Matrix4x4.Identity);
+               }
+
+               public class Node {
+            public string Name;
+                       public Node Parent;
+                       public List<Node> Children;
+                       public Matrix4x4 localMatrix;
+                       public Mesh Mesh;
+
+            public Node FindNode(string name)
+            {
+                if (Name == name)
+                    return this;
+                if (Children == null)
+                    return null;
+                foreach (Node child in Children) {
+                    Node n = child.FindNode(name);
+                    if (n != null)
+                        return n;
+                }
+                return null;
+            }
+
+            public Matrix4x4 Matrix {
+                               get { return Parent == null ? localMatrix : Parent.Matrix * localMatrix; }
+                       }
+
+                       public BoundingBox GetAABB (Matrix4x4 currentTransform) {
+                               Matrix4x4 curTransform = localMatrix * currentTransform;
+                               BoundingBox aabb = new BoundingBox();
+
+                               if (Mesh != null)
+                                       aabb = Mesh.bb.getAABB (curTransform);
+
+                               if (Children != null) {
+                                       for (int i = 0; i < Children.Count; i++) 
+                                               aabb += Children[i].GetAABB (curTransform);
+                               }
+                               return aabb;
+                       }
+               }
+               #endregion
+
+               protected Device dev;
+               protected int defaultSceneIndex;
+
+               public VkIndexType IndexBufferType;
+
+               public Scene DefaultScene => Scenes[defaultSceneIndex];
+               public List<Mesh> Meshes;
+               public List<Scene> Scenes;
+               public Dimensions dimensions = new Dimensions (new Vector3 (float.MaxValue), new Vector3 (float.MinValue));
+
+
+               public Node FindNode(string name) {
+            foreach (Scene scene in Scenes)
+            {
+                Node n = scene.FindNode(name);
+                if (n != null)
+                    return n;
+            }
+            return null;
+        }
+
+        
+       }
+}
diff --git a/vke/vke.csproj b/vke/vke.csproj
new file mode 100644 (file)
index 0000000..8c4ca40
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+       <PropertyGroup>
+               <ReleaseVersion>0.1.7</ReleaseVersion>          
+               <AssemblyVersion>$(ReleaseVersion)</AssemblyVersion>
+               <Description>C# vulkan library with IDispose model and references counting</Description>                
+               <PackageTags>vulkan game engine compute glfw c#</PackageTags>
+               <PackageVersion>$(AssemblyVersion)-beta</PackageVersion>
+               <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
+               <PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
+               <PackageProjectUrl>https://github.com/jpbruyere/vke.net/blob/master/README.md</PackageProjectUrl>
+               <License>MIT</License>
+               <PackageReleaseNotes>
+                       
+               </PackageReleaseNotes>
+               <!--<PackageIconUrl>https://github.com/KhronosGroup/glTF/blob/master/specification/figures/gltf.png</PackageIconUrl>-->
+               <SynchReleaseVersion>false</SynchReleaseVersion>
+               
+               <OutputPath>$(SolutionDir)build\$(Configuration)\</OutputPath>
+               <IntermediateOutputPath>$(SolutionDir)build\$(Configuration)\</IntermediateOutputPath>
+               
+               <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+               
+               <EnableDefaultNoneItems>false</EnableDefaultNoneItems>          
+       </PropertyGroup>
+       
+       <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+               <WarningLevel>4</WarningLevel>
+               <DebugType>full</DebugType>
+               <Optimize>false</Optimize>
+               <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+               <DefineConstants>MEMORY_POOLS;NETSTANDARD;NETSTANDARD2_0;DEBUG;NETFRAMEWORK;NET471</DefineConstants>
+       </PropertyGroup>
+       
+       <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+               <Optimize>true</Optimize>
+               <DefineConstants>MEMORY_POOLS</DefineConstants>
+       </PropertyGroup>
+       
+       <ItemGroup Condition="$(TargetFramework.StartsWith('netstandard'))">
+               <PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />          
+       </ItemGroup>
+               
+       <ItemGroup>
+               <PackageReference Include="SpirVTasks" Version="0.1.9-beta" />
+               <PackageReference Include="Vulkan" Version="0.1.4" />
+       </ItemGroup>
+       <ItemGroup>
+               <GLSLShader Include="shaders\**\*.frag;shaders\**\*.vert;shaders\**\*.comp;shaders\**\*.geom">
+                       <ResourceId>testRsID</ResourceId>
+               </GLSLShader>
+               <Compile Remove="Properties\AssemblyInfo.cs;src\Renderer.cs;src\QueueFamily.cs" />
+       </ItemGroup>
+</Project>