From 8dbcae1ff88927dc0734d5f0e24fbf8781d68590 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 26 Jul 2020 00:03:40 -0300 Subject: [PATCH] Implement BGRA texture support (#1418) * Implement BGRA texture support * Missing AppendLine * Remove empty lines * Address PR feedback --- Ryujinx.Graphics.GAL/Format.cs | 19 +++++ Ryujinx.Graphics.OpenGL/FormatTable.cs | 8 +-- Ryujinx.Graphics.OpenGL/Image/TextureBase.cs | 2 +- Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | 65 ++++++++++++++++- Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 44 +++++++++--- Ryujinx.Graphics.OpenGL/Pipeline.cs | 71 +++++++++++-------- Ryujinx.Graphics.OpenGL/Program.cs | 2 + Ryujinx.Graphics.OpenGL/Window.cs | 9 ++- .../CodeGen/Glsl/Declarations.cs | 6 ++ .../CodeGen/Glsl/DefaultNames.cs | 2 + .../CodeGen/Glsl/OperandManager.cs | 16 +++-- .../Translation/AttributeConsts.cs | 3 + .../Translation/EmitterContext.cs | 44 +++++++++--- .../Services/SurfaceFlinger/SurfaceFlinger.cs | 2 +- 14 files changed, 232 insertions(+), 61 deletions(-) diff --git a/Ryujinx.Graphics.GAL/Format.cs b/Ryujinx.Graphics.GAL/Format.cs index 19939c38a..a66673614 100644 --- a/Ryujinx.Graphics.GAL/Format.cs +++ b/Ryujinx.Graphics.GAL/Format.cs @@ -230,6 +230,25 @@ namespace Ryujinx.Graphics.GAL return false; } + /// + /// Checks if the texture format is a BGRA format with 8-bit components. + /// + /// Texture format + /// True if the texture format is a BGRA format with 8-bit components, false otherwise + public static bool IsBgra8(this Format format) + { + switch (format) + { + case Format.B8G8R8X8Unorm: + case Format.B8G8R8A8Unorm: + case Format.B8G8R8X8Srgb: + case Format.B8G8R8A8Srgb: + return true; + } + + return false; + } + /// /// Checks if the texture format is a depth, stencil or depth-stencil format. /// diff --git a/Ryujinx.Graphics.OpenGL/FormatTable.cs b/Ryujinx.Graphics.OpenGL/FormatTable.cs index b178553cd..4200ea7e0 100644 --- a/Ryujinx.Graphics.OpenGL/FormatTable.cs +++ b/Ryujinx.Graphics.OpenGL/FormatTable.cs @@ -164,10 +164,10 @@ namespace Ryujinx.Graphics.OpenGL Add(Format.B5G5R5X1Unorm, new FormatInfo(4, true, false, All.Rgb5, PixelFormat.Bgra, PixelType.UnsignedShort5551)); Add(Format.B5G5R5A1Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort5551)); Add(Format.A1B5G5R5Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort1555Reversed)); - Add(Format.B8G8R8X8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte)); - Add(Format.B8G8R8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte)); - Add(Format.B8G8R8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.BgraInteger, PixelType.UnsignedByte)); - Add(Format.B8G8R8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.BgraInteger, PixelType.UnsignedByte)); + Add(Format.B8G8R8X8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.B8G8R8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.B8G8R8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.B8G8R8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte)); } private static void Add(Format format, FormatInfo info) diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs index dfb81642c..5f786decc 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { public int Handle { get; protected set; } - protected TextureCreateInfo Info { get; } + public TextureCreateInfo Info { get; } public int Width { get; } public int Height { get; } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index e89d5614c..edfbec5f8 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -11,6 +11,9 @@ namespace Ryujinx.Graphics.OpenGL.Image private int _srcFramebuffer; private int _dstFramebuffer; + private int _copyPboHandle; + private int _copyPboSize; + public TextureCopy(Renderer renderer) { _renderer = renderer; @@ -23,12 +26,14 @@ namespace Ryujinx.Graphics.OpenGL.Image Extents2D dstRegion, bool linearFilter) { + TextureView srcConverted = src.Format.IsBgra8() != dst.Format.IsBgra8() ? BgraSwap(src) : src; + (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers(); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); - Attach(FramebufferTarget.ReadFramebuffer, src.Format, src.Handle); + Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle); Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle); ClearBufferMask mask = GetMask(src.Format); @@ -68,6 +73,11 @@ namespace Ryujinx.Graphics.OpenGL.Image ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); + + if (srcConverted != src) + { + srcConverted.Dispose(); + } } private static void Attach(FramebufferTarget target, Format format, int handle) @@ -117,6 +127,52 @@ namespace Ryujinx.Graphics.OpenGL.Image format == Format.D32Float; } + public TextureView BgraSwap(TextureView from) + { + TextureView to = (TextureView)_renderer.CreateTexture(from.Info, 1f); + + EnsurePbo(from); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle); + + from.WriteToPbo(0, forceBgra: true); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPboHandle); + + to.ReadFromPbo(0, _copyPboSize); + + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); + + return to; + } + + private void EnsurePbo(TextureView view) + { + int requiredSize = 0; + + for (int level = 0; level < view.Info.Levels; level++) + { + requiredSize += view.Info.GetMipSize(level); + } + + if (_copyPboSize < requiredSize && _copyPboHandle != 0) + { + GL.DeleteBuffer(_copyPboHandle); + + _copyPboHandle = 0; + } + + if (_copyPboHandle == 0) + { + _copyPboHandle = GL.GenBuffer(); + _copyPboSize = requiredSize; + + GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle); + GL.BufferData(BufferTarget.PixelPackBuffer, requiredSize, IntPtr.Zero, BufferUsageHint.DynamicCopy); + } + } + private int GetSrcFramebufferLazy() { if (_srcFramebuffer == 0) @@ -152,6 +208,13 @@ namespace Ryujinx.Graphics.OpenGL.Image _dstFramebuffer = 0; } + + if (_copyPboHandle != 0) + { + GL.DeleteBuffer(_copyPboHandle); + + _copyPboHandle = 0; + } } } } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 02353ffaa..2d50eba5e 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -72,6 +72,15 @@ namespace Ryujinx.Graphics.OpenGL.Image (int)Info.SwizzleA.Convert() }; + if (Info.Format.IsBgra8()) + { + // Swap B <-> R for BGRA formats, as OpenGL has no support for them + // and we need to manually swap the components on read/write on the GPU. + int temp = swizzleRgba[0]; + swizzleRgba[0] = swizzleRgba[2]; + swizzleRgba[2] = temp; + } + GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); int maxLevel = Info.Levels - 1; @@ -189,7 +198,12 @@ namespace Ryujinx.Graphics.OpenGL.Image return data; } - private void WriteTo(IntPtr ptr) + public void WriteToPbo(int offset, bool forceBgra) + { + WriteTo(IntPtr.Zero + offset, forceBgra); + } + + private void WriteTo(IntPtr data, bool forceBgra = false) { TextureTarget target = Target.Convert(); @@ -197,6 +211,14 @@ namespace Ryujinx.Graphics.OpenGL.Image FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + PixelFormat pixelFormat = format.PixelFormat; + PixelType pixelType = format.PixelType; + + if (forceBgra) + { + pixelFormat = PixelFormat.Bgra; + } + int faces = 1; if (target == TextureTarget.TextureCubeMap) @@ -214,20 +236,15 @@ namespace Ryujinx.Graphics.OpenGL.Image if (format.IsCompressed) { - GL.GetCompressedTexImage(target + face, level, ptr + faceOffset); + GL.GetCompressedTexImage(target + face, level, data + faceOffset); } else { - GL.GetTexImage( - target + face, - level, - format.PixelFormat, - format.PixelType, - ptr + faceOffset); + GL.GetTexImage(target + face, level, pixelFormat, pixelType, data + faceOffset); } } - ptr += Info.GetMipSize(level); + data += Info.GetMipSize(level); } } @@ -237,12 +254,17 @@ namespace Ryujinx.Graphics.OpenGL.Image { fixed (byte* ptr = data) { - SetData((IntPtr)ptr, data.Length); + ReadFrom((IntPtr)ptr, data.Length); } } } - private void SetData(IntPtr data, int size) + public void ReadFromPbo(int offset, int size) + { + ReadFrom(IntPtr.Zero + offset, size); + } + + private void ReadFrom(IntPtr data, int size) { TextureTarget target = Target.Convert(); diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 2016d8524..09ba9be0b 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.OpenGL private int _boundDrawFramebuffer; private int _boundReadFramebuffer; + private int[] _fpIsBgra = new int[8]; private float[] _fpRenderScale = new float[33]; private float[] _cpRenderScale = new float[32]; @@ -722,12 +723,12 @@ namespace Ryujinx.Graphics.OpenGL GL.Disable(EnableCap.ProgramPointSize); } - GL.PointParameter(origin == Origin.LowerLeft - ? PointSpriteCoordOriginParameter.LowerLeft + GL.PointParameter(origin == Origin.LowerLeft + ? PointSpriteCoordOriginParameter.LowerLeft : PointSpriteCoordOriginParameter.UpperLeft); // Games seem to set point size to 0 which generates a GL_INVALID_VALUE - // From the spec, GL_INVALID_VALUE is generated if size is less than or equal to 0. + // From the spec, GL_INVALID_VALUE is generated if size is less than or equal to 0. GL.PointSize(Math.Max(float.Epsilon, size)); } @@ -765,6 +766,7 @@ namespace Ryujinx.Graphics.OpenGL _program.Bind(); } + UpdateFpIsBgra(); SetRenderTargetScale(_fpRenderScale[0]); } @@ -814,12 +816,15 @@ namespace Ryujinx.Graphics.OpenGL TextureView color = (TextureView)colors[index]; _framebuffer.AttachColor(index, color); + + _fpIsBgra[index] = color != null && color.Format.IsBgra8() ? 1 : 0; } + UpdateFpIsBgra(); + TextureView depthStencilView = (TextureView)depthStencil; _framebuffer.AttachDepthStencil(depthStencilView); - _framebuffer.SetDrawBuffers(colors.Length); _hasDepthBuffer = depthStencil != null && depthStencilView.Format != Format.S8Uint; @@ -938,7 +943,9 @@ namespace Ryujinx.Graphics.OpenGL if (activeTarget != null && activeTarget.Width / (float)texture.Width == activeTarget.Height / (float)texture.Height) { - // If the texture's size is a multiple of the sampler size, enable interpolation using gl_FragCoord. (helps "invent" new integer values between scaled pixels) + // If the texture's size is a multiple of the sampler size, + // enable interpolation using gl_FragCoord. + // (helps "invent" new integer values between scaled pixels) interpolate = true; } } @@ -1112,6 +1119,14 @@ namespace Ryujinx.Graphics.OpenGL return (_boundDrawFramebuffer, _boundReadFramebuffer); } + private void UpdateFpIsBgra() + { + if (_program != null) + { + GL.Uniform1(_program.FragmentIsBgraUniform, 8, _fpIsBgra); + } + } + private void UpdateDepthTest() { // Enabling depth operations is only valid when we have @@ -1137,6 +1152,29 @@ namespace Ryujinx.Graphics.OpenGL } } + public void UpdateRenderScale(ShaderStage stage, int textureCount) + { + if (_program != null) + { + switch (stage) + { + case ShaderStage.Fragment: + if (_program.FragmentRenderScaleUniform != -1) + { + GL.Uniform1(_program.FragmentRenderScaleUniform, textureCount + 1, _fpRenderScale); + } + break; + + case ShaderStage.Compute: + if (_program.ComputeRenderScaleUniform != -1) + { + GL.Uniform1(_program.ComputeRenderScaleUniform, textureCount, _cpRenderScale); + } + break; + } + } + } + private void PrepareForDispatch() { if (_unit0Texture != null) @@ -1230,28 +1268,5 @@ namespace Ryujinx.Graphics.OpenGL _framebuffer?.Dispose(); _vertexArray?.Dispose(); } - - public void UpdateRenderScale(ShaderStage stage, int textureCount) - { - if (_program != null) - { - switch (stage) - { - case ShaderStage.Fragment: - if (_program.FragmentRenderScaleUniform != -1) - { - GL.Uniform1(_program.FragmentRenderScaleUniform, textureCount + 1, _fpRenderScale); - } - break; - - case ShaderStage.Compute: - if (_program.ComputeRenderScaleUniform != -1) - { - GL.Uniform1(_program.ComputeRenderScaleUniform, textureCount, _cpRenderScale); - } - break; - } - } - } } } diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs index a0f8eb015..06f05f67c 100644 --- a/Ryujinx.Graphics.OpenGL/Program.cs +++ b/Ryujinx.Graphics.OpenGL/Program.cs @@ -25,6 +25,7 @@ namespace Ryujinx.Graphics.OpenGL public int Handle { get; private set; } + public int FragmentIsBgraUniform { get; } public int FragmentRenderScaleUniform { get; } public int ComputeRenderScaleUniform { get; } @@ -218,6 +219,7 @@ namespace Ryujinx.Graphics.OpenGL } } + FragmentIsBgraUniform = GL.GetUniformLocation(Handle, "is_bgra"); FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale"); ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale"); } diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index b7dc37843..a2f4e4ce9 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -51,10 +51,12 @@ namespace Ryujinx.Graphics.OpenGL GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); + TextureView viewConverted = view.Format.IsBgra8() ? _renderer.TextureCopy.BgraSwap(view) : view; + GL.FramebufferTexture( FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, - view.Handle, + viewConverted.Handle, 0); GL.ReadBuffer(ReadBufferMode.ColorAttachment0); @@ -138,6 +140,11 @@ namespace Ryujinx.Graphics.OpenGL ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); + + if (viewConverted != view) + { + viewConverted.Dispose(); + } } private int GetCopyFramebufferHandleLazy() diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index efd30143b..40e277e06 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -139,6 +139,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) { + if (context.Config.Stage == ShaderStage.Fragment) + { + context.AppendLine($"uniform bool {DefaultNames.IsBgraName}[8];"); + context.AppendLine(); + } + if (DeclareRenderScale(context)) { context.AppendLine(); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 4da38b2de..d1cf4636b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -23,5 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string SharedMemoryName = "shared_mem"; public const string UndefinedName = "undef"; + + public const string IsBgraName = "is_bgra"; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 8801fc111..4ae9a00ac 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -64,6 +64,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeConsts.GtMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupGtMaskARB).x", VariableType.U32) }, { AttributeConsts.LeMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupLeMaskARB).x", VariableType.U32) }, { AttributeConsts.LtMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupLtMaskARB).x", VariableType.U32) }, + + // Support uniforms. + { AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.IsBgraName}[0]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.IsBgraName}[1]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.IsBgraName}[2]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.IsBgraName}[3]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.IsBgraName}[4]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.IsBgraName}[5]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.IsBgraName}[6]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.IsBgraName}[7]", VariableType.Bool) } }; private Dictionary _locals; @@ -149,8 +159,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl char swzMask = GetSwizzleMask((value >> 2) & 3); - if (value >= AttributeConsts.UserAttributeBase && - value < AttributeConsts.UserAttributeEnd) + if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) { value -= AttributeConsts.UserAttributeBase; @@ -169,8 +178,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - if (value >= AttributeConsts.FragmentOutputColorBase && - value < AttributeConsts.FragmentOutputColorEnd) + if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) { value -= AttributeConsts.FragmentOutputColorBase; diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 8ff37429a..c194b5c2e 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -35,6 +35,9 @@ namespace Ryujinx.Graphics.Shader.Translation public const int FragmentOutputColorBase = 0x1000010; public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16; + public const int FragmentOutputIsBgraBase = 0x1000100; + public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4; + public const int ThreadIdX = 0x2000000; public const int ThreadIdY = 0x2000004; public const int ThreadIdZ = 0x2000008; diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 39532a64f..9b26fa4a6 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -115,22 +115,46 @@ namespace Ryujinx.Graphics.Shader.Translation int regIndex = 0; - for (int attachment = 0; attachment < 8; attachment++) + for (int rtIndex = 0; rtIndex < 8; rtIndex++) { - OmapTarget target = Config.OmapTargets[attachment]; + OmapTarget target = Config.OmapTargets[rtIndex]; for (int component = 0; component < 4; component++) { - if (target.ComponentEnabled(component)) + if (!target.ComponentEnabled(component)) { - Operand dest = Attribute(AttributeConsts.FragmentOutputColorBase + attachment * 16 + component * 4); - - Operand src = Register(regIndex, RegisterType.Gpr); - - this.Copy(dest, src); - - regIndex++; + continue; } + + int fragmentOutputColorAttr = AttributeConsts.FragmentOutputColorBase + rtIndex * 16; + + Operand src = Register(regIndex, RegisterType.Gpr); + + // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL). + if (component == 0 || component == 2) + { + Operand isBgra = Attribute(AttributeConsts.FragmentOutputIsBgraBase + rtIndex * 4); + + Operand lblIsBgra = Label(); + Operand lblEnd = Label(); + + this.BranchIfTrue(lblIsBgra, isBgra); + + this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src); + this.Branch(lblEnd); + + MarkLabel(lblIsBgra); + + this.Copy(Attribute(fragmentOutputColorAttr + (2 - component) * 4), src); + + MarkLabel(lblEnd); + } + else + { + this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src); + } + + regIndex++; } } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index e1eb55ade..dbb05c772 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -247,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } private void PostFrameBuffer(Layer layer, BufferItem item) - { + { int frameBufferWidth = item.GraphicBuffer.Object.Width; int frameBufferHeight = item.GraphicBuffer.Object.Height;