diff options
Diffstat (limited to 'Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs')
| -rw-r--r-- | Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs new file mode 100644 index 00000000..d36bac1b --- /dev/null +++ b/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs @@ -0,0 +1,549 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.Texture; +using System; + +namespace Ryujinx.Graphics.Gal.OpenGL +{ + class OglRenderTarget : IGalRenderTarget + { + private const int NativeWidth = 1280; + private const int NativeHeight = 720; + + private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount; + + private struct Rect + { + public int X { get; private set; } + public int Y { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } + + public Rect(int x, int y, int width, int height) + { + X = x; + Y = y; + Width = width; + Height = height; + } + } + + private class FrameBufferAttachments + { + public int MapCount { get; set; } + + public DrawBuffersEnum[] Map { get; private set; } + + public long[] Colors { get; private set; } + + public long Zeta { get; set; } + + public FrameBufferAttachments() + { + Colors = new long[RenderTargetsCount]; + + Map = new DrawBuffersEnum[RenderTargetsCount]; + } + + public void Update(FrameBufferAttachments source) + { + for (int index = 0; index < RenderTargetsCount; index++) + { + Map[index] = source.Map[index]; + + Colors[index] = source.Colors[index]; + } + + MapCount = source.MapCount; + Zeta = source.Zeta; + } + } + + private int[] _colorHandles; + private int _zetaHandle; + + private OglTexture _texture; + + private ImageHandler _readTex; + + private Rect _window; + + private float[] _viewports; + + private bool _flipX; + private bool _flipY; + + private int _cropTop; + private int _cropLeft; + private int _cropRight; + private int _cropBottom; + + //This framebuffer is used to attach guest rendertargets, + //think of it as a dummy OpenGL VAO + private int _dummyFrameBuffer; + + //These framebuffers are used to blit images + private int _srcFb; + private int _dstFb; + + private FrameBufferAttachments _attachments; + private FrameBufferAttachments _oldAttachments; + + private int _copyPbo; + + public bool FramebufferSrgb { get; set; } + + public OglRenderTarget(OglTexture texture) + { + _attachments = new FrameBufferAttachments(); + + _oldAttachments = new FrameBufferAttachments(); + + _colorHandles = new int[RenderTargetsCount]; + + _viewports = new float[RenderTargetsCount * 4]; + + _texture = texture; + + texture.TextureDeleted += TextureDeletionHandler; + } + + private void TextureDeletionHandler(object sender, int handle) + { + //Texture was deleted, the handle is no longer valid, so + //reset all uses of this handle on a render target. + for (int attachment = 0; attachment < RenderTargetsCount; attachment++) + { + if (_colorHandles[attachment] == handle) + { + _colorHandles[attachment] = 0; + } + } + + if (_zetaHandle == handle) + { + _zetaHandle = 0; + } + } + + public void Bind() + { + if (_dummyFrameBuffer == 0) + { + _dummyFrameBuffer = GL.GenFramebuffer(); + } + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, _dummyFrameBuffer); + + ImageHandler cachedImage; + + for (int attachment = 0; attachment < RenderTargetsCount; attachment++) + { + long key = _attachments.Colors[attachment]; + + int handle = 0; + + if (key != 0 && _texture.TryGetImageHandler(key, out cachedImage)) + { + handle = cachedImage.Handle; + } + + if (handle == _colorHandles[attachment]) + { + continue; + } + + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.ColorAttachment0 + attachment, + handle, + 0); + + _colorHandles[attachment] = handle; + } + + if (_attachments.Zeta != 0 && _texture.TryGetImageHandler(_attachments.Zeta, out cachedImage)) + { + if (cachedImage.Handle != _zetaHandle) + { + if (cachedImage.HasDepth && cachedImage.HasStencil) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthStencilAttachment, + cachedImage.Handle, + 0); + } + else if (cachedImage.HasDepth) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthAttachment, + cachedImage.Handle, + 0); + + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.StencilAttachment, + 0, + 0); + } + else + { + throw new InvalidOperationException("Invalid image format \"" + cachedImage.Format + "\" used as Zeta!"); + } + + _zetaHandle = cachedImage.Handle; + } + } + else if (_zetaHandle != 0) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthStencilAttachment, + 0, + 0); + + _zetaHandle = 0; + } + + if (OglExtension.ViewportArray) + { + GL.ViewportArray(0, RenderTargetsCount, _viewports); + } + else + { + GL.Viewport( + (int)_viewports[0], + (int)_viewports[1], + (int)_viewports[2], + (int)_viewports[3]); + } + + if (_attachments.MapCount > 1) + { + GL.DrawBuffers(_attachments.MapCount, _attachments.Map); + } + else if (_attachments.MapCount == 1) + { + GL.DrawBuffer((DrawBufferMode)_attachments.Map[0]); + } + else + { + GL.DrawBuffer(DrawBufferMode.None); + } + + _oldAttachments.Update(_attachments); + } + + public void BindColor(long key, int attachment) + { + _attachments.Colors[attachment] = key; + } + + public void UnbindColor(int attachment) + { + _attachments.Colors[attachment] = 0; + } + + public void BindZeta(long key) + { + _attachments.Zeta = key; + } + + public void UnbindZeta() + { + _attachments.Zeta = 0; + } + + public void Present(long key) + { + _texture.TryGetImageHandler(key, out _readTex); + } + + public void SetMap(int[] map) + { + if (map != null) + { + _attachments.MapCount = map.Length; + + for (int attachment = 0; attachment < _attachments.MapCount; attachment++) + { + _attachments.Map[attachment] = DrawBuffersEnum.ColorAttachment0 + map[attachment]; + } + } + else + { + _attachments.MapCount = 0; + } + } + + public void SetTransform(bool flipX, bool flipY, int top, int left, int right, int bottom) + { + _flipX = flipX; + _flipY = flipY; + + _cropTop = top; + _cropLeft = left; + _cropRight = right; + _cropBottom = bottom; + } + + public void SetWindowSize(int width, int height) + { + _window = new Rect(0, 0, width, height); + } + + public void SetViewport(int attachment, int x, int y, int width, int height) + { + int offset = attachment * 4; + + _viewports[offset + 0] = x; + _viewports[offset + 1] = y; + _viewports[offset + 2] = width; + _viewports[offset + 3] = height; + } + + public void Render() + { + if (_readTex == null) + { + return; + } + + int srcX0, srcX1, srcY0, srcY1; + + if (_cropLeft == 0 && _cropRight == 0) + { + srcX0 = 0; + srcX1 = _readTex.Width; + } + else + { + srcX0 = _cropLeft; + srcX1 = _cropRight; + } + + if (_cropTop == 0 && _cropBottom == 0) + { + srcY0 = 0; + srcY1 = _readTex.Height; + } + else + { + srcY0 = _cropTop; + srcY1 = _cropBottom; + } + + float ratioX = MathF.Min(1f, (_window.Height * (float)NativeWidth) / ((float)NativeHeight * _window.Width)); + float ratioY = MathF.Min(1f, (_window.Width * (float)NativeHeight) / ((float)NativeWidth * _window.Height)); + + int dstWidth = (int)(_window.Width * ratioX); + int dstHeight = (int)(_window.Height * ratioY); + + int dstPaddingX = (_window.Width - dstWidth) / 2; + int dstPaddingY = (_window.Height - dstHeight) / 2; + + int dstX0 = _flipX ? _window.Width - dstPaddingX : dstPaddingX; + int dstX1 = _flipX ? dstPaddingX : _window.Width - dstPaddingX; + + int dstY0 = _flipY ? dstPaddingY : _window.Height - dstPaddingY; + int dstY1 = _flipY ? _window.Height - dstPaddingY : dstPaddingY; + + GL.Viewport(0, 0, _window.Width, _window.Height); + + if (_srcFb == 0) + { + _srcFb = GL.GenFramebuffer(); + } + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _srcFb); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); + + GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, _readTex.Handle, 0); + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + + GL.Clear(ClearBufferMask.ColorBufferBit); + + GL.Disable(EnableCap.FramebufferSrgb); + + GL.BlitFramebuffer( + srcX0, + srcY0, + srcX1, + srcY1, + dstX0, + dstY0, + dstX1, + dstY1, + ClearBufferMask.ColorBufferBit, + BlitFramebufferFilter.Linear); + + if (FramebufferSrgb) + { + GL.Enable(EnableCap.FramebufferSrgb); + } + } + + public void Copy( + GalImage srcImage, + GalImage dstImage, + long srcKey, + long dstKey, + int srcLayer, + int dstLayer, + int srcX0, + int srcY0, + int srcX1, + int srcY1, + int dstX0, + int dstY0, + int dstX1, + int dstY1) + { + if (_texture.TryGetImageHandler(srcKey, out ImageHandler srcTex) && + _texture.TryGetImageHandler(dstKey, out ImageHandler dstTex)) + { + if (srcTex.HasColor != dstTex.HasColor || + srcTex.HasDepth != dstTex.HasDepth || + srcTex.HasStencil != dstTex.HasStencil) + { + throw new NotImplementedException(); + } + + if (_srcFb == 0) + { + _srcFb = GL.GenFramebuffer(); + } + + if (_dstFb == 0) + { + _dstFb = GL.GenFramebuffer(); + } + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _srcFb); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, _dstFb); + + FramebufferAttachment attachment = GetAttachment(srcTex); + + if (ImageUtils.IsArray(srcImage.TextureTarget) && srcLayer > 0) + { + GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, attachment, srcTex.Handle, 0, srcLayer); + } + else + { + GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, attachment, srcTex.Handle, 0); + } + + if (ImageUtils.IsArray(dstImage.TextureTarget) && dstLayer > 0) + { + GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, attachment, dstTex.Handle, 0, dstLayer); + } + else + { + GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, attachment, dstTex.Handle, 0); + } + + + BlitFramebufferFilter filter = BlitFramebufferFilter.Nearest; + + if (srcTex.HasColor) + { + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + + filter = BlitFramebufferFilter.Linear; + } + + ClearBufferMask mask = GetClearMask(srcTex); + + GL.BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + } + } + + public void Reinterpret(long key, GalImage newImage) + { + if (!_texture.TryGetImage(key, out GalImage oldImage)) + { + return; + } + + if (newImage.Format == oldImage.Format && + newImage.Width == oldImage.Width && + newImage.Height == oldImage.Height && + newImage.Depth == oldImage.Depth && + newImage.LayerCount == oldImage.LayerCount && + newImage.TextureTarget == oldImage.TextureTarget) + { + return; + } + + if (_copyPbo == 0) + { + _copyPbo = GL.GenBuffer(); + } + + GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPbo); + + //The buffer should be large enough to hold the largest texture. + int bufferSize = Math.Max(ImageUtils.GetSize(oldImage), + ImageUtils.GetSize(newImage)); + + GL.BufferData(BufferTarget.PixelPackBuffer, bufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy); + + if (!_texture.TryGetImageHandler(key, out ImageHandler cachedImage)) + { + throw new InvalidOperationException(); + } + + (_, PixelFormat format, PixelType type) = OglEnumConverter.GetImageFormat(cachedImage.Format); + + TextureTarget target = ImageUtils.GetTextureTarget(newImage.TextureTarget); + + GL.BindTexture(target, cachedImage.Handle); + + GL.GetTexImage(target, 0, format, type, IntPtr.Zero); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPbo); + + GL.PixelStore(PixelStoreParameter.UnpackRowLength, oldImage.Width); + + _texture.Create(key, ImageUtils.GetSize(newImage), newImage); + + GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0); + + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); + } + + private static FramebufferAttachment GetAttachment(ImageHandler cachedImage) + { + if (cachedImage.HasColor) + { + return FramebufferAttachment.ColorAttachment0; + } + else if (cachedImage.HasDepth && cachedImage.HasStencil) + { + return FramebufferAttachment.DepthStencilAttachment; + } + else if (cachedImage.HasDepth) + { + return FramebufferAttachment.DepthAttachment; + } + else if (cachedImage.HasStencil) + { + return FramebufferAttachment.StencilAttachment; + } + else + { + throw new InvalidOperationException(); + } + } + + private static ClearBufferMask GetClearMask(ImageHandler cachedImage) + { + return (cachedImage.HasColor ? ClearBufferMask.ColorBufferBit : 0) | + (cachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) | + (cachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0); + } + } +}
\ No newline at end of file |
