diff --git a/Content/core/shaders/attributes.mshdri b/Content/core/shaders/attributes.mshdri new file mode 100644 index 00000000..59e37730 --- /dev/null +++ b/Content/core/shaders/attributes.mshdri @@ -0,0 +1,11 @@ +[__AttributeUsage(_AttributeTargets.Var)] +struct SrgbReadAttribute {} + +[__AttributeUsage(_AttributeTargets.Var)] +struct DefaultAttribute +{ + float valueR; + float valueG; + float valueB; + float valueA; +} \ No newline at end of file diff --git a/Content/core/shaders/pbr.mshdr b/Content/core/shaders/pbr.mshdr index 1d000420..6edc4009 100644 --- a/Content/core/shaders/pbr.mshdr +++ b/Content/core/shaders/pbr.mshdr @@ -108,13 +108,28 @@ Fragment { #define PI 3.14159265359 - [[vk::binding(0, 0)]] Texture2D diffuseTexture; - [[vk::binding(1, 0)]] Texture2D normalTexture; - [[vk::binding(2, 0)]] Texture2D ambientOcclusionTexture; - [[vk::binding(3, 0)]] Texture2D metalnessTexture; - [[vk::binding(4, 0)]] Texture2D roughnessTexture; + [[vk::binding(0, 0)]] + [SrgbRead] + Texture2D diffuseTexture; - [[vk::binding(5, 0)]] SamplerState samplerState; + [[vk::binding(1, 0)]] + [Default(0.5f, 0.5f, 1.0f, 1.0f)] + Texture2D normalTexture; + + [[vk::binding(2, 0)]] + [Default(1.0f, 1.0f, 1.0f, 1.0f)] + Texture2D ambientOcclusionTexture; + + [[vk::binding(3, 0)]] + [Default(0.0f, 0.0f, 0.0f, 1.0f)] + Texture2D metalnessTexture; + + [[vk::binding(4, 0)]] + [Default(1.0f, 1.0f, 1.0f, 1.0f)] + Texture2D roughnessTexture; + + [[vk::binding(5, 0)]] + SamplerState samplerState; float3 fresnelSchlick(float cosTheta, float3 F0) { diff --git a/Source/Mocha.Common/Resources/ShaderInfo.cs b/Source/Mocha.Common/Resources/ShaderInfo.cs index cf88505c..b878b2e9 100644 --- a/Source/Mocha.Common/Resources/ShaderInfo.cs +++ b/Source/Mocha.Common/Resources/ShaderInfo.cs @@ -1,5 +1,4 @@ -using Mocha.Glue; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace Mocha.Common; @@ -12,6 +11,46 @@ public enum ShaderReflectionType Sampler } +public enum ShaderReflectionAttributeType +{ + Unknown, + + // SrgbReadAttribute + SrgbRead, + + // DefaultAttribute + Default +}; + +[StructLayout( LayoutKind.Sequential )] +public struct DefaultAttributeData +{ + public float ValueR { get; set; } + public float ValueG { get; set; } + public float ValueB { get; set; } + public float ValueA { get; set; } +}; + +[StructLayout( LayoutKind.Sequential )] +public struct ShaderReflectionAttribute +{ + public ShaderReflectionAttributeType Type { get; set; } + public byte[] Data { get; set; } + + public void SetData( T? obj ) where T : new() + { + if ( obj == null ) + return; + + Data = Serializer.Serialize( obj ); + } + + public readonly T? GetData() where T : new() + { + return Serializer.Deserialize( Data ); + } +}; + [StructLayout( LayoutKind.Sequential )] public struct ShaderReflectionBinding { @@ -19,11 +58,8 @@ public struct ShaderReflectionBinding public int Binding { get; set; } public ShaderReflectionType Type { get; set; } public string Name { get; set; } -} -public struct NativeShaderReflectionInfo -{ - public UtilArray Bindings { get; set; } + public ShaderReflectionAttribute[] Attributes { get; set; } } public struct ShaderReflectionInfo @@ -31,6 +67,12 @@ public struct ShaderReflectionInfo public ShaderReflectionBinding[] Bindings { get; set; } } +public struct NativeShaderStageInfo +{ + public int[] Data { get; set; } + public ShaderReflectionInfo Reflection { get; set; } +} + public struct ShaderStageInfo { public int[] Data { get; set; } diff --git a/Source/Mocha.Engine/Render/Assets/Material.cs b/Source/Mocha.Engine/Render/Assets/Material.cs index f62dcc0c..23a14a4e 100644 --- a/Source/Mocha.Engine/Render/Assets/Material.cs +++ b/Source/Mocha.Engine/Render/Assets/Material.cs @@ -30,25 +30,50 @@ public Material( string path ) Log.Warning( $"Material '{path}' does not exist" ); } - foreach ( var texturePath in textureBindings.Data ) - { - // todo: How do we determine SRGB here? We should probably just fetch it from the texture itself right? - Textures.Add( texturePath.Key, new Texture( texturePath.Value, false ) ); - } - { var shaderFileBytes = FileSystem.Mounted.ReadAllBytes( "shaders/pbr.mshdr" ); var shaderFormat = Serializer.Deserialize>( shaderFileBytes ); - var boundTextures = shaderFormat.Data.Fragment.Reflection.Bindings + // resolve bindings + var requiredBindings = shaderFormat.Data.Fragment.Reflection.Bindings .Where( binding => binding.Type == ShaderReflectionType.Texture ) - .Select( binding => ( - binding.Name, - Textures.TryGetValue( binding.Name, out var tex ) ? tex : Texture.MissingTexture, - binding - ) ) - .OrderBy( x => x.binding.Binding ) - .Select( x => x.Item2.NativeTexture ) + .ToList(); + + // load textures + foreach ( var binding in requiredBindings ) + { + if ( !Textures.ContainsKey( binding.Name ) && textureBindings.Data.ContainsKey( binding.Name ) ) + { + bool isSrgb = binding.Attributes.Any( a => a.Type == ShaderReflectionAttributeType.SrgbRead ); + + Textures.Add( binding.Name, new Texture( textureBindings.Data[binding.Name], isSrgb ) ); + } + } + + // create the ordered list of bound textures + var boundTextures = requiredBindings + .OrderBy( x => x.Binding ) + .Select( binding => + { + if ( Textures.TryGetValue( binding.Name, out var tex ) ) + return tex; + + // check for default attribute + var defaultAttr = binding.Attributes.Where( a => a.Type == ShaderReflectionAttributeType.Default ).Any() + ? binding.Attributes.First( a => a.Type == ShaderReflectionAttributeType.Default ) + : default; + + bool isSrgb = binding.Attributes.Any( a => a.Type == ShaderReflectionAttributeType.SrgbRead ); + + if ( defaultAttr.Type == ShaderReflectionAttributeType.Default ) + { + var data = defaultAttr.GetData(); + return new Texture( data.ValueR, data.ValueG, data.ValueB, data.ValueA, isSrgb ); + } + + return Texture.MissingTexture; + } ) + .Select( x => x.NativeTexture ) .ToList(); NativeMaterial = new( diff --git a/Source/Mocha.Engine/Render/Assets/Texture.cs b/Source/Mocha.Engine/Render/Assets/Texture.cs index 1b5e4526..1547c1da 100644 --- a/Source/Mocha.Engine/Render/Assets/Texture.cs +++ b/Source/Mocha.Engine/Render/Assets/Texture.cs @@ -1,4 +1,6 @@ -namespace Mocha; +using static Mocha.Common.Font; + +namespace Mocha; [Icon( FontAwesome.Image ), Title( "Texture" )] public partial class Texture : Asset @@ -70,6 +72,15 @@ public Texture( uint width, uint height, bool isSrgb = true ) NativeTexture.SetData( Width, Height, 1, new byte[Width * Height * 4].ToInterop(), (int)textureFormat ); } + internal Texture( float r, float g, float b, float a = 1.0f, bool isSrgb = true ) + { + var textureFormat = GetRenderTextureFormat( TextureFormat.RGBA, isSrgb ); + var data = new byte[] { (byte)(r * 255.0f), (byte)(g * 255.0f), (byte)(b * 255.0f), (byte)(a * 255.0f) }; + + NativeTexture = new( "pixel", 1, 1 ); + NativeTexture.SetData( 1, 1, 1, data.ToInterop(), (int)textureFormat ); + } + public void Copy( uint srcX, uint srcY, uint dstX, uint dstY, uint width, uint height, Texture src ) { NativeTexture.Copy( srcX, srcY, dstX, dstY, width, height, src.NativeTexture ); diff --git a/Source/Mocha.Host/Rendering/shadercompiler.cpp b/Source/Mocha.Host/Rendering/shadercompiler.cpp index bd068e45..fbb3d32d 100644 --- a/Source/Mocha.Host/Rendering/shadercompiler.cpp +++ b/Source/Mocha.Host/Rendering/shadercompiler.cpp @@ -77,7 +77,7 @@ bool ShaderCompiler::Compile( const ShaderType shaderType, const char* pShader, std::vector reflectionBindings = {}; ShaderReflectionInfo shaderReflectionInfo = {}; - + { slang::ProgramLayout* layout = program->getLayout( targetIndex ); auto globalScope = layout->getGlobalParamsVarLayout(); @@ -116,12 +116,53 @@ bool ShaderCompiler::Compile( const ShaderType shaderType, const char* pShader, break; } - reflectionBindings.push_back( ShaderReflectionBinding{ - .Set = ( int )set, .Binding = ( int )binding, .Type = mochaReflectionType, .Name = name } ); + // get attributes + auto attributeCount = param->getVariable()->getUserAttributeCount(); + spdlog::info( "Variable {} has {} attributes", name, attributeCount ); + + std::vector reflectedAttributes{}; + + for ( int attributeIndex = 0; attributeIndex < attributeCount; ++attributeIndex ) + { + auto attribute = param->getVariable()->getUserAttributeByIndex( attributeIndex ); + auto attributeName = attribute->getName(); + + auto attributeArgumentCount = attribute->getArgumentCount(); + + ShaderReflectionAttribute reflectedAttribute; + + if ( strcmp( attributeName, "Default" ) == 0 ) + { + // Default attribute + reflectedAttribute.Type = SHADER_REFLECTION_ATTRIBUTE_TYPE_DEFAULT; + reflectedAttribute.Data = new DefaultAttributeData(); + attribute->getArgumentValueFloat( 0, &( ( DefaultAttributeData* )reflectedAttribute.Data )->ValueR ); + attribute->getArgumentValueFloat( 1, &( ( DefaultAttributeData* )reflectedAttribute.Data )->ValueG ); + attribute->getArgumentValueFloat( 2, &( ( DefaultAttributeData* )reflectedAttribute.Data )->ValueB ); + attribute->getArgumentValueFloat( 3, &( ( DefaultAttributeData* )reflectedAttribute.Data )->ValueA ); + } + else if ( strcmp( attributeName, "SrgbRead" ) == 0 ) + { + // SRGB read attribute + reflectedAttribute.Type = SHADER_REFLECTION_ATTRIBUTE_TYPE_SRGB_READ; + } + else + { + spdlog::warn( "Unhandled attribute '{}'", attributeName ); + } + + reflectedAttributes.emplace_back( reflectedAttribute ); + } + + reflectionBindings.push_back( ShaderReflectionBinding{ .Set = ( int )set, + .Binding = ( int )binding, + .Type = mochaReflectionType, + .Name = name, + .Attributes = UtilArray::FromVector( reflectedAttributes ) } ); } } - outResult.ReflectionData = ShaderReflectionInfo { .Bindings = UtilArray::FromVector( reflectionBindings ) }; + outResult.ReflectionData = ShaderReflectionInfo{ .Bindings = UtilArray::FromVector( reflectionBindings ) }; return true; } \ No newline at end of file diff --git a/Source/Mocha.Host/Rendering/shadercompiler.h b/Source/Mocha.Host/Rendering/shadercompiler.h index 0b4f4526..14f2dbb6 100644 --- a/Source/Mocha.Host/Rendering/shadercompiler.h +++ b/Source/Mocha.Host/Rendering/shadercompiler.h @@ -1,14 +1,13 @@ #pragma once -#include +#include "baserendercontext.h" + #include +#include #include -#include "baserendercontext.h" - using namespace slang; - enum ShaderReflectionType { SHADER_REFLECTION_TYPE_UNKNOWN, @@ -18,12 +17,39 @@ enum ShaderReflectionType SHADER_REFLECTION_TYPE_SAMPLER }; +enum ShaderReflectionAttributeType +{ + SHADER_REFLECTION_ATTRIBUTE_TYPE_UNKNOWN, + + // SrgbReadAttribute + SHADER_REFLECTION_ATTRIBUTE_TYPE_SRGB_READ, + + // DefaultAttribute + SHADER_REFLECTION_ATTRIBUTE_TYPE_DEFAULT +}; + +struct DefaultAttributeData +{ + float ValueR = 0.0f; + float ValueG = 0.0f; + float ValueB = 0.0f; + float ValueA = 0.0f; +}; + +struct ShaderReflectionAttribute +{ + ShaderReflectionAttributeType Type = SHADER_REFLECTION_ATTRIBUTE_TYPE_UNKNOWN; + void* Data = nullptr; +}; + struct ShaderReflectionBinding { - int Set; - int Binding; - ShaderReflectionType Type; - const char* Name; + int Set = -1; + int Binding = -1; + ShaderReflectionType Type = SHADER_REFLECTION_TYPE_UNKNOWN; + const char* Name = nullptr; + + UtilArray Attributes; }; struct ShaderReflectionInfo diff --git a/Source/Mocha.Tests/Mocha.Tests.csproj b/Source/Mocha.Tests/Mocha.Tests.csproj index c9c47733..05770888 100644 --- a/Source/Mocha.Tests/Mocha.Tests.csproj +++ b/Source/Mocha.Tests/Mocha.Tests.csproj @@ -6,6 +6,8 @@ enable false + + AnyCPU;x64 diff --git a/Source/Mocha.sln b/Source/Mocha.sln index a5a0a6db..91983b9f 100644 --- a/Source/Mocha.sln +++ b/Source/Mocha.sln @@ -193,8 +193,8 @@ Global {860C57C4-6E4B-445F-9614-9084AF4CD46B}.Release|x86.Build.0 = Release|Win32 {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Debug|x64.ActiveCfg = Debug|Any CPU - {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Debug|x64.Build.0 = Debug|Any CPU + {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Debug|x64.ActiveCfg = Debug|x64 + {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Debug|x64.Build.0 = Debug|x64 {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Debug|x86.ActiveCfg = Debug|Any CPU {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Debug|x86.Build.0 = Debug|Any CPU {267A391D-CD51-4A29-A41B-11D57E9F9AAF}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Source/MochaTool.AssetCompiler/Handlers/Shader/ShaderCompiler.NativeTypes.cs b/Source/MochaTool.AssetCompiler/Handlers/Shader/ShaderCompiler.NativeTypes.cs new file mode 100644 index 00000000..cbe70a8b --- /dev/null +++ b/Source/MochaTool.AssetCompiler/Handlers/Shader/ShaderCompiler.NativeTypes.cs @@ -0,0 +1,144 @@ +using Mocha; +using Mocha.Glue; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace MochaTool.AssetCompiler; + +partial class ShaderCompiler +{ + [StructLayout( LayoutKind.Sequential )] + struct DefaultAttributeData + { + public float ValueR { get; set; } + public float ValueG { get; set; } + public float ValueB { get; set; } + public float ValueA { get; set; } + }; + + [StructLayout( LayoutKind.Sequential )] + struct NativeShaderReflectionAttribute + { + public ShaderReflectionAttributeType Type { get; set; } + public IntPtr Data { get; set; } + + internal ShaderReflectionAttribute ToManaged() + { + var ret = new ShaderReflectionAttribute() + { + Type = Type + }; + + if ( Data != IntPtr.Zero ) + { + switch ( Type ) + { + case ShaderReflectionAttributeType.Default: + var d = Marshal.PtrToStructure( Data ); + ret.SetData( d ); + break; + + // No data + case ShaderReflectionAttributeType.Unknown: + case ShaderReflectionAttributeType.SrgbRead: + break; + } + } + + return ret; + } + }; + + [StructLayout( LayoutKind.Sequential )] + struct NativeShaderReflectionBinding + { + public int Set { get; set; } + public int Binding { get; set; } + public ShaderReflectionType Type { get; set; } + public string Name { get; set; } + + public UtilArray Attributes { get; set; } + + internal ShaderReflectionBinding ToManaged() + { + var attributes = new ShaderReflectionAttribute[Attributes.count]; + for ( int i = 0; i < Attributes.count; i++ ) + { + var native = Marshal.PtrToStructure( Attributes.data + (i * Marshal.SizeOf()) ); + attributes[i] = native.ToManaged(); + } + + return new ShaderReflectionBinding() + { + Set = Set, + Binding = Binding, + Type = Type, + Name = new string( Name ), + + Attributes = attributes + }; + } + } + + struct NativeShaderReflectionInfo + { + public UtilArray Bindings { get; set; } + + internal ShaderReflectionInfo ToManaged() + { + var bindings = new ShaderReflectionBinding[Bindings.count]; + for ( int i = 0; i < Bindings.count; i++ ) + { + var native = Marshal.PtrToStructure( Bindings.data + (i * Marshal.SizeOf()) ); + bindings[i] = native.ToManaged(); + } + + return new ShaderReflectionInfo() + { + Bindings = bindings + }; + } + } + + struct NativeShaderStageInfo + { + public int[] Data { get; set; } + public NativeShaderReflectionInfo Reflection { get; set; } + + internal ShaderStageInfo ToManaged() + { + return new ShaderStageInfo() + { + Data = Data, + Reflection = Reflection.ToManaged() + }; + } + } + + struct NativeShaderInfo + { + public NativeShaderStageInfo Vertex { get; set; } + public NativeShaderStageInfo Fragment { get; set; } + public NativeShaderStageInfo Compute { get; set; } + + internal ShaderInfo ToManaged() + { + return new ShaderInfo() + { + Compute = Compute.ToManaged(), + Fragment = Fragment.ToManaged(), + Vertex = Vertex.ToManaged() + }; + } + } + + [StructLayout( LayoutKind.Sequential )] + private struct NativeShaderCompilerResult + { + public UtilArray ShaderData; + public NativeShaderReflectionInfo ReflectionData; + } + + [DllImport( "MochaTool.ShaderCompilerBindings.dll", CharSet = CharSet.Ansi )] + private static extern NativeShaderCompilerResult CompileShader( ShaderType shaderType, string shaderSource ); +} diff --git a/Source/MochaTool.AssetCompiler/Handlers/Shader/ShaderCompiler.cs b/Source/MochaTool.AssetCompiler/Handlers/Shader/ShaderCompiler.cs index edd6671f..c10da50e 100644 --- a/Source/MochaTool.AssetCompiler/Handlers/Shader/ShaderCompiler.cs +++ b/Source/MochaTool.AssetCompiler/Handlers/Shader/ShaderCompiler.cs @@ -17,16 +17,6 @@ public partial class ShaderCompiler : BaseCompiler /// public override bool SupportsMochaFile => true; - [StructLayout( LayoutKind.Sequential )] - private struct ShaderCompilerResult - { - public UtilArray ShaderData; - public NativeShaderReflectionInfo ReflectionData; - } - - [DllImport( "MochaTool.ShaderCompilerBindings.dll", CharSet = CharSet.Ansi )] - private static extern ShaderCompilerResult CompileShader( ShaderType shaderType, string shaderSource ); - /// /// Compiles a shader from GLSL into SPIR-V using Veldrid's libshaderc bindings. /// @@ -54,15 +44,7 @@ private void CompileShader( string? commonSource, string shaderSource, ShaderTyp // // Shader reflection info // - reflectionInfo = new(); - - var bindings = new ShaderReflectionBinding[shaderResult.ReflectionData.Bindings.count]; - for ( int i = 0; i < shaderResult.ReflectionData.Bindings.count; i++ ) - { - bindings[i] = Marshal.PtrToStructure( shaderResult.ReflectionData.Bindings.data + (i * Marshal.SizeOf()) ); - } - - reflectionInfo.Bindings = bindings; + reflectionInfo = shaderResult.ReflectionData.ToManaged(); } /// @@ -76,9 +58,13 @@ public override CompileResult Compile( ref CompileInput input ) var shaderFormat = new ShaderInfo(); + // Prepend attributes.mshdri (horrible hack) + var attributeSource = File.ReadAllText( Path.GetDirectoryName( input.SourcePath ) + "\\attributes.mshdri" ); + var common = (shaderFile.Common ?? "") + "\n\n" + attributeSource; + if ( shaderFile.Vertex != null ) { - CompileShader( shaderFile.Common, shaderFile.Vertex, ShaderType.Vertex, out var reflection, out var data ); + CompileShader( common, shaderFile.Vertex, ShaderType.Vertex, out var reflection, out var data ); shaderFormat.Vertex = new() { Data = data, @@ -88,7 +74,7 @@ public override CompileResult Compile( ref CompileInput input ) if ( shaderFile.Fragment != null ) { - CompileShader( shaderFile.Common, shaderFile.Fragment, ShaderType.Fragment, out var reflection, out var data ); + CompileShader( common, shaderFile.Fragment, ShaderType.Fragment, out var reflection, out var data ); shaderFormat.Fragment = new() { Data = data, @@ -96,15 +82,10 @@ public override CompileResult Compile( ref CompileInput input ) }; } - /* - if ( shaderFile.Compute != null ) - shaderFormat.ComputeShaderData = CompileShader( shaderFile.Common, shaderFile.Compute, ShaderStages.Compute, debugName ); - */ - // Wrapper for file. var mochaFile = new MochaFile { - MajorVersion = 5, + MajorVersion = 6, MinorVersion = 0, Data = shaderFormat, AssetHash = input.DataHash