aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/BackgroundDiskCacheWriter.cs
blob: e0f17ba9c46f1ffc60174c5d5ca75920aa06b7f7 (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
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
using System.IO;

namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
    /// <summary>
    /// Represents a background disk cache writer.
    /// </summary>
    class BackgroundDiskCacheWriter : IDisposable
    {
        /// <summary>
        /// Possible operation to do on the <see cref="_fileWriterWorkerQueue"/>.
        /// </summary>
        private enum CacheFileOperation
        {
            /// <summary>
            /// Operation to add a shader to the cache.
            /// </summary>
            AddShader,
        }

        /// <summary>
        /// Represents an operation to perform on the <see cref="_fileWriterWorkerQueue"/>.
        /// </summary>
        private readonly struct CacheFileOperationTask
        {
            /// <summary>
            /// The type of operation to perform.
            /// </summary>
            public readonly CacheFileOperation Type;

            /// <summary>
            /// The data associated to this operation or null.
            /// </summary>
            public readonly object Data;

            public CacheFileOperationTask(CacheFileOperation type, object data)
            {
                Type = type;
                Data = data;
            }
        }

        /// <summary>
        /// Background shader cache write information.
        /// </summary>
        private readonly struct AddShaderData
        {
            /// <summary>
            /// Cached shader program.
            /// </summary>
            public readonly CachedShaderProgram Program;

            /// <summary>
            /// Binary host code.
            /// </summary>
            public readonly byte[] HostCode;

            /// <summary>
            /// Creates a new background shader cache write information.
            /// </summary>
            /// <param name="program">Cached shader program</param>
            /// <param name="hostCode">Binary host code</param>
            public AddShaderData(CachedShaderProgram program, byte[] hostCode)
            {
                Program = program;
                HostCode = hostCode;
            }
        }

        private readonly GpuContext _context;
        private readonly DiskCacheHostStorage _hostStorage;
        private readonly AsyncWorkQueue<CacheFileOperationTask> _fileWriterWorkerQueue;

        /// <summary>
        /// Creates a new background disk cache writer.
        /// </summary>
        /// <param name="context">GPU context</param>
        /// <param name="hostStorage">Disk cache host storage</param>
        public BackgroundDiskCacheWriter(GpuContext context, DiskCacheHostStorage hostStorage)
        {
            _context = context;
            _hostStorage = hostStorage;
            _fileWriterWorkerQueue = new AsyncWorkQueue<CacheFileOperationTask>(ProcessTask, "GPU.BackgroundDiskCacheWriter");
        }

        /// <summary>
        /// Processes a shader cache background operation.
        /// </summary>
        /// <param name="task">Task to process</param>
        private void ProcessTask(CacheFileOperationTask task)
        {
            switch (task.Type)
            {
                case CacheFileOperation.AddShader:
                    AddShaderData data = (AddShaderData)task.Data;
                    try
                    {
                        _hostStorage.AddShader(_context, data.Program, data.HostCode);
                    }
                    catch (DiskCacheLoadException diskCacheLoadException)
                    {
                        Logger.Error?.Print(LogClass.Gpu, $"Error writing shader to disk cache. {diskCacheLoadException.Message}");
                    }
                    catch (IOException ioException)
                    {
                        Logger.Error?.Print(LogClass.Gpu, $"Error writing shader to disk cache. {ioException.Message}");
                    }
                    break;
            }
        }

        /// <summary>
        /// Adds a shader program to be cached in the background.
        /// </summary>
        /// <param name="program">Shader program to cache</param>
        /// <param name="hostCode">Host binary code of the program</param>
        public void AddShader(CachedShaderProgram program, byte[] hostCode)
        {
            _fileWriterWorkerQueue.Add(new CacheFileOperationTask(CacheFileOperation.AddShader, new AddShaderData(program, hostCode)));
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                _fileWriterWorkerQueue.Dispose();
            }
        }
    }
}