TextureCopyIncompatible.cs
1 using OpenTK.Graphics.OpenGL; 2 using System; 3 using System.Collections.Generic; 4 using System.Globalization; 5 using System.Numerics; 6 7 namespace Ryujinx.Graphics.OpenGL.Image 8 { 9 class TextureCopyIncompatible 10 { 11 private const string ComputeShaderShortening = @"#version 450 core 12 13 layout (binding = 0, $SRC_FORMAT$) uniform uimage2D src; 14 layout (binding = 1, $DST_FORMAT$) uniform uimage2D dst; 15 16 layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; 17 18 void main() 19 { 20 uvec2 coords = gl_GlobalInvocationID.xy; 21 ivec2 imageSz = imageSize(src); 22 23 if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y) 24 { 25 return; 26 } 27 28 uint coordsShifted = coords.x << $RATIO_LOG2$; 29 30 uvec2 dstCoords0 = uvec2(coordsShifted, coords.y); 31 uvec2 dstCoords1 = uvec2(coordsShifted + 1, coords.y); 32 uvec2 dstCoords2 = uvec2(coordsShifted + 2, coords.y); 33 uvec2 dstCoords3 = uvec2(coordsShifted + 3, coords.y); 34 35 uvec4 rgba = imageLoad(src, ivec2(coords)); 36 37 imageStore(dst, ivec2(dstCoords0), rgba.rrrr); 38 imageStore(dst, ivec2(dstCoords1), rgba.gggg); 39 imageStore(dst, ivec2(dstCoords2), rgba.bbbb); 40 imageStore(dst, ivec2(dstCoords3), rgba.aaaa); 41 }"; 42 43 private const string ComputeShaderWidening = @"#version 450 core 44 45 layout (binding = 0, $SRC_FORMAT$) uniform uimage2D src; 46 layout (binding = 1, $DST_FORMAT$) uniform uimage2D dst; 47 48 layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in; 49 50 void main() 51 { 52 uvec2 coords = gl_GlobalInvocationID.xy; 53 ivec2 imageSz = imageSize(dst); 54 55 if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y) 56 { 57 return; 58 } 59 60 uvec2 srcCoords = uvec2(coords.x << $RATIO_LOG2$, coords.y); 61 62 uint r = imageLoad(src, ivec2(srcCoords) + ivec2(0, 0)).r; 63 uint g = imageLoad(src, ivec2(srcCoords) + ivec2(1, 0)).r; 64 uint b = imageLoad(src, ivec2(srcCoords) + ivec2(2, 0)).r; 65 uint a = imageLoad(src, ivec2(srcCoords) + ivec2(3, 0)).r; 66 67 imageStore(dst, ivec2(coords), uvec4(r, g, b, a)); 68 }"; 69 70 private readonly OpenGLRenderer _renderer; 71 private readonly Dictionary<int, int> _shorteningProgramHandles; 72 private readonly Dictionary<int, int> _wideningProgramHandles; 73 74 public TextureCopyIncompatible(OpenGLRenderer renderer) 75 { 76 _renderer = renderer; 77 _shorteningProgramHandles = new Dictionary<int, int>(); 78 _wideningProgramHandles = new Dictionary<int, int>(); 79 } 80 81 public void CopyIncompatibleFormats(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int srcLevel, int dstLevel, int depth, int levels) 82 { 83 int srcBpp = src.Info.BytesPerPixel; 84 int dstBpp = dst.Info.BytesPerPixel; 85 86 // Calculate ideal component size, given our constraints: 87 // - Component size must not exceed bytes per pixel of source and destination image formats. 88 // - Maximum component size is 4 (R32). 89 int componentSize = Math.Min(Math.Min(srcBpp, dstBpp), 4); 90 91 int srcComponentsCount = srcBpp / componentSize; 92 int dstComponentsCount = dstBpp / componentSize; 93 94 var srcFormat = GetFormat(componentSize, srcComponentsCount); 95 var dstFormat = GetFormat(componentSize, dstComponentsCount); 96 97 GL.UseProgram(srcBpp < dstBpp 98 ? GetWideningShader(componentSize, srcComponentsCount, dstComponentsCount) 99 : GetShorteningShader(componentSize, srcComponentsCount, dstComponentsCount)); 100 101 for (int l = 0; l < levels; l++) 102 { 103 int srcWidth = Math.Max(1, src.Info.Width >> l); 104 int srcHeight = Math.Max(1, src.Info.Height >> l); 105 106 int dstWidth = Math.Max(1, dst.Info.Width >> l); 107 int dstHeight = Math.Max(1, dst.Info.Height >> l); 108 109 int width = Math.Min(srcWidth, dstWidth); 110 int height = Math.Min(srcHeight, dstHeight); 111 112 for (int z = 0; z < depth; z++) 113 { 114 GL.BindImageTexture(0, src.Handle, srcLevel + l, false, srcLayer + z, TextureAccess.ReadOnly, srcFormat); 115 GL.BindImageTexture(1, dst.Handle, dstLevel + l, false, dstLayer + z, TextureAccess.WriteOnly, dstFormat); 116 117 GL.DispatchCompute((width + 31) / 32, (height + 31) / 32, 1); 118 } 119 } 120 121 Pipeline pipeline = (Pipeline)_renderer.Pipeline; 122 123 pipeline.RestoreProgram(); 124 pipeline.RestoreImages1And2(); 125 } 126 127 private static SizedInternalFormat GetFormat(int componentSize, int componentsCount) 128 { 129 if (componentSize == 1) 130 { 131 return componentsCount switch 132 { 133 1 => SizedInternalFormat.R8ui, 134 2 => SizedInternalFormat.Rg8ui, 135 4 => SizedInternalFormat.Rgba8ui, 136 _ => throw new ArgumentException($"Invalid components count {componentsCount}."), 137 }; 138 } 139 else if (componentSize == 2) 140 { 141 return componentsCount switch 142 { 143 1 => SizedInternalFormat.R16ui, 144 2 => SizedInternalFormat.Rg16ui, 145 4 => SizedInternalFormat.Rgba16ui, 146 _ => throw new ArgumentException($"Invalid components count {componentsCount}."), 147 }; 148 } 149 else if (componentSize == 4) 150 { 151 return componentsCount switch 152 { 153 1 => SizedInternalFormat.R32ui, 154 2 => SizedInternalFormat.Rg32ui, 155 4 => SizedInternalFormat.Rgba32ui, 156 _ => throw new ArgumentException($"Invalid components count {componentsCount}."), 157 }; 158 } 159 else 160 { 161 throw new ArgumentException($"Invalid component size {componentSize}."); 162 } 163 } 164 165 private int GetShorteningShader(int componentSize, int srcComponentsCount, int dstComponentsCount) 166 { 167 return GetShader(ComputeShaderShortening, _shorteningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount); 168 } 169 170 private int GetWideningShader(int componentSize, int srcComponentsCount, int dstComponentsCount) 171 { 172 return GetShader(ComputeShaderWidening, _wideningProgramHandles, componentSize, srcComponentsCount, dstComponentsCount); 173 } 174 175 private static int GetShader( 176 string code, 177 Dictionary<int, int> programHandles, 178 int componentSize, 179 int srcComponentsCount, 180 int dstComponentsCount) 181 { 182 int componentSizeLog2 = BitOperations.Log2((uint)componentSize); 183 184 int srcIndex = componentSizeLog2 + BitOperations.Log2((uint)srcComponentsCount) * 3; 185 int dstIndex = componentSizeLog2 + BitOperations.Log2((uint)dstComponentsCount) * 3; 186 187 int key = srcIndex | (dstIndex << 8); 188 189 if (!programHandles.TryGetValue(key, out int programHandle)) 190 { 191 int csHandle = GL.CreateShader(ShaderType.ComputeShader); 192 193 string[] formatTable = new[] { "r8ui", "r16ui", "r32ui", "rg8ui", "rg16ui", "rg32ui", "rgba8ui", "rgba16ui", "rgba32ui" }; 194 195 string srcFormat = formatTable[srcIndex]; 196 string dstFormat = formatTable[dstIndex]; 197 198 int srcBpp = srcComponentsCount * componentSize; 199 int dstBpp = dstComponentsCount * componentSize; 200 201 int ratio = srcBpp < dstBpp ? dstBpp / srcBpp : srcBpp / dstBpp; 202 int ratioLog2 = BitOperations.Log2((uint)ratio); 203 204 GL.ShaderSource(csHandle, code 205 .Replace("$SRC_FORMAT$", srcFormat) 206 .Replace("$DST_FORMAT$", dstFormat) 207 .Replace("$RATIO_LOG2$", ratioLog2.ToString(CultureInfo.InvariantCulture))); 208 209 GL.CompileShader(csHandle); 210 211 programHandle = GL.CreateProgram(); 212 213 GL.AttachShader(programHandle, csHandle); 214 GL.LinkProgram(programHandle); 215 GL.DetachShader(programHandle, csHandle); 216 GL.DeleteShader(csHandle); 217 218 GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status); 219 220 if (status == 0) 221 { 222 throw new Exception(GL.GetProgramInfoLog(programHandle)); 223 } 224 225 programHandles.Add(key, programHandle); 226 } 227 228 return programHandle; 229 } 230 231 public void Dispose() 232 { 233 foreach (int handle in _shorteningProgramHandles.Values) 234 { 235 GL.DeleteProgram(handle); 236 } 237 238 _shorteningProgramHandles.Clear(); 239 240 foreach (int handle in _wideningProgramHandles.Values) 241 { 242 GL.DeleteProgram(handle); 243 } 244 245 _wideningProgramHandles.Clear(); 246 } 247 } 248 }