Skip to content

Commit

Permalink
Move shaders to seperate files
Browse files Browse the repository at this point in the history
  • Loading branch information
IsaacMarovitz committed Mar 19, 2024
1 parent dd8b9e4 commit 184ff4f
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 187 deletions.
40 changes: 2 additions & 38 deletions src/SharpMetal.Examples.Animation/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,6 @@ namespace SharpMetal.Examples.Animation
[SupportedOSPlatform("macos")]
public class Renderer : IRenderer
{
private const string ShaderSource = """
#include <metal_stdlib>
using namespace metal;

struct v2f
{
float4 position [[position]];
half3 color;
};

struct VertexData
{
device float3* positions [[id(0)]];
device float3* colors [[id(1)]];
};

struct FrameData
{
float angle;
};

v2f vertex vertexMain( device const VertexData* vertexData [[buffer(0)]], constant FrameData* frameData [[buffer(1)]], uint vertexId [[vertex_id]] )
{
float a = frameData->angle;
float3x3 rotationMatrix = float3x3( sin(a), cos(a), 0.0, cos(a), -sin(a), 0.0, 0.0, 0.0, 1.0 );
v2f o;
o.position = float4( rotationMatrix * vertexData->positions[ vertexId ], 1.0 );
o.color = half3(vertexData->colors[ vertexId ]);
return o;
}

half4 fragment fragmentMain( v2f in [[stage_in]] )
{
return half4( in.color, 1.0 );
}
""";

private const int MaxFramesInFlight = 3;
private const int NumVertices = 3;

Expand Down Expand Up @@ -79,8 +42,9 @@ public static IRenderer Init(MTLDevice device)
private void BuildShaders()
{
// Build shader
var shaderSource = EmbeddedResources.ReadAllText("Animation/Shaders/Shader.metal");
var libraryError = new NSError(IntPtr.Zero);
_shaderLibrary = _device.NewLibrary(StringHelper.NSString(ShaderSource), new(IntPtr.Zero), ref libraryError);
_shaderLibrary = _device.NewLibrary(StringHelper.NSString(shaderSource), new(IntPtr.Zero), ref libraryError);
if (libraryError != IntPtr.Zero)
{
throw new Exception($"Failed to create library! {StringHelper.String(libraryError.LocalizedDescription)}");
Expand Down
34 changes: 34 additions & 0 deletions src/SharpMetal.Examples.Animation/Shaders/Shader.metal
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <metal_stdlib>
using namespace metal;

struct v2f
{
float4 position [[position]];
half3 color;
};

struct VertexData
{
device float3* positions [[id(0)]];
device float3* colors [[id(1)]];
};

struct FrameData
{
float angle;
};

v2f vertex vertexMain( device const VertexData* vertexData [[buffer(0)]], constant FrameData* frameData [[buffer(1)]], uint vertexId [[vertex_id]] )
{
float a = frameData->angle;
float3x3 rotationMatrix = float3x3( sin(a), cos(a), 0.0, cos(a), -sin(a), 0.0, 0.0, 0.0, 1.0 );
v2f o;
o.position = float4( rotationMatrix * vertexData->positions[ vertexId ], 1.0 );
o.color = half3(vertexData->colors[ vertexId ]);
return o;
}

half4 fragment fragmentMain( v2f in [[stage_in]] )
{
return half4( in.color, 1.0 );
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@
<ItemGroup>
<ProjectReference Include="..\SharpMetal.Examples.Common\SharpMetal.Examples.Common.csproj" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Shaders\Shader.metal" />
</ItemGroup>

</Project>
62 changes: 62 additions & 0 deletions src/SharpMetal.Examples.Common/EmbeddedResources.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Reflection;

namespace SharpMetal.Examples.Common
{
public static class EmbeddedResources
{
private static readonly Assembly _resourceAssembly;

static EmbeddedResources()

Check warning on line 9 in src/SharpMetal.Examples.Common/EmbeddedResources.cs

View workflow job for this annotation

GitHub Actions / build / build

Non-nullable field '_resourceAssembly' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
{
_resourceAssembly = Assembly.GetAssembly(typeof(EmbeddedResources));

Check warning on line 11 in src/SharpMetal.Examples.Common/EmbeddedResources.cs

View workflow job for this annotation

GitHub Actions / build / build

Possible null reference assignment.
}

public static string ReadAllText(string filename)
{
var (assembly, path) = ResolveManifestPath(filename);

return ReadAllText(assembly, path);
}

public static string ReadAllText(Assembly assembly, string filename)
{
using var stream = GetStream(assembly, filename);
if (stream == null)
{
throw new FileNotFoundException($"{filename} in {assembly}");
}

using var reader = new StreamReader(stream);
return reader.ReadToEnd();
}

public static Stream GetStream(Assembly assembly, string filename)
{
// Assumes one namespace per assembly
var @namespace = assembly.GetTypes()[0].Namespace;
var manifestUri = @namespace + "." + filename.Replace('/', '.');

var stream = assembly.GetManifestResourceStream(manifestUri);

return stream;

Check warning on line 41 in src/SharpMetal.Examples.Common/EmbeddedResources.cs

View workflow job for this annotation

GitHub Actions / build / build

Possible null reference return.
}

private static (Assembly, string) ResolveManifestPath(string filename)
{
var segments = filename.Split('/', 2, StringSplitOptions.RemoveEmptyEntries);

if (segments.Length >= 2)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.GetName().Name == segments[0])
{
return (assembly, segments[1]);
}
}
}

return (_resourceAssembly, filename);
}
}
}
126 changes: 4 additions & 122 deletions src/SharpMetal.Examples.ComputeToRender/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,126 +11,6 @@ namespace SharpMetal.Examples.ComputeToRender
[SupportedOSPlatform("macos")]
public class Renderer : IRenderer
{
private const string ShaderSource = """
#include <metal_stdlib>
using namespace metal;

struct v2f
{
float4 position [[position]];
float3 normal;
half3 color;
float2 texcoord;
};

struct VertexData
{
float3 position;
float3 normal;
float2 texcoord;
};

struct InstanceData
{
float4x4 instanceTransform;
float3x3 instanceNormalTransform;
float4 instanceColor;
};

struct CameraData
{
float4x4 perspectiveTransform;
float4x4 worldTransform;
float3x3 worldNormalTransform;
};

v2f vertex vertexMain( device const VertexData* vertexData [[buffer(0)]],
device const InstanceData* instanceData [[buffer(1)]],
device const CameraData& cameraData [[buffer(2)]],
uint vertexId [[vertex_id]],
uint instanceId [[instance_id]] )
{
v2f o;

const device VertexData& vd = vertexData[ vertexId ];
float4 pos = float4( vd.position, 1.0 );
pos = instanceData[ instanceId ].instanceTransform * pos;
pos = cameraData.perspectiveTransform * cameraData.worldTransform * pos;
o.position = pos;

float3 normal = instanceData[ instanceId ].instanceNormalTransform * vd.normal;
normal = cameraData.worldNormalTransform * normal;
o.normal = normal;

o.texcoord = vd.texcoord.xy;

o.color = half3( instanceData[ instanceId ].instanceColor.rgb );
return o;
}

half4 fragment fragmentMain( v2f in [[stage_in]], texture2d< half, access::sample > tex [[texture(0)]] )
{
constexpr sampler s( address::repeat, filter::linear );
half3 texel = tex.sample( s, in.texcoord ).rgb;

// Assume light coming from (front-top-right)
float3 l = normalize(float3( 1.0, 1.0, 0.8 ));
float3 n = normalize( in.normal );

half ndotl = half( saturate( dot( n, l ) ) );

half3 illum = (in.color * texel * 0.1) + (in.color * texel * ndotl);
return half4( illum, 1.0 );
}
""";

private const string KernelSource = """
#include <metal_stdlib>
using namespace metal;

kernel void mandelbrot_set(texture2d< half, access::write > tex [[texture(0)]],
uint2 index [[thread_position_in_grid]],
uint2 gridSize [[threads_per_grid]],
device const uint* frame [[buffer(0)]])
{
constexpr float kAnimationFrequency = 0.01;
constexpr float kAnimationSpeed = 4;
constexpr float kAnimationScaleLow = 0.62;
constexpr float kAnimationScale = 0.38;

constexpr float2 kMandelbrotPixelOffset = {-0.2, -0.35};
constexpr float2 kMandelbrotOrigin = {-1.2, -0.32};
constexpr float2 kMandelbrotScale = {2.2, 2.0};

// Map time to zoom value in [kAnimationScaleLow, 1]
float zoom = kAnimationScaleLow + kAnimationScale * cos(kAnimationFrequency * *frame);
// Speed up zooming
zoom = pow(zoom, kAnimationSpeed);

// Scale
float x0 = zoom * kMandelbrotScale.x * ((float)index.x / gridSize.x + kMandelbrotPixelOffset.x) + kMandelbrotOrigin.x;
float y0 = zoom * kMandelbrotScale.y * ((float)index.y / gridSize.y + kMandelbrotPixelOffset.y) + kMandelbrotOrigin.y;

// Implement Mandelbrot set
float x = 0.0;
float y = 0.0;
uint iteration = 0;
uint max_iteration = 1000;
float xtmp = 0.0;
while(x * x + y * y <= 4 && iteration < max_iteration)
{
xtmp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtmp;
iteration += 1;
}

// Convert iteration result to colors
half color = (0.5 + 0.5 * cos(3.0 + iteration * 0.15));
tex.write(half4(color, color, color, 1.0), index, 0);
}
""";

private const int MaxFramesInFlight = 3;
private const int TextureWidth = 128;
private const int TextureHeight = 128;
Expand Down Expand Up @@ -176,8 +56,9 @@ public static IRenderer Init(MTLDevice device)
private void BuildShaders()
{
// Build shader
var shaderSource = EmbeddedResources.ReadAllText("ComputeToRender/Shaders/Shader.metal");
var libraryError = new NSError(IntPtr.Zero);
_shaderLibrary = _device.NewLibrary(StringHelper.NSString(ShaderSource), new(IntPtr.Zero), ref libraryError);
_shaderLibrary = _device.NewLibrary(StringHelper.NSString(shaderSource), new(IntPtr.Zero), ref libraryError);
if (libraryError != IntPtr.Zero)
{
throw new Exception($"Failed to create library! {StringHelper.String(libraryError.LocalizedDescription)}");
Expand Down Expand Up @@ -205,8 +86,9 @@ private void BuildShaders()

private void BuildComputePipeline()
{
var kernelSource = EmbeddedResources.ReadAllText("ComputeToRender/Shaders/Compute.metal");
var error = new NSError(IntPtr.Zero);
var library = _device.NewLibrary(StringHelper.NSString(KernelSource), new(IntPtr.Zero), ref error);
var library = _device.NewLibrary(StringHelper.NSString(kernelSource), new(IntPtr.Zero), ref error);
if (error != IntPtr.Zero)
{
throw new Exception($"Failed to create library! {StringHelper.String(error.LocalizedDescription)}");
Expand Down
44 changes: 44 additions & 0 deletions src/SharpMetal.Examples.ComputeToRender/Shaders/Compute.metal
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <metal_stdlib>
using namespace metal;

kernel void mandelbrot_set(texture2d< half, access::write > tex [[texture(0)]],
uint2 index [[thread_position_in_grid]],
uint2 gridSize [[threads_per_grid]],
device const uint* frame [[buffer(0)]])
{
constexpr float kAnimationFrequency = 0.01;
constexpr float kAnimationSpeed = 4;
constexpr float kAnimationScaleLow = 0.62;
constexpr float kAnimationScale = 0.38;

constexpr float2 kMandelbrotPixelOffset = {-0.2, -0.35};
constexpr float2 kMandelbrotOrigin = {-1.2, -0.32};
constexpr float2 kMandelbrotScale = {2.2, 2.0};

// Map time to zoom value in [kAnimationScaleLow, 1]
float zoom = kAnimationScaleLow + kAnimationScale * cos(kAnimationFrequency * *frame);
// Speed up zooming
zoom = pow(zoom, kAnimationSpeed);

// Scale
float x0 = zoom * kMandelbrotScale.x * ((float)index.x / gridSize.x + kMandelbrotPixelOffset.x) + kMandelbrotOrigin.x;
float y0 = zoom * kMandelbrotScale.y * ((float)index.y / gridSize.y + kMandelbrotPixelOffset.y) + kMandelbrotOrigin.y;

// Implement Mandelbrot set
float x = 0.0;
float y = 0.0;
uint iteration = 0;
uint max_iteration = 1000;
float xtmp = 0.0;
while(x * x + y * y <= 4 && iteration < max_iteration)
{
xtmp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtmp;
iteration += 1;
}

// Convert iteration result to colors
half color = (0.5 + 0.5 * cos(3.0 + iteration * 0.15));
tex.write(half4(color, color, color, 1.0), index, 0);
}
Loading

0 comments on commit 184ff4f

Please sign in to comment.