aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image/Sampler.cs
blob: 52d5ccec1e01095483f3a75b24b7e45162ed214a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
using Ryujinx.Graphics.GAL;
using System;
using System.Numerics;

namespace Ryujinx.Graphics.Gpu.Image
{
    /// <summary>
    /// Cached sampler entry for sampler pools.
    /// </summary>
    class Sampler : IDisposable
    {
        private const int MinLevelsForAnisotropic = 5;

        /// <summary>
        /// Host sampler object.
        /// </summary>
        private readonly ISampler _hostSampler;

        /// <summary>
        /// Host sampler object, with anisotropy forced.
        /// </summary>
        private readonly ISampler _anisoSampler;

        /// <summary>
        /// Creates a new instance of the cached sampler.
        /// </summary>
        /// <param name="context">The GPU context the sampler belongs to</param>
        /// <param name="descriptor">The Maxwell sampler descriptor</param>
        public Sampler(GpuContext context, SamplerDescriptor descriptor)
        {
            MinFilter minFilter = descriptor.UnpackMinFilter();
            MagFilter magFilter = descriptor.UnpackMagFilter();

            bool seamlessCubemap = descriptor.UnpackSeamlessCubemap();

            AddressMode addressU = descriptor.UnpackAddressU();
            AddressMode addressV = descriptor.UnpackAddressV();
            AddressMode addressP = descriptor.UnpackAddressP();

            CompareMode compareMode = descriptor.UnpackCompareMode();
            CompareOp   compareOp   = descriptor.UnpackCompareOp();

            ColorF color = new ColorF(
                descriptor.BorderColorR,
                descriptor.BorderColorG,
                descriptor.BorderColorB,
                descriptor.BorderColorA);

            float minLod     = descriptor.UnpackMinLod();
            float maxLod     = descriptor.UnpackMaxLod();
            float mipLodBias = descriptor.UnpackMipLodBias();

            float maxRequestedAnisotropy = descriptor.UnpackMaxAnisotropy();
            float maxSupportedAnisotropy = context.Capabilities.MaximumSupportedAnisotropy;

            _hostSampler = context.Renderer.CreateSampler(new SamplerCreateInfo(
                minFilter,
                magFilter,
                seamlessCubemap,
                addressU,
                addressV,
                addressP,
                compareMode,
                compareOp,
                color,
                minLod,
                maxLod,
                mipLodBias,
                Math.Min(maxRequestedAnisotropy, maxSupportedAnisotropy)));

            if (GraphicsConfig.MaxAnisotropy >= 0 && GraphicsConfig.MaxAnisotropy <= 16 && (minFilter == MinFilter.LinearMipmapNearest || minFilter == MinFilter.LinearMipmapLinear))
            {
                maxRequestedAnisotropy = GraphicsConfig.MaxAnisotropy;

                _anisoSampler = context.Renderer.CreateSampler(new SamplerCreateInfo(
                    minFilter,
                    magFilter,
                    seamlessCubemap,
                    addressU,
                    addressV,
                    addressP,
                    compareMode,
                    compareOp,
                    color,
                    minLod,
                    maxLod,
                    mipLodBias,
                    Math.Min(maxRequestedAnisotropy, maxSupportedAnisotropy)));
            }
        }

        /// <summary>
        /// Gets a host sampler for the given texture.
        /// </summary>
        /// <param name="texture">Texture to be sampled</param>
        /// <returns>A host sampler</returns>
        public ISampler GetHostSampler(Texture texture)
        {
            return _anisoSampler != null && AllowForceAnisotropy(texture) ? _anisoSampler : _hostSampler;
        }

        /// <summary>
        /// Determine if the given texture can have anisotropic filtering forced.
        /// Filtered textures that we might want to force anisotropy on should have a lot of mip levels.
        /// </summary>
        /// <param name="texture">The texture</param>
        /// <returns>True if anisotropic filtering can be forced, false otherwise</returns>
        private static bool AllowForceAnisotropy(Texture texture)
        {
            if (texture == null || !(texture.Target == Target.Texture2D || texture.Target == Target.Texture2DArray))
            {
                return false;
            }

            int maxSize = Math.Max(texture.Info.Width, texture.Info.Height);
            int maxLevels = BitOperations.Log2((uint)maxSize) + 1;

            return texture.Info.Levels >= Math.Min(MinLevelsForAnisotropic, maxLevels);
        }

        /// <summary>
        /// Disposes the host sampler object.
        /// </summary>
        public void Dispose()
        {
            _hostSampler.Dispose();
            _anisoSampler?.Dispose();
        }
    }
}