--- /dev/null
+.vs
+build/
+packages/
+bin/
+obj/
+*.user
+*.userprefs
--- /dev/null
+language: csharp
+dist: xenial
+
+dotnet: 2.1.502
+mono: latest
+
+script:
+ - nuget restore
+ - msbuild
+ - dotnet restore
+ - dotnet build
+
--- /dev/null
+<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>
--- /dev/null
+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.
--- /dev/null
+<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 ();
+}
+```
+
--- /dev/null
+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);
+ }
+
+ }
+}
--- /dev/null
+<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.
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<?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>
--- /dev/null
+<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>
--- /dev/null
+// 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;
+ }
+}
--- /dev/null
+// 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;
+ }
+
+ }
+ }
+
+}
--- /dev/null
+<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>
--- /dev/null
+// 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);
+ }
+ }
+
+}
--- /dev/null
+<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>
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+// 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
--- /dev/null
+<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>
--- /dev/null
+#!/bin/bash
+rm -fr build && find . -iname bin -o -iname obj | xargs rm -rf
--- /dev/null
+<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>
+
--- /dev/null
+<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>
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$(RootDirectory)netfx.props" />
+ <ItemGroup>
+ <ProjectReference Include="..\..\addons\DistanceFieldFont\DistanceFieldFont.csproj" />
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="shaders\" />
+ </ItemGroup>
+</Project>
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$(RootDirectory)netfx.props" />
+ <ItemGroup>
+ <ProjectReference Include="..\..\addons\gltfLoader\gltfLoader.csproj" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+version https://git-lfs.github.com/spec/v1
+oid sha256:efe99dfac198094a30c71dc02a4d3421f0eef6bf335aeb695daa4d62134cd93f
+size 4537
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$(RootDirectory)netfx.props" />
+
+ <ItemGroup>
+ <PackageReference Include="glTF2Loader" Version="1.1.3-alpha" />
+ </ItemGroup>
+
+</Project>
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$(RootDirectory)netfx.props" />
+ <ItemGroup>
+ <ProjectReference Include="..\..\addons\VkvgPipeline\VkvgPipeline.csproj" />
+ </ItemGroup>
+</Project>
+
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$(RootDirectory)netfx.props" />
+ <PropertyGroup>
+ <IncludeDefaultNoneItems>false</IncludeDefaultNoneItems>
+ </PropertyGroup>
+</Project>
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+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
+ }
+}
--- /dev/null
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
--- /dev/null
+<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
--- /dev/null
+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);
+ }
+
+
+ }
+}
--- /dev/null
+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 ();
+ }
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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
--- /dev/null
+#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);
+}
+
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+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 ();
+ }
+ }
+}
--- /dev/null
+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);
+ }
+
+
+ }
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+<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
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+// 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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+/*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 ();
+ }
+ }
+}
--- /dev/null
+<?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>
+
--- /dev/null
+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";
+}
--- /dev/null
+<?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>
+
--- /dev/null
+<?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>
+
--- /dev/null
+<?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
--- /dev/null
+<?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>
+
--- /dev/null
+<?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>
+
--- /dev/null
+<?xml version="1.0"?>
+<EnumSelector Height="300" EnumValue="{²CurrentDebugView}"/>
+
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+// 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);
+ }
+ }
+
+}
--- /dev/null
+/* 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);
+ }
+ }
+}
--- /dev/null
+<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>
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+// 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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+// 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
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+<?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"/>-->
+
--- /dev/null
+/* 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 ();
+ }
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <Import Project="$(RootDirectory)netfx.props" />
+ <ItemGroup>
+ <Folder Include="shaders\" />
+ </ItemGroup>
+</Project>
--- /dev/null
+
+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
--- /dev/null
+../netfx.props
\ No newline at end of file
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+// 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);
+ }
+ }
+ }
+}
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+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
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+//
+// 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 ();
+ }
+ }
+}
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+// 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;
+ }
+ }
+ }
+}
--- /dev/null
+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
--- /dev/null
+// 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;
+ }
+ }
+}
--- /dev/null
+// 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;
+ }
+ }
+ }
+}
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+// 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
+
+ }
+}
--- /dev/null
+//
+// 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);
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+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
+ }
+}
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+// 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);
+ }
+ }
+}
--- /dev/null
+// 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
+ }
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+//
+// 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
+
+ }
+}
--- /dev/null
+//
+// 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 ();
+ }
+ }
+ }
+}
--- /dev/null
+//
+// 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);
+ }
+ }
+}
--- /dev/null
+//
+// 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 ();
+ }
+ }
+}
--- /dev/null
+//
+// 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 ();
+ }
+ }
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+//
+// 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;
+ }
+ }
+}
--- /dev/null
+//
+// 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);
+ }
+ }
+}
--- /dev/null
+//
+// 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
+
+ }
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+//
+// 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
+
+ }
+}
--- /dev/null
+//
+// 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);
+ }
+ }
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+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
--- /dev/null
+//
+// 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;
+ }
+}
--- /dev/null
+//
+// 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
+ }
+}
--- /dev/null
+// 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;
+ }
+
+
+
+ }
+}
--- /dev/null
+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();
+ }
+ }
+}
--- /dev/null
+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);
+
+}
+
--- /dev/null
+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
+ }
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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
+ }
+}
--- /dev/null
+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,
+ }
+}
--- /dev/null
+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
--- /dev/null
+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
+ }
+}
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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
+ }
+}
--- /dev/null
+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;
+ }
+ }
+}
--- /dev/null
+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]";
+ }
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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
+ }
+}
--- /dev/null
+//
+// 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
--- /dev/null
+//
+// 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";
+ }
+}
--- /dev/null
+//
+// 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;
+ }
+
+
+ }
+}
--- /dev/null
+<?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>