diff --git a/GlitchyEngine/src/Platform/DX11/Renderer/Dx11SamplerState.bf b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11SamplerState.bf new file mode 100644 index 0000000..6c85b9a --- /dev/null +++ b/GlitchyEngine/src/Platform/DX11/Renderer/Dx11SamplerState.bf @@ -0,0 +1,123 @@ +using DirectX.D3D11; +using DirectX.Common; + +namespace DirectX.D3D11 +{ + extension Filter + { + public static Filter SamplerToFilter(GlitchyEngine.Renderer.SamplerStateDescription samplerDesc) + { + /* + While I'm writing this Function it turns out that the numbers in the D3D11_FILTER-enum + aren't as random as I thought. One could even say they are quite clever. + + Explanation: + If the Mip filter is Linear the first bit (LSB) will be 1 (Filter = 0x01) + If the Mag filter is Linear the third bit will be 1 (Filter = 0x04) + If the Min filter is Linear the fifth bit will be 1 (Filter = 0x10) + if any of them is Point their respective bit will be 0 + + If the filter is Anisotropic the Filter will be 0x0101'0101. + + Comparison-, Minimum- and Maximum-Filter modes will also set their respective bit. + (0x80, 0x100 and 0x180 respectively) + */ + + Filter output = .Min_Mag_Mip_Point; + + // Mip filter + if(samplerDesc.MipFilter == .Linear) + { + output |= .Min_Mag_Point_Mip_Linear; + } + else if(samplerDesc.MipFilter == .Anisotropic) + { + output = .Anisotropic; + } + + // Min filter + if(samplerDesc.MagFilter == .Linear) + { + output |= .Min_Point_Mag_Linear_Mip_Point; + } + else if(samplerDesc.MagFilter == .Anisotropic) + { + output = .Anisotropic; + } + + // Mag filter + if(samplerDesc.MinFilter == .Linear) + { + output |= .Min_Linear_Mag_Mip_Point; + } + else if(samplerDesc.MagFilter == .Anisotropic) + { + output = .Anisotropic; + } + + // Sets filter mode bit + switch(samplerDesc.FilterMode) + { + case .Default: + output |= .Min_Mag_Mip_Point; + case .Comparison: + output |= .Comparison_Min_Mag_Mip_Point; + case .Minimum: + output |= .Minimum_Min_Mag_Mip_Point; + case .Maximum: + output |= .Maximum_Min_Mag_Mip_Point; + } + + return output; + } + } +} + +using internal GlitchyEngine.Renderer; + +namespace GlitchyEngine.Renderer +{ + extension SamplerStateDescription + { + typealias NativeDesc = DirectX.D3D11.SamplerStateDescription; + + public void ToNative(out NativeDesc samplerDesc) + { + samplerDesc.Filter = .SamplerToFilter(this); + + samplerDesc.ComparisonFunc = (.)ComparisonFunction; + + samplerDesc.AddressU = (.)AddressModeU; + samplerDesc.AddressV = (.)AddressModeV; + samplerDesc.AddressW = (.)AddressModeW; + + samplerDesc.MipLODBias = MipLODBias; + samplerDesc.MinLOD = MipMinLOD; + samplerDesc.MaxLOD = MipMaxLOD; + + samplerDesc.MaxAnisotropy = MaxAnisotropy; + + samplerDesc.BorderColor = BorderColor; + } + } + + extension SamplerState + { + internal ID3D11SamplerState* nativeSamplerState ~ _?.Release(); + + protected override void PlatformCreateSamplerState() + { + _desc.ToNative(var nativeDesc); + + HResult result = _context.nativeDevice.CreateSamplerState(ref nativeDesc, &nativeSamplerState); + + Log.EngineLogger.Assert(result.Succeeded, scope $"Failed to create SamplerState. Error({(int)result}): {result}"); + } + + public override void Bind(uint32 slot) + { + _context.nativeContext.VertexShader.SetSamplers(slot, 1, &nativeSamplerState); + _context.nativeContext.PixelShader.SetSamplers(slot, 1, &nativeSamplerState); + } + } +} diff --git a/GlitchyEngine/src/Renderer/SamplerState.bf b/GlitchyEngine/src/Renderer/SamplerState.bf new file mode 100644 index 0000000..a50a578 --- /dev/null +++ b/GlitchyEngine/src/Renderer/SamplerState.bf @@ -0,0 +1,169 @@ +using System; +using GlitchyEngine.Math; + +namespace GlitchyEngine.Renderer +{ + public enum FilterFunction + { + /// Use point filtering (nearest neighbor) for sampling. + Point, + /// Use linear interpolation for sampling. + Linear, + /// Use anisotropic interpolation for sampling. + Anisotropic + } + + public enum FilterMode + { + /// Just sample the texture. + Default, + /// Sample the texture and compares it with a comparison value. + Comparison, + /// Return the minimum value of the fetched texels. + Minimum, + /// Return the maximum value of the fetched texels. + Maximum + } + + public enum ComparisonFunction + { + /** + * Never pass the comparison. + */ + Never = 1, + /** + * If the source data is less than the destination data, the comparison passes. + */ + Less = 2, + /** + * If the source data is equal to the destination data, the comparison passes. + */ + Equal = 3, + /** + * If the source data is less than or equal to the destination data, the comparison passes. + */ + LessOrEqual = 4, + /** + * If the source data is greater than the destination data, the comparison passes. + */ + Greater = 5, + /** + * If the source data is not equal to the destination data, the comparison passes. + */ + NotEqual = 6, + /** + * If the source data is greater than or equal to the destination data, the comparison passes. + */ + GreaterOrEqual = 7, + /** + * Always pass the comparison. + */ + Always = 8 + } + + public enum TextureAddressMode + { + /// Tile the texture at every (u,v) integer junction. + Wrap = 1, + /* + * Tile the texture at every (u,v) integer junction. However it is flipped every other tile. + * Example + * Between 0 and 1 the texture is addressed normally. + * Between 1 and 2 the texture is flipped. + * Between 2 and 3 the texture is addressed normally. + */ + Mirror, + /** + * Texture coordinates outside the range [0.0, 1.0] are set to the texture color at 0.0 or 1.0, respectively. + */ + Clamp, + /** + * Texture coordinates outside the range [0.0, 1.0] are set to the border color. + */ + Border, + /** + * Like a combination of Mirror and Clamp. + * Sample using the absolute value and clamp to 1.0. + */ + MirrorOnce + } + + public struct SamplerStateDescription + { + public FilterFunction MinFilter = .Linear; + public FilterFunction MagFilter = .Linear; + public FilterFunction MipFilter = .Linear; + + public FilterMode FilterMode = .Default; + + /** + * The function that is used to compare the sampled data against the existing sampled data. + * Only applies if FilterMode is set to FilterMode.Comparison. + */ + public ComparisonFunction ComparisonFunction = .Never; + + public TextureAddressMode AddressModeU = .Clamp; + public TextureAddressMode AddressModeV = .Clamp; + public TextureAddressMode AddressModeW = .Clamp; + + /** + * Offset from the calculated mipmap level. + * For example, if Direct3D calculates that a texture should be sampled at mipmap level 3 and MipLODBias is 2, then the texture will be sampled at mipmap level 5. + */ + public float MipLODBias = 0; + + /** + * Lower end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level and any level higher than that is less detailed. + */ + public float MipMinLOD = float.MinValue; + /** + * Upper end of the mipmap range to clamp access to, where 0 is the largest and most detailed mipmap level and any level higher than that is less detailed. + * This value must be greater than or equal to MinLOD. To have no upper limit on LOD set this to a large value such as float.MaxValue. + */ + public float MipMaxLOD = float.MaxValue; + + /** + * Clamping value used if FilterMode.Anisotropic is specified in the Filters. + * Valid values are between 1 and 16. + */ + public uint32 MaxAnisotropy = 1; // TODO: we could use a smaller int + + /** + * Border color to use if TextureAddressMode.Border is specified for AddressModeU, AddressModeV, or AddressModeW. + */ + public ColorRGBA BorderColor = .White; + } + + public class SamplerState : RefCounted + { + protected GraphicsContext _context ~ _?.ReleaseRef(); + + protected SamplerStateDescription _desc; + + [Inline] + public FilterFunction MinificationFilter => _desc.MinFilter; + [Inline] + public FilterFunction MagnificationFilter => _desc.MagFilter; + [Inline] + public FilterFunction MipFilter => _desc.MipFilter; + [Inline] + public FilterMode FilterMode => _desc.FilterMode; + + protected this(GraphicsContext context) + { + _context = context..AddRef(); + } + + public this(GraphicsContext context, SamplerStateDescription desc) : this(context) + { + _desc = desc; + + PlatformCreateSamplerState(); + } + + /// Creates the platform specific Sampler State + protected extern void PlatformCreateSamplerState(); + + public extern void Bind(uint32 slot = 0); + } +} diff --git a/Sandbox/src/SandboxApp.bf b/Sandbox/src/SandboxApp.bf index 3cd88a3..fe348d9 100644 --- a/Sandbox/src/SandboxApp.bf +++ b/Sandbox/src/SandboxApp.bf @@ -69,6 +69,7 @@ namespace Sandbox GraphicsContext _context ~ _?.ReleaseRef(); Texture2D _texture ~ _?.ReleaseRef(); + SamplerState _sampler ~ _?.ReleaseRef(); private Vector3 CircleCoord(float angle) { @@ -193,6 +194,12 @@ namespace Sandbox */ _texture = new Texture2D(_context, "content/Textures/Checkerboard.dds"); + + _sampler = new SamplerState(_context, + SamplerStateDescription() + { + MagFilter = .Point + }); } public override void Update(GameTime gameTime) @@ -262,6 +269,7 @@ namespace Sandbox _cBuffer.Update(); _texture.Bind(); + _sampler.Bind(); Renderer.Submit(_geometryBinding, _effect); Renderer.Submit(_quadGeometryBinding, _textureEffect, .Scaling(1.5f));