OpenGLRenderer.cs
1 using OpenTK.Graphics.OpenGL; 2 using Ryujinx.Common.Configuration; 3 using Ryujinx.Common.Logging; 4 using Ryujinx.Graphics.GAL; 5 using Ryujinx.Graphics.OpenGL.Image; 6 using Ryujinx.Graphics.OpenGL.Queries; 7 using Ryujinx.Graphics.Shader.Translation; 8 using System; 9 10 namespace Ryujinx.Graphics.OpenGL 11 { 12 public sealed class OpenGLRenderer : IRenderer 13 { 14 private readonly Pipeline _pipeline; 15 16 public IPipeline Pipeline => _pipeline; 17 18 private readonly Counters _counters; 19 20 private readonly Window _window; 21 22 public IWindow Window => _window; 23 24 private readonly TextureCopy _textureCopy; 25 private readonly TextureCopy _backgroundTextureCopy; 26 internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy; 27 internal TextureCopyIncompatible TextureCopyIncompatible { get; } 28 internal TextureCopyMS TextureCopyMS { get; } 29 30 private readonly Sync _sync; 31 32 public event EventHandler<ScreenCaptureImageInfo> ScreenCaptured; 33 34 internal PersistentBuffers PersistentBuffers { get; } 35 36 internal ResourcePool ResourcePool { get; } 37 38 internal int BufferCount { get; private set; } 39 40 public string GpuVendor { get; private set; } 41 public string GpuRenderer { get; private set; } 42 public string GpuVersion { get; private set; } 43 44 public bool PreferThreading => true; 45 46 public OpenGLRenderer() 47 { 48 _pipeline = new Pipeline(); 49 _counters = new Counters(); 50 _window = new Window(this); 51 _textureCopy = new TextureCopy(this); 52 _backgroundTextureCopy = new TextureCopy(this); 53 TextureCopyIncompatible = new TextureCopyIncompatible(this); 54 TextureCopyMS = new TextureCopyMS(this); 55 _sync = new Sync(); 56 PersistentBuffers = new PersistentBuffers(); 57 ResourcePool = new ResourcePool(); 58 } 59 60 public BufferHandle CreateBuffer(int size, GAL.BufferAccess access) 61 { 62 BufferCount++; 63 64 var memType = access & GAL.BufferAccess.MemoryTypeMask; 65 66 if (memType == GAL.BufferAccess.HostMemory) 67 { 68 BufferHandle handle = Buffer.CreatePersistent(size); 69 70 PersistentBuffers.Map(handle, size); 71 72 return handle; 73 } 74 else 75 { 76 return Buffer.Create(size); 77 } 78 } 79 80 public BufferHandle CreateBuffer(nint pointer, int size) 81 { 82 throw new NotSupportedException(); 83 } 84 85 public BufferHandle CreateBufferSparse(ReadOnlySpan<BufferRange> storageBuffers) 86 { 87 throw new NotSupportedException(); 88 } 89 90 public IImageArray CreateImageArray(int size, bool isBuffer) 91 { 92 return new ImageArray(size); 93 } 94 95 public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) 96 { 97 return new Program(shaders, info.FragmentOutputMap); 98 } 99 100 public ISampler CreateSampler(SamplerCreateInfo info) 101 { 102 return new Sampler(info); 103 } 104 105 public ITexture CreateTexture(TextureCreateInfo info) 106 { 107 if (info.Target == Target.TextureBuffer) 108 { 109 return new TextureBuffer(this, info); 110 } 111 else 112 { 113 return ResourcePool.GetTextureOrNull(info) ?? new TextureStorage(this, info).CreateDefaultView(); 114 } 115 } 116 117 public ITextureArray CreateTextureArray(int size, bool isBuffer) 118 { 119 return new TextureArray(size); 120 } 121 122 public void DeleteBuffer(BufferHandle buffer) 123 { 124 PersistentBuffers.Unmap(buffer); 125 126 Buffer.Delete(buffer); 127 } 128 129 public HardwareInfo GetHardwareInfo() 130 { 131 return new HardwareInfo(GpuVendor, GpuRenderer, GpuVendor); // OpenGL does not provide a driver name, vendor name is closest analogue. 132 } 133 134 public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) 135 { 136 return Buffer.GetData(this, buffer, offset, size); 137 } 138 139 public Capabilities GetCapabilities() 140 { 141 bool intelWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows; 142 bool intelUnix = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelUnix; 143 bool amdWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows; 144 145 return new Capabilities( 146 api: TargetApi.OpenGL, 147 vendorName: GpuVendor, 148 memoryType: SystemMemoryType.BackendManaged, 149 hasFrontFacingBug: intelWindows, 150 hasVectorIndexingBug: amdWindows, 151 needsFragmentOutputSpecialization: false, 152 reduceShaderPrecision: false, 153 supportsAstcCompression: HwCapabilities.SupportsAstcCompression, 154 supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc, 155 supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc, 156 supportsBc67Compression: true, // Should check BPTC extension, but for some reason NVIDIA is not exposing the extension. 157 supportsEtc2Compression: true, 158 supports3DTextureCompression: false, 159 supportsBgraFormat: false, 160 supportsR4G4Format: false, 161 supportsR4G4B4A4Format: true, 162 supportsScaledVertexFormats: true, 163 supportsSnormBufferTextureFormat: false, 164 supports5BitComponentFormat: true, 165 supportsSparseBuffer: false, 166 supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced, 167 supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock, 168 supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering, 169 supportsGeometryShader: true, 170 supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, 171 supportsTransformFeedback: true, 172 supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted, 173 supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, 174 supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat, 175 supportsCubemapView: true, 176 supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset, 177 supportsQuads: HwCapabilities.SupportsQuads, 178 supportsSeparateSampler: false, 179 supportsShaderBallot: HwCapabilities.SupportsShaderBallot, 180 supportsShaderBarrierDivergence: !(intelWindows || intelUnix), 181 supportsShaderFloat64: true, 182 supportsTextureGatherOffsets: true, 183 supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod, 184 supportsVertexStoreAndAtomics: true, 185 supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, 186 supportsViewportMask: HwCapabilities.SupportsViewportArray2, 187 supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, 188 supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, 189 supportsDepthClipControl: true, 190 uniformBufferSetIndex: 0, 191 storageBufferSetIndex: 1, 192 textureSetIndex: 2, 193 imageSetIndex: 3, 194 extraSetBaseIndex: 0, 195 maximumExtraSets: 0, 196 maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver? 197 maximumStorageBuffersPerStage: 16, 198 maximumTexturesPerStage: 32, 199 maximumImagesPerStage: 8, 200 maximumComputeSharedMemorySize: HwCapabilities.MaximumComputeSharedMemorySize, 201 maximumSupportedAnisotropy: HwCapabilities.MaximumSupportedAnisotropy, 202 shaderSubgroupSize: Constants.MaxSubgroupSize, 203 storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment, 204 textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment, 205 gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0, // Precision is 8 for these vendors on Vulkan. 206 maximumGpuMemory: 0); 207 } 208 209 public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data) 210 { 211 Buffer.SetData(buffer, offset, data); 212 } 213 214 public void UpdateCounters() 215 { 216 _counters.Update(); 217 } 218 219 public void PreFrame() 220 { 221 _sync.Cleanup(); 222 ResourcePool.Tick(); 223 } 224 225 public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, float divisor, bool hostReserved) 226 { 227 return _counters.QueueReport(type, resultHandler, divisor, _pipeline.DrawCount, hostReserved); 228 } 229 230 public void Initialize(GraphicsDebugLevel glLogLevel) 231 { 232 Debugger.Initialize(glLogLevel); 233 234 PrintGpuInformation(); 235 236 if (HwCapabilities.SupportsParallelShaderCompile) 237 { 238 GL.Arb.MaxShaderCompilerThreads(Math.Min(Environment.ProcessorCount, 8)); 239 } 240 241 _counters.Initialize(); 242 243 // This is required to disable [0, 1] clamping for SNorm outputs on compatibility profiles. 244 // This call is expected to fail if we're running with a core profile, 245 // as this clamp target was deprecated, but that's fine as a core profile 246 // should already have the desired behaviour were outputs are not clamped. 247 GL.ClampColor(ClampColorTarget.ClampFragmentColor, ClampColorMode.False); 248 } 249 250 private void PrintGpuInformation() 251 { 252 GpuVendor = GL.GetString(StringName.Vendor); 253 GpuRenderer = GL.GetString(StringName.Renderer); 254 GpuVersion = GL.GetString(StringName.Version); 255 256 Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); 257 } 258 259 public void ResetCounter(CounterType type) 260 { 261 _counters.QueueReset(type); 262 } 263 264 public void BackgroundContextAction(Action action, bool alwaysBackground = false) 265 { 266 // alwaysBackground is ignored, since we cannot switch from the current context. 267 268 if (_window.BackgroundContext.HasContext()) 269 { 270 action(); // We have a context already - use that (assuming it is the main one). 271 } 272 else 273 { 274 _window.BackgroundContext.Invoke(action); 275 } 276 } 277 278 public void InitializeBackgroundContext(IOpenGLContext baseContext) 279 { 280 _window.InitializeBackgroundContext(baseContext); 281 } 282 283 public void Dispose() 284 { 285 _textureCopy.Dispose(); 286 _backgroundTextureCopy.Dispose(); 287 TextureCopyMS.Dispose(); 288 PersistentBuffers.Dispose(); 289 ResourcePool.Dispose(); 290 _pipeline.Dispose(); 291 _window.Dispose(); 292 _counters.Dispose(); 293 _sync.Dispose(); 294 } 295 296 public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) 297 { 298 return new Program(programBinary, hasFragmentShader, info.FragmentOutputMap); 299 } 300 301 public void CreateSync(ulong id, bool strict) 302 { 303 _sync.Create(id); 304 } 305 306 public void WaitSync(ulong id) 307 { 308 _sync.Wait(id); 309 } 310 311 public ulong GetCurrentSync() 312 { 313 return _sync.GetCurrent(); 314 } 315 316 public void SetInterruptAction(Action<Action> interruptAction) 317 { 318 // Currently no need for an interrupt action. 319 } 320 321 public void Screenshot() 322 { 323 _window.ScreenCaptureRequested = true; 324 } 325 326 public void OnScreenCaptured(ScreenCaptureImageInfo bitmap) 327 { 328 ScreenCaptured?.Invoke(this, bitmap); 329 } 330 331 public bool PrepareHostMapping(nint address, ulong size) 332 { 333 return false; 334 } 335 } 336 }