TextureCopyMS.cs
1 using OpenTK.Graphics.OpenGL; 2 using Ryujinx.Graphics.GAL; 3 using System; 4 using System.Numerics; 5 6 namespace Ryujinx.Graphics.OpenGL.Image 7 { 8 class TextureCopyMS 9 { 10 private const string ComputeShaderMSToNonMS = @"#version 450 core 11 12 layout (binding = 0, $FORMAT$) uniform uimage2DMS imgIn; 13 layout (binding = 1, $FORMAT$) uniform uimage2D imgOut; 14 15 layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; 16 17 void main() 18 { 19 uvec2 coords = gl_GlobalInvocationID.xy; 20 ivec2 imageSz = imageSize(imgOut); 21 if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y) 22 { 23 return; 24 } 25 int inSamples = imageSamples(imgIn); 26 int samplesInXLog2 = 0; 27 int samplesInYLog2 = 0; 28 switch (inSamples) 29 { 30 case 2: 31 samplesInXLog2 = 1; 32 break; 33 case 4: 34 samplesInXLog2 = 1; 35 samplesInYLog2 = 1; 36 break; 37 case 8: 38 samplesInXLog2 = 2; 39 samplesInYLog2 = 1; 40 break; 41 case 16: 42 samplesInXLog2 = 2; 43 samplesInYLog2 = 2; 44 break; 45 } 46 int samplesInX = 1 << samplesInXLog2; 47 int samplesInY = 1 << samplesInYLog2; 48 int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2); 49 uvec4 value = imageLoad(imgIn, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx); 50 imageStore(imgOut, ivec2(coords), value); 51 }"; 52 53 private const string ComputeShaderNonMSToMS = @"#version 450 core 54 55 layout (binding = 0, $FORMAT$) uniform uimage2D imgIn; 56 layout (binding = 1, $FORMAT$) uniform uimage2DMS imgOut; 57 58 layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; 59 60 void main() 61 { 62 uvec2 coords = gl_GlobalInvocationID.xy; 63 ivec2 imageSz = imageSize(imgIn); 64 if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y) 65 { 66 return; 67 } 68 int outSamples = imageSamples(imgOut); 69 int samplesInXLog2 = 0; 70 int samplesInYLog2 = 0; 71 switch (outSamples) 72 { 73 case 2: 74 samplesInXLog2 = 1; 75 break; 76 case 4: 77 samplesInXLog2 = 1; 78 samplesInYLog2 = 1; 79 break; 80 case 8: 81 samplesInXLog2 = 2; 82 samplesInYLog2 = 1; 83 break; 84 case 16: 85 samplesInXLog2 = 2; 86 samplesInYLog2 = 2; 87 break; 88 } 89 int samplesInX = 1 << samplesInXLog2; 90 int samplesInY = 1 << samplesInYLog2; 91 int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2); 92 uvec4 value = imageLoad(imgIn, ivec2(coords)); 93 imageStore(imgOut, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx, value); 94 }"; 95 96 private readonly OpenGLRenderer _renderer; 97 private readonly int[] _msToNonMSProgramHandles; 98 private readonly int[] _nonMSToMSProgramHandles; 99 100 public TextureCopyMS(OpenGLRenderer renderer) 101 { 102 _renderer = renderer; 103 _msToNonMSProgramHandles = new int[5]; 104 _nonMSToMSProgramHandles = new int[5]; 105 } 106 107 public void CopyMSToNonMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth) 108 { 109 TextureCreateInfo srcInfo = src.Info; 110 TextureCreateInfo dstInfo = dst.Info; 111 112 int srcHandle = CreateViewIfNeeded(src); 113 int dstHandle = CreateViewIfNeeded(dst); 114 115 int dstWidth = dstInfo.Width; 116 int dstHeight = dstInfo.Height; 117 118 GL.UseProgram(GetMSToNonMSShader(srcInfo.BytesPerPixel)); 119 120 for (int z = 0; z < depth; z++) 121 { 122 GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel)); 123 GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel)); 124 125 GL.DispatchCompute((dstWidth + 31) / 32, (dstHeight + 31) / 32, 1); 126 } 127 128 Pipeline pipeline = (Pipeline)_renderer.Pipeline; 129 130 pipeline.RestoreProgram(); 131 pipeline.RestoreImages1And2(); 132 133 DestroyViewIfNeeded(src, srcHandle); 134 DestroyViewIfNeeded(dst, dstHandle); 135 } 136 137 public void CopyNonMSToMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth) 138 { 139 TextureCreateInfo srcInfo = src.Info; 140 TextureCreateInfo dstInfo = dst.Info; 141 142 int srcHandle = CreateViewIfNeeded(src); 143 int dstHandle = CreateViewIfNeeded(dst); 144 145 int srcWidth = srcInfo.Width; 146 int srcHeight = srcInfo.Height; 147 148 GL.UseProgram(GetNonMSToMSShader(srcInfo.BytesPerPixel)); 149 150 for (int z = 0; z < depth; z++) 151 { 152 GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel)); 153 GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel)); 154 155 GL.DispatchCompute((srcWidth + 31) / 32, (srcHeight + 31) / 32, 1); 156 } 157 158 Pipeline pipeline = (Pipeline)_renderer.Pipeline; 159 160 pipeline.RestoreProgram(); 161 pipeline.RestoreImages1And2(); 162 163 DestroyViewIfNeeded(src, srcHandle); 164 DestroyViewIfNeeded(dst, dstHandle); 165 } 166 167 private static SizedInternalFormat GetFormat(int bytesPerPixel) 168 { 169 return bytesPerPixel switch 170 { 171 1 => SizedInternalFormat.R8ui, 172 2 => SizedInternalFormat.R16ui, 173 4 => SizedInternalFormat.R32ui, 174 8 => SizedInternalFormat.Rg32ui, 175 16 => SizedInternalFormat.Rgba32ui, 176 _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}."), 177 }; 178 } 179 180 private static int CreateViewIfNeeded(ITextureInfo texture) 181 { 182 // Binding sRGB textures as images doesn't work on NVIDIA, 183 // we need to create and bind a RGBA view for it to work. 184 if (texture.Info.Format == Format.R8G8B8A8Srgb) 185 { 186 int handle = GL.GenTexture(); 187 188 GL.TextureView( 189 handle, 190 texture.Info.Target.Convert(), 191 texture.Storage.Handle, 192 PixelInternalFormat.Rgba8, 193 texture.FirstLevel, 194 1, 195 texture.FirstLayer, 196 texture.Info.GetLayers()); 197 198 return handle; 199 } 200 201 return texture.Handle; 202 } 203 204 private static void DestroyViewIfNeeded(ITextureInfo info, int handle) 205 { 206 if (info.Handle != handle) 207 { 208 GL.DeleteTexture(handle); 209 } 210 } 211 212 private int GetMSToNonMSShader(int bytesPerPixel) 213 { 214 return GetShader(ComputeShaderMSToNonMS, _msToNonMSProgramHandles, bytesPerPixel); 215 } 216 217 private int GetNonMSToMSShader(int bytesPerPixel) 218 { 219 return GetShader(ComputeShaderNonMSToMS, _nonMSToMSProgramHandles, bytesPerPixel); 220 } 221 222 private static int GetShader(string code, int[] programHandles, int bytesPerPixel) 223 { 224 int index = BitOperations.Log2((uint)bytesPerPixel); 225 226 if (programHandles[index] == 0) 227 { 228 int csHandle = GL.CreateShader(ShaderType.ComputeShader); 229 230 string format = new[] { "r8ui", "r16ui", "r32ui", "rg32ui", "rgba32ui" }[index]; 231 232 GL.ShaderSource(csHandle, code.Replace("$FORMAT$", format)); 233 GL.CompileShader(csHandle); 234 235 int programHandle = GL.CreateProgram(); 236 237 GL.AttachShader(programHandle, csHandle); 238 GL.LinkProgram(programHandle); 239 GL.DetachShader(programHandle, csHandle); 240 GL.DeleteShader(csHandle); 241 242 GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status); 243 244 if (status == 0) 245 { 246 throw new Exception(GL.GetProgramInfoLog(programHandle)); 247 } 248 249 programHandles[index] = programHandle; 250 } 251 252 return programHandles[index]; 253 } 254 255 public void Dispose() 256 { 257 for (int i = 0; i < _msToNonMSProgramHandles.Length; i++) 258 { 259 if (_msToNonMSProgramHandles[i] != 0) 260 { 261 GL.DeleteProgram(_msToNonMSProgramHandles[i]); 262 _msToNonMSProgramHandles[i] = 0; 263 } 264 } 265 266 for (int i = 0; i < _nonMSToMSProgramHandles.Length; i++) 267 { 268 if (_nonMSToMSProgramHandles[i] != 0) 269 { 270 GL.DeleteProgram(_nonMSToMSProgramHandles[i]); 271 _nonMSToMSProgramHandles[i] = 0; 272 } 273 } 274 } 275 } 276 }