aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics/GpuResourceManager.cs
blob: d46129516dfa8defe3f54ecad5b271cecc359931 (plain) (blame)
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
131
132
133
134
135
136
137
138
139
140
using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture;
using System.Collections.Generic;

namespace Ryujinx.Graphics
{
    public class GpuResourceManager
    {
        private enum ImageType
        {
            None,
            Texture,
            ColorBuffer,
            ZetaBuffer
        }

        private NvGpu Gpu;

        private HashSet<long>[] UploadedKeys;

        private Dictionary<long, ImageType> ImageTypes;

        public GpuResourceManager(NvGpu Gpu)
        {
            this.Gpu = Gpu;

            UploadedKeys = new HashSet<long>[(int)NvGpuBufferType.Count];

            for (int Index = 0; Index < UploadedKeys.Length; Index++)
            {
                UploadedKeys[Index] = new HashSet<long>();
            }

            ImageTypes = new Dictionary<long, ImageType>();
        }

        public void SendColorBuffer(NvGpuVmm Vmm, long Position, int Attachment, GalImage NewImage)
        {
            long Size = (uint)ImageUtils.GetSize(NewImage);

            ImageTypes[Position] = ImageType.ColorBuffer;

            if (!TryReuse(Vmm, Position, NewImage))
            {
                Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage);
            }

            Gpu.Renderer.RenderTarget.BindColor(Position, Attachment);
        }

        public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage)
        {
            long Size = (uint)ImageUtils.GetSize(NewImage);

            ImageTypes[Position] = ImageType.ZetaBuffer;

            if (!TryReuse(Vmm, Position, NewImage))
            {
                Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage);
            }

            Gpu.Renderer.RenderTarget.BindZeta(Position);
        }

        public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage)
        {
            PrepareSendTexture(Vmm, Position, NewImage);

            ImageTypes[Position] = ImageType.Texture;
        }

        private void PrepareSendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage)
        {
            long Size = ImageUtils.GetSize(NewImage);

            bool SkipCheck = false;

            if (ImageTypes.TryGetValue(Position, out ImageType OldType))
            {
                if (OldType == ImageType.ColorBuffer || OldType == ImageType.ZetaBuffer)
                {
                    //Avoid data destruction
                    MemoryRegionModified(Vmm, Position, Size, NvGpuBufferType.Texture);

                    SkipCheck = true;
                }
            }

            if (SkipCheck || !MemoryRegionModified(Vmm, Position, Size, NvGpuBufferType.Texture))
            {
                if (TryReuse(Vmm, Position, NewImage))
                {
                    return;
                }
            }

            byte[] Data = ImageUtils.ReadTexture(Vmm, NewImage, Position);

            Gpu.Renderer.Texture.Create(Position, Data, NewImage);
        }

        private bool TryReuse(NvGpuVmm Vmm, long Position, GalImage NewImage)
        {
            if (Gpu.Renderer.Texture.TryGetImage(Position, out GalImage CachedImage) && CachedImage.SizeMatches(NewImage))
            {
                Gpu.Renderer.RenderTarget.Reinterpret(Position, NewImage);

                return true;
            }

            return false;
        }

        public bool MemoryRegionModified(NvGpuVmm Vmm, long Position, long Size, NvGpuBufferType Type)
        {
            HashSet<long> Uploaded = UploadedKeys[(int)Type];

            if (!Uploaded.Add(Position))
            {
                return false;
            }

            return Vmm.IsRegionModified(Position, Size, Type);
        }

        public void ClearPbCache()
        {
            for (int Index = 0; Index < UploadedKeys.Length; Index++)
            {
                UploadedKeys[Index].Clear();
            }
        }

        public void ClearPbCache(NvGpuBufferType Type)
        {
            UploadedKeys[(int)Type].Clear();
        }
    }
}