aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Ava/Ui/Backend/Vulkan/Skia/VulkanRenderTarget.cs
blob: ba7ddc7a38f6df378d4839dd440d62beec6b8f99 (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
using System;
using Avalonia.Skia;
using Ryujinx.Ava.Ui.Vulkan;
using Ryujinx.Ava.Ui.Vulkan.Surfaces;
using SkiaSharp;

namespace Ryujinx.Ava.Ui.Backend.Vulkan
{
    internal class VulkanRenderTarget : ISkiaGpuRenderTarget
    {
        public GRContext GrContext { get; set; }

        private readonly VulkanSurfaceRenderTarget _surface;
        private readonly IVulkanPlatformSurface _vulkanPlatformSurface;

        public VulkanRenderTarget(VulkanPlatformInterface vulkanPlatformInterface, IVulkanPlatformSurface vulkanPlatformSurface)
        {
            _surface = vulkanPlatformInterface.CreateRenderTarget(vulkanPlatformSurface);
            _vulkanPlatformSurface = vulkanPlatformSurface;
        }

        public void Dispose()
        {
            _surface.Dispose();
        }

        public ISkiaGpuRenderSession BeginRenderingSession()
        {
            var session = _surface.BeginDraw(_vulkanPlatformSurface.Scaling);
            bool success = false;
            try
            {
                var disp = session.Display;
                var api = session.Api;

                var size = session.Size;
                var scaling = session.Scaling;
                if (size.Width <= 0 || size.Height <= 0 || scaling < 0)
                {
                    size = new Avalonia.PixelSize(1, 1);
                    scaling = 1;
                }

                lock (GrContext)
                {
                    GrContext.ResetContext();

                    var imageInfo = new GRVkImageInfo()
                    {
                        CurrentQueueFamily = disp.QueueFamilyIndex,
                        Format = _surface.ImageFormat,
                        Image = _surface.Image.Handle,
                        ImageLayout = (uint)_surface.Image.CurrentLayout,
                        ImageTiling = (uint)_surface.Image.Tiling,
                        ImageUsageFlags = _surface.UsageFlags,
                        LevelCount = _surface.MipLevels,
                        SampleCount = 1,
                        Protected = false,
                        Alloc = new GRVkAlloc()
                        {
                            Memory = _surface.Image.MemoryHandle,
                            Flags = 0,
                            Offset = 0,
                            Size = _surface.MemorySize
                        }
                    };

                    var renderTarget =
                        new GRBackendRenderTarget((int)size.Width, (int)size.Height, 1,
                            imageInfo);
                    var surface = SKSurface.Create(GrContext, renderTarget,
                        GRSurfaceOrigin.TopLeft,
                        _surface.IsRgba ? SKColorType.Rgba8888 : SKColorType.Bgra8888, SKColorSpace.CreateSrgb());

                    if (surface == null)
                    {
                        throw new InvalidOperationException(
                            "Surface can't be created with the provided render target");
                    }

                    success = true;

                    return new VulkanGpuSession(GrContext, renderTarget, surface, session);
                }
            }
            finally
            {
                if (!success)
                {
                    session.Dispose();
                }
            }
        }

        public bool IsCorrupted { get; }

        internal class VulkanGpuSession : ISkiaGpuRenderSession
        {
            private readonly GRBackendRenderTarget _backendRenderTarget;
            private readonly VulkanSurfaceRenderingSession _vulkanSession;

            public VulkanGpuSession(GRContext grContext,
                GRBackendRenderTarget backendRenderTarget,
                SKSurface surface,
                VulkanSurfaceRenderingSession vulkanSession)
            {
                GrContext = grContext;
                _backendRenderTarget = backendRenderTarget;
                SkSurface = surface;
                _vulkanSession = vulkanSession;

                SurfaceOrigin = GRSurfaceOrigin.TopLeft;
            }

            public void Dispose()
            {
                lock (_vulkanSession.Display.Lock)
                {
                    SkSurface.Canvas.Flush();

                    SkSurface.Dispose();
                    _backendRenderTarget.Dispose();
                    GrContext.Flush();

                    _vulkanSession.Dispose();
                }
            }

            public GRContext GrContext { get; }
            public SKSurface SkSurface { get; }
            public double ScaleFactor => _vulkanSession.Scaling;
            public GRSurfaceOrigin SurfaceOrigin { get; }
        }
    }
}