Window.cs
1 using OpenTK.Graphics.OpenGL; 2 using Ryujinx.Graphics.GAL; 3 using Ryujinx.Graphics.OpenGL.Effects; 4 using Ryujinx.Graphics.OpenGL.Effects.Smaa; 5 using Ryujinx.Graphics.OpenGL.Image; 6 using System; 7 8 namespace Ryujinx.Graphics.OpenGL 9 { 10 class Window : IWindow, IDisposable 11 { 12 private readonly OpenGLRenderer _renderer; 13 14 private bool _initialized; 15 16 private int _width; 17 private int _height; 18 private bool _updateSize; 19 private int _copyFramebufferHandle; 20 private IPostProcessingEffect _antiAliasing; 21 private IScalingFilter _scalingFilter; 22 private bool _isLinear; 23 private AntiAliasing _currentAntiAliasing; 24 private bool _updateEffect; 25 private ScalingFilter _currentScalingFilter; 26 private float _scalingFilterLevel; 27 private bool _updateScalingFilter; 28 private bool _isBgra; 29 private TextureView _upscaledTexture; 30 31 internal BackgroundContextWorker BackgroundContext { get; private set; } 32 33 internal bool ScreenCaptureRequested { get; set; } 34 35 public Window(OpenGLRenderer renderer) 36 { 37 _renderer = renderer; 38 } 39 40 public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) 41 { 42 GL.Disable(EnableCap.FramebufferSrgb); 43 44 (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers(); 45 46 CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback); 47 48 GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); 49 GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); 50 51 GL.Enable(EnableCap.FramebufferSrgb); 52 53 // Restore unpack alignment to 4, as performance overlays such as RTSS may change this to load their resources. 54 GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4); 55 } 56 57 public void ChangeVSyncMode(bool vsyncEnabled) { } 58 59 public void SetSize(int width, int height) 60 { 61 _width = width; 62 _height = height; 63 64 _updateSize = true; 65 } 66 67 private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback) 68 { 69 GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); 70 GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); 71 72 TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view; 73 74 UpdateEffect(); 75 76 if (_antiAliasing != null) 77 { 78 var oldView = viewConverted; 79 80 viewConverted = _antiAliasing.Run(viewConverted, _width, _height); 81 82 if (viewConverted.Format.IsBgr()) 83 { 84 var swappedView = _renderer.TextureCopy.BgraSwap(viewConverted); 85 86 viewConverted?.Dispose(); 87 88 viewConverted = swappedView; 89 } 90 91 if (viewConverted != oldView && oldView != view) 92 { 93 oldView.Dispose(); 94 } 95 } 96 97 GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); 98 GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); 99 100 GL.FramebufferTexture( 101 FramebufferTarget.ReadFramebuffer, 102 FramebufferAttachment.ColorAttachment0, 103 viewConverted.Handle, 104 0); 105 106 GL.ReadBuffer(ReadBufferMode.ColorAttachment0); 107 108 GL.Disable(EnableCap.RasterizerDiscard); 109 GL.Disable(IndexedEnableCap.ScissorTest, 0); 110 111 GL.Clear(ClearBufferMask.ColorBufferBit); 112 113 int srcX0, srcX1, srcY0, srcY1; 114 115 if (crop.Left == 0 && crop.Right == 0) 116 { 117 srcX0 = 0; 118 srcX1 = viewConverted.Width; 119 } 120 else 121 { 122 srcX0 = crop.Left; 123 srcX1 = crop.Right; 124 } 125 126 if (crop.Top == 0 && crop.Bottom == 0) 127 { 128 srcY0 = 0; 129 srcY1 = viewConverted.Height; 130 } 131 else 132 { 133 srcY0 = crop.Top; 134 srcY1 = crop.Bottom; 135 } 136 137 float ratioX = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _height * crop.AspectRatioX / (_width * crop.AspectRatioY)); 138 float ratioY = crop.IsStretched ? 1.0f : MathF.Min(1.0f, _width * crop.AspectRatioY / (_height * crop.AspectRatioX)); 139 140 int dstWidth = (int)(_width * ratioX); 141 int dstHeight = (int)(_height * ratioY); 142 143 int dstPaddingX = (_width - dstWidth) / 2; 144 int dstPaddingY = (_height - dstHeight) / 2; 145 146 int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX; 147 int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX; 148 149 int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; 150 int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; 151 152 if (ScreenCaptureRequested) 153 { 154 CaptureFrame(srcX0, srcY0, srcX1, srcY1, view.Format.IsBgr(), crop.FlipX, crop.FlipY); 155 156 ScreenCaptureRequested = false; 157 } 158 159 if (_scalingFilter != null) 160 { 161 if (viewConverted.Format.IsBgr() && !_isBgra) 162 { 163 RecreateUpscalingTexture(true); 164 } 165 166 _scalingFilter.Run( 167 viewConverted, 168 _upscaledTexture, 169 _width, 170 _height, 171 new Extents2D( 172 srcX0, 173 srcY0, 174 srcX1, 175 srcY1), 176 new Extents2D( 177 dstX0, 178 dstY0, 179 dstX1, 180 dstY1) 181 ); 182 183 srcX0 = dstX0; 184 srcY0 = dstY0; 185 srcX1 = dstX1; 186 srcY1 = dstY1; 187 188 GL.FramebufferTexture( 189 FramebufferTarget.ReadFramebuffer, 190 FramebufferAttachment.ColorAttachment0, 191 _upscaledTexture.Handle, 192 0); 193 } 194 195 GL.BlitFramebuffer( 196 srcX0, 197 srcY0, 198 srcX1, 199 srcY1, 200 dstX0, 201 dstY0, 202 dstX1, 203 dstY1, 204 ClearBufferMask.ColorBufferBit, 205 _isLinear ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest); 206 207 // Remove Alpha channel 208 GL.ColorMask(false, false, false, true); 209 GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f); 210 GL.Clear(ClearBufferMask.ColorBufferBit); 211 212 for (int i = 0; i < Constants.MaxRenderTargets; i++) 213 { 214 ((Pipeline)_renderer.Pipeline).RestoreComponentMask(i); 215 } 216 217 // Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture. 218 GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne); 219 GL.Viewport(0, 0, _width, _height); 220 GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer); 221 222 swapBuffersCallback(); 223 224 ((Pipeline)_renderer.Pipeline).RestoreClipControl(); 225 ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); 226 ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); 227 ((Pipeline)_renderer.Pipeline).RestoreViewport0(); 228 229 if (viewConverted != view) 230 { 231 viewConverted.Dispose(); 232 } 233 } 234 235 private int GetCopyFramebufferHandleLazy() 236 { 237 int handle = _copyFramebufferHandle; 238 239 if (handle == 0) 240 { 241 handle = GL.GenFramebuffer(); 242 243 _copyFramebufferHandle = handle; 244 } 245 246 return handle; 247 } 248 249 public void InitializeBackgroundContext(IOpenGLContext baseContext) 250 { 251 BackgroundContext = new BackgroundContextWorker(baseContext); 252 _initialized = true; 253 } 254 255 public void CaptureFrame(int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY) 256 { 257 long size = Math.Abs(4 * width * height); 258 byte[] bitmap = new byte[size]; 259 260 GL.ReadPixels(x, y, width, height, isBgra ? PixelFormat.Bgra : PixelFormat.Rgba, PixelType.UnsignedByte, bitmap); 261 262 _renderer.OnScreenCaptured(new ScreenCaptureImageInfo(width, height, isBgra, bitmap, flipX, flipY)); 263 } 264 265 public void Dispose() 266 { 267 if (!_initialized) 268 { 269 return; 270 } 271 272 BackgroundContext.Dispose(); 273 274 if (_copyFramebufferHandle != 0) 275 { 276 GL.DeleteFramebuffer(_copyFramebufferHandle); 277 278 _copyFramebufferHandle = 0; 279 } 280 281 _antiAliasing?.Dispose(); 282 _scalingFilter?.Dispose(); 283 _upscaledTexture?.Dispose(); 284 } 285 286 public void SetAntiAliasing(AntiAliasing effect) 287 { 288 if (_currentAntiAliasing == effect && _antiAliasing != null) 289 { 290 return; 291 } 292 293 _currentAntiAliasing = effect; 294 295 _updateEffect = true; 296 } 297 298 public void SetScalingFilter(ScalingFilter type) 299 { 300 if (_currentScalingFilter == type && _antiAliasing != null) 301 { 302 return; 303 } 304 305 _currentScalingFilter = type; 306 307 _updateScalingFilter = true; 308 } 309 310 public void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled) { } 311 312 private void UpdateEffect() 313 { 314 if (_updateEffect) 315 { 316 _updateEffect = false; 317 318 switch (_currentAntiAliasing) 319 { 320 case AntiAliasing.Fxaa: 321 _antiAliasing?.Dispose(); 322 _antiAliasing = new FxaaPostProcessingEffect(_renderer); 323 break; 324 case AntiAliasing.None: 325 _antiAliasing?.Dispose(); 326 _antiAliasing = null; 327 break; 328 case AntiAliasing.SmaaLow: 329 case AntiAliasing.SmaaMedium: 330 case AntiAliasing.SmaaHigh: 331 case AntiAliasing.SmaaUltra: 332 var quality = _currentAntiAliasing - AntiAliasing.SmaaLow; 333 if (_antiAliasing is SmaaPostProcessingEffect smaa) 334 { 335 smaa.Quality = quality; 336 } 337 else 338 { 339 _antiAliasing?.Dispose(); 340 _antiAliasing = new SmaaPostProcessingEffect(_renderer, quality); 341 } 342 break; 343 } 344 } 345 346 if (_updateSize && !_updateScalingFilter) 347 { 348 RecreateUpscalingTexture(); 349 } 350 351 _updateSize = false; 352 353 if (_updateScalingFilter) 354 { 355 _updateScalingFilter = false; 356 357 switch (_currentScalingFilter) 358 { 359 case ScalingFilter.Bilinear: 360 case ScalingFilter.Nearest: 361 _scalingFilter?.Dispose(); 362 _scalingFilter = null; 363 _isLinear = _currentScalingFilter == ScalingFilter.Bilinear; 364 _upscaledTexture?.Dispose(); 365 _upscaledTexture = null; 366 break; 367 case ScalingFilter.Fsr: 368 if (_scalingFilter is not FsrScalingFilter) 369 { 370 _scalingFilter?.Dispose(); 371 _scalingFilter = new FsrScalingFilter(_renderer); 372 } 373 _isLinear = false; 374 _scalingFilter.Level = _scalingFilterLevel; 375 376 RecreateUpscalingTexture(); 377 break; 378 case ScalingFilter.Area: 379 if (_scalingFilter is not AreaScalingFilter) 380 { 381 _scalingFilter?.Dispose(); 382 _scalingFilter = new AreaScalingFilter(_renderer); 383 } 384 _isLinear = false; 385 386 RecreateUpscalingTexture(); 387 break; 388 } 389 } 390 } 391 392 private void RecreateUpscalingTexture(bool forceBgra = false) 393 { 394 _upscaledTexture?.Dispose(); 395 396 var info = new TextureCreateInfo( 397 _width, 398 _height, 399 1, 400 1, 401 1, 402 1, 403 1, 404 1, 405 Format.R8G8B8A8Unorm, 406 DepthStencilMode.Depth, 407 Target.Texture2D, 408 forceBgra ? SwizzleComponent.Blue : SwizzleComponent.Red, 409 SwizzleComponent.Green, 410 forceBgra ? SwizzleComponent.Red : SwizzleComponent.Blue, 411 SwizzleComponent.Alpha); 412 413 _isBgra = forceBgra; 414 _upscaledTexture = _renderer.CreateTexture(info) as TextureView; 415 } 416 417 public void SetScalingFilterLevel(float level) 418 { 419 _scalingFilterLevel = level; 420 _updateScalingFilter = true; 421 } 422 } 423 }