From: Jean-Philippe Bruyère Date: Tue, 10 Sep 2019 01:15:01 +0000 (+0200) Subject: split vk.net and cvkl, cvkl renamed in vke.net X-Git-Tag: v0.1.21~39 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=c1beec87ff7965487dbc658d0e123bfef1ca656a;p=jp%2Fvke.net.git split vk.net and cvkl, cvkl renamed in vke.net --- c1beec87ff7965487dbc658d0e123bfef1ca656a diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1aece0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.vs +build/ +packages/ +bin/ +obj/ +*.user +*.userprefs diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..610673e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: csharp +dist: xenial + +dotnet: 2.1.502 +mono: latest + +script: + - nuget restore + - msbuild + - dotnet restore + - dotnet build + diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..449260d --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,14 @@ + + + $(MSBuildThisFileDirectory) + true + + net471;netstandard2.0 + + https://github.com/jpbruyere/vke.net + MIT + Jean-Philippe Bruyère + + 7.2 + + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..2c2a0a1 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2016] [Jean-Philippe Bruyère] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3e59709 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +

+vke.net +
+

+ + + + +

+

+ +

+ + + +
adaptation of the gltf PBR sample from Sacha Willems
+

+**Vulkan Engine for .net** is composed of high level classes encapsulating vulkan objects with `IDispose` model and **reference counting**. [GLFW](https://www.glfw.org/) handles the windowing system. + +### Requirements +- [GLFW](https://www.glfw.org/) +- [libstb](https://github.com/nothings/stb), on debian install **libstb-dev**. +- [Vulkan Sdk](https://www.lunarg.com/vulkan-sdk/), **glslc** has to be in the path. +- optionaly for ui, you will need [vkvg](https://github.com/jpbruyere/vkvg). + +### Features + +- physicaly based rendering, direct and deferred +- glTF 2.0 +- ktx image loading. +- Memory pools + +### VkWindow class + +To create a new vulkan application, derrive your application from `VkWindow`. Validation and +debug reports may be activated with the static Fields of the `Instance` class. + +```csharp +class Program : VkWindow { + static void Main (string[] args) { + Instance.VALIDATION = true; + + using (Program vke = new Program ()) { + vke.Run (); + } + } +} +``` + +### Enabling features + +Override the `configureEnabledFeatures` method of `VkWindow` to enable features. +```csharp +protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) { + enabled_features.samplerAnisotropy = available_features.samplerAnisotropy; +} +``` +### Creating queues + +To create queues, override the `createQueues` method of `VkWindow`. This function is called before the logical device creation and will take care of physically available queues, creating duplicates if count exceed availability. The `base` method will create a default presentable queue. + +```csharp +protected override void createQueues () { + base.createQueues (); + transferQ = new Queue (dev, VkQueueFlags.Transfer); +} +``` +### Rendering + +The constructor of the `VkWIndow` will finish the vulkan initialisation, so that you may create pipelines, buffers, and so on in your constructor. + +VkWindow will provide the default swapchain, but it's up to you to create the frame buffers. For the triangle example, create them in the `OnResize` override. +```csharp +Framebuffer[] frameBuffers; + +protected override void OnResize () { + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height, + (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + null + } : new Image[] { + null, + null, + swapChain.images[i] + }); + + buildCommandBuffers (); +} +``` + diff --git a/SpirVTasks/CompileGLSLTask.cs b/SpirVTasks/CompileGLSLTask.cs new file mode 100644 index 0000000..cac29fc --- /dev/null +++ b/SpirVTasks/CompileGLSLTask.cs @@ -0,0 +1,203 @@ +using System; +using System.Diagnostics; +using System.IO; +using Microsoft.Build.Framework; + +namespace SpirVTasks { + + public class IncludeFileNotFound : FileNotFoundException { + public string SourceFile; + public int SourceLine; + + public IncludeFileNotFound(string srcFileName, int srcLine, string includeFileName) : + base ("include file not found", includeFileName) { + + SourceFile = srcFileName; + SourceLine = srcLine; + } + } + + public class CompileGLSLTask : Microsoft.Build.Utilities.Task { + + [Required] + public ITaskItem SourceFile { + get; + set; + } + [Required] + public ITaskItem TempDirectory { + get; + set; + } + public ITaskItem AdditionalIncludeDirectories { + get; + set; + } + public ITaskItem SpirVCompilerPath { + get; + set; + } + [Required] + [Output] + public ITaskItem DestinationFile { + get; + set; + } + + volatile bool success; + + bool tryFindInclude (string include, out string incFile) { + if (!string.IsNullOrEmpty (AdditionalIncludeDirectories?.ItemSpec)) { + foreach (string incDir in AdditionalIncludeDirectories.ItemSpec.Split (';', ',', '|')) { + incFile = Path.Combine (incDir, include); + if (File.Exists (incFile)) + return true; + } + } + incFile = ""; + return false; + } + + void build_source (string src, StreamWriter temp) { + using (StreamReader sr = new StreamReader (File.OpenRead (src))) { + int srcLine = 0; + while (!sr.EndOfStream) { + string line = sr.ReadLine (); + if (line.Trim ().StartsWith ("#include", StringComparison.Ordinal)) { + string include = line.Split ('"', '<', '>')[1]; + string incFile = Path.Combine (Path.GetDirectoryName (src), include); + if (!File.Exists (incFile)) { + if (!tryFindInclude(include, out incFile)) + throw new IncludeFileNotFound (src, srcLine, include); + } + build_source (incFile, temp); + } else + temp.WriteLine (line); + srcLine++; + } + } + } + + bool tryFindGlslcExecutable (out string glslcPath) { + if (!string.IsNullOrEmpty (SpirVCompilerPath?.ItemSpec)) { + glslcPath = SpirVCompilerPath.ItemSpec; + if (!File.Exists (glslcPath)) + return false; + } + + string glslcExec = "glslc"; + if (Environment.OSVersion.Platform.ToString ().StartsWith ("Win", StringComparison.Ordinal)) + glslcExec = glslcExec + "exe"; + + glslcPath = Path.Combine (Environment.GetEnvironmentVariable ("VULKAN_SDK"), "bin"); + glslcPath = Path.Combine (glslcPath, glslcExec); + if (File.Exists (glslcPath)) + return true; + + string envStrPathes = Environment.GetEnvironmentVariable ("PATH"); + if (!string.IsNullOrEmpty (envStrPathes)) { + foreach (string path in envStrPathes.Split (';')) { + glslcPath = Path.Combine (path, glslcExec); + if (File.Exists (glslcPath)) + return true; + } + } + return false; + } + + + public override bool Execute () { + + success = true; + + if (!tryFindGlslcExecutable(out string glslcPath)) { + BuildErrorEventArgs err = new BuildErrorEventArgs ("execute", "VK001", BuildEngine.ProjectFileOfTaskNode, 0, 0, 0, 0, $"glslc command not found: {glslcPath}", "Set 'VULKAN_SDK' environment variable", "SpirVTasks"); + BuildEngine.LogErrorEvent (err); + return false; + } + + string tempFile = Path.Combine (TempDirectory.ItemSpec, SourceFile.ItemSpec); + if (File.Exists (tempFile)) + File.Delete (tempFile); + try { + Directory.CreateDirectory (Path.GetDirectoryName (tempFile)); + using (StreamWriter sw = new StreamWriter (File.OpenWrite(tempFile))) { + string src = SourceFile.ItemSpec; + build_source (SourceFile.ItemSpec, sw); + } + } catch (IncludeFileNotFound ex) { + BuildErrorEventArgs err = new BuildErrorEventArgs ("include", "VK002", ex.SourceFile, ex.SourceLine, 0, 0, 0, $"include file not found: {ex.FileName}", "", "SpirVTasks"); + BuildEngine.LogErrorEvent (err); + return false; + }catch (Exception ex) { + BuildErrorEventArgs err = new BuildErrorEventArgs ("include", "VK000", SourceFile.ItemSpec, 0, 0, 0, 0, ex.ToString(), "", "SpirVTasks"); + BuildEngine.LogErrorEvent (err); + return false; + } + + Directory.CreateDirectory (Path.GetDirectoryName (DestinationFile.ItemSpec)); + + Process glslc = new Process(); + //glslc.StartInfo.StandardOutputEncoding = System.Text.Encoding.ASCII; + //glslc.StartInfo.StandardErrorEncoding = System.Text.Encoding.ASCII; + glslc.StartInfo.UseShellExecute = false; + glslc.StartInfo.RedirectStandardOutput = true; + glslc.StartInfo.RedirectStandardError = true; + glslc.StartInfo.FileName = glslcPath; + glslc.StartInfo.Arguments = $"{tempFile} -o {DestinationFile.ItemSpec}"; + glslc.StartInfo.CreateNoWindow = true; + + glslc.EnableRaisingEvents = true; + glslc.OutputDataReceived += Glslc_OutputDataReceived; + glslc.ErrorDataReceived += Glslc_ErrorDataReceived; + + glslc.Start (); + + glslc.BeginErrorReadLine (); + glslc.BeginOutputReadLine (); + + DestinationFile.SetMetadata ("LogicalName", $"FromCS.{SourceFile.ItemSpec.Replace (Path.DirectorySeparatorChar, '.')}"); + + glslc.WaitForExit (); + + return success; + } + + void Glslc_ErrorDataReceived (object sender, DataReceivedEventArgs e) { + if (e.Data == null) + return; + + if (string.Equals (e.Data, "(0)", StringComparison.Ordinal)) + return; + + string[] tmp = e.Data.Split (':'); + + Log.LogMessage (MessageImportance.High, $"glslc: {e.Data}"); + + if (tmp.Length == 5) { + string srcFile = SourceFile.ItemSpec; + int line = Math.Max (0, int.Parse (tmp[1]) - 1); + + BuildErrorEventArgs err = new BuildErrorEventArgs ("compile", tmp[2], srcFile, line, 0, 0, 0, $"{tmp[3]} {tmp[4]}", "no help", "SpirVTasks"); + BuildEngine.LogErrorEvent (err); + success = false; + } else { + Log.LogMessage (MessageImportance.High, $"{e.Data}"); + } + } + + + void Glslc_OutputDataReceived (object sender, DataReceivedEventArgs e) { + if (e.Data == null) + return; + if (string.Equals (e.Data, "(0)", StringComparison.Ordinal)) + return; + + Log.LogMessage (MessageImportance.High, $"data:{e.Data}"); + + BuildWarningEventArgs taskEvent = new BuildWarningEventArgs ("glslc", "0", BuildEngine.ProjectFileOfTaskNode, 0, 0, 0, 0, $"{e.Data}", "no help", "SpirVTasks"); + BuildEngine.LogWarningEvent (taskEvent); + } + + } +} diff --git a/SpirVTasks/README.md b/SpirVTasks/README.md new file mode 100644 index 0000000..226e438 --- /dev/null +++ b/SpirVTasks/README.md @@ -0,0 +1,62 @@ +

+ SpirVTasks MSBuild add-on +
+

+ + + + + + +

+

+ +**SpirVTasks** package add **SpirV** compilation support to msbuild project. Error and warning +are routed to the **IDE**. + + +#### Usage +```xml + + + +``` +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 + + + NewName.vert.spv + + +``` +**VULKAN_SDK**/bin then **PATH** are searched for the **glslc** executable. You can also use **`SpirVglslcPath`** property. +```xml + + bin\glslc.exe + +``` + + +#### Include in glsl +```glsl +#include + +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 **``** directories if present. + +```xml + + $(MSBuildThisFileDirectory)common;testdir;../anotherdir + +``` + +#### todo +- Error source file and line with included files. diff --git a/SpirVTasks/SpirVTasks.csproj b/SpirVTasks/SpirVTasks.csproj new file mode 100644 index 0000000..314ddc7 --- /dev/null +++ b/SpirVTasks/SpirVTasks.csproj @@ -0,0 +1,39 @@ + + + net471;netstandard2.0 + 0.1.9 + MSBuild addon to compile and embed spirV shaders + + SpirVTasks + vulkan msbuild spirv glsl addons + $(AssemblyVersion)-beta + True + False + https://github.com/jpbruyere/vk.net/blob/master/SpirVTasks/README.md + MIT + Jean-Philippe Bruyère + $(SolutionDir)build\$(Configuration)\ + + false + false + false + true + + SpirVTasks_$(TargetFramework) + + + + + + + true + true + build\ + + + true + true + build + + + diff --git a/SpirVTasks/SpirVTasks.targets b/SpirVTasks/SpirVTasks.targets new file mode 100644 index 0000000..6825c47 --- /dev/null +++ b/SpirVTasks/SpirVTasks.targets @@ -0,0 +1,30 @@ + + + + + + DXC + + + + + + + + + + + + + diff --git a/SpirVTasks/spirv.xml b/SpirVTasks/spirv.xml new file mode 100644 index 0000000..9635ad6 --- /dev/null +++ b/SpirVTasks/spirv.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/addons/Directory.Build.props b/addons/Directory.Build.props new file mode 100644 index 0000000..0e3fb55 --- /dev/null +++ b/addons/Directory.Build.props @@ -0,0 +1,45 @@ + + + $(MSBuildThisFileDirectory)../ + true + + net471;netstandard2.0 + + https://github.com/jpbruyere/vke.net + MIT + Jean-Philippe Bruyère + + $(SolutionDir)build\$(Configuration)\ + true + false + + 7.2 + + $(MSBuildThisFileDirectory)common\shaders + + + + + TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;_WITH_SHADOWS;WITH_VKVG + + + NETSTANDARD;NETSTANDARD2_0;WITH_SHADOWS;_WITH_VKVG + + + + + + + + + + + + + + + + + + + diff --git a/addons/DistanceFieldFont/BMChar.cs b/addons/DistanceFieldFont/BMChar.cs new file mode 100644 index 0000000..0667fc3 --- /dev/null +++ b/addons/DistanceFieldFont/BMChar.cs @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// 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; + /// + /// The left position of the character image in the texture. + /// + public uint x; + public uint y; + /// + /// The width of the character image in the texture. + /// + public uint width; + /// + /// The height of the character image in the texture. + /// + public uint height; + /// + /// How much the current position should be offset when copying the image from the texture to the screen. + /// + public int xoffset; + /// + /// How much the current position should be offset when copying the image from the texture to the screen. + /// + public int yoffset; + /// + /// How much the current position should be advanced after drawing the character. + /// + public int xadvance; + /// + /// The texture page where the character image is found. + /// + public uint page; + /// + /// The texture channel where the character image is found (1 = blue, 2 = green, 4 = red, 8 = alpha, 15 = all channels). + /// + public Channel channel; + } +} diff --git a/addons/DistanceFieldFont/BMFont.cs b/addons/DistanceFieldFont/BMFont.cs new file mode 100644 index 0000000..e277517 --- /dev/null +++ b/addons/DistanceFieldFont/BMFont.cs @@ -0,0 +1,207 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// 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 { + /// + /// BMF font. (http://www.angelcode.com/products/bmfont/doc/file_format.html) + /// + public class BMFont { + /// + /// distance in pixels between each line of text. + /// + public readonly ushort lineHeight; + /// + /// The number of pixels from the absolute top of the line to the base of the characters. + /// + public readonly ushort @base; + public readonly ushort width, height; + + public readonly Dictionary CharMap = new Dictionary (); + public readonly Dictionary PageImagePathes = new Dictionary (); + + readonly string fntFilePath; + + public BMFont (string path) { + fntFilePath = path; + object thisFont = this; + + + using (BMFontStreamReader bmf = new BMFontStreamReader (path)) { + while (!bmf.EndOfStream) { + + string w = bmf.ReadWord (); + if (string.IsNullOrEmpty (w)) + continue; + + if (string.Equals (w, "info", StringComparison.OrdinalIgnoreCase) || + string.Equals (w, "common", StringComparison.OrdinalIgnoreCase)) + bmf.ReadDatas (ref thisFont); + else if (string.Equals (w, "page", StringComparison.OrdinalIgnoreCase)) { + int p = bmf.ReadPageDatas (out string pageImg); + PageImagePathes.Add (p, pageImg); + } else if (string.Equals (w, "chars", StringComparison.OrdinalIgnoreCase)) + bmf.ReadLine ();//skip char count + else if (string.Equals (w, "char", StringComparison.OrdinalIgnoreCase)) { + BMChar c = bmf.NextBMChar; + CharMap.Add ((int)c.id, c); + } else if (string.Equals (w, "kernings", StringComparison.OrdinalIgnoreCase)) + bmf.ReadLine ();//skip + else if (string.Equals (w, "kerning", StringComparison.OrdinalIgnoreCase)) + bmf.ReadLine ();//skip + } + } + } + + public Image GetPageTexture (int page, Queue staggingQ, CommandPool cmdPool, + VkMemoryPropertyFlags imgProp = VkMemoryPropertyFlags.DeviceLocal, bool genMipMaps = true, VkImageTiling tiling = VkImageTiling.Optimal) { + + string path = Path.Combine (Path.GetDirectoryName (fntFilePath), PageImagePathes[page]); + + if (path.EndsWith ("ktx", StringComparison.OrdinalIgnoreCase)) + return KTX.KTX.Load (staggingQ, cmdPool, path, + VkImageUsageFlags.Sampled, imgProp, genMipMaps, tiling); + return Image.Load (staggingQ.Dev, staggingQ, cmdPool, path, VkFormat.R8g8b8a8Unorm, imgProp, tiling, genMipMaps); + } + + } + + + + internal class BMFontStreamReader : StreamReader { + public BMFontStreamReader (string bmPath) : base (bmPath) { } + + public char PeekedChar => (char)Peek (); + public char ReadedChar => (char)Read (); + public bool EndOfLine => PeekedChar == '\n'; + + public string ReadWord () { + string word = ""; + SkipWhiteSpacesAndLineBreak (); + while (!EndOfStream) { + if (!Char.IsLetterOrDigit (PeekedChar)) + break; + word += ReadedChar; + } + return word; + } + public void SkipWhiteSpaces () { + while (!EndOfStream) { + if (!char.IsWhiteSpace (PeekedChar) || PeekedChar == '\n') + break; + Read (); + } + } + public void SkipWhiteSpacesAndLineBreak () { + while (!EndOfStream) { + if (!char.IsWhiteSpace (PeekedChar)) + break; + Read (); + } + } + + public string NextValue { + get { + SkipWhiteSpaces (); + if (ReadedChar != '=') + throw new Exception ("expecting '='"); + SkipWhiteSpaces (); + if (PeekedChar == '"') { + Read (); + return ReadUntil ('"'); + } + return ReadUntilSpace (); + } + } + public string ReadUntil (char limit) { + string word = ""; + while (!EndOfStream) { + char c = ReadedChar; + if (c == limit) + break; + word += c; + } + return word; + } + public string ReadUntilSpace () { + string word = ""; + while (!EndOfStream) { + if (char.IsWhiteSpace (PeekedChar)) + break; + word += ReadedChar; + } + return word; + } + + public void ReadField (ref object obj, string fieldName) { + FieldInfo fi = obj.GetType ().GetField (fieldName); + if (fi == null) { + System.Diagnostics.Debug.WriteLine ($"BMFont property not handled: {fieldName}"); + string tmp = NextValue; + return; + } + if (fi.FieldType == typeof (ushort)) { + fi.SetValue (obj, ushort.Parse (NextValue)); + return; + } + if (fi.FieldType == typeof (short)) { + fi.SetValue (obj, short.Parse (NextValue)); + return; + } + if (fi.FieldType == typeof (int)) { + fi.SetValue (obj, int.Parse (NextValue)); + return; + } + if (fi.FieldType == typeof (uint)) { + fi.SetValue (obj, uint.Parse (NextValue)); + return; + } + if (fi.FieldType == typeof (string)) { + fi.SetValue (obj, NextValue); + return; + } + } + public void ReadDatas (ref object obj) { + while (!EndOfLine && !EndOfStream) { + string field = ReadWord (); + ReadField (ref obj, field); + } + + } + public int ReadPageDatas (out string pageImgPath) { + int idx = 0; + pageImgPath = ""; + + while (!EndOfLine && !EndOfStream) { + string field = ReadWord (); + if (string.Equals (field, "id", StringComparison.OrdinalIgnoreCase)) + idx = int.Parse (NextValue); + else if (string.Equals (field, "file", StringComparison.OrdinalIgnoreCase)) + pageImgPath = NextValue; + else + Console.WriteLine ($"BMFont page property not handled: {field} = {NextValue}"); + } + return idx; + } + public BMChar NextBMChar { + get { + object c = default (BMChar); + + while (!EndOfLine && !EndOfStream) { + string field = ReadWord (); + ReadField (ref c, field); + SkipWhiteSpaces ();//trailing spaces + } + + return (BMChar)c; + } + + } + } + +} diff --git a/addons/DistanceFieldFont/DistanceFieldFont.csproj b/addons/DistanceFieldFont/DistanceFieldFont.csproj new file mode 100644 index 0000000..a131f27 --- /dev/null +++ b/addons/DistanceFieldFont/DistanceFieldFont.csproj @@ -0,0 +1,20 @@ + + + + + CVKL.DistanceFieldFont + CVKL.DistanceFieldFont + 0.1.0 + CVKL signed distance field font addons, BMFont file format + C# vulkan CVKL gltf + $(AssemblyVersion)-beta + True + False + https://github.com/jpbruyere/vk.net/blob/master/README.md + MIT + + false + true + + + diff --git a/addons/VkvgPipeline/VkvgPipeline.cs b/addons/VkvgPipeline/VkvgPipeline.cs new file mode 100644 index 0000000..1d4ce02 --- /dev/null +++ b/addons/VkvgPipeline/VkvgPipeline.cs @@ -0,0 +1,135 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Numerics; +using CVKL; +using VK; + +namespace VkvgPipeline { + + public class VkvgPipeline : GraphicPipeline { + vkvg.Device vkvgDev; + vkvg.Surface vkvgSurf; + Image uiImage; + + public vkvg.Context CreateContext () => new vkvg.Context (vkvgSurf); + + public VkvgPipeline (Instance instance, Device dev, Queue queue, GraphicPipeline pipeline, PipelineCache pipelineCache = null) + : base (pipeline.RenderPass, pipelineCache, "vkvg pipeline") { + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, this.RenderPass.Samples, false); + cfg.RenderPass = RenderPass; + cfg.Layout = pipeline.Layout; + cfg.AddShader (VkShaderStageFlags.Vertex, "#vke.FullScreenQuad.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "#vke.simpletexture.frag.spv"); + cfg.multisampleState.rasterizationSamples = Samples; + + cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true); + + layout = cfg.Layout; + + init (cfg); + + vkvgDev = new vkvg.Device (instance.Handle, dev.phy.Handle, dev.VkDev.Handle, queue.qFamIndex, + vkvg.SampleCount.Sample_4, queue.index); + } + + void initUISurface (int width, int height) { + uiImage?.Dispose (); + vkvgSurf?.Dispose (); + vkvgSurf = new vkvg.Surface (vkvgDev, width, height); + uiImage = new Image (Dev, new VkImage ((ulong)vkvgSurf.VkImage.ToInt64 ()), VkFormat.B8g8r8a8Unorm, + VkImageUsageFlags.ColorAttachment, (uint)vkvgSurf.Width, (uint)vkvgSurf.Height); + uiImage.CreateView (VkImageViewType.ImageView2D, VkImageAspectFlags.Color); + uiImage.CreateSampler (VkFilter.Nearest, VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder); + uiImage.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + } + + + public void Resize (int width, int height, DescriptorSetWrites dsUpdate) { + initUISurface (width, height); + dsUpdate.Write (Dev, uiImage.Descriptor); + } + public void RecordDraw (CommandBuffer cmd) { + Bind (cmd); + + uiImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ColorAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader); + + cmd.Draw (3, 1, 0, 0); + + uiImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.ColorAttachmentOptimal, + VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.BottomOfPipe); + } + public void DrawResources (vkvg.Context ctx, int width, int height) { + ResourceManager rm = Dev.resourceManager; + + int margin = 5, memPoolHeight = 15; + int drawingWidth = width - 4 * margin; + int drawingHeight = (memPoolHeight + margin) * (rm.memoryPools.Length) + margin; + int y = height - drawingHeight - margin; + int x = 2 * margin; + ctx.LineWidth = 1; + ctx.SetSource (0.1, 0.1, 0.1, 0.8); + ctx.Rectangle (0.5 + margin, 0.5 + y, width - 2 * margin, drawingHeight); + ctx.FillPreserve (); + ctx.Flush (); + ctx.SetSource (0.8, 0.8, 0.8); + ctx.Stroke (); + + foreach (MemoryPool mp in rm.memoryPools) { + float byteWidth = (float)drawingWidth / mp.Size; + + y += margin; + ctx.Rectangle (x, y, drawingWidth, memPoolHeight); + ctx.SetSource (0.3, 0.3, 0.3, 0.4); + ctx.Fill (); + + if (mp.Last == null) + return; + + Resource r = mp.Last; + do { + float w = Math.Max (1f, byteWidth * r.AllocatedDeviceMemorySize); + + Vector3 c = new Vector3 (0); + Image img = r as Image; + if (img != null) { + c.Z = 1f; + if (img.CreateInfo.usage.HasFlag (VkImageUsageFlags.InputAttachment)) + c.Y += 0.3f; + if (img.CreateInfo.usage.HasFlag (VkImageUsageFlags.ColorAttachment)) + c.Y += 0.1f; + if (img.CreateInfo.usage.HasFlag (VkImageUsageFlags.Sampled)) + c.X += 0.3f; + } else { + CVKL.Buffer buff = r as CVKL.Buffer; + c.X = 1f; + if (buff.Infos.usage.HasFlag (VkBufferUsageFlags.IndexBuffer)) + c.Y += 0.2f; + if (buff.Infos.usage.HasFlag (VkBufferUsageFlags.VertexBuffer)) + c.Y += 0.4f; + if (buff.Infos.usage.HasFlag (VkBufferUsageFlags.UniformBuffer)) + c.Z += 0.3f; + } + ctx.SetSource (c.X, c.Y, c.Z, 0.5); + ctx.Rectangle (0.5f + x + byteWidth * r.poolOffset, 0.5f + y, w, memPoolHeight); + ctx.FillPreserve (); + ctx.SetSource (0.01f, 0.01f, 0.01f); + ctx.Stroke (); + r = r.next; + } while (r != mp.Last && r != null); + y += memPoolHeight; + } + } + protected override void Dispose (bool disposing) { + uiImage?.Dispose (); + vkvgSurf?.Dispose (); + vkvgDev.Dispose (); + + base.Dispose (disposing); + } + } + +} diff --git a/addons/VkvgPipeline/VkvgPipeline.csproj b/addons/VkvgPipeline/VkvgPipeline.csproj new file mode 100644 index 0000000..3d07598 --- /dev/null +++ b/addons/VkvgPipeline/VkvgPipeline.csproj @@ -0,0 +1,23 @@ + + + + + CVKL.vkvgPipeline + CVKL.vkvgPipeline + 0.1.0 + CVKL vkvg addons, vectorial drawing with cairo like api. + C# vulkan CVKL gltf + $(AssemblyVersion)-beta + True + False + https://github.com/jpbruyere/vk.net/blob/master/README.md + MIT + + false + true + + + + + + diff --git a/addons/gltfLoader/PbrModel.cs b/addons/gltfLoader/PbrModel.cs new file mode 100644 index 0000000..396c42f --- /dev/null +++ b/addons/gltfLoader/PbrModel.cs @@ -0,0 +1,124 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// 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 (ctx); + } + + protected void loadSolids (glTFLoader ctx) { + ulong vertexCount, indexCount; + + ctx.GetVertexCount (out vertexCount, out indexCount, out IndexBufferType); + + ulong vertSize = vertexCount * (ulong)Marshal.SizeOf (); + 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 (ctx.LoadMeshes (IndexBufferType, vbo, 0, ibo, 0)); + Scenes = new List (ctx.LoadScenes (out defaultSceneIndex)); + } + + /// bind vertex and index buffers + 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()); + cmd.DrawIndexed(p.indexCount, instances[i].count, p.indexBase, p.vertexBase, firstInstance); + } + firstInstance += instances[i].count; + } + } + + #region IDisposable Support + protected bool isDisposed; + + protected virtual void Dispose (bool disposing) { + if (!isDisposed) { + if (disposing) { + ibo?.Dispose (); + vbo?.Dispose (); + materialUBO?.Dispose (); + descriptorPool?.Dispose (); + } else + Debug.WriteLine ("model was not disposed"); + isDisposed = true; + } + } + public void Dispose () { + Dispose (true); + } + #endregion + } +} diff --git a/addons/gltfLoader/PbrModelSeparateTextures.cs b/addons/gltfLoader/PbrModelSeparateTextures.cs new file mode 100644 index 0000000..c1a6ee9 --- /dev/null +++ b/addons/gltfLoader/PbrModelSeparateTextures.cs @@ -0,0 +1,162 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// 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 { + /// + /// Indexed pbr model whith one descriptorSet per material with separate textures attachments + /// + public class PbrModelSeparatedTextures : PbrModel { + /// + /// Pbr data structure suitable for push constant or ubo, containing + /// availablility of attached textures and the coef of pbr inputs + /// + 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; + /// + /// one descriptor per material containing textures + /// + 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 (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 ()); + if (descriptorSets[p.material] != null) + cmd.BindDescriptorSet (pipelineLayout, descriptorSets[p.material], 2); + } + cmd.DrawIndexed (p.indexCount, 1, p.indexBase, p.vertexBase, 0); + } + } + if (node.Children == null) + return; + foreach (Node child in node.Children) + RenderNode (cmd, pipelineLayout, child, localMat, shadowPass); + } + + protected override void Dispose (bool disposing) { + if (!isDisposed) { + if (disposing) { + foreach (Image txt in textures) + txt.Dispose (); + } else + Debug.WriteLine ("model was not disposed"); + } + base.Dispose (disposing); + } + } +} diff --git a/addons/gltfLoader/PbrModelTexArray.cs b/addons/gltfLoader/PbrModelTexArray.cs new file mode 100644 index 0000000..5943774 --- /dev/null +++ b/addons/gltfLoader/PbrModelTexArray.cs @@ -0,0 +1,139 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// 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 { + /// + /// Indexed pbr model whith one descriptorSet per material with separate textures attachments + /// + 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 (); + } + }; + + /// + /// Material structure for ubo containing texture indices in tex array + /// + 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 (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 (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 ()); + cmd.DrawIndexed (p.indexCount, 1, p.indexBase, p.vertexBase, 0); + } + } + if (node.Children == null) + return; + foreach (Node child in node.Children) + RenderNode (cmd, pipelineLayout, child, localMat, shadowPass); + } + + protected override void Dispose (bool disposing) { + if (!isDisposed) { + if (disposing) { + texArray?.Dispose (); + } else + Debug.WriteLine ("model was not disposed"); + } + base.Dispose (disposing); + } + } +} diff --git a/addons/gltfLoader/glTFLoader.cs b/addons/gltfLoader/glTFLoader.cs new file mode 100644 index 0000000..06e8ad6 --- /dev/null +++ b/addons/gltfLoader/glTFLoader.cs @@ -0,0 +1,635 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// 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; + + /// + /// Loading context with I as the vertex index type (uint16,uint32) + /// + public class glTFLoader : IDisposable { + /// + /// Material class used by the gltfLoader to fetch values. + /// + 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 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 (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 (); + 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 (); + + 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 scenes = new List (); + 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 () + }; + + 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 (); + 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]; + } + + ///// + ///// build texture array + ///// + ///// The images. + ///// Uniformized Texture size for all images + 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 (); + } + /// + /// Load model images as separate texture in a c# array + /// + /// The images. + public Image[] LoadImages () { + if (gltf.Images == null) + return new Image[] {}; + + List textures = new List (); + + 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 materials = new List (); + + foreach (GL.Material mat in gltf.Materials) { + Debug.WriteLine ("loading material: " + mat.Name); + Material pbr = new Material (); + pbr.Name = mat.Name; + + pbr.alphaCutoff = mat.AlphaCutoff; + pbr.alphaMode = (AlphaMode)mat.AlphaMode; + + FromFloatArray (ref pbr.emissiveFactor, mat.EmissiveFactor); + + if (mat.EmissiveTexture != null) { + pbr.emissiveTexture = mat.EmissiveTexture.Index; + if (mat.EmissiveTexture.TexCoord == 1) + pbr.availableAttachments1 |= AttachmentType.Emissive; + else + pbr.availableAttachments |= AttachmentType.Emissive; + } + if (mat.NormalTexture != null) { + pbr.normalTexture = mat.NormalTexture.Index; + if (mat.NormalTexture.TexCoord == 1) + pbr.availableAttachments1 |= AttachmentType.Normal; + else + pbr.availableAttachments |= AttachmentType.Normal; + } + if (mat.OcclusionTexture != null) { + pbr.occlusionTexture = mat.OcclusionTexture.Index; + if (mat.OcclusionTexture.TexCoord == 1) + pbr.availableAttachments1 |= AttachmentType.AmbientOcclusion; + else + pbr.availableAttachments |= AttachmentType.AmbientOcclusion; + } + + if (mat.PbrMetallicRoughness != null) { + if (mat.PbrMetallicRoughness.BaseColorTexture != null) { + pbr.baseColorTexture = mat.PbrMetallicRoughness.BaseColorTexture.Index; + if (mat.PbrMetallicRoughness.BaseColorTexture.TexCoord == 1) + pbr.availableAttachments1 |= AttachmentType.Color; + else + pbr.availableAttachments |= AttachmentType.Color; + } + + FromFloatArray (ref pbr.baseColorFactor, mat.PbrMetallicRoughness.BaseColorFactor); + + if (mat.PbrMetallicRoughness.MetallicRoughnessTexture != null) { + pbr.metallicRoughnessTexture = mat.PbrMetallicRoughness.MetallicRoughnessTexture.Index; + if (mat.PbrMetallicRoughness.MetallicRoughnessTexture.TexCoord == 1) + pbr.availableAttachments1 |= AttachmentType.PhysicalProps; + else + pbr.availableAttachments |= AttachmentType.PhysicalProps; + } + pbr.metallicFactor = mat.PbrMetallicRoughness.MetallicFactor; + pbr.roughnessFactor = mat.PbrMetallicRoughness.RoughnessFactor; + + pbr.workflow = Material.Workflow.PhysicalyBaseRendering; + } + materials.Add (pbr); + } + return materials.ToArray (); + } + + + #region IDisposable Support + private bool isDisposed = false; // Pour détecter les appels redondants + + protected virtual void Dispose (bool disposing) { + if (!isDisposed) { + if (disposing) { + // TODO: supprimer l'état managé (objets managés). + } + + for (int i = 0; i < gltf.Buffers.Length; i++) { + if (bufferHandles[i].IsAllocated) + bufferHandles[i].Free (); + } + + isDisposed = true; + } + } + + ~glTFLoader () { + Dispose (false); + } + public void Dispose () { + Dispose (true); + GC.SuppressFinalize (this); + } + #endregion + } +} \ No newline at end of file diff --git a/addons/gltfLoader/gltfLoader.csproj b/addons/gltfLoader/gltfLoader.csproj new file mode 100644 index 0000000..a439773 --- /dev/null +++ b/addons/gltfLoader/gltfLoader.csproj @@ -0,0 +1,21 @@ + + + + CVKL.gltfLoader + CVKL.gltfLoader + 0.1.6 + CVKL gltf addons + C# vulkan CVKL gltf + $(AssemblyVersion)-beta + True + False + https://github.com/jpbruyere/vk.net/blob/master/README.md + MIT + + false + true + + + + + diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..d39df8f --- /dev/null +++ b/clean.sh @@ -0,0 +1,2 @@ +#!/bin/bash +rm -fr build && find . -iname bin -o -iname obj | xargs rm -rf diff --git a/netfx.props b/netfx.props new file mode 100644 index 0000000..25fdbe0 --- /dev/null +++ b/netfx.props @@ -0,0 +1,27 @@ + + + + + true + + + /Library/Frameworks/Mono.framework/Versions/Current/lib/mono + /usr/lib/mono + /usr/local/lib/mono + + + $(BaseFrameworkPathOverrideForMono)/4.5-api + $(BaseFrameworkPathOverrideForMono)/4.5.1-api + $(BaseFrameworkPathOverrideForMono)/4.5.2-api + $(BaseFrameworkPathOverrideForMono)/4.6-api + $(BaseFrameworkPathOverrideForMono)/4.6.1-api + $(BaseFrameworkPathOverrideForMono)/4.6.2-api + $(BaseFrameworkPathOverrideForMono)/4.7-api + $(BaseFrameworkPathOverrideForMono)/4.7.1-api + true + + + $(FrameworkPathOverride)/Facades;$(AssemblySearchPaths) + + + diff --git a/samples/Directory.Build.props b/samples/Directory.Build.props new file mode 100644 index 0000000..738ae0b --- /dev/null +++ b/samples/Directory.Build.props @@ -0,0 +1,44 @@ + + + $(MSBuildThisFileDirectory)../ + true + + net471;netstandard2.0 + + https://github.com/jpbruyere/vke.net + MIT + Jean-Philippe Bruyère + + $(SolutionDir)build\$(Configuration)\ + Exe + true + false + + $(MSBuildThisFileDirectory)common\shaders + + + + + TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;_WITH_SHADOWS;WITH_VKVG + + + NETSTANDARD;NETSTANDARD2_0;WITH_SHADOWS;_WITH_VKVG + + + + + + + + + + + + + + + + + + + diff --git a/samples/DistanceFieldFontTest/DistanceFieldFontTest.csproj b/samples/DistanceFieldFontTest/DistanceFieldFontTest.csproj new file mode 100644 index 0000000..c603527 --- /dev/null +++ b/samples/DistanceFieldFontTest/DistanceFieldFontTest.csproj @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/samples/DistanceFieldFontTest/Program.cs b/samples/DistanceFieldFontTest/Program.cs new file mode 100644 index 0000000..1eea932 --- /dev/null +++ b/samples/DistanceFieldFontTest/Program.cs @@ -0,0 +1,300 @@ +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; + +using VK; +using CVKL; +using CVKL.DistanceFieldFont; + +namespace DistanceFieldFontTest { + + + + class Program : VkWindow { + static void Main (string[] args) { +#if DEBUG + Instance.VALIDATION = true; + Instance.DEBUG_UTILS = true; + Instance.RENDER_DOC_CAPTURE = false; +#endif + using (Program vke = new Program ()) { + vke.Run (); + } + } + + float rotSpeed = 0.01f, zoomSpeed = 0.01f; + float rotX, rotY, rotZ = 0f, zoom = 1f; + + struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + } + + Matrices matrices; + + HostBuffer uboMats; + GPUBuffer vbo; + GPUBuffer 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 (dev, VkBufferUsageFlags.VertexBuffer, 1024); + ibo = new GPUBuffer (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 () * 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 staggingVbo, out HostBuffer 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 svbo, out HostBuffer sibo) { + List vertices = new List (); + List indices = new List (); + 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 (dev, VkBufferUsageFlags.TransferSrc, vx); + sibo = new HostBuffer (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 ()); + + updateViewRequested = false; + } + + protected override void onMouseMove (double xPos, double yPos) { + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton[0]) { + rotY -= rotSpeed * (float)diffX; + rotX += rotSpeed * (float)diffY; + } else if (MouseButton[1]) { + zoom += zoomSpeed * (float)diffY; + } + + updateViewRequested = true; + } + + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + outlineColor.W -= 0.01f; + else + outlineColor.W += 0.01f; + rebuildBuffers = true; + break; + default: + base.onKeyDown (key, scanCode, modifiers); + break; + } + } + protected override void OnResize () { + + updateViewRequested = true; + + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height, + (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + } : new Image[] { + null, + swapChain.images[i] + }); + } + + buildCommandBuffers (); + } + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + pipeline.Dispose (); + dsLayout.Dispose (); + for (int i = 0; i < swapChain.ImageCount; i++) + frameBuffers[i].Dispose (); + descriptorPool.Dispose (); + fontTexture?.Dispose (); + vbo.Dispose (); + ibo.Dispose (); + uboMats.Dispose (); + } + } + base.Dispose (disposing); + } + } +} diff --git a/samples/DistanceFieldFontTest/shaders/main.frag b/samples/DistanceFieldFontTest/shaders/main.frag new file mode 100644 index 0000000..fa1f6c6 --- /dev/null +++ b/samples/DistanceFieldFontTest/shaders/main.frag @@ -0,0 +1,32 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 1) uniform sampler2D samplerColor; + +layout (push_constant) uniform PushConsts { + vec4 color; + vec4 outlineColor; +}; + +layout (location = 0) in vec2 inUV; +layout (location = 0) out vec4 outFragColor; + +void main() +{ + float distance = texture(samplerColor, inUV).a; + float smoothWidth = fwidth(distance); + float alpha = smoothstep(0.5 - smoothWidth, 0.5 + smoothWidth, distance); + vec3 rgb = color.rgb * vec3(alpha); + + if (outlineColor.a > 0.0) + { + float w = 1.0 - outlineColor.a; + alpha = smoothstep(w - smoothWidth, w + smoothWidth, distance); + rgb += mix(vec3(alpha), outlineColor.rgb, alpha); + } + + outFragColor = vec4(rgb, alpha * color.a); + +} \ No newline at end of file diff --git a/samples/DistanceFieldFontTest/shaders/main.vert b/samples/DistanceFieldFontTest/shaders/main.vert new file mode 100644 index 0000000..3b395bf --- /dev/null +++ b/samples/DistanceFieldFontTest/shaders/main.vert @@ -0,0 +1,28 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inColor; + +layout (binding = 0) uniform UBO +{ + mat4 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} ubo; + +layout (location = 0) out vec3 outColor; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + + +void main() +{ + outColor = inColor; + gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0); +} diff --git a/samples/Model/Model.csproj b/samples/Model/Model.csproj new file mode 100644 index 0000000..d6da2bb --- /dev/null +++ b/samples/Model/Model.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/Model/data/DamagedHelmet.bin b/samples/Model/data/DamagedHelmet.bin new file mode 100644 index 0000000..662eacc Binary files /dev/null and b/samples/Model/data/DamagedHelmet.bin differ diff --git a/samples/Model/data/DamagedHelmet.gltf b/samples/Model/data/DamagedHelmet.gltf new file mode 100644 index 0000000..d685d77 --- /dev/null +++ b/samples/Model/data/DamagedHelmet.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:efe99dfac198094a30c71dc02a4d3421f0eef6bf335aeb695daa4d62134cd93f +size 4537 diff --git a/samples/Model/main.cs b/samples/Model/main.cs new file mode 100644 index 0000000..6a98629 --- /dev/null +++ b/samples/Model/main.cs @@ -0,0 +1,257 @@ +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.InteropServices; +using CVKL; +using CVKL.glTF; +using VK; + +namespace ModelSample { + class Program : VkWindow { + static void Main (string[] args) { +#if DEBUG + Instance.VALIDATION = true; + Instance.DEBUG_UTILS = true; + Instance.RENDER_DOC_CAPTURE = false; +#endif + using (Program vke = new Program ()) { + vke.Run (); + } + } + + struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + //public Vector4 lightPos; + } + + public struct PushConstants { + public Matrix4x4 matrix; + } + + Matrices matrices; + + HostBuffer uboMats; + + DescriptorPool descriptorPool; + DescriptorSet dsMatrices, dsTextures; + DescriptorSetLayout descLayoutMatrix; + DescriptorSetLayout descLayoutTextures; + + Framebuffer[] frameBuffers; + + GraphicPipeline pipeline; + + VkSampleCountFlags NUM_SAMPLES = VkSampleCountFlags.SampleCount1; + + float rotSpeed = 0.01f, zoomSpeed = 0.01f; + float rotX, rotY, rotZ = 0f, zoom = 2f; + + SimpleModel helmet; + CVKL.DebugUtils.Messenger dbgmsg; + + Program () : base () { + + + descriptorPool = new DescriptorPool (dev, 2, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer), + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 3)); + + descLayoutMatrix = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex, VkDescriptorType.UniformBuffer)); + + descLayoutTextures = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + VkPushConstantRange pushConstantRange = new VkPushConstantRange { + stageFlags = VkShaderStageFlags.Vertex, + size = (uint)Marshal.SizeOf(), + 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 (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 ()); + 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 ()); + + 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 (ctx); + textures = ctx.LoadImages (); + } + } + } + + public void DrawAll (CommandBuffer cmd, PipelineLayout pipelineLayout) { + //helmet.Meshes + cmd.BindVertexBuffer (vbo); + cmd.BindIndexBuffer (ibo, IndexBufferType); + foreach (Mesh m in Meshes) { + foreach (var p in m.Primitives) { + cmd.DrawIndexed (p.indexCount,1,p.indexBase,p.vertexBase); + } + } + + //foreach (Scene sc in Scenes) { + // foreach (Node node in sc.Root.Children) + // RenderNode (cmd, pipelineLayout, node, sc.Root.localMatrix, shadowPass); + //} + } + + public override void RenderNode (CommandBuffer cmd, PipelineLayout pipelineLayout, Node node, Matrix4x4 currentTransform, bool shadowPass = false) { + throw new System.NotImplementedException (); + } + } + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + helmet.vbo.Dispose (); + helmet.ibo.Dispose (); + foreach (var t in helmet.textures) + t.Dispose (); + + pipeline.Dispose (); + descLayoutMatrix.Dispose (); + descLayoutTextures.Dispose (); + for (int i = 0; i < swapChain.ImageCount; i++) + frameBuffers[i]?.Dispose (); + descriptorPool.Dispose (); + uboMats.Dispose (); + dbgmsg?.Dispose (); + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/Model/shaders/model.frag b/samples/Model/shaders/model.frag new file mode 100644 index 0000000..7083a06 --- /dev/null +++ b/samples/Model/shaders/model.frag @@ -0,0 +1,84 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (set = 1, binding = 0) uniform sampler2D samplerColor; +layout (set = 1, binding = 1) uniform sampler2D samplerNormal; +layout (set = 1, binding = 2) uniform sampler2D samplerOcclusion; + +layout (location = 0) in vec2 inUV; +layout (location = 1) in vec3 inN; +layout (location = 2) in vec3 inV;//ViewDir +layout (location = 3) in vec3 inL; + +layout (location = 0) out vec4 outFragColor; + + + +// http://www.thetenthplanet.de/archives/1180 +mat3 cotangent_frame(vec3 N, vec3 p, vec2 uv) +{ + // get edge vectors of the pixel triangle + vec3 dp1 = dFdx( p ); + vec3 dp2 = dFdy( p ); + vec2 duv1 = dFdx( uv ); + vec2 duv2 = dFdy( uv ); + + // solve the linear system + vec3 dp2perp = cross( dp2, N ); + vec3 dp1perp = cross( N, dp1 ); + vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; + vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; + + // construct a scale-invariant frame + float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) ); + return mat3( T * invmax, B * invmax, N ); +} + +vec3 perturb_normal( vec3 N, vec3 V, vec2 texcoord ) +{ + // assume N, the interpolated vertex normal and + // V, the view vector (vertex to eye) + vec3 map = texture(samplerNormal, texcoord).xyz; + map = map * 255./127. - 128./127.; + mat3 TBN = cotangent_frame(N, -V, texcoord); + return normalize(TBN * map); +} + + +void main() +{ + vec4 color = texture(samplerColor, inUV); + + vec3 N = normalize(inN); + vec3 L = normalize(inL); + vec3 V = normalize(inV); + vec3 R = reflect(-L, N); + vec3 diffuse = max(dot(N, L), 0.0) * vec3(0.9); + vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.75); + outFragColor = vec4(diffuse * color.rgb + specular, 1.0); +} +/*void main() +{ + vec4 diff = texture(samplerColor, inUV); + + vec3 n = normalize(inN); + vec3 l = normalize(-light); + vec3 pn = perturb_normal(n, inV, inUV); + + float lambert = max(0.0, dot(pn, l)); + + //diff.rgb *= lambert * texture(samplerOcclusion, inUV).rgb; + vec3 spec = vec3(0); + vec3 amb = vec3(0.1); + if (lambert >= 0.0) { + vec3 rd = reflect(l, pn); + float s = dot(rd, normalize(inV)); + spec = vec3(0.9,0.9,0.9)* //light.specular.xyz * material.specular.xyz * + pow(max(0.0, s), 10.5); //0.5 = mat shininess + } + //outFragColor = vec4(1.0); // + //outFragColor = vec4(diff.rgb , 1.0); // + outFragColor = vec4(diff.rgb + amb + spec , diff.a); // +}*/ \ No newline at end of file diff --git a/samples/Model/shaders/model.vert b/samples/Model/shaders/model.vert new file mode 100644 index 0000000..c4dbfb8 --- /dev/null +++ b/samples/Model/shaders/model.vert @@ -0,0 +1,50 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV; + +layout (binding = 0) uniform UBO +{ + mat4 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} ubo; + +layout (location = 0) out vec2 outUV; +layout (location = 1) out vec3 outN; +layout (location = 2) out vec3 outV;//ViewDir +layout (location = 3) out vec3 outL; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(push_constant) uniform PushConsts { + mat4 model; +} pc; + +vec3 light = vec3(2.0,2.0,-2.0); + +void main() +{ + outUV = inUV; + + mat4 mod = ubo.modelMatrix;// * pc.model; + vec4 pos = mod * vec4(inPos.xyz, 1.0); + vec3 lPos = mat3(mod) * light; + + //outN = normalize(transpose(inverse(mat3(mod))) * inNormal); + outN = mat3(mod)* inNormal; + + //mat4 viewInv = inverse(ubo.viewMatrix); + + outV = -pos.xyz;//normalize(vec3(viewInv * vec4(0.0, 0.0, 0.0, 1.0) - pos)); + outL = lPos - pos.xyz; + + gl_Position = ubo.projectionMatrix * ubo.viewMatrix * pos; +} diff --git a/samples/Textured/Textured.csproj b/samples/Textured/Textured.csproj new file mode 100644 index 0000000..1785b4a --- /dev/null +++ b/samples/Textured/Textured.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/samples/Textured/main.cs b/samples/Textured/main.cs new file mode 100644 index 0000000..6b64eed --- /dev/null +++ b/samples/Textured/main.cs @@ -0,0 +1,266 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using CVKL; +using VK; + +namespace Textured { + class Program : VkWindow { + static void Main (string[] args) { +#if DEBUG + Instance.VALIDATION = true; + Instance.DEBUG_UTILS = true; + Instance.RENDER_DOC_CAPTURE = true; +#endif + + foreach (string s in System.Reflection.Assembly.GetEntryAssembly ().GetManifestResourceNames ()) + Console.WriteLine (s); + + using (Program vke = new Program ()) { + vke.Run (); + } + } + protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (available_features, ref features); + features.textureCompressionBC = available_features.textureCompressionBC; + features.textureCompressionASTC_LDR = available_features.textureCompressionASTC_LDR; + } + + float rotSpeed = 0.01f, zoomSpeed = 0.01f; + float rotX, rotY, rotZ = 0f, zoom = 1f; + + struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + } + + Matrices matrices; + + HostBuffer uboMats; + GPUBuffer vbo; + GPUBuffer 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 (presentQueue, cmdPool, VkBufferUsageFlags.VertexBuffer, vertices); + ibo = new GPUBuffer (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 ()); + } + + public override void UpdateView () { + if (nextTexture != null) { + updateTextureSet (); + buildCommandBuffers (); + }else + updateMatrices (); + + updateViewRequested = false; + } + + protected override void onMouseMove (double xPos, double yPos) { + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton[0]) { + rotY -= rotSpeed * (float)diffX; + rotX += rotSpeed * (float)diffY; + } else if (MouseButton[1]) { + zoom += zoomSpeed * (float)diffY; + } + + updateViewRequested = true; + } + + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.Space: + currentImgIndex++; + if (currentImgIndex == imgPathes.Length) + currentImgIndex = 0; + loadTexture (imgPathes[currentImgIndex]); + break; + default: + base.onKeyDown (key, scanCode, modifiers); + break; + } + } + + protected override void OnResize () { + + updateMatrices (); + + if (frameBuffers!=null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height, + (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + null + } : new Image[] { + null, + null, + swapChain.images[i] + }); + } + + buildCommandBuffers (); + } + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + pipeline.Dispose (); + dsLayout.Dispose (); + for (int i = 0; i < swapChain.ImageCount; i++) + frameBuffers[i].Dispose (); + descriptorPool.Dispose (); + texture?.Dispose (); + nextTexture?.Dispose (); + vbo.Dispose (); + ibo.Dispose (); + uboMats.Dispose (); + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/Textured/shaders/main.frag b/samples/Textured/shaders/main.frag new file mode 100644 index 0000000..73dfd79 --- /dev/null +++ b/samples/Textured/shaders/main.frag @@ -0,0 +1,15 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 1) uniform sampler2D samplerColor; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = texture(samplerColor, inUV); +} \ No newline at end of file diff --git a/samples/Textured/shaders/main.vert b/samples/Textured/shaders/main.vert new file mode 100644 index 0000000..3b395bf --- /dev/null +++ b/samples/Textured/shaders/main.vert @@ -0,0 +1,28 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inColor; + +layout (binding = 0) uniform UBO +{ + mat4 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} ubo; + +layout (location = 0) out vec3 outColor; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + + +void main() +{ + outColor = inColor; + gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0); +} diff --git a/samples/TexturedCube/TexturedCube.csproj b/samples/TexturedCube/TexturedCube.csproj new file mode 100644 index 0000000..8235ef1 --- /dev/null +++ b/samples/TexturedCube/TexturedCube.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/samples/TexturedCube/main.cs b/samples/TexturedCube/main.cs new file mode 100644 index 0000000..21aef33 --- /dev/null +++ b/samples/TexturedCube/main.cs @@ -0,0 +1,330 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// 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 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 (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 ()); + } + + protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) { + base.configureEnabledFeatures (available_features, ref enabled_features); + enabled_features.textureCompressionBC = available_features.textureCompressionBC; + } + + public override void UpdateView () { + if (nextTexture != null) { + dev.WaitIdle (); + updateTextureSet (); + buildCommandBuffers (); + }else + updateMatrices (); + + updateViewRequested = false; + dev.WaitIdle (); + } + + protected override void onMouseMove (double xPos, double yPos) { + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton[0]) { + rotY -= rotSpeed * (float)diffX; + rotX += rotSpeed * (float)diffY; + } else if (MouseButton[1]) { + zoom += zoomSpeed * (float)diffY; + } + + updateViewRequested = true; + } + + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.Space: + currentImgIndex++; + if (currentImgIndex == imgPathes.Length) + currentImgIndex = 0; + loadTexture (imgPathes[currentImgIndex]); + break; + default: + base.onKeyDown (key, scanCode, modifiers); + break; + } + } + + protected override void OnResize () { + dev.WaitIdle (); + +#if WITH_VKVG + vkvgPipeline.Resize ((int)swapChain.Width, (int)swapChain.Height, new DescriptorSetWrites (dsVkvg, dsLayout.Bindings[1])); +#endif + + updateMatrices (); + + if (frameBuffers!=null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height, + (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + null + } : new Image[] { + null, + null, + swapChain.images[i] + }); + } + + buildCommandBuffers (); + + dev.WaitIdle (); + } + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + pipeline.Dispose (); + for (int i = 0; i < swapChain.ImageCount; i++) + frameBuffers[i].Dispose (); + descriptorPool.Dispose (); + texture.Dispose (); + uboMats.Dispose (); + vbo.Dispose (); + +#if WITH_VKVG + vkvgPipeline.Dispose (); +#endif + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/TexturedCube/shaders/FullScreenQuad.vert b/samples/TexturedCube/shaders/FullScreenQuad.vert new file mode 100644 index 0000000..826720b --- /dev/null +++ b/samples/TexturedCube/shaders/FullScreenQuad.vert @@ -0,0 +1,10 @@ +#version 450 + +layout (location = 0) out vec2 outUV; + +void main() +{ + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); + //gl_Position = vec4(outUV -1.0f, 0.0f, 1.0f); +} diff --git a/samples/TexturedCube/shaders/main.frag b/samples/TexturedCube/shaders/main.frag new file mode 100644 index 0000000..73dfd79 --- /dev/null +++ b/samples/TexturedCube/shaders/main.frag @@ -0,0 +1,15 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 1) uniform sampler2D samplerColor; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = texture(samplerColor, inUV); +} \ No newline at end of file diff --git a/samples/TexturedCube/shaders/main.vert b/samples/TexturedCube/shaders/main.vert new file mode 100644 index 0000000..3b395bf --- /dev/null +++ b/samples/TexturedCube/shaders/main.vert @@ -0,0 +1,28 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inColor; + +layout (binding = 0) uniform UBO +{ + mat4 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} ubo; + +layout (location = 0) out vec3 outColor; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + + +void main() +{ + outColor = inColor; + gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0); +} diff --git a/samples/TexturedCube/shaders/simpletexture.frag b/samples/TexturedCube/shaders/simpletexture.frag new file mode 100644 index 0000000..cee63cb --- /dev/null +++ b/samples/TexturedCube/shaders/simpletexture.frag @@ -0,0 +1,15 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (set = 0, binding = 1) uniform sampler2D samplerColor; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = texture(samplerColor, inUV); +} diff --git a/samples/TexturedCube/shaders/skybox.frag b/samples/TexturedCube/shaders/skybox.frag new file mode 100644 index 0000000..bdd272d --- /dev/null +++ b/samples/TexturedCube/shaders/skybox.frag @@ -0,0 +1,15 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (binding = 1) uniform samplerCube skybox; + +layout (location = 0) in vec3 TexCoords; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = textureLod(skybox, TexCoords,0); +} diff --git a/samples/TexturedCube/shaders/skybox.vert b/samples/TexturedCube/shaders/skybox.vert new file mode 100644 index 0000000..2d6a6b6 --- /dev/null +++ b/samples/TexturedCube/shaders/skybox.vert @@ -0,0 +1,26 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec2 inUV; + +layout (location = 0) out vec3 outUVW; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 view; +} ubo; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + outUVW = inPos; + gl_Position = ubo.projection * ubo.view * vec4(inPos, 1.0); +} diff --git a/samples/Triangle/Triangle.csproj b/samples/Triangle/Triangle.csproj new file mode 100644 index 0000000..dbb4bc8 --- /dev/null +++ b/samples/Triangle/Triangle.csproj @@ -0,0 +1,6 @@ + + + + false + + diff --git a/samples/Triangle/main.cs b/samples/Triangle/main.cs new file mode 100644 index 0000000..75c20ed --- /dev/null +++ b/samples/Triangle/main.cs @@ -0,0 +1,184 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// 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 (dev, VkBufferUsageFlags.VertexBuffer, vertices); + ibo = new HostBuffer (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 (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 ()); + updateViewRequested = false; + } + + protected override void onMouseMove (double xPos, double yPos) { + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton[0]) { + rotY -= rotSpeed * (float)diffX; + rotX += rotSpeed * (float)diffY; + } else if (MouseButton[1]) { + zoom += zoomSpeed * (float)diffY; + } + updateViewRequested = true; + } + void buildCommandBuffers() { + cmdPool.Reset (VkCommandPoolResetFlags.ReleaseResources); + cmds = cmdPool.AllocateCommandBuffer (swapChain.ImageCount); + + for (int i = 0; i < swapChain.ImageCount; ++i) { + Framebuffer fb = frameBuffers[i]; + cmds[i].Start (); + + pipeline.RenderPass.Begin (cmds[i], fb); + + cmds[i].SetViewport (swapChain.Width, swapChain.Height); + cmds[i].SetScissor (swapChain.Width, swapChain.Height); + + cmds[i].BindDescriptorSet (pipeline.Layout, descriptorSet); + + cmds[i].BindPipeline (pipeline); + + cmds[i].BindVertexBuffer (vbo); + cmds[i].BindIndexBuffer (ibo, VkIndexType.Uint16); + cmds[i].DrawIndexed ((uint)indices.Length); + + pipeline.RenderPass.End (cmds[i]); + + cmds[i].End (); + } + } + + protected override void OnResize () { + dev.WaitIdle (); + + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height, + (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + null + } : new Image[] { + null, + null, + swapChain.images[i] + }); + + buildCommandBuffers (); + + dev.WaitIdle (); + } + + protected override void Dispose (bool disposing) { + dev.WaitIdle (); + if (disposing) { + if (!isDisposed) { + pipeline.Dispose (); + for (int i = 0; i < swapChain.ImageCount; i++) + frameBuffers[i].Dispose (); + descriptorPool.Dispose (); + vbo.Dispose (); + ibo.Dispose (); + uboMats.Dispose (); + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/Triangle/shaders/main.frag b/samples/Triangle/shaders/main.frag new file mode 100644 index 0000000..5ff09f5 --- /dev/null +++ b/samples/Triangle/shaders/main.frag @@ -0,0 +1,9 @@ +#include + +layout (location = 0) in vec3 inColor; +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = vec4(inColor, 1.0); +} \ No newline at end of file diff --git a/samples/Triangle/shaders/main.vert b/samples/Triangle/shaders/main.vert new file mode 100644 index 0000000..542d015 --- /dev/null +++ b/samples/Triangle/shaders/main.vert @@ -0,0 +1,30 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inColor; + +layout (binding = 0) uniform UBO +{ + mat4 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} ubo; + +layout (location = 0) out vec3 outColor; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + + +void main() +{ + outColor = inColor; + gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0); + //gl_Position.y = -gl_Position.y; + //gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0; +} diff --git a/samples/common/CrowWin.cs b/samples/common/CrowWin.cs new file mode 100644 index 0000000..4eb8730 --- /dev/null +++ b/samples/common/CrowWin.cs @@ -0,0 +1,254 @@ +using System; +using Glfw; +using VK; +using System.Threading; + +namespace Crow { + public class CrowWin : CVKL.VkWindow, IValueChange { + #region IValueChange implementation + public event EventHandler 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 (); + } + } + + /// + /// rebuild command buffers if needed + /// + public override void Update () { + if (rebuildBuffers) { + buildCommandBuffers (); + rebuildBuffers = false; + } + } + + protected override void OnResize () { + dev.WaitIdle (); + + crow.ProcessResize (new Rectangle (0, 0, (int)swapChain.Width, (int)swapChain.Height)); + + initUISurface (); + + CVKL.DescriptorSetWrites uboUpdate = new CVKL.DescriptorSetWrites (dsCrow, descLayout); + uboUpdate.Write (dev, uiImage.Descriptor); + + if (uiFrameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + uiFrameBuffers[i]?.Dispose (); + + uiFrameBuffers = new CVKL.Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + uiFrameBuffers[i] = new CVKL.Framebuffer (uiPipeline.RenderPass, swapChain.Width, swapChain.Height, + (uiPipeline.Samples == VkSampleCountFlags.SampleCount1) ? new CVKL.Image[] { + swapChain.images[i], + } : new CVKL.Image[] { + null, + swapChain.images[i] + }); + uiFrameBuffers[i].SetName ("ui FB " + i); + } + + buildCommandBuffers (); + dev.WaitIdle (); + } + + #region Mouse and keyboard + protected override void onScroll (double xOffset, double yOffset) { + if (KeyModifiers.HasFlag (Modifier.Shift)) + crow.ProcessMouseWheelChanged ((float)xOffset); + else + crow.ProcessMouseWheelChanged ((float)yOffset); + } + protected override void onMouseMove (double xPos, double yPos) { + if (crow.ProcessMouseMove ((int)xPos, (int)yPos)) + return; + base.onMouseMove (xPos, yPos); + } + protected override void onMouseButtonDown (Glfw.MouseButton button) { + if (crow.ProcessMouseButtonDown ((MouseButton)button)) + return; + base.onMouseButtonDown (button); + } + protected override void onMouseButtonUp (Glfw.MouseButton button) { + if (crow.ProcessMouseButtonUp ((MouseButton)button)) + return; + base.onMouseButtonUp (button); + } + protected override void onKeyDown (Glfw.Key key, int scanCode, Modifier modifiers) { + if (crow.ProcessKeyDown ((Key)key)) + return; + base.onKeyDown (key, scanCode, modifiers); + } + protected override void onKeyUp (Glfw.Key key, int scanCode, Modifier modifiers) { + if (crow.ProcessKeyUp ((Key)key)) + return; + } + protected override void onChar (CodePoint cp) { + if (crow.ProcessKeyPress (cp.ToChar ())) + return; + } + #endregion + + #region dispose + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + isRunning = false; + + for (int i = 0; i < swapChain.ImageCount; ++i) + uiFrameBuffers[i]?.Dispose (); + + uiPipeline.Dispose (); + descLayout.Dispose (); + descriptorPool.Dispose (); + + uiImage?.Dispose (); + while (crow != null) + Thread.Sleep (1); + } + } + + base.Dispose (disposing); + } + #endregion + } +} diff --git a/samples/common/shaders/preamble.inc b/samples/common/shaders/preamble.inc new file mode 100644 index 0000000..da434f8 --- /dev/null +++ b/samples/common/shaders/preamble.inc @@ -0,0 +1,4 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable diff --git a/samples/compute/compute.csproj b/samples/compute/compute.csproj new file mode 100644 index 0000000..e1734ff --- /dev/null +++ b/samples/compute/compute.csproj @@ -0,0 +1,10 @@ + + + + false + + + + + + \ No newline at end of file diff --git a/samples/compute/delaunay.cs b/samples/compute/delaunay.cs new file mode 100644 index 0000000..a01cd85 --- /dev/null +++ b/samples/compute/delaunay.cs @@ -0,0 +1,329 @@ +using System; +using Glfw; +using VK; +using CVKL; + +namespace delaunay { + class Program : VkWindow { + static void Main (string[] args) { +#if DEBUG + Instance.VALIDATION = true; + Instance.DEBUG_UTILS = true; + Instance.RENDER_DOC_CAPTURE = false; +#endif + using (Program vke = new Program ()) { + vke.Run (); + } + } + + Framebuffer[] frameBuffers; + GraphicPipeline grPipeline; + + Image imgResult; + + Queue computeQ, transferQ; + + GPUBuffer inBuff, outBuff; + HostBuffer 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 (dev, VkBufferUsageFlags.TransferSrc, datas); + stagingDataBuff.Map (); + + inBuff = new GPUBuffer (dev, VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferSrc | VkBufferUsageFlags.TransferDst, (int)data_size); + outBuff = new GPUBuffer (dev, VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferSrc, (int)data_size); + + dsPool = new DescriptorPool (dev, 3, + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler), + new VkDescriptorPoolSize (VkDescriptorType.StorageBuffer, 4)); + dslImage = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + dslCompute = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer) + ); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1); + + cfg.Layout = new PipelineLayout (dev, dslImage); + cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), VkSampleCountFlags.SampleCount1); + cfg.RenderPass.ClearValues[0] = new VkClearValue { color = new VkClearColorValue (0.0f, 0.1f, 0.0f) }; + + cfg.ResetShadersAndVerticesInfos (); + cfg.AddShader (VkShaderStageFlags.Vertex, "#compute.FullScreenQuad.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "#compute.simpletexture.frag.spv"); + + cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true); + + grPipeline = new GraphicPipeline (cfg); + + plCompute = new ComputePipeline ( + new PipelineLayout (dev, new VkPushConstantRange (VkShaderStageFlags.Compute, 2 * sizeof (int)), dslCompute), + "#compute.computeTest.comp.spv"); + plNormalize = new ComputePipeline ( + plCompute.Layout, + "#compute.normalize.comp.spv"); + + dsImage = dsPool.Allocate (dslImage); + dsetPing = dsPool.Allocate (dslCompute); + dsetPong = dsPool.Allocate (dslCompute); + + DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dsetPing, dslCompute); + dsUpdate.Write (dev, inBuff.Descriptor, outBuff.Descriptor); + dsUpdate.Write (dev, dsetPong, outBuff.Descriptor, inBuff.Descriptor); + dsUpdate = new DescriptorSetWrites (dsImage, dslImage); + dsUpdate.Write (dev, imgResult.Descriptor); + + UpdateFrequency = 5; + } + + protected override void createQueues () { + computeQ = new Queue (dev, VkQueueFlags.Compute); + transferQ = new Queue (dev, VkQueueFlags.Transfer); + + base.createQueues (); + } + + protected override void OnResize () { + + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (grPipeline.RenderPass, swapChain.Width, swapChain.Height, + (grPipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + null + } : new Image[] { + null, + null, + swapChain.images[i] + }); + + cmds[i] = cmdPool.AllocateCommandBuffer (); + cmds[i].Start (); + + imgResult.SetLayout (cmds[i], VkImageAspectFlags.Color, + VkImageLayout.Undefined, VkImageLayout.ShaderReadOnlyOptimal, + VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.FragmentShader); + + grPipeline.RenderPass.Begin (cmds[i], frameBuffers[i]); + + int xPad = (int)swapChain.Width / 2 - (int)imgDim * (int)zoom / 2; + int yPad = (int)swapChain.Height / 2- (int)imgDim * (int)zoom / 2; + + cmds[i].SetViewport (imgDim * zoom, imgDim * zoom, xPad, yPad); + cmds[i].SetScissor (imgDim * zoom, imgDim * zoom, Math.Max (0, xPad), Math.Max (0, yPad)); + + cmds[i].BindDescriptorSet (grPipeline.Layout, dsImage); + cmds[i].BindPipeline (grPipeline); + cmds[i].Draw (3, 1, 0, 0); + + grPipeline.RenderPass.End (cmds[i]); + + cmds[i].End (); + } + } + bool pong; + + public override void Update () { + initGpuBuffers (); + + using (CommandPool cmdPoolCompute = new CommandPool (dev, computeQ.qFamIndex)) { + + CommandBuffer cmd = cmdPoolCompute.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); + + pong = false; + uint stepSize = imgDim / 2; + + plCompute.Bind (cmd); + cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, imgDim, sizeof(int)); + + int pass = 0; + while (stepSize > 0 && pass < invocationCount) { + cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, stepSize); + + if (pong) + plCompute.BindDescriptorSet (cmd, dsetPong); + else + plCompute.BindDescriptorSet (cmd, dsetPing); + + cmd.Dispatch (imgDim, imgDim); + + VkMemoryBarrier memBar = VkMemoryBarrier.New (); + memBar.srcAccessMask = VkAccessFlags.ShaderWrite; + memBar.dstAccessMask = VkAccessFlags.ShaderRead; + Vk.vkCmdPipelineBarrier (cmd.Handle, VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.ComputeShader, VkDependencyFlags.ByRegion, + 1, ref memBar, 0, IntPtr.Zero, 0, IntPtr.Zero); + + pong = !pong; + stepSize /= 2; + pass++; + } + + plNormalize.Bind (cmd); + if (pong) + plNormalize.BindDescriptorSet (cmd, dsetPong); + else + plNormalize.BindDescriptorSet (cmd, dsetPing); + cmd.Dispatch (imgDim, imgDim); + pong = !pong; + + cmd.End (); + + computeQ.Submit (cmd); + computeQ.WaitIdle (); + } + + printResults (); + } + + protected override void onMouseButtonDown (MouseButton button) { + int xPad = (int)swapChain.Width / 2 - (int)imgDim * (int)zoom / 2; + int yPad = (int)swapChain.Height / 2 - (int)imgDim * (int)zoom / 2; + + int localX = (int)((lastMouseX - xPad) / zoom); + int localY = (int)((lastMouseY - yPad) / zoom); + + if (localX < 0 || localY < 0 || localX >= imgDim || localY >= imgDim) + base.onMouseButtonDown (button); + else { + addSeed ((uint)localX, (uint)localY); + stagingDataBuff.Update (datas); + } + } + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.Delete: + datas = new float[data_size]; + stagingDataBuff.Update (datas); + seedCount = 0; + break; + case Key.KeypadAdd: + invocationCount++; + break; + case Key.KeypadSubtract: + if (invocationCount>0) + invocationCount--; + break; + default: + base.onKeyDown (key, scanCode, modifiers); + break; + } + Console.WriteLine ($"break after {invocationCount} step"); + } + + void printResults () { + dev.WaitIdle (); + using (CommandPool cmdPoolTransfer = new CommandPool (dev, transferQ.qFamIndex)) { + + CommandBuffer cmd = cmdPoolTransfer.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); + + imgResult.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.TransferDstOptimal, + VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.Transfer); + + if (pong) + outBuff.CopyTo (cmd, imgResult, VkImageLayout.ShaderReadOnlyOptimal); + else + inBuff.CopyTo (cmd, imgResult, VkImageLayout.ShaderReadOnlyOptimal); + + cmd.End (); + + transferQ.Submit (cmd); + transferQ.WaitIdle (); + } + } + + void initGpuBuffers () { + using (CommandPool staggingCmdPool = new CommandPool (dev, transferQ.qFamIndex)) { + CommandBuffer cmd = staggingCmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); + + stagingDataBuff.CopyTo (cmd, inBuff); + + transferQ.EndSubmitAndWait (cmd); + } + } + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + grPipeline.Dispose (); + plCompute.Dispose (); + plNormalize.Dispose (); + + dslCompute.Dispose (); + dslImage.Dispose (); + + dsPool.Dispose (); + + inBuff.Dispose (); + outBuff.Dispose (); + stagingDataBuff.Dispose (); + + imgResult.Dispose (); + + dbgReport?.Dispose (); + } + } + + base.Dispose (disposing); + } + + + } +} diff --git a/samples/compute/main.cs b/samples/compute/main.cs new file mode 100644 index 0000000..02f7069 --- /dev/null +++ b/samples/compute/main.cs @@ -0,0 +1,133 @@ +using System; +using System.Runtime.InteropServices; +using CVKL; +using VK; +using System.Linq; + +namespace SimpleCompute { + class Program : IDisposable { + VkPhysicalDeviceFeatures enabledFeatures = default (VkPhysicalDeviceFeatures); + string[] enabledExtensions = { Ext.D.VK_KHR_swapchain }; + + Instance instance; + PhysicalDevice phy; + Device dev; + Queue computeQ; + + HostBuffer inBuff, outBuff; + DescriptorPool dsPool; + DescriptorSetLayout dsLayout; + DescriptorSet dset; + + ComputePipeline plCompute; + + DebugReport dbgReport; + + const uint data_size = 256; + int[] datas; + + public Program () { + instance = new Instance (); + +#if DEBUG + dbgReport = new DebugReport (instance, + VkDebugReportFlagsEXT.ErrorEXT + | VkDebugReportFlagsEXT.DebugEXT + | VkDebugReportFlagsEXT.WarningEXT + | VkDebugReportFlagsEXT.PerformanceWarningEXT + + ); +#endif + + phy = instance.GetAvailablePhysicalDevice ().FirstOrDefault (); + dev = new Device (phy); + computeQ = new Queue (dev, VkQueueFlags.Compute); + dev.Activate (enabledFeatures, enabledExtensions); + + datas = new int[data_size]; + Random rnd = new Random (); + for (uint i = 0; i < data_size; i++) { + datas[i] = rnd.Next (); + } + + inBuff = new HostBuffer (dev, VkBufferUsageFlags.StorageBuffer, datas); + outBuff = new HostBuffer (dev, VkBufferUsageFlags.StorageBuffer, data_size); + + dsPool = new DescriptorPool (dev, 1, new VkDescriptorPoolSize (VkDescriptorType.StorageBuffer, 2)); + dsLayout = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer) + ); + + plCompute = new ComputePipeline (new PipelineLayout (dev, dsLayout), "shaders/compute.comp.spv" ); + + dset = dsPool.Allocate (dsLayout); + DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dset, dsLayout); + dsUpdate.Write (dev, inBuff.Descriptor, outBuff.Descriptor); + } + + + + public void Run () { + using (CommandPool cmdPool = new CommandPool (dev, computeQ.qFamIndex)) { + + CommandBuffer cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); + + plCompute.Bind (cmd); + plCompute.BindDescriptorSet (cmd, dset); + + cmd.Dispatch (data_size * sizeof (int)); + + cmd.End (); + + computeQ.Submit (cmd); + computeQ.WaitIdle (); + } + + printResults (); + } + + void printResults () { + int[] results = new int[data_size]; + + outBuff.Map (); + Marshal.Copy (outBuff.MappedData, results, 0, results.Length); + + Console.ForegroundColor = ConsoleColor.DarkBlue; + Console.Write ("IN :"); + for (int i = 0; i < data_size; i++) { + Console.Write ($" {datas[i]} "); + } + Console.WriteLine (); + Console.Write ("OUT:"); + for (int i = 0; i < data_size; i++) { + Console.Write ($" {results[i]} "); + } + Console.WriteLine (); + outBuff.Unmap (); + } + + public void Dispose () { + dev.WaitIdle (); + + plCompute.Dispose (); + dsLayout.Dispose (); + dsPool.Dispose (); + + inBuff.Dispose (); + outBuff.Dispose (); + + dev.Dispose (); + +#if DEBUG + dbgReport.Dispose (); +#endif + instance.Dispose (); + } + + static void Main (string[] args) { + using (Program vke = new Program ()) + vke.Run (); + } + } +} diff --git a/samples/compute/shaders/FullScreenQuad.vert b/samples/compute/shaders/FullScreenQuad.vert new file mode 100644 index 0000000..826720b --- /dev/null +++ b/samples/compute/shaders/FullScreenQuad.vert @@ -0,0 +1,10 @@ +#version 450 + +layout (location = 0) out vec2 outUV; + +void main() +{ + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); + //gl_Position = vec4(outUV -1.0f, 0.0f, 1.0f); +} diff --git a/samples/compute/shaders/compute.comp b/samples/compute/shaders/compute.comp new file mode 100644 index 0000000..10cfddd --- /dev/null +++ b/samples/compute/shaders/compute.comp @@ -0,0 +1,23 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) buffer buffIn { + int dataIn[]; +}; + +layout(binding = 1) buffer buffOut { + int dataOut[]; +}; + +//layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + uint i = gl_GlobalInvocationID.x; + int d = dataIn[i]; + + for (int j=0; j<8; j++) + d += 1; + dataOut[i] = d; +} diff --git a/samples/compute/shaders/computeTest.comp b/samples/compute/shaders/computeTest.comp new file mode 100644 index 0000000..f24bee3 --- /dev/null +++ b/samples/compute/shaders/computeTest.comp @@ -0,0 +1,80 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) buffer buffIn { + vec4 dataIn[]; +}; + +layout(binding = 1) buffer buffOut { + vec4 dataOut[]; +}; + +layout(set = 1, binding = 0) buffer VBO { + vec2 vertices[]; +}; + +layout(set = 1, binding = 1) buffer IBO { + uint indices[]; +}; + + +layout(push_constant) uniform PushConsts { + int iStepSize; + int imgDim; + int pointCount; +}; + +const ivec2 dirs[] = ivec2[8] ( + ivec2( 1, 0), + ivec2( 1, 1), + ivec2( 0, 1), + ivec2(-1, 1), + ivec2(-1, 0), + ivec2(-1,-1), + ivec2( 0,-1), + ivec2( 1,-1) +); + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + +vec4 getPixel (ivec2 uv) { + return dataIn[uv.y * imgDim + uv.x]; +} +void setPixel (ivec2 uv, vec4 pix) { + dataOut[uv.y * imgDim + uv.x] = pix; +} + +void main() +{ + ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); + vec4 d = getPixel (uv); + + ivec2 thisSeedPos; + if (d.r > 0.0) + thisSeedPos = ivec2(int(d.g), int(d.b)); + else//no seed in current pixel + thisSeedPos = ivec2(-1); + + for (int j=0; j<8; j++){ + ivec2 otherUV = uv + iStepSize * dirs[j]; + if (otherUV.x < 0 || otherUV.y < 0 || otherUV.x >= imgDim || otherUV.y >= imgDim) + continue; + + vec4 other = getPixel (otherUV); + + if (other.r > 0.0) {//seed in other + ivec2 otherSeedPos = ivec2(int(other.g), int(other.b)); + if (thisSeedPos.x < 0) {//replace current + d = other; + thisSeedPos = otherSeedPos; + }else if (distance (uv, thisSeedPos) > distance (uv, otherSeedPos)) { + d = other; + thisSeedPos = otherSeedPos; + } + } + } + + setPixel (uv, d); +} diff --git a/samples/compute/shaders/delaunay.comp b/samples/compute/shaders/delaunay.comp new file mode 100644 index 0000000..410dcee --- /dev/null +++ b/samples/compute/shaders/delaunay.comp @@ -0,0 +1,70 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) buffer buffIn { + vec4 dataIn[]; +}; + +layout(binding = 1) buffer buffOut { + vec4 dataOut[]; +}; + +layout(push_constant) uniform PushConsts { + int iStepSize; + int imgDim; +}; + +const ivec2 dirs[] = ivec2[8] ( + ivec2( 1, 0), + ivec2( 1, 1), + ivec2( 0, 1), + ivec2(-1, 1), + ivec2(-1, 0), + ivec2(-1,-1), + ivec2( 0,-1), + ivec2( 1,-1) +); + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + +vec4 getPixel (ivec2 uv) { + return dataIn[uv.y * imgDim + uv.x]; +} +void setPixel (ivec2 uv, vec4 pix) { + dataOut[uv.y * imgDim + uv.x] = pix; +} + +void main() +{ + ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); + vec4 d = getPixel (uv); + + ivec2 thisSeedPos; + if (d.r > 0.0) + thisSeedPos = ivec2(int(d.g), int(d.b)); + else//no seed in current pixel + thisSeedPos = ivec2(-1); + + for (int j=0; j<8; j++){ + ivec2 otherUV = uv + iStepSize * dirs[j]; + if (otherUV.x < 0 || otherUV.y < 0 || otherUV.x >= imgDim || otherUV.y >= imgDim) + continue; + + vec4 other = getPixel (otherUV); + + if (other.r > 0.0) {//seed in other + ivec2 otherSeedPos = ivec2(int(other.g), int(other.b)); + if (thisSeedPos.x < 0) {//replace current + d = other; + thisSeedPos = otherSeedPos; + }else if (distance (uv, thisSeedPos) > distance (uv, otherSeedPos)) { + d = other; + thisSeedPos = otherSeedPos; + } + } + } + + setPixel (uv, d); +} diff --git a/samples/compute/shaders/init.comp b/samples/compute/shaders/init.comp new file mode 100644 index 0000000..fc615ee --- /dev/null +++ b/samples/compute/shaders/init.comp @@ -0,0 +1,36 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) buffer buffIn { + vec4 dataIn[]; +}; + +layout(binding = 1) buffer buffOut { + vec4 dataOut[]; +}; + +layout(set = 1, binding = 0) buffer VBO { + vec2 vertices[]; +}; + +layout(set = 1, binding = 1) buffer IBO { + uint indices[]; +}; + +layout(push_constant) uniform PushConsts { + int iStepSize; + int imgDim; + int pointCount; +}; + + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + vec2 v = vertices[gl_GlobalInvocationID.x]; + ivec2 uv = ivec2(int(v.x), int(v.y)); + dataIn[uv.y * imgDim + uv.x] = vec4 (gl_GlobalInvocationID.x + 1, uv.x, uv.y, 1.0); +} + diff --git a/samples/compute/shaders/mandelbrot.comp b/samples/compute/shaders/mandelbrot.comp new file mode 100644 index 0000000..a03f4a0 --- /dev/null +++ b/samples/compute/shaders/mandelbrot.comp @@ -0,0 +1,56 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +#define WIDTH 3200 +#define HEIGHT 2400 +#define WORKGROUP_SIZE 32 +layout (local_size_x = WORKGROUP_SIZE, local_size_y = WORKGROUP_SIZE, local_size_z = 1 ) in; + +struct Pixel{ + vec4 value; +}; + +layout(std140, binding = 0) buffer buf +{ + Pixel imageData[]; +}; + +void main() { + + /* + In order to fit the work into workgroups, some unnecessary threads are launched. + We terminate those threads here. + */ + if(gl_GlobalInvocationID.x >= WIDTH || gl_GlobalInvocationID.y >= HEIGHT) + return; + + float x = float(gl_GlobalInvocationID.x) / float(WIDTH); + float y = float(gl_GlobalInvocationID.y) / float(HEIGHT); + + /* + What follows is code for rendering the mandelbrot set. + */ + vec2 uv = vec2(x,y); + float n = 0.0; + vec2 c = vec2(-.445, 0.0) + (uv - 0.5)*(2.0+ 1.7*0.2 ), + z = vec2(0.0); + const int M =128; + for (int i = 0; i 2) break; + n++; + } + + // we use a simple cosine palette to determine color: + // http://iquilezles.org/www/articles/palettes/palettes.htm + float t = float(n) / float(M); + vec3 d = vec3(0.3, 0.3 ,0.5); + vec3 e = vec3(-0.2, -0.3 ,-0.5); + vec3 f = vec3(2.1, 2.0, 3.0); + vec3 g = vec3(0.0, 0.1, 0.0); + vec4 color = vec4( d + e*cos( 6.28318*(f*t+g) ) ,1.0); + + // store the rendered mandelbrot set into a storage buffer: + imageData[WIDTH * gl_GlobalInvocationID.y + gl_GlobalInvocationID.x].value = color; +} \ No newline at end of file diff --git a/samples/compute/shaders/normalize.comp b/samples/compute/shaders/normalize.comp new file mode 100644 index 0000000..2bc6aa6 --- /dev/null +++ b/samples/compute/shaders/normalize.comp @@ -0,0 +1,73 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) buffer buffIn { + vec4 dataIn[]; +}; + +layout(binding = 1) buffer buffOut { + vec4 dataOut[]; +}; + +layout(set = 1, binding = 0) buffer VBO { + vec2 vertices[]; +}; + +layout(set = 1, binding = 1) buffer IBO { + uint indices[]; +}; + +layout(push_constant) uniform PushConsts { + int iStepSize; + int imgDim; + int pointCount; +}; + +layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +vec4 getPixel (ivec2 uv) { + return dataIn[uv.y * imgDim + uv.x]; +} +void setPixel (ivec2 uv, vec4 pix) { + dataOut[uv.y * imgDim + uv.x] = pix; +} + +const vec4[] colors = vec4[]( + vec4(1,0,0,1), + vec4(0,1,0,1), + vec4(0,0,1,1), + vec4(1,0,1,1), + vec4(0,1,1,1), + vec4(1,1,0,1), + vec4(0.5,0,0,1), + vec4(0,0.5,0,1), + vec4(0,0,0.5,1), + vec4(0.5,0,0.5,1), + vec4(0,0.5,0.5,1), + vec4(0.5,0.5,0,1), + vec4(0,0.5,0.5,1), + vec4(0.1,0.9,0.2,1), + vec4(0.3,0.7,0.4,1), + vec4(0.5,0.5,0.6,1), + vec4(0.7,0.3,0.8,1), + vec4(0.9,0.1,0.0,1) + ); + +void main() +{ + ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); + vec4 d = getPixel (uv); + + if (d.r > 0.0) { + if (int(d.g) == uv.x && int(d.b) == uv.y) + d.a = 1.0; + else + d.a = 0.4; + + d.rgb = colors [int(d.r)].rgb; + } + + setPixel (uv, d); +} + diff --git a/samples/compute/shaders/simpletexture.frag b/samples/compute/shaders/simpletexture.frag new file mode 100644 index 0000000..a081876 --- /dev/null +++ b/samples/compute/shaders/simpletexture.frag @@ -0,0 +1,15 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (set = 0, binding = 0) uniform sampler2D samplerColor; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = texture(samplerColor, inUV); +} diff --git a/samples/compute/shaders/test.comp b/samples/compute/shaders/test.comp new file mode 100644 index 0000000..8ab1ec7 --- /dev/null +++ b/samples/compute/shaders/test.comp @@ -0,0 +1,21 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable + +layout(binding = 0) buffer readonly buff { + vec4 data[]; +}; +layout(binding = 1) buffer writeonly buffOut { + vec4 dataOut[]; +}; +layout(push_constant) uniform PushConsts { + int iStepSize; + int imgDim; +}; + + +void main() +{ + ivec2 uv = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); + +} diff --git a/samples/compute/shaders/triangle2.frag b/samples/compute/shaders/triangle2.frag new file mode 100644 index 0000000..3f905f7 --- /dev/null +++ b/samples/compute/shaders/triangle2.frag @@ -0,0 +1,13 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +//layout (location = 0) in vec3 inColor; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = vec4(1.0, 1.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/samples/compute/shaders/triangle2.vert b/samples/compute/shaders/triangle2.vert new file mode 100644 index 0000000..2922694 --- /dev/null +++ b/samples/compute/shaders/triangle2.vert @@ -0,0 +1,24 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec2 inPos; + +layout(push_constant) uniform PushConsts { + int imgDim; + int xPad; + int yPad; + int zoom; +}; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + + +void main() +{ + gl_Position = vec4(inPos.xy * vec2(2) / vec2(imgDim) - vec2(1), 0.0, 1.0); +} diff --git a/samples/compute/test.cs b/samples/compute/test.cs new file mode 100644 index 0000000..80e66d4 --- /dev/null +++ b/samples/compute/test.cs @@ -0,0 +1,133 @@ +using System; +using System.Runtime.InteropServices; +using CVKL; +using VK; +using System.Linq; + +namespace test { + class Program : IDisposable { + VkPhysicalDeviceFeatures enabledFeatures = default (VkPhysicalDeviceFeatures); + string[] enabledExtensions = { Ext.D.VK_KHR_swapchain }; + + static void Main (string[] args) { + Instance.VALIDATION = true; + //Instance.DEBUG_UTILS = true; + + using (Program vke = new Program ()) + vke.Run (); + } + + Instance instance; + PhysicalDevice phy; + Device dev; + Queue computeQ; + + HostBuffer inBuff, outBuff; + DescriptorPool dsPool; + DescriptorSetLayout dsLayoutCompute; + DescriptorSet dsetPing, dsetPong; + + ComputePipeline plCompute; + + DebugReport dbgReport; + + const uint imgDim = 256; + + uint data_size => imgDim * imgDim * 4; + + float[] datas; + + public Program () { + instance = new Instance (); + +#if DEBUG + /*dbgReport = new DebugReport (instance, + VkDebugReportFlagsEXT.ErrorEXT + | VkDebugReportFlagsEXT.DebugEXT + | VkDebugReportFlagsEXT.WarningEXT + | VkDebugReportFlagsEXT.PerformanceWarningEXT + + );*/ +#endif + + phy = instance.GetAvailablePhysicalDevice ().FirstOrDefault (); + dev = new Device (phy); + computeQ = new Queue (dev, VkQueueFlags.Compute); + dev.Activate (enabledFeatures, enabledExtensions); + + datas = new float[data_size]; + Random rnd = new Random (); + for (uint i = 0; i < data_size; i++) { + datas[i] = (float)rnd.NextDouble (); + } + + inBuff = new HostBuffer (dev, VkBufferUsageFlags.StorageBuffer, datas); + outBuff = new HostBuffer (dev, VkBufferUsageFlags.StorageBuffer, data_size); + + dsPool = new DescriptorPool (dev, 2, new VkDescriptorPoolSize (VkDescriptorType.StorageBuffer, 4)); + dsLayoutCompute = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageBuffer) + ); + + plCompute = new ComputePipeline ( + new PipelineLayout (dev, new VkPushConstantRange (VkShaderStageFlags.Compute, sizeof (int)), dsLayoutCompute), + "#compute.computeTest.comp.spv" ); + + dsetPing = dsPool.Allocate (dsLayoutCompute); + dsetPong = dsPool.Allocate (dsLayoutCompute); + DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dsetPing, dsLayoutCompute); + dsUpdate.Write (dev, inBuff.Descriptor, outBuff.Descriptor); + + dsUpdate.Write (dev, dsetPong, outBuff.Descriptor, inBuff.Descriptor); + } + + + + public void Run () { + using (CommandPool cmdPool = new CommandPool (dev, computeQ.qFamIndex)) { + + CommandBuffer cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); + + bool pong = false; + + plCompute.Bind (cmd); + cmd.PushConstant (plCompute.Layout, VkShaderStageFlags.Compute, imgDim); + + for (int i = 0; i < 4; i++) { + if (pong) + plCompute.BindDescriptorSet (cmd, dsetPong); + else + plCompute.BindDescriptorSet (cmd, dsetPing); + + cmd.Dispatch (imgDim, imgDim); + } + + cmd.End (); + + computeQ.Submit (cmd); + computeQ.WaitIdle (); + } + + } + + + public void Dispose () { + dev.WaitIdle (); + + plCompute.Dispose (); + dsLayoutCompute.Dispose (); + dsPool.Dispose (); + + inBuff.Dispose (); + outBuff.Dispose (); + + dev.Dispose (); + +#if DEBUG + dbgReport?.Dispose (); +#endif + instance.Dispose (); + } + } +} diff --git a/samples/compute/test2.cs b/samples/compute/test2.cs new file mode 100644 index 0000000..689e7e8 --- /dev/null +++ b/samples/compute/test2.cs @@ -0,0 +1,389 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using VK; +using CVKL; + +namespace triangulation { + class Program : VkWindow { + static void Main (string[] args) { + using (Program vke = new Program ()) { + vke.Run (); + } + } + + Framebuffer[] frameBuffers; + GraphicPipeline grPipeline, trianglesPipeline; + + Image imgResult; + + Queue computeQ, transferQ; + + GPUBuffer inBuff, outBuff; + HostBuffer staggingVBO; + DescriptorPool dsPool; + DescriptorSetLayout dslCompute, dslImage, dslVAO; + DescriptorSet dsPing, dsPong, dsImage, dsVAO; + + ComputePipeline plCompute, plNormalize, plInit; + + DebugReport dbgReport; + + + GPUBuffer vbo; + GPUBuffer 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 ()); + + + } + 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 (dev, VkBufferUsageFlags.TransferSrc, MAX_VERTICES); + staggingVBO.Map (); + + vbo = new GPUBuffer (dev, VkBufferUsageFlags.VertexBuffer | VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferDst, MAX_VERTICES); + ibo = new GPUBuffer (dev, VkBufferUsageFlags.IndexBuffer | VkBufferUsageFlags.StorageBuffer, MAX_VERTICES * 3); + + inBuff = new GPUBuffer (dev, VkBufferUsageFlags.StorageBuffer | VkBufferUsageFlags.TransferSrc | VkBufferUsageFlags.TransferDst, (int)data_size); + outBuff = new GPUBuffer (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 (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()); + + transferQ.EndSubmitAndWait (cmd); + } + } + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + grPipeline.Dispose (); + trianglesPipeline.Dispose (); + + plInit.Dispose (); + plCompute.Dispose (); + plNormalize.Dispose (); + + dslCompute.Dispose (); + dslImage.Dispose (); + + dsPool.Dispose (); + + inBuff.Dispose (); + outBuff.Dispose (); + staggingVBO.Dispose (); + vbo.Dispose (); + ibo.Dispose (); + + imgResult.Dispose (); + +#if DEBUG + dbgReport.Dispose (); +#endif + } + } + + base.Dispose (disposing); + } + + + } +} diff --git a/samples/deferred/DebuDrawPipeline.cs b/samples/deferred/DebuDrawPipeline.cs new file mode 100644 index 0000000..fef2751 --- /dev/null +++ b/samples/deferred/DebuDrawPipeline.cs @@ -0,0 +1,78 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using VK; + +namespace CVKL { + public class DebugDrawPipeline : GraphicPipeline { + public HostBuffer Vertices; + uint vertexCount; + uint vboLength = 100 * 6 * sizeof (float); + + public DebugDrawPipeline (Device dev, DescriptorSetLayout dsLayout, VkFormat colorFormat, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1) : + base (new RenderPass (dev, colorFormat), "Debug draw pipeline") { + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.LineList, samples); + cfg.rasterizationState.lineWidth = 1.0f; + cfg.RenderPass = RenderPass; + cfg.Layout = new PipelineLayout(dev, dsLayout); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf () * 2) + ); + cfg.AddVertexBinding (0, 6 * sizeof(float)); + cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat); + + cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true); + + cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/debug.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/debug.frag.spv"); + + layout = cfg.Layout; + + init (cfg); + + Vertices = new HostBuffer (dev, VkBufferUsageFlags.VertexBuffer, vboLength); + Vertices.Map (); + } + + public void AddLine (Vector3 start, Vector3 end, float r, float g, float b) { + float[] data = { + start.X, start.Y, start.Z, + r, g, b, + end.X, end.Y, end.Z, + r, g, b + }; + Vertices.Update (data, 12 * sizeof (float), vertexCount * 6 * sizeof (float)); + vertexCount+=2; + } + + public void RecordDraw (CommandBuffer cmd, Framebuffer fb, Camera camera) { + RenderPass.Begin (cmd, fb); + const int ratio = 8; + cmd.SetViewport (fb.Width/ratio, fb.Height/ratio, (ratio-1) * (int)fb.Width / ratio, (ratio-1) * (int)fb.Height / ratio); + //cmd.SetViewport (200, 200,100,100,-10,10);//, 4 * (int)fb.Width / 5, 4 * (int)fb.Height / 5); + cmd.SetScissor (fb.Width / ratio, fb.Height / ratio, (ratio-1) * (int)fb.Width / ratio, (ratio-1) * (int)fb.Height / ratio); + //cmd.SetScissor (200, 200,100,100); + + Matrix4x4 ortho = Matrix4x4.CreateOrthographic (4, 4.0f / camera.AspectRatio,-1,1); + + cmd.PushConstant (layout, VkShaderStageFlags.Vertex, ortho); + + Bind (cmd); + + cmd.BindVertexBuffer (Vertices); + cmd.Draw (vertexCount); + RenderPass.End (cmd); + } + + protected override void Dispose (bool disposing) { + if (disposing) { + Vertices.Unmap (); + Vertices.Dispose (); + } + + base.Dispose (disposing); + } + } + +} diff --git a/samples/deferred/DeferredPbrRenderer.cs b/samples/deferred/DeferredPbrRenderer.cs new file mode 100644 index 0000000..3dc8520 --- /dev/null +++ b/samples/deferred/DeferredPbrRenderer.cs @@ -0,0 +1,528 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using CVKL; +using CVKL.glTF; +using VK; + +namespace deferred { + public class DeferredPbrRenderer : IDisposable { + Device dev; + Queue gQueue; + public static int MAX_MATERIAL_COUNT = 4; + public static VkSampleCountFlags NUM_SAMPLES = VkSampleCountFlags.SampleCount1; + public static VkFormat HDR_FORMAT = VkFormat.R16g16b16a16Sfloat; + public static VkFormat MRT_FORMAT = VkFormat.R32g32b32a32Sfloat; + public static bool TEXTURE_ARRAY; + + public enum DebugView { + none, + color, + normal, + pos, + occlusion, + emissive, + metallic, + roughness, + depth, + prefill, + irradiance, + shadowMap + } + public DebugView currentDebugView = DebugView.none; + public int lightNumDebug = 0; + public int debugMip = 0; + public int debugFace = 0; + + const float lightMoveSpeed = 0.1f; + public float exposure = 2.0f; + public float gamma = 1.2f; + + public struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 model; + public Matrix4x4 view; + public Vector4 camPos; + public float prefilteredCubeMipLevels; + public float scaleIBLAmbient; + } + public struct Light { + public Vector4 position; + public Vector4 color; + public Matrix4x4 mvp; + } + + public Matrices matrices = new Matrices { + scaleIBLAmbient = 0.5f, + }; + public Light[] lights = { + new Light { + position = new Vector4(2.5f,5.5f,2,0f), + color = new Vector4(1,0.8f,0.8f,1) + }, + /*new Light { + position = new Vector4(-2.5f,5.5f,2,0f), + color = new Vector4(0.8f,0.8f,1,1) + }*/ + }; + + Framebuffer frameBuffer; + public Image gbColorRough, gbEmitMetal, gbN_AO, gbPos, hdrImgResolved, hdrImgMS; + + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutMain, descLayoutTextures, descLayoutGBuff; + DescriptorSet dsMain, dsGBuff; + + public PipelineCache pipelineCache; + Pipeline gBuffPipeline, composePipeline, debugPipeline; + + public HostBuffer uboMatrices { get; private set; } + public HostBuffer 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 (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 ()), + 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 (); + + using (SpecializationInfo constants = new SpecializationInfo ( + new SpecializationConstant (0, nearPlane), + new SpecializationConstant (1, farPlane), + new SpecializationConstant (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 (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 ()); + } + + 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 ()); + } + + #endregion + + + void createGBuff () { + gbColorRough?.Dispose (); + gbEmitMetal?.Dispose (); + gbN_AO?.Dispose (); + gbPos?.Dispose (); + hdrImgResolved?.Dispose (); + hdrImgMS?.Dispose (); + + + hdrImgResolved = new Image (dev, HDR_FORMAT, VkImageUsageFlags.Sampled | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferSrc, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, VkSampleCountFlags.SampleCount1); + gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + gbN_AO = new Image (dev, MRT_FORMAT, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + gbPos = new Image (dev, MRT_FORMAT, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1) + hdrImgMS = new Image (dev, HDR_FORMAT, VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + + + gbColorRough.CreateView (); gbColorRough.CreateSampler (); + gbColorRough.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + gbEmitMetal.CreateView (); gbEmitMetal.CreateSampler (); + gbEmitMetal.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + gbN_AO.CreateView (); gbN_AO.CreateSampler (); + gbN_AO.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + gbPos.CreateView (); gbPos.CreateSampler (); + gbPos.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + hdrImgResolved.CreateView (); hdrImgResolved.CreateSampler (); + hdrImgResolved.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + + hdrImgMS?.CreateView (); + + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff); + uboUpdate.Write (dev, dsGBuff, gbColorRough.Descriptor, + gbEmitMetal.Descriptor, + gbN_AO.Descriptor, + gbPos.Descriptor); + + gbColorRough.SetName ("GBuffColorRough"); + gbEmitMetal.SetName ("GBuffEmitMetal"); + gbN_AO.SetName ("GBuffN"); + gbPos.SetName ("GBuffPos"); + hdrImgResolved.SetName ("HDRimg resolved"); + hdrImgMS?.SetName ("HDRimg resolved"); + } + + public void Resize (uint width, uint height) { + this.width = width; + this.height = height; + + frameBuffer?.Dispose (); + createGBuff (); + + frameBuffer = (NUM_SAMPLES == VkSampleCountFlags.SampleCount1) ? + new Framebuffer (renderPass, width, height, new Image[] { + hdrImgResolved, null, gbColorRough, gbEmitMetal, gbN_AO, gbPos}) : + new Framebuffer (renderPass, width, height, new Image[] { + hdrImgResolved, null, gbColorRough, gbEmitMetal, gbN_AO, gbPos, hdrImgMS}); + } + + public void Dispose () { + dev.WaitIdle (); + + frameBuffer?.Dispose (); + + gbColorRough.Dispose (); + gbEmitMetal.Dispose (); + gbN_AO.Dispose (); + gbPos.Dispose (); + hdrImgMS?.Dispose (); + hdrImgResolved.Dispose (); + + gBuffPipeline.Dispose (); + composePipeline.Dispose (); + //toneMappingPipeline.Dispose (); + debugPipeline?.Dispose (); + + descLayoutMain.Dispose (); + descLayoutTextures?.Dispose (); + descLayoutGBuff.Dispose (); + + uboMatrices.Dispose (); + uboLights.Dispose (); + model.Dispose (); + envCube.Dispose (); + +#if WITH_SHADOWS + shadowMapRenderer.Dispose (); +#endif + + descriptorPool.Dispose (); + + dev.DestroySemaphore (DrawComplete); + } + } +} diff --git a/samples/deferred/EnvironmentPipeline.cs b/samples/deferred/EnvironmentPipeline.cs new file mode 100644 index 0000000..4e97a1c --- /dev/null +++ b/samples/deferred/EnvironmentPipeline.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.InteropServices; +using VK; + +namespace CVKL { + public class EnvironmentCube : GraphicPipeline { + + GPUBuffer vboSkybox; + + public Image cubemap { get; private set; } + public Image lutBrdf { get; private set; } + public Image irradianceCube { get; private set; } + public Image prefilterCube { get; set; } + + public EnvironmentCube (string cubemapPath, DescriptorSet dsSkybox, PipelineLayout plLayout, Queue staggingQ, RenderPass renderPass, PipelineCache cache = null) + : base (renderPass, cache, "EnvCube pipeline") { + + using (CommandPool cmdPool = new CommandPool (staggingQ.Dev, staggingQ.index)) { + + vboSkybox = new GPUBuffer (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 () + 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 ()); + cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaTheta, (uint)Marshal.SizeOf () + 4); + } else { + cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, roughness, (uint)Marshal.SizeOf ()); + cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 64u, (uint)Marshal.SizeOf () + 4); + } + + cmd.BindDescriptorSet (pl.Layout, dset); + cmd.BindVertexBuffer (vboSkybox); + cmd.Draw (36); + + pl.RenderPass.End (cmd); + + imgFbOffscreen.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal); + + VkImageCopy region = new VkImageCopy (); + region.srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1); + region.dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1, (uint)m, (uint)f); + region.extent = new VkExtent3D { width = (uint)viewPortSize, height = (uint)viewPortSize, depth = 1 }; + + Vk.vkCmdCopyImage (cmd.Handle, + imgFbOffscreen.Handle, VkImageLayout.TransferSrcOptimal, + cmap.Handle, VkImageLayout.TransferDstOptimal, + 1, region.Pin ()); + region.Unpin (); + + imgFbOffscreen.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.TransferSrcOptimal, VkImageLayout.ColorAttachmentOptimal); + + } + } + + cmap.SetLayout (cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, subRes); + + cmd.End (); + + staggingQ.Submit (cmd); + staggingQ.WaitIdle (); + + cmd.Free (); + } + } + cmap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + + dsLayout.Dispose (); + imgFbOffscreen.Dispose (); + dsPool.Dispose (); + + return cmap; + } + + void generateCubemaps (Queue staggingQ, CommandPool cmdPool) { + irradianceCube = generateCubeMap (staggingQ, cmdPool, CBTarget.IRRADIANCE); + prefilterCube = generateCubeMap (staggingQ, cmdPool, CBTarget.PREFILTEREDENV); + } + + protected override void Dispose (bool disposing) { + vboSkybox.Dispose (); + cubemap.Dispose (); + lutBrdf.Dispose (); + irradianceCube.Dispose (); + prefilterCube.Dispose (); + + base.Dispose (disposing); + } + } + +} diff --git a/samples/deferred/deferred.csproj b/samples/deferred/deferred.csproj new file mode 100644 index 0000000..89a5f38 --- /dev/null +++ b/samples/deferred/deferred.csproj @@ -0,0 +1,43 @@ + + + + false + + + + + TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;MEMORY_POOLS;WITH_SHADOWS;_WITH_VKVG + + + TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;MEMORY_POOLS;WITH_SHADOWS;_WITH_VKVG + + + + + + + + + + + + deferred.%(Filename)%(Extension) + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/deferred/main-crow.cs b/samples/deferred/main-crow.cs new file mode 100644 index 0000000..fe0fc4c --- /dev/null +++ b/samples/deferred/main-crow.cs @@ -0,0 +1,404 @@ +using System; +using System.Numerics; +using Glfw; +using VK; +using CVKL; +using System.Collections.Generic; +using System.Linq; + +namespace deferred { + class Program : Crow.CrowWin { + static void Main (string[] args) { +#if DEBUG + Instance.VALIDATION = true; + Instance.DEBUG_UTILS = true; + Instance.RENDER_DOC_CAPTURE = false; +#endif + DeferredPbrRenderer.TEXTURE_ARRAY = true; + DeferredPbrRenderer.NUM_SAMPLES = VkSampleCountFlags.SampleCount1; + DeferredPbrRenderer.HDR_FORMAT = VkFormat.R32g32b32a32Sfloat; + DeferredPbrRenderer.MRT_FORMAT = VkFormat.R16g16b16a16Sfloat; + + PbrModelTexArray.TEXTURE_DIM = 1024; + + using (Program vke = new Program ()) { + vke.Run (); + } + } + + #region crow ui + public Crow.Command CMDViewScenes, CMDViewEditor, CMDViewDebug, CMDViewMaterials; + void init_crow_commands () { + CMDViewScenes = new Crow.Command (new Action (() => loadWindow ("#deferred.main.crow", this))) { Caption = "Lighting", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true }; + CMDViewEditor = new Crow.Command (new Action (() => loadWindow ("#deferred.scenes.crow", this))) { Caption = "Scenes", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true }; + CMDViewDebug = new Crow.Command (new Action (() => loadWindow ("#deferred.debug.crow", this))) { Caption = "Debug", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true }; + CMDViewMaterials = new Crow.Command (new Action (() => loadWindow ("#deferred.materials.crow", this))) { Caption = "Materials", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true }; + } + + public DeferredPbrRenderer.DebugView CurrentDebugView { + get { return renderer.currentDebugView; } + set { + if (value == renderer.currentDebugView) + return; + lock(crow.UpdateMutex) + renderer.currentDebugView = value; + rebuildBuffers = true; + NotifyValueChanged ("CurrentDebugView", renderer.currentDebugView); + } + } + + public float Gamma { + get { return renderer.matrices.gamma; } + set { + if (value == renderer.matrices.gamma) + return; + renderer.matrices.gamma = value; + NotifyValueChanged ("Gamma", value); + updateViewRequested = true; + } + } + public float Exposure { + get { return renderer.matrices.exposure; } + set { + if (value == renderer.matrices.exposure) + return; + renderer.matrices.exposure = value; + NotifyValueChanged ("Exposure", value); + updateViewRequested = true; + } + } + public float LightStrength { + get { return renderer.lights[renderer.lightNumDebug].color.X; } + set { + if (value == renderer.lights[renderer.lightNumDebug].color.X) + return; + renderer.lights[renderer.lightNumDebug].color = new Vector4(value); + NotifyValueChanged ("LightStrength", value); + renderer.uboLights.Update (renderer.lights); + } + } + public List Lights => renderer.lights.ToList (); + public List Scenes => renderer.model.Scenes; + #endregion + + public override string[] EnabledDeviceExtensions => new string[] { + Ext.D.VK_KHR_swapchain, + Ext.D.VK_EXT_debug_marker + }; + + protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (available_features, ref features); + + features.samplerAnisotropy = available_features.samplerAnisotropy; + features.sampleRateShading = available_features.sampleRateShading; + features.geometryShader = available_features.geometryShader; + features.pipelineStatisticsQuery = true; + + if (available_features.textureCompressionETC2) { + features.textureCompressionETC2 = true; + Image.DefaultTextureFormat = VkFormat.Etc2R8g8b8a8UnormBlock; + }else if (available_features.textureCompressionBC) { + //features.textureCompressionBC = true; + //Image.DefaultTextureFormat = VkFormat.Bc3UnormBlock; + } + + + } + + protected override void createQueues () { + base.createQueues (); + transferQ = new Queue (dev, VkQueueFlags.Transfer); + } + + string[] cubemapPathes = { + Utils.DataDirectory + "textures/papermill.ktx", + Utils.DataDirectory + "textures/cubemap_yokohama_bc3_unorm.ktx", + Utils.DataDirectory + "textures/gcanyon_cube.ktx", + Utils.DataDirectory + "textures/pisa_cube.ktx", + Utils.DataDirectory + "textures/uffizi_cube.ktx", + }; + string[] modelPathes = { + //"/mnt/devel/gts/vkChess.net/data/models/chess.glb", + //"/home/jp/gltf/jaguar/scene.gltf", + Utils.DataDirectory + "models/DamagedHelmet/glTF/DamagedHelmet.gltf", + Utils.DataDirectory + "models/shadow.glb", + Utils.DataDirectory + "models/Hubble.glb", + Utils.DataDirectory + "models/MER_static.glb", + Utils.DataDirectory + "models/ISS_stationary.glb", + }; + + int curModelIndex = 0; + bool reloadModel; + bool rebuildBuffers; + + Queue transferQ; + DeferredPbrRenderer renderer; + PipelineStatisticsQueryPool statPool; + TimestampQueryPool timestampQPool; + ulong[] results; + + DebugReport dbgRepport; + + Program () : base() { + + if (Instance.DEBUG_UTILS) + dbgRepport = new DebugReport (instance, + VkDebugReportFlagsEXT.ErrorEXT + | VkDebugReportFlagsEXT.DebugEXT + | VkDebugReportFlagsEXT.WarningEXT + | VkDebugReportFlagsEXT.PerformanceWarningEXT + ); + + camera = new Camera (Utils.DegreesToRadians (45f), 1f, 0.1f, 16f); + camera.SetPosition (0, 0, 2); + + renderer = new DeferredPbrRenderer (dev, swapChain, presentQueue, cubemapPathes[2], camera.NearPlane, camera.FarPlane); + renderer.LoadModel (transferQ, modelPathes[curModelIndex]); + camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); + + statPool = new PipelineStatisticsQueryPool (dev, + VkQueryPipelineStatisticFlags.InputAssemblyVertices | + VkQueryPipelineStatisticFlags.InputAssemblyPrimitives | + VkQueryPipelineStatisticFlags.ClippingInvocations | + VkQueryPipelineStatisticFlags.ClippingPrimitives | + VkQueryPipelineStatisticFlags.FragmentShaderInvocations); + + timestampQPool = new TimestampQueryPool (dev); + + init_crow_commands (); + + crow.Load ("#deferred.menu.crow").DataSource = this; + + } + + protected override void recordDraw (CommandBuffer cmd, int imageIndex) { + statPool.Begin (cmd); + renderer.buildCommandBuffers (cmd, imageIndex); + statPool.End (cmd); + } + + public override void UpdateView () { + renderer.UpdateView (camera); + updateViewRequested = false; +#if WITH_SHADOWS + if (renderer.shadowMapRenderer.updateShadowMap) + renderer.shadowMapRenderer.update_shadow_map (cmdPool); +#endif + } + + int frameCount = 0; + public override void Update () { + if (reloadModel) { + renderer.LoadModel (transferQ, modelPathes[curModelIndex]); + reloadModel = false; + camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); + updateViewRequested = true; + rebuildBuffers = true; +#if WITH_SHADOWS + renderer.shadowMapRenderer.updateShadowMap = true; +#endif + } + + base.Update (); + + if (++frameCount > 20) { + NotifyValueChanged ("fps", fps); + frameCount = 0; + } + + results = statPool.GetResults (); + } + protected override void OnResize () { + renderer.Resize (); + base.OnResize (); + } + + #region Mouse and keyboard + protected override void onMouseMove (double xPos, double yPos) { + if (crow.ProcessMouseMove ((int)xPos, (int)yPos)) + return; + + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton[0]) { + camera.Rotate ((float)-diffX, (float)-diffY); + } else if (MouseButton[1]) { + camera.SetZoom ((float)diffY); + } else + return; + + updateViewRequested = true; + } + protected override void onMouseButtonDown (Glfw.MouseButton button) { + if (crow.ProcessMouseButtonDown ((Crow.MouseButton)button)) + return; + base.onMouseButtonDown (button); + } + protected override void onMouseButtonUp (Glfw.MouseButton button) { + if (crow.ProcessMouseButtonUp ((Crow.MouseButton)button)) + return; + base.onMouseButtonUp (button); + } + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + if (crow.ProcessKeyDown ((Crow.Key)key)) + return; + switch (key) { + case Key.F: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.debugFace--; + if (renderer.debugFace < 0) + renderer.debugFace = 5; + } else { + renderer.debugFace++; + if (renderer.debugFace >= 5) + renderer.debugFace = 0; + } + rebuildBuffers = true; + break; + case Key.M: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.debugMip--; + if (renderer.debugMip < 0) + renderer.debugMip = (int)renderer.envCube.prefilterCube.CreateInfo.mipLevels - 1; + } else { + renderer.debugMip++; + if (renderer.debugMip >= renderer.envCube.prefilterCube.CreateInfo.mipLevels) + renderer.debugMip = 0; + } + rebuildBuffers = true; + break; + case Key.L: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.lightNumDebug--; + if (renderer.lightNumDebug < 0) + renderer.lightNumDebug = (int)renderer.lights.Length - 1; + } else { + renderer.lightNumDebug++; + if (renderer.lightNumDebug >= renderer.lights.Length) + renderer.lightNumDebug = 0; + } + rebuildBuffers = true; + break; + case Key.Keypad0: + case Key.Keypad1: + case Key.Keypad2: + case Key.Keypad3: + case Key.Keypad4: + case Key.Keypad5: + case Key.Keypad6: + case Key.Keypad7: + case Key.Keypad8: + case Key.Keypad9: + renderer.currentDebugView = (DeferredPbrRenderer.DebugView)(int)key-320; + rebuildBuffers = true; + break; + case Key.KeypadDivide: + renderer.currentDebugView = DeferredPbrRenderer.DebugView.irradiance; + rebuildBuffers = true; + break; + case Key.S: + if (modifiers.HasFlag (Modifier.Control)) { + renderer.pipelineCache.Save (); + Console.WriteLine ($"Pipeline Cache saved."); + } else { + renderer.currentDebugView = DeferredPbrRenderer.DebugView.shadowMap; + rebuildBuffers = true; + } + break; + case Key.Up: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight(-Vector4.UnitZ); + else + camera.Move (0, 0, 1); + break; + case Key.Down: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitZ); + else + camera.Move (0, 0, -1); + break; + case Key.Left: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (-Vector4.UnitX); + else + camera.Move (1, 0, 0); + break; + case Key.Right: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitX); + else + camera.Move (-1, 0, 0); + break; + case Key.PageUp: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitY); + else + camera.Move (0, 1, 0); + break; + case Key.PageDown: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (-Vector4.UnitY); + else + camera.Move (0, -1, 0); + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.matrices.exposure -= 0.3f; + else + renderer.matrices.exposure += 0.3f; + break; + case Key.F3: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.matrices.gamma -= 0.1f; + else + renderer.matrices.gamma += 0.1f; + break; + case Key.F4: + if (camera.Type == Camera.CamType.FirstPerson) + camera.Type = Camera.CamType.LookAt; + else + camera.Type = Camera.CamType.FirstPerson; + Console.WriteLine ($"camera type = {camera.Type}"); + break; + case Key.KeypadAdd: + curModelIndex++; + if (curModelIndex >= modelPathes.Length) + curModelIndex = 0; + reloadModel = true; + break; + case Key.KeypadSubtract: + curModelIndex--; + if (curModelIndex < 0) + curModelIndex = modelPathes.Length -1; + reloadModel = true; + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } + protected override void onKeyUp (Key key, int scanCode, Modifier modifiers) { + if (crow.ProcessKeyUp ((Crow.Key)key)) + return; + } + protected override void onChar (CodePoint cp) { + if (crow.ProcessKeyPress (cp.ToChar ())) + return; + } + #endregion + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + renderer.Dispose (); + statPool.Dispose (); + timestampQPool.Dispose (); + dbgRepport?.Dispose (); + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/deferred/main.cs b/samples/deferred/main.cs new file mode 100644 index 0000000..f15132b --- /dev/null +++ b/samples/deferred/main.cs @@ -0,0 +1,549 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using CVKL; +using CVKL.glTF; +using Glfw; +using VK; + +namespace deferred { + class Deferred : VkWindow { + static void Main (string[] args) { +#if DEBUG + Instance.VALIDATION = true; + Instance.DEBUG_UTILS = true; + Instance.RENDER_DOC_CAPTURE = false; +#endif + SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Srgb; + DeferredPbrRenderer.TEXTURE_ARRAY = true; + DeferredPbrRenderer.NUM_SAMPLES = VkSampleCountFlags.SampleCount1; + DeferredPbrRenderer.HDR_FORMAT = VkFormat.R16g16b16a16Sfloat; + DeferredPbrRenderer.MRT_FORMAT = VkFormat.R32g32b32a32Sfloat; + + PbrModelTexArray.TEXTURE_DIM = 1024; + + using (Deferred vke = new Deferred ()) { + vke.Run (); + } + } + + public override string[] EnabledDeviceExtensions => new string[] { + Ext.D.VK_KHR_swapchain, + Ext.D.VK_EXT_debug_marker + }; + + protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) { + base.configureEnabledFeatures (available_features, ref enabled_features); + + enabled_features.samplerAnisotropy = available_features.samplerAnisotropy; + enabled_features.sampleRateShading = available_features.sampleRateShading; + enabled_features.geometryShader = available_features.geometryShader; + + enabled_features.textureCompressionBC = available_features.textureCompressionBC; + } + + protected override void createQueues () { + base.createQueues (); + transferQ = new Queue (dev, VkQueueFlags.Transfer); + computeQ = new Queue (dev, VkQueueFlags.Compute); + } + string[] cubemapPathes = { + Utils.DataDirectory + "textures/papermill.ktx", + Utils.DataDirectory + "textures/cubemap_yokohama_bc3_unorm.ktx", + Utils.DataDirectory + "textures/gcanyon_cube.ktx", + Utils.DataDirectory + "textures/pisa_cube.ktx", + Utils.DataDirectory + "textures/uffizi_cube.ktx", + }; + string[] modelPathes = { + //"/mnt/devel/gts/vkChess.net/data/models/chess.glb", + Utils.DataDirectory + "models/DamagedHelmet/glTF/DamagedHelmet.gltf", + Utils.DataDirectory + "models/shadow.glb", + Utils.DataDirectory + "models/Hubble.glb", + Utils.DataDirectory + "models/MER_static.glb", + Utils.DataDirectory + "models/ISS_stationary.glb", + }; + + int curModelIndex = 0; + bool reloadModel; + bool rebuildBuffers; + + Queue transferQ, computeQ; + DeferredPbrRenderer renderer; + + + GraphicPipeline plToneMap; + Framebuffer[] frameBuffers; + DescriptorPool descriptorPool; + DescriptorSet descriptorSet; + + + + Deferred () : base("deferred") { + camera = new Camera (Utils.DegreesToRadians (45f), 1f, 0.1f, 16f); + camera.SetPosition (0, 0, 2); + + //renderer = new DeferredPbrRenderer (presentQueue, cubemapPathes[2], swapChain.Width, swapChain.Height, camera.NearPlane, camera.FarPlane); + renderer = new DeferredPbrRenderer (presentQueue, cubemapPathes[2], swapChain.Width, swapChain.Height, camera.NearPlane, camera.FarPlane); + renderer.LoadModel (transferQ, modelPathes[curModelIndex]); + camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); + + init_final_pl (); + } + + void init_final_pl() { + descriptorPool = new DescriptorPool (dev, 3, + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 2), + new VkDescriptorPoolSize (VkDescriptorType.StorageImage, 4) + ); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, DeferredPbrRenderer.NUM_SAMPLES); + + cfg.Layout = new PipelineLayout (dev, + new VkPushConstantRange (VkShaderStageFlags.Fragment, 2 * sizeof (float)), + new DescriptorSetLayout (dev, 0, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + )); + + cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, DeferredPbrRenderer.NUM_SAMPLES); + + cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.FullScreenQuad.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.tone_mapping.frag.spv"); + + plToneMap = new GraphicPipeline (cfg); + + descriptorSet = descriptorPool.Allocate (cfg.Layout.DescriptorSetLayouts[0]); + + init_blur (); + } + + ComputePipeline plBlur; + DescriptorSetLayout dsLayoutBlur; + DescriptorSet dsetBlurPing, dsetBlurPong; + Image downSamp, downSamp2; + CommandPool computeCmdPool; + + struct BlurPushCsts { + public Vector2 texSize; + public int dir; + public float scale; + public float strength; + }; + BlurPushCsts pcBloom = new BlurPushCsts () { strength = 1.3f, scale = 0.4f }; + + void init_blur () { + computeCmdPool = new CommandPool (computeQ); + + blurComplete = dev.CreateSemaphore (); + + dsLayoutBlur = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Compute, VkDescriptorType.StorageImage), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Compute, VkDescriptorType.StorageImage) + ); + plBlur = new ComputePipeline ( + new PipelineLayout (dev, new VkPushConstantRange (VkShaderStageFlags.Compute, (uint)Marshal.SizeOf ()), 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 ()); + cmd.Dispatch (downSamp.Width / 16, downSamp.Height / 16); + + downSamp.SetLayout (cmd, VkImageAspectFlags.Color, + VkAccessFlags.MemoryWrite, VkAccessFlags.ShaderRead, + VkImageLayout.General, VkImageLayout.ShaderReadOnlyOptimal, + VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.FragmentShader);*/ + + downSamp.SetLayout (cmd, VkImageAspectFlags.Color, + VkAccessFlags.TransferWrite, VkAccessFlags.ShaderRead, + VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, + VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader); + + cmd.End (); + } + + + CommandBuffer cmdPbr; + CommandBuffer cmdBlur; + VkSemaphore blurComplete; + const uint downSizing = 1; + float finalDebug = -1.0f; + + void buildCommandBuffers () { + cmdPbr?.Free (); + cmdPbr = cmdPool.AllocateAndStart (); + renderer.buildCommandBuffers (cmdPbr); + cmdPbr.End (); + + cmdBlur?.Free (); + cmdBlur = computeCmdPool.AllocateAndStart (); + buildBlurCmd (cmdBlur); + + + for (int i = 0; i < swapChain.ImageCount; ++i) { + cmds[i]?.Free (); + cmds[i] = cmdPool.AllocateAndStart (); + + //renderer.hdrImgResolved.SetLayout (cmds[i], VkImageAspectFlags.Color, + //VkAccessFlags.TransferRead, VkAccessFlags.ShaderRead, + //VkImageLayout.TransferSrcOptimal, VkImageLayout.ShaderReadOnlyOptimal, + //VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader); + + plToneMap.RenderPass.Begin (cmds[i], frameBuffers[i]); + + cmds[i].SetViewport (frameBuffers[i].Width, frameBuffers[i].Height); + cmds[i].SetScissor (frameBuffers[i].Width, frameBuffers[i].Height); + + plToneMap.Bind (cmds[i]); + plToneMap.BindDescriptorSet (cmds[i], descriptorSet); + + cmds[i].PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 12, new float[] { renderer.exposure, renderer.gamma, finalDebug }, 0); + + cmds[i].Draw (3, 1, 0, 0); + + plToneMap.RenderPass.End (cmds[i]); + + renderer.hdrImgResolved.SetLayout (cmds[i], VkImageAspectFlags.Color, + VkAccessFlags.ShaderRead, VkAccessFlags.ColorAttachmentWrite, + VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.ColorAttachmentOptimal, + VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.ColorAttachmentOutput); + + cmds[i].End (); + } + } + + public override void UpdateView () { + renderer.UpdateView (camera); + updateViewRequested = false; +#if WITH_SHADOWS + if (renderer.shadowMapRenderer.updateShadowMap) + renderer.shadowMapRenderer.update_shadow_map (cmdPool); +#endif + } + + public override void Update () { + if (reloadModel) { + renderer.LoadModel (transferQ, modelPathes[curModelIndex]); + reloadModel = false; + camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); + updateViewRequested = true; + rebuildBuffers = true; +#if WITH_SHADOWS + renderer.shadowMapRenderer.updateShadowMap = true; +#endif + } + + if (rebuildBuffers) { + buildCommandBuffers (); + rebuildBuffers = false; + } + + } + + + protected override void render () { + int idx = swapChain.GetNextImage (); + if (idx < 0) { + OnResize (); + return; + } + + if (cmds[idx] == null) + return; + + presentQueue.Submit (cmdPbr, swapChain.presentComplete, renderer.DrawComplete); + + computeQ.Submit (cmdBlur, renderer.DrawComplete, blurComplete); + + presentQueue.Submit (cmds[idx], blurComplete, drawComplete[idx]); + presentQueue.Present (swapChain, drawComplete[idx]); + + presentQueue.WaitIdle (); + } + protected override void OnResize () { + dev.WaitIdle (); + + renderer.Resize (swapChain.Width, swapChain.Height); + + UpdateView (); + + downSamp?.Dispose (); + downSamp2?.Dispose (); + downSamp = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Storage | VkImageUsageFlags.Sampled, + VkMemoryPropertyFlags.DeviceLocal, renderer.Width / downSizing, renderer.Height / downSizing, VkImageType.Image2D); + downSamp2 = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.Storage, + VkMemoryPropertyFlags.DeviceLocal, renderer.Width / downSizing, renderer.Height/ downSizing, VkImageType.Image2D); + downSamp.CreateView (); downSamp.CreateSampler (); + downSamp.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + downSamp2.CreateView (); downSamp2.CreateSampler (); + downSamp2.Descriptor.imageLayout = VkImageLayout.General; + + downSamp.SetName ("HDRimgDownScaled"); + downSamp2.SetName ("HDRimgDownScaled2"); + + pcBloom.texSize.X = downSamp.Width; + pcBloom.texSize.Y = downSamp.Height; + + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (plToneMap.RenderPass, swapChain.Width, swapChain.Height, + (plToneMap.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + } : new Image[] { + null, + swapChain.images[i] + }); + } + + DescriptorSetWrites dsUpdate = new DescriptorSetWrites (plToneMap.Layout.DescriptorSetLayouts[0]); + dsUpdate.Write (dev, descriptorSet, renderer.hdrImgResolved.Descriptor, downSamp.Descriptor); + + dsUpdate = new DescriptorSetWrites (dsLayoutBlur); + downSamp.Descriptor.imageLayout = VkImageLayout.General; + dsUpdate.Write (dev, dsetBlurPong, downSamp2.Descriptor, downSamp.Descriptor); + dsUpdate.Write (dev, dsetBlurPing, downSamp.Descriptor, downSamp2.Descriptor); + + buildCommandBuffers (); + + dev.WaitIdle (); + } + + #region Mouse and keyboard + protected override void onScroll (double xOffset, double yOffset) { + } + protected override void onMouseMove (double xPos, double yPos) { + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton[0]) { + camera.Rotate ((float)-diffX, (float)-diffY); + } else if (MouseButton[1]) { + camera.SetZoom ((float)diffY); + } else + return; + + updateViewRequested = true; + } + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.debugFace--; + if (renderer.debugFace < 0) + renderer.debugFace = 5; + } else { + renderer.debugFace++; + if (renderer.debugFace >= 5) + renderer.debugFace = 0; + } + rebuildBuffers = true; + break; + case Key.M: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.debugMip--; + if (renderer.debugMip < 0) + renderer.debugMip = (int)renderer.envCube.prefilterCube.CreateInfo.mipLevels - 1; + } else { + renderer.debugMip++; + if (renderer.debugMip >= renderer.envCube.prefilterCube.CreateInfo.mipLevels) + renderer.debugMip = 0; + } + rebuildBuffers = true; + break; + case Key.L: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.lightNumDebug--; + if (renderer.lightNumDebug < 0) + renderer.lightNumDebug = (int)renderer.lights.Length - 1; + } else { + renderer.lightNumDebug++; + if (renderer.lightNumDebug >= renderer.lights.Length) + renderer.lightNumDebug = 0; + } + rebuildBuffers = true; + break; + case Key.Keypad0: + case Key.Keypad1: + case Key.Keypad2: + case Key.Keypad3: + case Key.Keypad4: + case Key.Keypad5: + case Key.Keypad6: + case Key.Keypad7: + case Key.Keypad8: + case Key.Keypad9: + renderer.currentDebugView = (DeferredPbrRenderer.DebugView)(int)key-320; + rebuildBuffers = true; + break; + case Key.KeypadDivide: + renderer.currentDebugView = DeferredPbrRenderer.DebugView.irradiance; + rebuildBuffers = true; + break; + case Key.S: + if (modifiers.HasFlag (Modifier.Control)) { + renderer.pipelineCache.Save (); + Console.WriteLine ($"Pipeline Cache saved."); + } else { + renderer.currentDebugView = DeferredPbrRenderer.DebugView.shadowMap; + rebuildBuffers = true; + } + break; + case Key.Up: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight(-Vector4.UnitZ); + else + camera.Move (0, 0, 1); + break; + case Key.Down: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitZ); + else + camera.Move (0, 0, -1); + break; + case Key.Left: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (-Vector4.UnitX); + else + camera.Move (1, 0, 0); + break; + case Key.Right: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitX); + else + camera.Move (-1, 0, 0); + break; + case Key.PageUp: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitY); + else + camera.Move (0, 1, 0); + break; + case Key.PageDown: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (-Vector4.UnitY); + else + camera.Move (0, -1, 0); + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.exposure -= 0.3f; + else + renderer.exposure += 0.3f; + rebuildBuffers = true; + break; + case Key.F3: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.gamma -= 0.1f; + else + renderer.gamma += 0.1f; + rebuildBuffers = true; + break; + case Key.D: + finalDebug = -finalDebug; + rebuildBuffers = true; + break; + case Key.B: + if (modifiers.HasFlag (Modifier.Control)) { + if (modifiers.HasFlag (Modifier.Shift)) + pcBloom.strength -= 0.1f; + else + pcBloom.strength += 0.1f; + } else { + if (modifiers.HasFlag (Modifier.Shift)) + pcBloom.scale *= 1.1f; + else + pcBloom.scale *= 0.9f; + } + Console.WriteLine ($"Bloom: scale = {pcBloom.scale}, strength = {pcBloom.strength}"); + rebuildBuffers = true; + //if (camera.Type == Camera.CamType.FirstPerson) + // camera.Type = Camera.CamType.LookAt; + //else + // camera.Type = Camera.CamType.FirstPerson; + //Console.WriteLine ($"camera type = {camera.Type}"); + break; + case Key.KeypadAdd: + curModelIndex++; + if (curModelIndex >= modelPathes.Length) + curModelIndex = 0; + reloadModel = true; + break; + case Key.KeypadSubtract: + curModelIndex--; + if (curModelIndex < 0) + curModelIndex = modelPathes.Length -1; + reloadModel = true; + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } + #endregion + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + computeCmdPool.Dispose (); + downSamp?.Dispose (); + downSamp2?.Dispose (); + if (frameBuffers != null) + foreach (Framebuffer fb in frameBuffers) + fb.Dispose (); + renderer.Dispose (); + plBlur.Dispose (); + plToneMap.Dispose (); + descriptorPool.Dispose (); + } + dev.DestroySemaphore (blurComplete); + } + base.Dispose (disposing); + } + } +} diff --git a/samples/deferred/mainShadow.cs b/samples/deferred/mainShadow.cs new file mode 100644 index 0000000..0bc3103 --- /dev/null +++ b/samples/deferred/mainShadow.cs @@ -0,0 +1,415 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using VK; +using CVKL; + +namespace deferredShadow { + class Program : VkWindow{ + static void Main (string[] args) { + using (Program vke = new Program ()) { + vke.Run (); + } + } + + VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1; + + public struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + public Vector4 lightPos; + public float gamma; + public float exposure; + } + + public Matrices matrices = new Matrices { + lightPos = new Vector4 (1.0f, 0.0f, 0.0f, 1.0f), + gamma = 1.0f, + exposure = 2.0f, + }; + + const uint SHADOW_MAP_SIZE = 4096; + +#if DEBUG + PipelineStatisticsQueryPool statPool; + TimestampQueryPool timestampQPool; + ulong[] results; +#endif + + protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (ref features); +#if DEBUG + features.pipelineStatisticsQuery = true; +#endif + } + + Program () : base(true) { + //camera.Model = Matrix4x4.CreateRotationX (Utils.DegreesToRadians (-90)) * Matrix4x4.CreateRotationY (Utils.DegreesToRadians (180)); + //camera.SetRotation (-0.1f,-0.4f); + camera.SetPosition (0, 0, -3); + + init (); + +#if DEBUG + statPool = new PipelineStatisticsQueryPool (dev, + VkQueryPipelineStatisticFlags.InputAssemblyVertices | + VkQueryPipelineStatisticFlags.InputAssemblyPrimitives | + VkQueryPipelineStatisticFlags.ClippingInvocations | + VkQueryPipelineStatisticFlags.ClippingPrimitives | + VkQueryPipelineStatisticFlags.FragmentShaderInvocations); + + timestampQPool = new TimestampQueryPool (dev); +#endif + } + + Framebuffer[] frameBuffers; + Image gbColorRough, gbEmitMetal, gbN, gbPos; + + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutMain, descLayoutModelTextures, descLayoutGBuff; + DescriptorSet dsMain, dsGBuff; + + Pipeline gBuffPipeline, composePipeline; + + HostBuffer uboMats; + + RenderPass renderPass; + + Model model; + EnvironmentCube envCube; + + DebugDrawPipeline debugDraw; + Framebuffer[] debugFB; + + void init () { + VkFormat depthFormat = dev.GetSuitableDepthFormat (); + renderPass = new RenderPass (dev); + + renderPass.AddAttachment (swapChain.ColorFormat, VkImageLayout.ColorAttachmentOptimal, VkSampleCountFlags.SampleCount1); + renderPass.AddAttachment (depthFormat, VkImageLayout.DepthStencilAttachmentOptimal, samples); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal); + //renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.DepthStencilReadOnlyOptimal); + + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + + SubPass[] subpass = { new SubPass (), new SubPass (), new SubPass() }; + subpass[0].AddColorReference ( new VkAttachmentReference (2, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (3, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (4, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (5, VkImageLayout.ColorAttachmentOptimal)); + subpass[0].SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal); + + subpass[1].AddColorReference (0, VkImageLayout.ColorAttachmentOptimal); + subpass[1].AddInputReference ( new VkAttachmentReference (2, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (3, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (4, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (5, VkImageLayout.ShaderReadOnlyOptimal)); + renderPass.AddSubpass (subpass); + + renderPass.AddDependency (Vk.SubpassExternal, 0, + VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput, + VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite); + renderPass.AddDependency (0, 1, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader, + VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead); + renderPass.AddDependency (1, Vk.SubpassExternal, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe, + VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead); + + + descriptorPool = new DescriptorPool (dev, 3, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2), + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 3), + new VkDescriptorPoolSize (VkDescriptorType.InputAttachment, 4) + ); + + descLayoutMain = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)); + + descLayoutModelTextures = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + descLayoutGBuff = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment)); + + dsMain = descriptorPool.Allocate (descLayoutMain); + dsGBuff = descriptorPool.Allocate (descLayoutGBuff); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples); + cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutModelTextures, descLayoutGBuff); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf ()), + new VkPushConstantRange (VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf (), 64) + ); + cfg.RenderPass = renderPass; + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + + cfg.AddVertexBinding (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 () * 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 (); + 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.view *= Matrix4x4.CreateTranslation (-matrices.view.Translation); + matrices.model = Matrix4x4.Identity; + uboMats.Update (matrices, (uint)Marshal.SizeOf (), (uint)Marshal.SizeOf ()); + } + + public override void UpdateView () { + updateMatrices (); + updateViewRequested = false; + } + public override void Update () { +#if DEBUG + results = statPool.GetResults (); +#endif + } +#endregion + + + + void createGBuff () { + gbColorRough?.Dispose (); + gbEmitMetal?.Dispose (); + gbN?.Dispose (); + gbPos?.Dispose (); + + gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbN = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbPos = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + + gbColorRough.CreateView (); + gbColorRough.CreateSampler (); + gbEmitMetal.CreateView (); + gbEmitMetal.CreateSampler (); + gbN.CreateView (); + gbN.CreateSampler (); + gbPos.CreateView (); + gbPos.CreateSampler (); + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff); + uboUpdate.Write (dev, dsGBuff, gbColorRough.Descriptor, + gbEmitMetal.Descriptor, + gbN.Descriptor, + gbPos.Descriptor); + gbColorRough.SetName ("GBuffColorRough"); + gbEmitMetal.SetName ("GBuffEmitMetal"); + gbN.SetName ("GBuffN"); + gbPos.SetName ("GBuffPos"); + } + + protected override void OnResize () { + updateMatrices (); + + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); +#if DEBUG + if (debugFB != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + debugFB[i]?.Dispose (); +#endif + + createGBuff (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + debugFB = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (renderPass, swapChain.Width, swapChain.Height, new Image[] { + swapChain.images[i], null, gbColorRough, gbEmitMetal, gbN, gbPos}); +#if DEBUG + debugFB[i] = new Framebuffer (debugDraw.RenderPass, swapChain.Width, swapChain.Height,(debugDraw.Samples == VkSampleCountFlags.SampleCount1) + ? new Image[] { swapChain.images[i] } : new Image[] { null, swapChain.images[i] }); + debugFB[i].SetName ("main FB " + i); +#endif + } + + buildCommandBuffers (); + } + + + #region Mouse and keyboard + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F1: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.exposure -= 0.3f; + else + matrices.exposure += 0.3f; + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.gamma -= 0.1f; + else + matrices.gamma += 0.1f; + break; + case Key.F3: + if (camera.Type == Camera.CamType.FirstPerson) + camera.Type = Camera.CamType.LookAt; + else + camera.Type = Camera.CamType.FirstPerson; + Console.WriteLine ($"camera type = {camera.Type}"); + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } +#endregion + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + gbColorRough.Dispose (); + gbEmitMetal.Dispose (); + gbN.Dispose (); + gbPos.Dispose (); + + gBuffPipeline.Dispose (); + composePipeline.Dispose (); + + descLayoutMain.Dispose (); + descLayoutModelTextures.Dispose (); + descLayoutGBuff.Dispose (); + + uboMats.Dispose (); + model.Dispose (); + envCube.Dispose (); + + descriptorPool.Dispose (); +#if DEBUG + debugDraw.Dispose (); + timestampQPool?.Dispose (); + statPool?.Dispose (); +#endif + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/deferred/mainWithDebugDrawer.cs b/samples/deferred/mainWithDebugDrawer.cs new file mode 100644 index 0000000..f95ed0a --- /dev/null +++ b/samples/deferred/mainWithDebugDrawer.cs @@ -0,0 +1,412 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using VK; +using CVKL; + +namespace deferredDebug { + class Program : VkWindow{ + static void Main (string[] args) { + using (Program vke = new Program ()) { + vke.Run (); + } + } + + VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1; + + public struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + public Vector4 lightPos; + public float gamma; + public float exposure; + } + + public Matrices matrices = new Matrices { + lightPos = new Vector4 (1.0f, 0, 0, 1.0f), + gamma = 1.0f, + exposure = 2.0f, + }; + +#if DEBUG + PipelineStatisticsQueryPool statPool; + TimestampQueryPool timestampQPool; + ulong[] results; +#endif + + protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (ref features); +#if DEBUG + features.pipelineStatisticsQuery = true; +#endif + } + + Program () : base(true) { + camera.Model = Matrix4x4.CreateRotationX (Utils.DegreesToRadians (-90)) * Matrix4x4.CreateRotationY (Utils.DegreesToRadians (180)); + camera.SetRotation (-0.1f,-0.4f); + camera.SetPosition (0, 0, -3); + + init (); + +#if DEBUG + statPool = new PipelineStatisticsQueryPool (dev, + VkQueryPipelineStatisticFlags.InputAssemblyVertices | + VkQueryPipelineStatisticFlags.InputAssemblyPrimitives | + VkQueryPipelineStatisticFlags.ClippingInvocations | + VkQueryPipelineStatisticFlags.ClippingPrimitives | + VkQueryPipelineStatisticFlags.FragmentShaderInvocations); + + timestampQPool = new TimestampQueryPool (dev); +#endif + } + + Framebuffer[] frameBuffers; + Image gbColorRough, gbEmitMetal, gbN, gbPos; + + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutMain, descLayoutModelTextures, descLayoutGBuff; + DescriptorSet dsMain, dsGBuff; + + Pipeline gBuffPipeline, composePipeline; + + HostBuffer uboMats; + + RenderPass renderPass; + + Model model; + EnvironmentCube envCube; + + DebugDrawPipeline debugDraw; + Framebuffer[] debugFB; + + void init () { + renderPass = new RenderPass (dev); + renderPass.AddAttachment (swapChain.ColorFormat, VkImageLayout.ColorAttachmentOptimal, VkSampleCountFlags.SampleCount1); + renderPass.AddAttachment (dev.GetSuitableDepthFormat(), VkImageLayout.DepthStencilAttachmentOptimal, samples); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal); + + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + + SubPass[] subpass = { new SubPass (), new SubPass () }; + subpass[0].AddColorReference ( new VkAttachmentReference (2, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (3, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (4, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (5, VkImageLayout.ColorAttachmentOptimal)); + subpass[0].SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal); + + subpass[1].AddColorReference (0, VkImageLayout.ColorAttachmentOptimal); + subpass[1].AddInputReference ( new VkAttachmentReference (2, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (3, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (4, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (5, VkImageLayout.ShaderReadOnlyOptimal)); + renderPass.AddSubpass (subpass); + + renderPass.AddDependency (Vk.SubpassExternal, 0, + VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput, + VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite); + renderPass.AddDependency (0, 1, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader, + VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead); + renderPass.AddDependency (1, Vk.SubpassExternal, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe, + VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead); + + + descriptorPool = new DescriptorPool (dev, 3, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2), + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 3), + new VkDescriptorPoolSize (VkDescriptorType.InputAttachment, 4) + ); + + descLayoutMain = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)); + + descLayoutModelTextures = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + descLayoutGBuff = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment)); + + dsMain = descriptorPool.Allocate (descLayoutMain); + dsGBuff = descriptorPool.Allocate (descLayoutGBuff); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples); + cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutModelTextures, descLayoutGBuff); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf ()), + 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 (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 () * 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 (); + 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.view *= Matrix4x4.CreateTranslation (-matrices.view.Translation); + matrices.model = Matrix4x4.Identity; + uboMats.Update (matrices, (uint)Marshal.SizeOf (), (uint)Marshal.SizeOf ()); + } + + public override void UpdateView () { + updateMatrices (); + updateViewRequested = false; + } + public override void Update () { +#if DEBUG + results = statPool.GetResults (); +#endif + } +#endregion + + + + void createGBuff () { + gbColorRough?.Dispose (); + gbEmitMetal?.Dispose (); + gbN?.Dispose (); + gbPos?.Dispose (); + + gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbN = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbPos = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + + gbColorRough.CreateView (); + gbColorRough.CreateSampler (); + gbEmitMetal.CreateView (); + gbEmitMetal.CreateSampler (); + gbN.CreateView (); + gbN.CreateSampler (); + gbPos.CreateView (); + gbPos.CreateSampler (); + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff); + uboUpdate.Write (dev, dsGBuff, gbColorRough.Descriptor, + gbEmitMetal.Descriptor, + gbN.Descriptor, + gbPos.Descriptor); + gbColorRough.SetName ("GBuffColorRough"); + gbEmitMetal.SetName ("GBuffEmitMetal"); + gbN.SetName ("GBuffN"); + gbPos.SetName ("GBuffPos"); + } + + protected override void OnResize () { + updateMatrices (); + + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); +#if DEBUG + if (debugFB != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + debugFB[i]?.Dispose (); +#endif + + createGBuff (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + debugFB = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (renderPass, swapChain.Width, swapChain.Height, new Image[] { + swapChain.images[i], null, gbColorRough, gbEmitMetal, gbN, gbPos}); +#if DEBUG + debugFB[i] = new Framebuffer (debugDraw.RenderPass, swapChain.Width, swapChain.Height,(debugDraw.Samples == VkSampleCountFlags.SampleCount1) + ? new Image[] { swapChain.images[i] } : new Image[] { null, swapChain.images[i] }); + debugFB[i].SetName ("main FB " + i); +#endif + } + + buildCommandBuffers (); + } + + + #region Mouse and keyboard + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F1: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.exposure -= 0.3f; + else + matrices.exposure += 0.3f; + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.gamma -= 0.1f; + else + matrices.gamma += 0.1f; + break; + case Key.F3: + if (camera.Type == Camera.CamType.FirstPerson) + camera.Type = Camera.CamType.LookAt; + else + camera.Type = Camera.CamType.FirstPerson; + Console.WriteLine ($"camera type = {camera.Type}"); + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } +#endregion + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + gbColorRough.Dispose (); + gbEmitMetal.Dispose (); + gbN.Dispose (); + gbPos.Dispose (); + + gBuffPipeline.Dispose (); + composePipeline.Dispose (); + + descLayoutMain.Dispose (); + descLayoutModelTextures.Dispose (); + descLayoutGBuff.Dispose (); + + uboMats.Dispose (); + model.Dispose (); + envCube.Dispose (); + + descriptorPool.Dispose (); +#if DEBUG + foreach (Framebuffer fb in debugFB) + fb.Dispose (); + + debugDraw.Dispose (); + timestampQPool?.Dispose (); + statPool?.Dispose (); +#endif + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/deferred/modelWithVkvgStats.cs b/samples/deferred/modelWithVkvgStats.cs new file mode 100644 index 0000000..f4ea123 --- /dev/null +++ b/samples/deferred/modelWithVkvgStats.cs @@ -0,0 +1,359 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using CVKL; +using VK; +using static CVKL.Camera; + +namespace modelWithVkvgStats { + class Program : VkWindow { + + PipelineStatisticsQueryPool statPool; + TimestampQueryPool timestampQPool; + + ulong[] results; + + protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (ref features); + features.pipelineStatisticsQuery = true; + } + public struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + public Vector4 lightPos; + public float gamma; + public float exposure; + } + + public Matrices matrices = new Matrices { + lightPos = new Vector4 (0.0f, 0.0f, -2.0f, 1.0f), + gamma = 1.0f, + exposure = 2.0f, + }; + + HostBuffer uboMats; + + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutMatrix; + DescriptorSetLayout descLayoutTextures; + DescriptorSet dsMats; + + GraphicPipeline pipeline; + GraphicPipeline uiPipeline; + Framebuffer[] frameBuffers; + + Model model; + + vkvg.Device vkvgDev; + vkvg.Surface vkvgSurf; + Image vkvgImage; + + void vkvgDraw () { + + using (vkvg.Context ctx = new vkvg.Context (vkvgSurf)) { + ctx.Operator = vkvg.Operator.Clear; + ctx.Paint (); + ctx.Operator = vkvg.Operator.Over; + + ctx.LineWidth = 1; + ctx.SetSource (0.1, 0.1, 0.1, 0.3); + ctx.Rectangle (5.5, 5.5, 400, 250); + ctx.FillPreserve (); + ctx.Flush (); + ctx.SetSource (0.8, 0.8, 0.8); + ctx.Stroke (); + + ctx.FontFace = "mono"; + ctx.FontSize = 10; + int x = 16; + int y = 40, dy = 16; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"fps: {fps,5} ")); + ctx.MoveTo (x + 200, y - 0.5); + ctx.Rectangle (x + 200, y - 8.5, 0.1 * fps, 10); + ctx.SetSource (0.1, 0.9, 0.1); + ctx.Fill (); + ctx.SetSource (0.8, 0.8, 0.8); + y += dy; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"Exposure:{matrices.exposure,5} ")); + y += dy; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"Gamma: {matrices.gamma,5} ")); + if (results == null) + return; + + y += dy*2; + ctx.MoveTo (x, y); + ctx.ShowText ("Pipeline Statistics"); + ctx.MoveTo (x-2, 2.5+y); + ctx.LineTo (x+160, 2.5+y); + ctx.Stroke (); + y += 4; + x += 20; + + for (int i = 0; i < statPool.RequestedStats.Length; i++) { + y += dy; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"{statPool.RequestedStats[i].ToString(),-30} :{results[i],12:0,0} ")); + } + + y += dy; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"{"Elapsed microsecond",-20} :{timestampQPool.ElapsedMiliseconds:0.0000} ")); + } + } + + Program () : base () { + vkvgDev = new vkvg.Device (instance.Handle, phy.Handle, dev.VkDev.Handle, presentQueue.qFamIndex, + vkvg.SampleCount.Sample_4, presentQueue.index); + + camera.Type = CamType.FirstPerson; + camera.Model = Matrix4x4.CreateScale (0.05f) * Matrix4x4.CreateRotationX((float)Math.PI); + //Camera.SetRotation (-0.1f,-0.4f); + camera.SetPosition (0, 2, -3); + + init (); + + model = new Model (presentQueue, "../data/models/Sponza/glTF/Sponza.gltf"); + model.WriteMaterialsDescriptorSets (descLayoutTextures, + VK.AttachmentType.Color, + VK.AttachmentType.Normal, + VK.AttachmentType.AmbientOcclusion, + VK.AttachmentType.PhysicalProps, + VK.AttachmentType.Emissive); + } + + void init (VkSampleCountFlags samples = VkSampleCountFlags.SampleCount4) { + descriptorPool = new DescriptorPool (dev, 2, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer), + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler) + ); + + descLayoutMatrix = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex|VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + descLayoutTextures = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + dsMats = descriptorPool.Allocate (descLayoutMatrix); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples); + + cfg.Layout = new PipelineLayout (dev, descLayoutMatrix, descLayoutTextures); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf ()), + new VkPushConstantRange (VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf (), 64) + ); + cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), samples); + + cfg.AddVertexBinding (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()); + 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 ()); + } + + public override void UpdateView () { + updateMatrices (); + updateViewRequested = false; + } + public override void Update () { + results = statPool.GetResults (); + vkvgDraw (); + } + #endregion + + #region mouse and keyboard + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F1: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.exposure -= 0.3f; + else + matrices.exposure += 0.3f; + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.gamma -= 0.1f; + else + matrices.gamma += 0.1f; + break; + case Key.F3: + if (camera.Type == CamType.FirstPerson) + camera.Type = CamType.LookAt; + else + camera.Type = CamType.FirstPerson; + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } + #endregion + + protected override void OnResize () { + + vkvgImage?.Dispose (); + vkvgSurf?.Dispose (); + vkvgSurf = new vkvg.Surface (vkvgDev, (int)swapChain.Width, (int)swapChain.Height); + vkvgImage = new Image (dev, new VkImage ((ulong)vkvgSurf.VkImage.ToInt64 ()), VkFormat.B8g8r8a8Unorm, + VkImageUsageFlags.ColorAttachment, (uint)vkvgSurf.Width, (uint)vkvgSurf.Height); + vkvgImage.CreateView (VkImageViewType.ImageView2D, VkImageAspectFlags.Color); + vkvgImage.CreateSampler (VkFilter.Nearest,VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder); + + vkvgImage.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMats, descLayoutMatrix.Bindings[1]); + uboUpdate.Write (dev, vkvgImage.Descriptor); + + updateMatrices (); + + if (frameBuffers!=null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height, + (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + null + } : new Image[] { + null, + null, + swapChain.images[i] + }); + frameBuffers[i].SetName ("main FB " + i); + + } + + buildCommandBuffers (); + } + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + model.Dispose (); + pipeline.Dispose (); + descLayoutMatrix.Dispose (); + descLayoutTextures.Dispose (); + descriptorPool.Dispose (); + + uboMats.Dispose (); + + vkvgSurf?.Dispose (); + vkvgDev.Dispose (); + + timestampQPool.Dispose (); + statPool.Dispose (); + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/deferred/shaders/FullScreenQuad.vert b/samples/deferred/shaders/FullScreenQuad.vert new file mode 100644 index 0000000..826720b --- /dev/null +++ b/samples/deferred/shaders/FullScreenQuad.vert @@ -0,0 +1,10 @@ +#version 450 + +layout (location = 0) out vec2 outUV; + +void main() +{ + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); + //gl_Position = vec4(outUV -1.0f, 0.0f, 1.0f); +} diff --git a/samples/deferred/shaders/GBuffPbr.frag b/samples/deferred/shaders/GBuffPbr.frag new file mode 100644 index 0000000..ca398e6 --- /dev/null +++ b/samples/deferred/shaders/GBuffPbr.frag @@ -0,0 +1,212 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +#define MANUAL_SRGB 0 +#define DEBUG 0 + +layout (constant_id = 0) const float NEAR_PLANE = 0.1f; +layout (constant_id = 1) const float FAR_PLANE = 256.0f; +layout (constant_id = 2) const int MAT_COUNT = 1; + +struct Material { + vec4 baseColorFactor; + vec4 emissiveFactor; + vec4 diffuseFactor; + vec4 specularFactor; + float workflow; + uint tex0; + uint tex1; + float metallicFactor; + float roughnessFactor; + float alphaMask; + float alphaMaskCutoff; + int pad0; +}; +const float M_PI = 3.141592653589793; +const float c_MinRoughness = 0.04; + +const float PBR_WORKFLOW_METALLIC_ROUGHNESS = 1.0; +const float PBR_WORKFLOW_SPECULAR_GLOSINESS = 2.0f; + +const uint MAP_COLOR = 0x1; +const uint MAP_NORMAL = 0x2; +const uint MAP_AO = 0x4; +const uint MAP_METAL = 0x8; +const uint MAP_ROUGHNESS = 0x10; +const uint MAP_METALROUGHNESS = 0x20; +const uint MAP_EMISSIVE = 0x40; + +layout (location = 0) in vec3 inWorldPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV0; +layout (location = 3) in vec2 inUV1; + + +layout (set = 0, binding = 5) uniform UBOMaterials { + Material materials[MAT_COUNT]; +}; + +// Material bindings +layout (set = 2, binding = 0) uniform sampler2D colorMap; +layout (set = 2, binding = 1) uniform sampler2D physicalDescriptorMap; +layout (set = 2, binding = 2) uniform sampler2D normalMap; +layout (set = 2, binding = 3) uniform sampler2D aoMap; +layout (set = 2, binding = 4) uniform sampler2D emissiveMap; + + +layout (push_constant) uniform PushCsts { + layout(offset = 64) + int materialIdx; +}; + + +layout (location = 0) out vec4 outColorRough; +layout (location = 1) out vec4 outEmitMetal; +layout (location = 2) out vec4 outN_AO; +layout (location = 3) out vec4 outPos; + +vec4 SRGBtoLINEAR(vec4 srgbIn) +{ + #ifdef MANUAL_SRGB + #ifdef SRGB_FAST_APPROXIMATION + vec3 linOut = pow(srgbIn.xyz,vec3(2.2)); + #else //SRGB_FAST_APPROXIMATION + vec3 bLess = step(vec3(0.04045),srgbIn.xyz); + vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); + #endif //SRGB_FAST_APPROXIMATION + return vec4(linOut,srgbIn.w);; + #else //MANUAL_SRGB + return srgbIn; + #endif //MANUAL_SRGB +} + +// Find the normal for this fragment, pulling either from a predefined normal map +// or from the interpolated mesh normal and tangent attributes. +vec3 getNormal() +{ + vec3 tangentNormal; + // Perturb normal, see http://www.thetenthplanet.de/archives/1180 + if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(normalMap, inUV0).xyz * 2.0 - 1.0; + else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(normalMap, inUV1).xyz * 2.0 - 1.0; + else + return normalize(inNormal); + + vec3 q1 = dFdx(inWorldPos); + vec3 q2 = dFdy(inWorldPos); + vec2 st1 = dFdx(inUV0); + vec2 st2 = dFdy(inUV0); + + vec3 N = normalize(inNormal); + vec3 T = normalize(q1 * st2.t - q2 * st1.t); + vec3 B = -normalize(cross(N, T)); + mat3 TBN = mat3(T, B, N); + + return normalize(TBN * tangentNormal); +} + +// Gets metallic factor from specular glossiness workflow inputs +float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) { + float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b); + float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b); + if (perceivedSpecular < c_MinRoughness) { + return 0.0; + } + float a = c_MinRoughness; + float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness; + float c = c_MinRoughness - perceivedSpecular; + float D = max(b * b - 4.0 * a * c, 0.0); + return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); +} + +float linearDepth(float depth) +{ + float z = depth * 2.0f - 1.0f; + return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE)); +} + +void main() +{ + float perceptualRoughness; + float metallic; + vec4 baseColor; + vec3 emissive = vec3(0); + + baseColor = materials[materialIdx].baseColorFactor; + + if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) { + perceptualRoughness = materials[materialIdx].roughnessFactor; + metallic = materials[materialIdx].metallicFactor; + // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. + // This layout intentionally reserves the 'r' channel for (optional) occlusion map data + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(physicalDescriptorMap, inUV0).g; + metallic *= texture(physicalDescriptorMap, inUV0).b; + }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(physicalDescriptorMap, inUV1).g; + metallic *= texture(physicalDescriptorMap, inUV1).b; + } + perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); + metallic = clamp(metallic, 0.0, 1.0); + + // The albedo may be defined from a base texture or a flat color + if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(colorMap, inUV0)); + else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(colorMap, inUV1)); + } + + if (materials[materialIdx].alphaMask == 1.0f) { + if (baseColor.a < materials[materialIdx].alphaMaskCutoff) + discard; + } + + if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) { + // Values from specular glossiness workflow are converted to metallic roughness + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV0).a; + else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV1).a; + else + perceptualRoughness = 0.0; + + const float epsilon = 1e-6; + + vec4 diffuse = SRGBtoLINEAR(texture(colorMap, inUV0)); + vec3 specular = SRGBtoLINEAR(texture(physicalDescriptorMap, inUV0)).rgb; + + float maxSpecular = max(max(specular.r, specular.g), specular.b); + + // Convert metallic value from specular glossiness inputs + metallic = convertMetallic(diffuse.rgb, specular, maxSpecular); + + vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb; + vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb; + baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a); + + } + + const float u_OcclusionStrength = 1.0f; + const float u_EmissiveFactor = 1.0f; + float ao = 1.0f; + + if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(emissiveMap, inUV0)).rgb * u_EmissiveFactor; + else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(emissiveMap, inUV1)).rgb * u_EmissiveFactor; + + if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO) + ao = texture(aoMap, inUV0).r; + else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO) + ao = texture(aoMap, inUV1).r; + + vec3 n = getNormal(); + vec3 p = inWorldPos; + + outColorRough = vec4 (baseColor.rgb, perceptualRoughness); + outEmitMetal = vec4 (emissive, metallic); + outN_AO = vec4 (n, ao); + outPos = vec4 (p, linearDepth(gl_FragCoord.z)); +} \ No newline at end of file diff --git a/samples/deferred/shaders/GBuffPbr.vert b/samples/deferred/shaders/GBuffPbr.vert new file mode 100644 index 0000000..7cafe49 --- /dev/null +++ b/samples/deferred/shaders/GBuffPbr.vert @@ -0,0 +1,37 @@ +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV0; +layout (location = 3) in vec2 inUV1; + +layout (set = 0, binding = 0) uniform UBO { + mat4 projection; + mat4 model; + mat4 view; +} ubo; + +layout(push_constant) uniform PushConsts { + mat4 model; +} pc; + +layout (location = 0) out vec3 outWorldPos; +layout (location = 1) out vec3 outNormal; +layout (location = 2) out vec2 outUV0; +layout (location = 3) out vec2 outUV1; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + vec4 locPos = ubo.model * pc.model * vec4(inPos, 1.0); + outNormal = normalize(transpose(inverse(mat3(ubo.model * pc.model))) * inNormal); + + outWorldPos = locPos.xyz; + outUV0 = inUV0; + outUV1 = inUV1; + gl_Position = ubo.projection * ubo.view * vec4(outWorldPos, 1.0); +} diff --git a/samples/deferred/shaders/GBuffPbrTexArray.frag b/samples/deferred/shaders/GBuffPbrTexArray.frag new file mode 100644 index 0000000..dee3471 --- /dev/null +++ b/samples/deferred/shaders/GBuffPbrTexArray.frag @@ -0,0 +1,211 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +#define MANUAL_SRGB 0 +#define DEBUG 0 + +layout (constant_id = 0) const float NEAR_PLANE = 0.1f; +layout (constant_id = 1) const float FAR_PLANE = 256.0f; +layout (constant_id = 2) const int MAT_COUNT = 1; + +struct Material { + vec4 baseColorFactor; + vec4 emissiveFactor; + vec4 diffuseFactor; + vec4 specularFactor; + + float workflow; + uint tex0; + uint tex1; + int baseColorTex; + + int physicalDescTex; + int normalTex; + int occlusionTex; + int emissiveTex; + + float metallicFactor; + float roughnessFactor; + float alphaMask; + float alphaMaskCutoff; +}; +const float M_PI = 3.141592653589793; +const float c_MinRoughness = 0.04; + +const float PBR_WORKFLOW_METALLIC_ROUGHNESS = 1.0; +const float PBR_WORKFLOW_SPECULAR_GLOSINESS = 2.0f; + +const uint MAP_COLOR = 0x1; +const uint MAP_NORMAL = 0x2; +const uint MAP_AO = 0x4; +const uint MAP_METAL = 0x8; +const uint MAP_ROUGHNESS = 0x10; +const uint MAP_METALROUGHNESS = 0x20; +const uint MAP_EMISSIVE = 0x40; + +layout (location = 0) in vec3 inWorldPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV0; +layout (location = 3) in vec2 inUV1; + +layout (set = 0, binding = 5) uniform UBOMaterials { + Material materials[MAT_COUNT]; +}; +layout (set = 0, binding = 7) uniform sampler2DArray texArray; + +layout (push_constant) uniform PushCsts { + layout(offset = 64) + int materialIdx; +}; + + +layout (location = 0) out vec4 outColorRough; +layout (location = 1) out vec4 outEmitMetal; +layout (location = 2) out vec4 outN_AO; +layout (location = 3) out vec4 outPos; + +vec4 SRGBtoLINEAR(vec4 srgbIn) +{ + #ifdef MANUAL_SRGB + #ifdef SRGB_FAST_APPROXIMATION + vec3 linOut = pow(srgbIn.xyz,vec3(2.2)); + #else //SRGB_FAST_APPROXIMATION + vec3 bLess = step(vec3(0.04045),srgbIn.xyz); + vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); + #endif //SRGB_FAST_APPROXIMATION + return vec4(linOut,srgbIn.w);; + #else //MANUAL_SRGB + return srgbIn; + #endif //MANUAL_SRGB +} + +// Find the normal for this fragment, pulling either from a predefined normal map +// or from the interpolated mesh normal and tangent attributes. +vec3 getNormal() +{ + vec3 tangentNormal; + // Perturb normal, see http://www.thetenthplanet.de/archives/1180 + if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(texArray, vec3(inUV0, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0; + else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(texArray, vec3(inUV1, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0; + else + return normalize(inNormal); + + vec3 q1 = dFdx(inWorldPos); + vec3 q2 = dFdy(inWorldPos); + vec2 st1 = dFdx(inUV0); + vec2 st2 = dFdy(inUV0); + + vec3 N = normalize(inNormal); + vec3 T = normalize(q1 * st2.t - q2 * st1.t); + vec3 B = -normalize(cross(N, T)); + mat3 TBN = mat3(T, B, N); + + return normalize(TBN * tangentNormal); +} + +// Gets metallic factor from specular glossiness workflow inputs +float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) { + float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b); + float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b); + if (perceivedSpecular < c_MinRoughness) { + return 0.0; + } + float a = c_MinRoughness; + float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness; + float c = c_MinRoughness - perceivedSpecular; + float D = max(b * b - 4.0 * a * c, 0.0); + return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); +} + +float linearDepth(float depth) +{ + float z = depth * 2.0f - 1.0f; + return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE)); +} + +void main() +{ + float perceptualRoughness; + float metallic; + vec4 baseColor; + vec3 emissive = vec3(0); + + baseColor = materials[materialIdx].baseColorFactor; + + if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) { + perceptualRoughness = materials[materialIdx].roughnessFactor; + metallic = materials[materialIdx].metallicFactor; + // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. + // This layout intentionally reserves the 'r' channel for (optional) occlusion map data + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).g; + metallic *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).b; + }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).g; + metallic *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).b; + } + perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); + metallic = clamp(metallic, 0.0, 1.0); + + // The albedo may be defined from a base texture or a flat color + if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex))); + else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].baseColorTex))); + } + + if (materials[materialIdx].alphaMask == 1.0f) { + if (baseColor.a < materials[materialIdx].alphaMaskCutoff) + discard; + } + + if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) { + // Values from specular glossiness workflow are converted to metallic roughness + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).a; + else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).a; + else + perceptualRoughness = 0.0; + + const float epsilon = 1e-6; + + vec4 diffuse = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex))); + vec3 specular = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex))).rgb; + + float maxSpecular = max(max(specular.r, specular.g), specular.b); + + // Convert metallic value from specular glossiness inputs + metallic = convertMetallic(diffuse.rgb, specular, maxSpecular); + + vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb; + vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb; + baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a); + + } + + const float u_OcclusionStrength = 1.0f; + const float u_EmissiveFactor = 1.0f; + float ao = 1.0f; + + if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor; + else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor; + + if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO) + ao = texture(texArray, vec3(inUV0, materials[materialIdx].occlusionTex)).r; + else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO) + ao = texture(texArray, vec3(inUV1, materials[materialIdx].occlusionTex)).r; + + vec3 n = getNormal(); + vec3 p = inWorldPos; + + outColorRough = vec4 (baseColor.rgb, perceptualRoughness); + outEmitMetal = vec4 (emissive, metallic); + outN_AO = vec4 (n, ao); + outPos = vec4 (p, linearDepth(gl_FragCoord.z)); +} \ No newline at end of file diff --git a/samples/deferred/shaders/bloom.comp b/samples/deferred/shaders/bloom.comp new file mode 100644 index 0000000..e2b1318 --- /dev/null +++ b/samples/deferred/shaders/bloom.comp @@ -0,0 +1,60 @@ +#version 450 + +layout (local_size_x = 16, local_size_y = 16) in; +layout (binding = 0, rgba16f) uniform readonly image2D inputImage; +layout (binding = 1, rgba16f) uniform image2D resultImage; + +layout(push_constant) uniform PushConsts { + vec2 texSize; + int dir; + float scale; + float strength; +} pc; + +void main(void) +{ + // From the OpenGL Super bible + const float weights[] = float[](0.0024499299678342, + 0.0043538453346397, + 0.0073599963704157, + 0.0118349786570722, + 0.0181026699707781, + 0.0263392293891488, + 0.0364543006660986, + 0.0479932050577658, + 0.0601029809166942, + 0.0715974486241365, + 0.0811305381519717, + 0.0874493212267511, + 0.0896631113333857, + 0.0874493212267511, + 0.0811305381519717, + 0.0715974486241365, + 0.0601029809166942, + 0.0479932050577658, + 0.0364543006660986, + 0.0263392293891488, + 0.0181026699707781, + 0.0118349786570722, + 0.0073599963704157, + 0.0043538453346397, + 0.0024499299678342); + + + float ar = 1.0; + vec4 color = vec4(0.0); + + // Aspect ratio for vertical blur pass + if (pc.dir == 1) + ar = pc.texSize.y / pc.texSize.x; + + vec2 P = gl_GlobalInvocationID.yx - vec2(0, (weights.length() >> 1) * ar * pc.scale); + + for (int i = 0; i < weights.length(); i++) + { + vec2 dv = vec2(0.0, i * pc.scale) * ar; + color += imageLoad (inputImage, ivec2(P + dv)) * weights[i] * pc.strength; + } + + imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), color); +} \ No newline at end of file diff --git a/samples/deferred/shaders/bloom.frag b/samples/deferred/shaders/bloom.frag new file mode 100644 index 0000000..c3bf402 --- /dev/null +++ b/samples/deferred/shaders/bloom.frag @@ -0,0 +1,63 @@ +#version 450 + +layout (binding = 0) uniform sampler2D samplerColor0; +layout (binding = 1) uniform sampler2D samplerColor1; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outColor; + +layout (constant_id = 0) const int dir = 0; + +void main(void) +{ + // From the OpenGL Super bible + const float weights[] = float[](0.0024499299678342, + 0.0043538453346397, + 0.0073599963704157, + 0.0118349786570722, + 0.0181026699707781, + 0.0263392293891488, + 0.0364543006660986, + 0.0479932050577658, + 0.0601029809166942, + 0.0715974486241365, + 0.0811305381519717, + 0.0874493212267511, + 0.0896631113333857, + 0.0874493212267511, + 0.0811305381519717, + 0.0715974486241365, + 0.0601029809166942, + 0.0479932050577658, + 0.0364543006660986, + 0.0263392293891488, + 0.0181026699707781, + 0.0118349786570722, + 0.0073599963704157, + 0.0043538453346397, + 0.0024499299678342); + + + const float blurScale = 0.003; + const float blurStrength = 1.0; + + float ar = 1.0; + // Aspect ratio for vertical blur pass + if (dir == 1) + { + vec2 ts = textureSize(samplerColor1, 0); + ar = ts.y / ts.x; + } + + vec2 P = inUV.yx - vec2(0, (weights.length() >> 1) * ar * blurScale); + + vec4 color = vec4(0.0); + for (int i = 0; i < weights.length(); i++) + { + vec2 dv = vec2(0.0, i * blurScale) * ar; + color += texture(samplerColor1, P + dv) * weights[i] * blurStrength; + } + + outColor = color; +} \ No newline at end of file diff --git a/samples/deferred/shaders/compose.frag b/samples/deferred/shaders/compose.frag new file mode 100644 index 0000000..81312bd --- /dev/null +++ b/samples/deferred/shaders/compose.frag @@ -0,0 +1,230 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (set = 0, binding = 0) uniform UBO { + mat4 projection; + mat4 model; + mat4 view; + vec4 camPos; + float exposure; + float gamma; + float prefilteredCubeMipLevels; + float scaleIBLAmbient; +} ubo; + +layout (constant_id = 0) const uint NUM_LIGHTS = 1; + +struct Light { + vec4 position; + vec4 color; + mat4 mvp; +}; + +layout (set = 0, binding = 4) uniform UBOLights { + Light lights[NUM_LIGHTS]; +}; + +const float M_PI = 3.141592653589793; +const float c_MinRoughness = 0.04; + +layout (input_attachment_index = 0, set = 1, binding = 0) uniform subpassInputMS samplerColorRough; +layout (input_attachment_index = 1, set = 1, binding = 1) uniform subpassInputMS samplerEmitMetal; +layout (input_attachment_index = 2, set = 1, binding = 2) uniform subpassInputMS samplerN_AO; +layout (input_attachment_index = 3, set = 1, binding = 3) uniform subpassInputMS samplerPos; + +layout (set = 0, binding = 1) uniform samplerCube samplerIrradiance; +layout (set = 0, binding = 2) uniform samplerCube prefilteredMap; +layout (set = 0, binding = 3) uniform sampler2D samplerBRDFLUT; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outColor; + +// Encapsulate the various inputs used by the various functions in the shading equation +// We store values in this struct to simplify the integration of alternative implementations +// of the shading terms, outlined in the Readme.MD Appendix. +struct PBRInfo +{ + float NdotL; // cos angle between normal and light direction + float NdotV; // cos angle between normal and view direction + float NdotH; // cos angle between normal and half vector + float LdotH; // cos angle between light direction and half vector + float VdotH; // cos angle between view direction and half vector + float perceptualRoughness; // roughness value, as authored by the model creator (input to shader) + float metalness; // metallic value at the surface + vec3 reflectance0; // full reflectance color (normal incidence angle) + vec3 reflectance90; // reflectance color at grazing angle + float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2]) + vec3 diffuseColor; // color contribution from diffuse lighting + vec3 specularColor; // color contribution from specular lighting +}; + +// Calculation of the lighting contribution from an optional Image Based Light source. +// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1]. +// See our README.md on Environment Maps [3] for additional discussion. +vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection) +{ + float lod = (pbrInputs.perceptualRoughness * ubo.prefilteredCubeMipLevels); + // retrieve a scale and bias to F0. See [1], Figure 3 + vec3 brdf = (texture(samplerBRDFLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb; + vec3 diffuseLight = texture(samplerIrradiance, reflection).rgb; + + vec3 specularLight = textureLod(prefilteredMap, reflection, lod).rgb; + + vec3 diffuse = diffuseLight * pbrInputs.diffuseColor; + vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y); + + // For presentation, this allows us to disable IBL terms + // For presentation, this allows us to disable IBL terms + diffuse *= ubo.scaleIBLAmbient; + specular *= ubo.scaleIBLAmbient; + + return diffuse + specular; +} + +// Basic Lambertian diffuse +// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog +// See also [1], Equation 1 +vec3 diffuse(PBRInfo pbrInputs) +{ + return pbrInputs.diffuseColor / M_PI; +} + +// The following equation models the Fresnel reflectance term of the spec equation (aka F()) +// Implementation of fresnel from [4], Equation 15 +vec3 specularReflection(PBRInfo pbrInputs) +{ + return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0); +} + +// This calculates the specular geometric attenuation (aka G()), +// where rougher material will reflect less light back to the viewer. +// This implementation is based on [1] Equation 4, and we adopt their modifications to +// alphaRoughness as input as originally proposed in [2]. +float geometricOcclusion(PBRInfo pbrInputs) +{ + float NdotL = pbrInputs.NdotL; + float NdotV = pbrInputs.NdotV; + float r = pbrInputs.alphaRoughness; + + float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL))); + float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV))); + return attenuationL * attenuationV; +} + +// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D()) +// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz +// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3. +float microfacetDistribution(PBRInfo pbrInputs) +{ + float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness; + float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0; + return roughnessSq / (M_PI * f * f); +} + +// Gets metallic factor from specular glossiness workflow inputs +float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) { + float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b); + float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b); + if (perceivedSpecular < c_MinRoughness) { + return 0.0; + } + float a = c_MinRoughness; + float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness; + float c = c_MinRoughness - perceivedSpecular; + float D = max(b * b - 4.0 * a * c, 0.0); + return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); +} + +void main() +{ + if (subpassLoad(samplerPos, gl_SampleID).a == 1.0f) + discard; + + float perceptualRoughness = subpassLoad(samplerColorRough, gl_SampleID).a; + float metallic = subpassLoad(samplerEmitMetal, gl_SampleID).a; + vec3 diffuseColor; + vec4 baseColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).rgb, 1); + vec3 emissive = subpassLoad (samplerEmitMetal, gl_SampleID).rgb; + + vec3 f0 = vec3(0.04); + + diffuseColor = baseColor.rgb * (vec3(1.0) - f0); + diffuseColor *= 1.0 - metallic; + + float alphaRoughness = perceptualRoughness * perceptualRoughness; + + vec3 specularColor = mix(f0, baseColor.rgb, metallic); + + // Compute reflectance. + float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); + + // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. + // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. + float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); + vec3 specularEnvironmentR0 = specularColor.rgb; + vec3 specularEnvironmentR90 = vec3(1.0) * reflectance90; + + vec3 n = subpassLoad(samplerN_AO, gl_SampleID).rgb; + vec3 pos = subpassLoad(samplerPos, gl_SampleID).rgb; + vec3 v = normalize(ubo.camPos.xyz - pos); // Vector from surface point to camera + + vec3 colors = vec3(0); + vec3 lightTarget = vec3(0); + + for (int i=0; i -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> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + float rdi = float(bits) * 2.3283064365386963e-10; + return vec2(float(i) /float(N), rdi); +} + +// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf +vec3 importanceSample_GGX(vec2 Xi, float roughness, vec3 normal) +{ + // Maps a 2D point to a hemisphere with spread based on roughness + float alpha = roughness * roughness; + float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + vec3 H = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); + + // Tangent space + vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 tangentX = normalize(cross(up, normal)); + vec3 tangentY = normalize(cross(normal, tangentX)); + + // Convert to world Space + return normalize(tangentX * H.x + tangentY * H.y + normal * H.z); +} + +// Geometric Shadowing function +float G_SchlicksmithGGX(float dotNL, float dotNV, float roughness) +{ + float k = (roughness * roughness) / 2.0; + float GL = dotNL / (dotNL * (1.0 - k) + k); + float GV = dotNV / (dotNV * (1.0 - k) + k); + return GL * GV; +} + +vec2 BRDF(float NoV, float roughness) +{ + // Normal always points along z-axis for the 2D lookup + const vec3 N = vec3(0.0, 0.0, 1.0); + vec3 V = vec3(sqrt(1.0 - NoV*NoV), 0.0, NoV); + + vec2 LUT = vec2(0.0); + for(uint i = 0u; i < NUM_SAMPLES; i++) { + vec2 Xi = hammersley2d(i, NUM_SAMPLES); + vec3 H = importanceSample_GGX(Xi, roughness, N); + vec3 L = 2.0 * dot(V, H) * H - V; + + float dotNL = max(dot(N, L), 0.0); + float dotNV = max(dot(N, V), 0.0); + float dotVH = max(dot(V, H), 0.0); + float dotNH = max(dot(H, N), 0.0); + + if (dotNL > 0.0) { + float G = G_SchlicksmithGGX(dotNL, dotNV, roughness); + float G_Vis = (G * dotVH) / (dotNH * dotNV); + float Fc = pow(1.0 - dotVH, 5.0); + LUT += vec2((1.0 - Fc) * G_Vis, Fc * G_Vis); + } + } + return LUT / float(NUM_SAMPLES); +} + +void main() +{ + outColor = vec4(BRDF(inUV.s, 1.0-inUV.t), 0.0, 1.0); +} \ No newline at end of file diff --git a/samples/deferred/shaders/genbrdflut.vert b/samples/deferred/shaders/genbrdflut.vert new file mode 100644 index 0000000..f3dd233 --- /dev/null +++ b/samples/deferred/shaders/genbrdflut.vert @@ -0,0 +1,9 @@ +#version 450 + +layout (location = 0) out vec2 outUV; + +void main() +{ + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f - 1.0f, 0.0f, 1.0f); +} \ No newline at end of file diff --git a/samples/deferred/shaders/irradiancecube.frag b/samples/deferred/shaders/irradiancecube.frag new file mode 100644 index 0000000..340d679 --- /dev/null +++ b/samples/deferred/shaders/irradiancecube.frag @@ -0,0 +1,37 @@ +// Generates an irradiance cube from an environment map using convolution + +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 0) out vec4 outColor; +layout (binding = 0) uniform samplerCube samplerEnv; + +layout(push_constant) uniform PushConsts { + layout (offset = 64) float deltaPhi; + layout (offset = 68) float deltaTheta; +} consts; + +#define PI 3.1415926535897932384626433832795 + +void main() +{ + vec3 N = normalize(inPos); + vec3 up = vec3(0.0, 1.0, 0.0); + vec3 right = normalize(cross(up, N)); + up = cross(N, right); + + const float TWO_PI = PI * 2.0; + const float HALF_PI = PI * 0.5; + + vec3 color = vec3(0.0); + uint sampleCount = 0u; + for (float phi = 0.0; phi < TWO_PI; phi += consts.deltaPhi) { + for (float theta = 0.0; theta < HALF_PI; theta += consts.deltaTheta) { + vec3 tempVec = cos(phi) * right + sin(phi) * up; + vec3 sampleVector = cos(theta) * N + sin(theta) * tempVec; + color += texture(samplerEnv, sampleVector).rgb * cos(theta) * sin(theta); + sampleCount++; + } + } + outColor = vec4(PI * color / float(sampleCount), 1.0);//texture(samplerEnv, inPos).rgba; +} diff --git a/samples/deferred/shaders/prefilterenvmap.frag b/samples/deferred/shaders/prefilterenvmap.frag new file mode 100644 index 0000000..ae1212e --- /dev/null +++ b/samples/deferred/shaders/prefilterenvmap.frag @@ -0,0 +1,105 @@ +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 0) out vec4 outColor; + +layout (binding = 0) uniform samplerCube samplerEnv; + +layout(push_constant) uniform PushConsts { + layout (offset = 64) float roughness; + layout (offset = 68) uint numSamples; +} consts; + +const float PI = 3.1415926536; + +// Based omn http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/ +float random(vec2 co) +{ + float a = 12.9898; + float b = 78.233; + float c = 43758.5453; + float dt= dot(co.xy ,vec2(a,b)); + float sn= mod(dt,3.14); + return fract(sin(sn) * c); +} + +vec2 hammersley2d(uint i, uint N) +{ + // Radical inverse based on http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html + uint bits = (i << 16u) | (i >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + float rdi = float(bits) * 2.3283064365386963e-10; + return vec2(float(i) /float(N), rdi); +} + +// Based on http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_slides.pdf +vec3 importanceSample_GGX(vec2 Xi, float roughness, vec3 normal) +{ + // Maps a 2D point to a hemisphere with spread based on roughness + float alpha = roughness * roughness; + float phi = 2.0 * PI * Xi.x + random(normal.xz) * 0.1; + float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha*alpha - 1.0) * Xi.y)); + float sinTheta = sqrt(1.0 - cosTheta * cosTheta); + vec3 H = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta); + + // Tangent space + vec3 up = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 tangentX = normalize(cross(up, normal)); + vec3 tangentY = normalize(cross(normal, tangentX)); + + // Convert to world Space + return normalize(tangentX * H.x + tangentY * H.y + normal * H.z); +} + +// Normal Distribution function +float D_GGX(float dotNH, float roughness) +{ + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float denom = dotNH * dotNH * (alpha2 - 1.0) + 1.0; + return (alpha2)/(PI * denom*denom); +} + +vec3 prefilterEnvMap(vec3 R, float roughness) +{ + vec3 N = R; + vec3 V = R; + vec3 color = vec3(0.0); + float totalWeight = 0.0; + float envMapDim = float(textureSize(samplerEnv, 0).s); + for(uint i = 0u; i < consts.numSamples; i++) { + vec2 Xi = hammersley2d(i, consts.numSamples); + vec3 H = importanceSample_GGX(Xi, roughness, N); + vec3 L = 2.0 * dot(V, H) * H - V; + float dotNL = clamp(dot(N, L), 0.0, 1.0); + if(dotNL > 0.0) { + // Filtering based on https://placeholderart.wordpress.com/2015/07/28/implementation-notes-runtime-environment-map-filtering-for-image-based-lighting/ + + float dotNH = clamp(dot(N, H), 0.0, 1.0); + float dotVH = clamp(dot(V, H), 0.0, 1.0); + + // Probability Distribution Function + float pdf = D_GGX(dotNH, roughness) * dotNH / (4.0 * dotVH) + 0.0001; + // Slid angle of current smple + float omegaS = 1.0 / (float(consts.numSamples) * pdf); + // Solid angle of 1 pixel across all cube faces + float omegaP = 4.0 * PI / (6.0 * envMapDim * envMapDim); + // Biased (+1.0) mip level for better result + float mipLevel = roughness == 0.0 ? 0.0 : max(0.5 * log2(omegaS / omegaP) + 1.0, 0.0f); + color += textureLod(samplerEnv, L, mipLevel).rgb * dotNL; + totalWeight += dotNL; + + } + } + return (color / totalWeight); +} + + +void main() +{ + vec3 N = normalize(inPos); + outColor = vec4(prefilterEnvMap(N, consts.roughness), 1.0); +} diff --git a/samples/deferred/shaders/shadow.geom b/samples/deferred/shaders/shadow.geom new file mode 100644 index 0000000..9fd322c --- /dev/null +++ b/samples/deferred/shaders/shadow.geom @@ -0,0 +1,34 @@ +#version 420 + +#define NUM_LIGHTS 2 + +layout (triangles, invocations = NUM_LIGHTS) in; +layout (triangle_strip, max_vertices = 3) out; + +struct Light { + vec4 position; + vec4 color; + mat4 mvp; +}; +layout (set = 0, binding = 0) uniform UBO { + mat4 projection; + mat4 model; + mat4 view; +}; +layout (set = 0, binding = 1) uniform UBOLights { + Light lights[NUM_LIGHTS]; +}; + +//layout (location = 0) in int inInstanceIndex[]; + + +void main() +{ + for (int i = 0; i < gl_in.length(); i++) + { + gl_Layer = gl_InvocationID; + gl_Position = lights[gl_InvocationID].mvp * model * gl_in[i].gl_Position; + EmitVertex(); + } + EndPrimitive(); +} \ No newline at end of file diff --git a/samples/deferred/shaders/shadow.vert b/samples/deferred/shaders/shadow.vert new file mode 100644 index 0000000..b0bd6c0 --- /dev/null +++ b/samples/deferred/shaders/shadow.vert @@ -0,0 +1,12 @@ +#version 450 + +layout (location = 0) in vec3 inPos; + +layout(push_constant) uniform PushConsts { + mat4 model; +} pc; + +void main() +{ + gl_Position = pc.model * vec4(inPos,1); +} \ No newline at end of file diff --git a/samples/deferred/shaders/show_gbuff.frag b/samples/deferred/shaders/show_gbuff.frag new file mode 100644 index 0000000..7859e36 --- /dev/null +++ b/samples/deferred/shaders/show_gbuff.frag @@ -0,0 +1,92 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (input_attachment_index = 0, set = 1, binding = 0) uniform subpassInputMS samplerColorRough; +layout (input_attachment_index = 1, set = 1, binding = 1) uniform subpassInputMS samplerEmitMetal; +layout (input_attachment_index = 2, set = 1, binding = 2) uniform subpassInputMS samplerN_AO; +layout (input_attachment_index = 3, set = 1, binding = 3) uniform subpassInputMS samplerPos; + +layout (set = 0, binding = 1) uniform samplerCube samplerIrradiance; +layout (set = 0, binding = 2) uniform samplerCube prefilteredMap; +layout (set = 0, binding = 3) uniform sampler2D samplerBRDFLUT; +layout (set = 0, binding = 6) uniform sampler2DArray samplerShadowMap; + +layout (push_constant) uniform PushCsts { + layout(offset = 64) + int imgIdx; +}; + +layout (location = 0) in vec2 inUV; +layout (location = 0) out vec4 outColor; + +const uint color = 0; +const uint normal = 1; +const uint pos = 2; +const uint occlusion = 3; +const uint emissive = 4; +const uint metallic = 5; +const uint roughness = 6; +const uint depth = 7; +const uint prefill = 8; +const uint irradiance = 9; +const uint shadowMap = 10; + +vec4 sampleCubeMap (samplerCube sc, uint face, uint lod) { + vec2 uv = 2.0 * inUV - vec2(1.0); + switch (face) { + case 0: + return vec4 (textureLod (sc, vec3(1, uv.t, uv.s), lod).rgb, 1); + case 1: + return vec4 (textureLod (sc, vec3(-1, uv.t, uv.s), lod).rgb, 1); + case 2: + return vec4 (textureLod (sc, vec3(uv.s, 1, -uv.t), lod).rgb, 1); + case 3: + return vec4 (textureLod (sc, vec3(uv.s, -1, uv.t), lod).rgb, 1); + case 4: + return vec4 (textureLod (sc, vec3(uv, 1), lod).rgb, 1); + case 5: + return vec4 (textureLod (sc, vec3(-uv.s, uv.t, -1), lod).rgb, 1); + } +} + +void main() +{ + uint imgNum = bitfieldExtract (imgIdx, 0, 8); + switch (imgNum) { + case color: + outColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).rgb, 1); + break; + case normal: + outColor = vec4(subpassLoad(samplerN_AO, gl_SampleID).rgb, 1); + break; + case pos: + outColor = vec4(subpassLoad(samplerPos, gl_SampleID).rgb, 1); + break; + case occlusion: + outColor = vec4(subpassLoad(samplerN_AO, gl_SampleID).aaa, 1); + break; + case emissive: + outColor = vec4(subpassLoad(samplerEmitMetal, gl_SampleID).rgb, 1); + break; + case metallic: + outColor = vec4(subpassLoad(samplerEmitMetal, gl_SampleID).aaa, 1); + break; + case roughness: + outColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).aaa, 1); + break; + case depth: + outColor = vec4(subpassLoad(samplerPos, gl_SampleID).aaa, 1); + break; + case shadowMap: + vec3 d = texture(samplerShadowMap, vec3(inUV, bitfieldExtract (imgIdx, 8, 8))).rrr; + outColor = vec4(d*d*d, 1); + break; + default: + if (imgNum == prefill) + outColor = sampleCubeMap (prefilteredMap, bitfieldExtract (imgIdx, 8, 8), bitfieldExtract (imgIdx, 16, 8)); + else if (imgNum == irradiance) + outColor = sampleCubeMap (samplerIrradiance, bitfieldExtract (imgIdx, 8, 8), bitfieldExtract (imgIdx, 16, 8)); + break; + } +} \ No newline at end of file diff --git a/samples/deferred/shaders/simpletexture.frag b/samples/deferred/shaders/simpletexture.frag new file mode 100644 index 0000000..a081876 --- /dev/null +++ b/samples/deferred/shaders/simpletexture.frag @@ -0,0 +1,15 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (set = 0, binding = 0) uniform sampler2D samplerColor; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = texture(samplerColor, inUV); +} diff --git a/samples/deferred/shaders/skybox.frag b/samples/deferred/shaders/skybox.frag new file mode 100644 index 0000000..e505ef1 --- /dev/null +++ b/samples/deferred/shaders/skybox.frag @@ -0,0 +1,12 @@ +#version 450 + +layout (binding = 2) uniform samplerCube samplerEnv; + +layout (set = 0, location = 0) in vec3 inUVW; + +layout (set = 0, location = 0) out vec4 outColor; + +void main() +{ + outColor = vec4(textureLod(samplerEnv, inUVW, 1.5).rgb, 1.0); +} \ No newline at end of file diff --git a/samples/deferred/shaders/skybox.vert b/samples/deferred/shaders/skybox.vert new file mode 100644 index 0000000..22e285e --- /dev/null +++ b/samples/deferred/shaders/skybox.vert @@ -0,0 +1,27 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (location = 0) in vec3 inPos; + +layout (binding = 0) uniform UBO +{ + mat4 projection; + mat4 model; + mat4 view; +} ubo; + +layout (location = 0) out vec3 outUVW; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + outUVW = inPos; + outUVW.y = -outUVW.y; + gl_Position = ubo.projection * mat4(mat3(ubo.view)) * vec4(inPos, 1.0); +} diff --git a/samples/deferred/shaders/tone_mapping.frag b/samples/deferred/shaders/tone_mapping.frag new file mode 100644 index 0000000..6a1a8d4 --- /dev/null +++ b/samples/deferred/shaders/tone_mapping.frag @@ -0,0 +1,70 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout(push_constant) uniform PushConsts { + float exposure; + float gamma; + float debug; +} pc; + +layout (set = 0, binding = 0) uniform sampler2D samplerHDR; +layout (set = 0, binding = 1) uniform sampler2D bloom; + +layout (location = 0) in vec2 inUV; +layout (location = 0) out vec4 outColor; + +vec3 Uncharted2Tonemap(vec3 color) +{ + const float A = 0.15; + const float B = 0.50; + const float C = 0.10; + const float D = 0.20; + const float E = 0.02; + const float F = 0.30; + const float W = 11.2; + return ((color*(A*color+C*B)+D*E)/(color*(A*color+B)+D*F))-E/F; +} + +vec3 tonemap(vec3 color) +{ + vec3 outcol = Uncharted2Tonemap(color.rgb * pc.exposure); + outcol = outcol * (1.0f / Uncharted2Tonemap(vec3(11.2f))); + return pow(outcol, vec3(1.0f / pc.gamma)); +} + +vec3 SRGBtoLINEAR(vec3 srgbIn) +{ + #ifdef MANUAL_SRGB + #ifdef SRGB_FAST_APPROXIMATION + return pow(srgbIn.xyz,vec3(2.2)); + #else //SRGB_FAST_APPROXIMATION + vec3 bLess = step(vec3(0.04045),srgbIn.xyz); + return linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess ); + #endif //SRGB_FAST_APPROXIMATION + #else //MANUAL_SRGB + return srgbIn; + #endif //MANUAL_SRGB +} + +void main() +{ + if (pc.debug < 0.0f) { + //vec4 hdrColor = texelFetch (samplerHDR, ivec2(inUV), gl_SampleID); + vec4 hdrColor = texture (samplerHDR, inUV); + //vec4 c = texture (bloom, inUV); + //float lum = (0.299*c.r + 0.587*c.g + 0.114*c.b); + //if (lum>1.0) + // hdrColor.rgb += c.rgb * 0.05; + outColor = vec4(SRGBtoLINEAR(tonemap(hdrColor.rgb)), hdrColor.a); + }else + outColor = texture (bloom, inUV); + + + /* + outColor = vec4(SRGBtoLINEAR(tonemap(hdrColor.rgb)), hdrColor.a);;*/ + +/* vec3 mapped = vec3(1.0) - exp(-hdrColor.rgb * pc.exposure); + mapped = pow(mapped, vec3(1.0 / pc.gamma)); + outColor = vec4(mapped, hdrColor.a);*/ +} \ No newline at end of file diff --git a/samples/deferred/shadowMapRenderer.cs b/samples/deferred/shadowMapRenderer.cs new file mode 100644 index 0000000..a4684ec --- /dev/null +++ b/samples/deferred/shadowMapRenderer.cs @@ -0,0 +1,157 @@ +/*shadow mapping greatly inspired from: +* Vulkan Example - Deferred shading with shadows from multiple light sources using geometry shader instancing +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using CVKL; +using CVKL.glTF; +using VK; +using static deferred.DeferredPbrRenderer; + +namespace deferred { + public class ShadowMapRenderer : IDisposable { + Device dev; + Queue gQueue; + + public static uint SHADOWMAP_SIZE = 4096; + public static VkFormat SHADOWMAP_FORMAT = VkFormat.D32SfloatS8Uint; + public static VkSampleCountFlags SHADOWMAP_NUM_SAMPLES = VkSampleCountFlags.SampleCount1; + public bool updateShadowMap = true; + + public float depthBiasConstant = 1.5f; + public float depthBiasSlope = 1.75f; + float lightFOV = Utils.DegreesToRadians (60); + float lightFarPlane; + + + + RenderPass shadowPass; + Framebuffer fbShadowMap; + public Image shadowMap { get; private set; } + Pipeline shadowPipeline; + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutShadow; + DescriptorSet dsShadow; + DeferredPbrRenderer renderer; + + public ShadowMapRenderer (Queue gQueue, DeferredPbrRenderer renderer, float farPlane = 16f) { + this.lightFarPlane = farPlane; + this.gQueue = gQueue; + this.dev = gQueue.Dev; + this.renderer = renderer; + + descriptorPool = new DescriptorPool (dev, 1, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2) + ); + + init (); + } + + void init () { + + //Shadow map renderpass + shadowPass = new RenderPass (dev, VkSampleCountFlags.SampleCount1); + shadowPass.AddAttachment (SHADOWMAP_FORMAT, VkImageLayout.DepthStencilReadOnlyOptimal, SHADOWMAP_NUM_SAMPLES); + shadowPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) }); + + SubPass subpass0 = new SubPass (); + subpass0.SetDepthReference (0); + shadowPass.AddSubpass (subpass0); + + shadowPass.AddDependency (Vk.SubpassExternal, 0, + VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.EarlyFragmentTests, + VkAccessFlags.ShaderRead, VkAccessFlags.DepthStencilAttachmentWrite); + shadowPass.AddDependency (0, Vk.SubpassExternal, + VkPipelineStageFlags.LateFragmentTests, VkPipelineStageFlags.FragmentShader, + VkAccessFlags.DepthStencilAttachmentWrite, VkAccessFlags.ShaderRead); + + descLayoutShadow = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Geometry, VkDescriptorType.UniformBuffer),//matrices + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Geometry, VkDescriptorType.UniformBuffer));//lights + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList); + cfg.rasterizationState.cullMode = VkCullModeFlags.Back; + cfg.rasterizationState.depthBiasEnable = true; + cfg.dynamicStates.Add (VkDynamicState.DepthBias); + + cfg.RenderPass = shadowPass; + + cfg.Layout = new PipelineLayout (dev, descLayoutShadow); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex|VkShaderStageFlags.Geometry, (uint)Marshal.SizeOf ()) + ); + + cfg.AddVertexBinding (0); + cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat); + + cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.shadow.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Geometry, "#deferred.shadow.geom.spv"); + + shadowPipeline = new GraphicPipeline (cfg); + + //shadow map image + shadowMap = new Image (dev, SHADOWMAP_FORMAT, VkImageUsageFlags.DepthStencilAttachment | VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal, SHADOWMAP_SIZE, SHADOWMAP_SIZE, + VkImageType.Image2D, SHADOWMAP_NUM_SAMPLES, VkImageTiling.Optimal, 1, (uint)renderer.lights.Length); + shadowMap.CreateView (VkImageViewType.ImageView2DArray, VkImageAspectFlags.Depth, shadowMap.CreateInfo.arrayLayers); + shadowMap.CreateSampler (VkSamplerAddressMode.ClampToBorder); + shadowMap.Descriptor.imageLayout = VkImageLayout.DepthStencilReadOnlyOptimal; + + fbShadowMap = new Framebuffer (shadowPass, SHADOWMAP_SIZE, SHADOWMAP_SIZE, (uint)renderer.lights.Length); + fbShadowMap.attachments.Add (shadowMap); + fbShadowMap.Activate (); + + dsShadow = descriptorPool.Allocate (descLayoutShadow); + + DescriptorSetWrites dsWrite = new DescriptorSetWrites (dsShadow, descLayoutShadow); + dsWrite.Write (dev, renderer.uboMatrices.Descriptor, renderer.uboLights.Descriptor); + } + + public void update_light_matrices () { + Matrix4x4 proj = Matrix4x4.CreatePerspectiveFieldOfView (lightFOV, 1, 0.1f, lightFarPlane); + for (int i = 0; i < renderer.lights.Length; i++) { + Matrix4x4 view = Matrix4x4.CreateLookAt (renderer.lights[i].position.ToVector3 (), Vector3.Zero, Vector3.UnitY); + renderer.lights[i].mvp = view * proj; + } + renderer.uboLights.Update (renderer.lights); + dev.WaitIdle (); + } + + public void update_shadow_map (CommandPool cmdPool) { + update_light_matrices (); + + CommandBuffer cmd = cmdPool.AllocateAndStart (); + + shadowPass.Begin (cmd, fbShadowMap); + + cmd.SetViewport (SHADOWMAP_SIZE, SHADOWMAP_SIZE); + cmd.SetScissor (SHADOWMAP_SIZE, SHADOWMAP_SIZE); + + cmd.BindDescriptorSet (shadowPipeline.Layout, dsShadow); + + Vk.vkCmdSetDepthBias (cmd.Handle, depthBiasConstant, 0.0f, depthBiasSlope); + + shadowPipeline.Bind (cmd); + + if (renderer.model != null) { + renderer.model.Bind (cmd); + renderer.model.DrawAll (cmd, shadowPipeline.Layout, true); + } + + shadowPass.End (cmd); + + gQueue.EndSubmitAndWait (cmd); + updateShadowMap = false; + } + + + public void Dispose () { + shadowPipeline?.Dispose (); + fbShadowMap?.Dispose (); + shadowMap?.Dispose (); + descriptorPool?.Dispose (); + } + } +} diff --git a/samples/deferred/ui/debug.crow b/samples/deferred/ui/debug.crow new file mode 100644 index 0000000..4d78bf1 --- /dev/null +++ b/samples/deferred/ui/debug.crow @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/samples/deferred/ui/deferred.style b/samples/deferred/ui/deferred.style new file mode 100644 index 0000000..c21ffb6 --- /dev/null +++ b/samples/deferred/ui/deferred.style @@ -0,0 +1,61 @@ +icon { + Width="14"; + Height="14"; +} +MemberViewLabel { + Margin="1"; + Height="Fit"; + Width="50%"; + Background="White"; +} +MemberViewHStack { + Focusable="true"; + Height="Fit"; + Spacing="1"; + MouseEnter="{Background=SteelBlue}"; + MouseLeave="{Background=Transparent}"; +} + +IcoBut { + Template = "#Crow.Coding.ui.IcoBut.template"; + MinimumSize = "10,10"; + Width = "8"; + Height = "14"; + Background = "White"; +} + +WinSchema { + Template="#Crow.Coding.ui.DockWindows.WinSchemaItem.template"; + Focusable = "true"; + MinimumSize="150,50"; + Width = "Fit"; + Height = "Fit"; +} +TabItem { + AllowDrag="false"; + Background="Jet"; +} +TreeItemBorder { + CornerRadius="2"; + Margin="0"; + Focusable="true"; + Height="Fit"; + Width="Stretched"; + Foreground="Transparent"; + MouseEnter="{Foreground=DimGrey}"; + MouseLeave="{Foreground=Transparent}"; +} +TreeIcon { + Margin="0"; + Width="14"; + Height="14"; +} +NormalizedFloat { + Minimum="0.0"; + Maximum="1.0"; + SmallIncrement="0.01"; + LargeIncrement="0.1"; + Decimals="2"; + Width="60"; + Foreground="Black"; +} diff --git a/samples/deferred/ui/main.crow b/samples/deferred/ui/main.crow new file mode 100644 index 0000000..14e6082 --- /dev/null +++ b/samples/deferred/ui/main.crow @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/deferred/ui/materials.crow b/samples/deferred/ui/materials.crow new file mode 100644 index 0000000..b4c5432 --- /dev/null +++ b/samples/deferred/ui/materials.crow @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + +