Merge branch 'master' into mobile-ffmpeg

# Conflicts:
#	osu.Framework.Android/AndroidGameHost.cs
This commit is contained in:
Shane Woolcock
2019-08-08 20:07:15 +09:30
40 changed files with 467 additions and 346 deletions

View File

@@ -5,7 +5,7 @@
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Cake" Version="0.32.1" />
<PackageReference Include="Cake.CoreCLR" Version="0.32.1" />
<PackageReference Include="Cake" Version="0.34.1" />
<PackageReference Include="Cake.CoreCLR" Version="0.34.1" />
</ItemGroup>
</Project>

View File

@@ -3,7 +3,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using Android.App;
using Android.Content;
using osu.Framework.Android.Graphics.Textures;
using osu.Framework.Android.Graphics.Video;
using osu.Framework.Android.Input;
@@ -13,6 +14,7 @@ using osu.Framework.Input;
using osu.Framework.Input.Handlers;
using osu.Framework.IO.Stores;
using osu.Framework.Platform;
using Uri = Android.Net.Uri;
namespace osu.Framework.Android
{
@@ -51,7 +53,13 @@ namespace osu.Framework.Android
=> throw new NotImplementedException();
public override void OpenUrlExternally(string url)
=> throw new NotImplementedException();
{
var activity = (Activity)gameView.Context;
using (var intent = new Intent(Intent.ActionView, Uri.Parse(url)))
if (intent.ResolveActivity(activity.PackageManager) != null)
activity.StartActivity(intent);
}
public override IResourceStore<TextureUpload> CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore)
=> new AndroidTextureLoaderStore(underlyingStore);

View File

@@ -0,0 +1,65 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Text.RegularExpressions;
using NUnit.Framework;
using osu.Framework.Graphics.Shaders;
namespace osu.Framework.Tests.Graphics
{
[TestFixture]
public class ShaderRegexTest
{
private readonly Regex shaderAttributeRegex = new Regex(ShaderPart.SHADER_ATTRIBUTE_PATTERN);
[Test]
public void TestComment()
{
const string test_string = "// The following implementation using mix and step may be faster, but stackoverflow indicates it is in fact a lot slower on some GPUs.";
performInvalidAttributeTest(test_string);
}
[Test]
public void TestNonAttribute()
{
const string test_string = "varying vec3 name;";
performInvalidAttributeTest(test_string);
}
[Test]
public void TestValidAttribute()
{
const string test_string = "attribute lowp float name;";
performValidAttributeTest(test_string);
}
[Test]
public void TestSpacedAttribute()
{
const string test_string = " attribute float name ;";
performValidAttributeTest(test_string);
}
[Test]
public void TestNoPrecisionQualifier()
{
const string test_string = "attribute float name;";
performValidAttributeTest(test_string);
}
private void performValidAttributeTest(string testString)
{
var match = shaderAttributeRegex.Match(testString);
Assert.IsTrue(match.Success);
Assert.AreEqual("name", match.Groups[1].Value.Trim());
}
private void performInvalidAttributeTest(string testString)
{
var match = shaderAttributeRegex.Match(testString);
Assert.IsFalse(match.Success);
}
}
}

View File

@@ -5,8 +5,10 @@ using System;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
namespace osu.Framework.Tests.Visual.Containers
@@ -88,7 +90,7 @@ namespace osu.Framework.Tests.Visual.Containers
/// <summary>
/// Tests whether the result of a <see cref="Container{T}.Contains(T)"/> operation is valid between multiple containers.
/// This tests whether the comparator + equality operation in <see cref="CompositeDrawable.IndexOfInternal(Graphics.Drawable)"/> is valid.
/// This tests whether the comparator + equality operation in <see cref="CompositeDrawable.IndexOfInternal(Drawable)"/> is valid.
/// </summary>
[Test]
public void TestContainerContains()
@@ -178,5 +180,59 @@ namespace osu.Framework.Tests.Visual.Containers
Assert.That(disposed, Is.EqualTo(shouldDispose));
}
[Test]
public void TestAsyncLoadClearWhileAsyncDisposing()
{
Container safeContainer = null;
DelayedLoadDrawable drawable = null;
// We are testing a disposal deadlock scenario. When the test runner exits, it will attempt to dispose the game hierarchy,
// and will fall into the deadlocked state itself. For this reason an intermediate "safe" container is used, which is
// removed from the hierarchy immediately after use and is thus not disposed when the test runner exits.
// This does NOT free up the LoadComponentAsync thread pool for use by other tests - that thread is in a deadlocked state forever.
AddStep("add safe container", () => Add(safeContainer = new Container()));
// Get the drawable into an async loading state
AddStep("begin async load", () =>
{
safeContainer.LoadComponentAsync(drawable = new DelayedLoadDrawable(), _ => { });
Remove(safeContainer);
});
AddUntilStep("wait until loading", () => drawable.LoadState == LoadState.Loading);
// Make the async disposal queue attempt to dispose the drawable
AddStep("enqueue async disposal", () => AsyncDisposalQueue.Enqueue(drawable));
AddWaitStep("wait for disposal task to run", 10);
// Clear the contents of the drawable, causing a second async disposal
AddStep("allow load", () => drawable.AllowLoad.Set());
AddUntilStep("drawable was cleared successfully", () => drawable.HasCleared);
}
private class DelayedLoadDrawable : CompositeDrawable
{
public readonly ManualResetEventSlim AllowLoad = new ManualResetEventSlim();
public bool HasCleared { get; private set; }
public DelayedLoadDrawable()
{
InternalChild = new Box();
}
[BackgroundDependencyLoader]
private void load()
{
if (!AllowLoad.Wait(TimeSpan.FromSeconds(10)))
throw new TimeoutException();
ClearInternal();
HasCleared = true;
}
}
}
}

View File

@@ -99,7 +99,7 @@
<Reference Include="mscorlib" />
</ItemGroup>
<ItemGroup>
<NativeReference Include="..\osu.Framework.NativeLibs\ios\*.a">
<NativeReference Include="..\osu.Framework.iOS\*.a">
<Kind>Static</Kind>
<SmartLink>False</SmartLink>
<ForceLoad>True</ForceLoad>

View File

@@ -35,4 +35,12 @@
<PackageReference Include="ppy.osuTK.iOS" Version="1.0.111" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0006" />
</ItemGroup>
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)\*.a" />
<None Include="@(NativeLibs)">
<Pack>true</Pack>
<PackageCopyToOutput>true</PackageCopyToOutput>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -15,28 +15,36 @@ namespace osu.Framework.Allocation
{
private static readonly GlobalStatistic<string> last_disposal = GlobalStatistics.Get<string>("Drawable", "Last disposal");
private static readonly Queue<IDisposable> disposal_queue = new Queue<IDisposable>();
private static readonly List<IDisposable> disposal_queue = new List<IDisposable>();
private static Task runTask;
public static void Enqueue(IDisposable disposable)
{
lock (disposal_queue)
disposal_queue.Enqueue(disposable);
disposal_queue.Add(disposable);
if (runTask?.Status < TaskStatus.Running)
return;
runTask = Task.Run(() =>
{
IDisposable[] itemsToDispose;
lock (disposal_queue)
{
while (disposal_queue.Count > 0)
{
var toDispose = disposal_queue.Dequeue();
last_disposal.Value = toDispose.ToString();
toDispose.Dispose();
}
itemsToDispose = disposal_queue.ToArray();
disposal_queue.Clear();
}
for (int i = 0; i < itemsToDispose.Length; i++)
{
ref var item = ref itemsToDispose[i];
last_disposal.Value = item.ToString();
item.Dispose();
item = null;
}
});
}

View File

@@ -6,11 +6,6 @@ using System;
namespace osu.Framework.Caching
{
public static class StaticCached
{
internal static bool BypassCache = false;
}
public struct Cached<T>
{
private T value;
@@ -35,7 +30,7 @@ namespace osu.Framework.Caching
private bool isValid;
public bool IsValid => !StaticCached.BypassCache && isValid;
public bool IsValid => isValid;
public static implicit operator T(Cached<T> value) => value.Value;
@@ -60,7 +55,7 @@ namespace osu.Framework.Caching
{
private bool isValid;
public bool IsValid => !StaticCached.BypassCache && isValid;
public bool IsValid => isValid;
/// <summary>
/// Invalidate the cache of this object.

View File

@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Caching;
namespace osu.Framework.Configuration
{
public class FrameworkDebugConfigManager : IniConfigManager<DebugSetting>
@@ -18,14 +16,12 @@ namespace osu.Framework.Configuration
{
base.InitialiseDefaults();
Set(DebugSetting.BypassCaching, false).ValueChanged += delegate { StaticCached.BypassCache = Get<bool>(DebugSetting.BypassCaching); };
Set(DebugSetting.BypassFrontToBackPass, false);
}
}
public enum DebugSetting
{
BypassCaching,
BypassFrontToBackPass
}
}

View File

@@ -9,7 +9,6 @@ using osu.Framework.Graphics.OpenGL.Vertices;
using osu.Framework.Graphics.Primitives;
using osuTK;
using osuTK.Graphics;
using osuTK.Graphics.ES30;
namespace osu.Framework.Graphics
{
@@ -38,18 +37,11 @@ namespace osu.Framework.Graphics
private RectangleF screenSpaceDrawRectangle;
private Vector2 frameBufferSize;
private readonly All filteringMode;
private readonly RenderbufferInternalFormat[] formats;
public BufferedDrawNode(IBufferedDrawable source, DrawNode child, BufferedDrawNodeSharedData sharedData, RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false)
public BufferedDrawNode(IBufferedDrawable source, DrawNode child, BufferedDrawNodeSharedData sharedData)
: base(source)
{
this.formats = formats;
Child = child;
SharedData = sharedData;
filteringMode = pixelSnapping ? All.Nearest : All.Linear;
}
public override void ApplyState()
@@ -61,7 +53,7 @@ namespace osu.Framework.Graphics
DrawColourInfo = Source.FrameBufferDrawColour ?? new DrawColourInfo(Color4.White, base.DrawColourInfo.Blending);
frameBufferSize = new Vector2((float)Math.Ceiling(screenSpaceDrawRectangle.Width), (float)Math.Ceiling(screenSpaceDrawRectangle.Height));
DrawRectangle = filteringMode == All.Nearest
DrawRectangle = SharedData.PixelSnapping
? new RectangleF(screenSpaceDrawRectangle.X, screenSpaceDrawRectangle.Y, frameBufferSize.X, frameBufferSize.Y)
: screenSpaceDrawRectangle;
@@ -141,9 +133,6 @@ namespace osu.Framework.Graphics
/// <returns>A token that must be disposed upon finishing use of <paramref name="frameBuffer"/>.</returns>
protected ValueInvokeOnDisposal BindFrameBuffer(FrameBuffer frameBuffer)
{
if (!frameBuffer.IsInitialized)
frameBuffer.Initialise(filteringMode, formats);
// This setter will also take care of allocating a texture of appropriate size within the frame buffer.
frameBuffer.Size = frameBufferSize;

View File

@@ -4,6 +4,7 @@
using System;
using osu.Framework.Graphics.OpenGL;
using osu.Framework.Graphics.OpenGL.Buffers;
using osuTK.Graphics.ES30;
namespace osu.Framework.Graphics
{
@@ -26,6 +27,12 @@ namespace osu.Framework.Graphics
/// </summary>
public FrameBuffer MainBuffer { get; }
/// <summary>
/// Whether the frame buffer position should be snapped to the nearest pixel when blitting.
/// This amounts to setting the texture filtering mode to "nearest".
/// </summary>
public readonly bool PixelSnapping;
/// <summary>
/// A set of <see cref="FrameBuffer"/>s which are used in a ping-pong manner to render effects to.
/// </summary>
@@ -34,8 +41,8 @@ namespace osu.Framework.Graphics
/// <summary>
/// Creates a new <see cref="BufferedDrawNodeSharedData"/> with no effect buffers.
/// </summary>
public BufferedDrawNodeSharedData()
: this(0)
public BufferedDrawNodeSharedData(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false)
: this(0, formats, pixelSnapping)
{
}
@@ -43,17 +50,23 @@ namespace osu.Framework.Graphics
/// Creates a new <see cref="BufferedDrawNodeSharedData"/> with a specific amount of effect buffers.
/// </summary>
/// <param name="effectBufferCount">The number of effect buffers.</param>
/// <param name="formats">The render buffer formats to attach to each frame buffer.</param>
/// <param name="pixelSnapping">Whether the frame buffer position should be snapped to the nearest pixel when blitting.
/// This amounts to setting the texture filtering mode to "nearest".</param>
/// <exception cref="ArgumentOutOfRangeException">If <paramref name="effectBufferCount"/> is less than 0.</exception>
public BufferedDrawNodeSharedData(int effectBufferCount)
public BufferedDrawNodeSharedData(int effectBufferCount, RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false)
{
if (effectBufferCount < 0)
throw new ArgumentOutOfRangeException(nameof(effectBufferCount), "Must be positive.");
MainBuffer = new FrameBuffer();
PixelSnapping = pixelSnapping;
All filterMode = pixelSnapping ? All.Nearest : All.Linear;
MainBuffer = new FrameBuffer(formats, filterMode);
effectBuffers = new FrameBuffer[effectBufferCount];
for (int i = 0; i < effectBufferCount; i++)
effectBuffers[i] = new FrameBuffer();
effectBuffers[i] = new FrameBuffer(formats, filterMode);
}
private int currentEffectBuffer = -1;

View File

@@ -9,8 +9,6 @@ using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using osu.Framework.MathUtils;
using System;
using System.Collections.Generic;
using osu.Framework.Caching;
using osu.Framework.Graphics.Sprites;
@@ -27,7 +25,12 @@ namespace osu.Framework.Graphics.Containers
/// </summary>
public class BufferedContainer : BufferedContainer<Drawable>
{
};
/// <inheritdoc />
public BufferedContainer(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false)
: base(formats, pixelSnapping)
{
}
}
/// <summary>
/// A container that renders its children to an internal framebuffer, and then
@@ -97,25 +100,6 @@ namespace osu.Framework.Graphics.Containers
}
}
private bool pixelSnapping;
/// <summary>
/// Whether the framebuffer's position is snapped to the nearest pixel when blitting.
/// Since the framebuffer's texels have the same size as pixels, this amounts to setting
/// the texture filtering mode to "nearest".
/// </summary>
public bool PixelSnapping
{
get => pixelSnapping;
set
{
if (sharedData.MainBuffer.IsInitialized)
throw new InvalidOperationException("May only set PixelSnapping before FrameBuffers are initialized (i.e. before the first draw).");
pixelSnapping = value;
}
}
private ColourInfo effectColour = Color4.White;
/// <summary>
@@ -223,14 +207,17 @@ namespace osu.Framework.Graphics.Containers
private IShader blurShader;
private readonly BufferedContainerDrawNodeSharedData sharedData;
/// <summary>
/// Constructs an empty buffered container.
/// </summary>
public BufferedContainer()
/// <param name="formats">The render buffer formats attached to the frame buffers of this <see cref="BufferedContainer"/>.</param>
/// <param name="pixelSnapping">Whether the frame buffer position should be snapped to the nearest pixel when blitting.
/// This amounts to setting the texture filtering mode to "nearest".</param>
public BufferedContainer(RenderbufferInternalFormat[] formats = null, bool pixelSnapping = false)
{
// The initial draw cannot be cached, and thus we need to initialize
// with a forced draw.
ForceRedraw();
sharedData = new BufferedContainerDrawNodeSharedData(formats, pixelSnapping);
}
[BackgroundDependencyLoader]
@@ -241,30 +228,7 @@ namespace osu.Framework.Graphics.Containers
blurShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR);
}
private readonly BufferedContainerDrawNodeSharedData sharedData = new BufferedContainerDrawNodeSharedData();
protected override DrawNode CreateDrawNode() => new BufferedContainerDrawNode(this, sharedData, attachedFormats.ToArray(), PixelSnapping);
private readonly List<RenderbufferInternalFormat> attachedFormats = new List<RenderbufferInternalFormat>();
/// <summary>
/// Attach an additional component to this <see cref="BufferedContainer{T}"/>. Such a component can e.g.
/// be a depth component, such that the framebuffer can hold fragment depth information.
/// </summary>
/// <param name="format">The component format to attach.</param>
public void Attach(RenderbufferInternalFormat format)
{
if (attachedFormats.Exists(f => f == format))
return;
attachedFormats.Add(format);
}
/// <summary>
/// Detaches an additional component of this <see cref="BufferedContainer{T}"/>.
/// </summary>
/// <param name="format">The component format to detach.</param>
public void Detach(RenderbufferInternalFormat format) => attachedFormats.Remove(format);
protected override DrawNode CreateDrawNode() => new BufferedContainerDrawNode(this, sharedData);
protected override RectangleF ComputeChildMaskingBounds(RectangleF maskingBounds) => ScreenSpaceDrawQuad.AABBFloat; // Make sure children never get masked away

View File

@@ -5,13 +5,13 @@ using System.Collections.Generic;
using osu.Framework.Graphics.OpenGL;
using osu.Framework.Graphics.OpenGL.Buffers;
using osuTK;
using osuTK.Graphics.ES30;
using osuTK.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using System;
using osu.Framework.Graphics.Colour;
using osu.Framework.MathUtils;
using osuTK.Graphics.ES30;
namespace osu.Framework.Graphics.Containers
{
@@ -36,9 +36,8 @@ namespace osu.Framework.Graphics.Containers
private IShader blurShader;
public BufferedContainerDrawNode(BufferedContainer<T> source, BufferedContainerDrawNodeSharedData sharedData, RenderbufferInternalFormat[] formats = null,
bool pixelSnapping = false)
: base(source, new CompositeDrawableDrawNode(source), sharedData, formats, pixelSnapping)
public BufferedContainerDrawNode(BufferedContainer<T> source, BufferedContainerDrawNodeSharedData sharedData)
: base(source, new CompositeDrawableDrawNode(source), sharedData)
{
}
@@ -129,8 +128,8 @@ namespace osu.Framework.Graphics.Containers
private class BufferedContainerDrawNodeSharedData : BufferedDrawNodeSharedData
{
public BufferedContainerDrawNodeSharedData()
: base(2)
public BufferedContainerDrawNodeSharedData(RenderbufferInternalFormat[] formats, bool pixelSnapping)
: base(2, formats, pixelSnapping)
{
}
}

View File

@@ -1575,7 +1575,7 @@ namespace osu.Framework.Graphics.Containers
{
get
{
if (!StaticCached.BypassCache && !isComputingChildrenSizeDependencies && AutoSizeAxes.HasFlag(Axes.X))
if (!isComputingChildrenSizeDependencies && AutoSizeAxes.HasFlag(Axes.X))
updateChildrenSizeDependencies();
return base.Width;
}
@@ -1593,7 +1593,7 @@ namespace osu.Framework.Graphics.Containers
{
get
{
if (!StaticCached.BypassCache && !isComputingChildrenSizeDependencies && AutoSizeAxes.HasFlag(Axes.Y))
if (!isComputingChildrenSizeDependencies && AutoSizeAxes.HasFlag(Axes.Y))
updateChildrenSizeDependencies();
return base.Height;
}
@@ -1613,7 +1613,7 @@ namespace osu.Framework.Graphics.Containers
{
get
{
if (!StaticCached.BypassCache && !isComputingChildrenSizeDependencies && AutoSizeAxes != Axes.None)
if (!isComputingChildrenSizeDependencies && AutoSizeAxes != Axes.None)
updateChildrenSizeDependencies();
return base.Size;
}

View File

@@ -18,24 +18,28 @@ namespace osu.Framework.Graphics.Containers.Markdown
/// </code>
public class MarkdownLinkText : CompositeDrawable, IHasTooltip, IMarkdownTextComponent
{
public string TooltipText => url;
public string TooltipText => Url;
[Resolved]
private IMarkdownTextComponent parentTextComponent { get; set; }
[Resolved]
private GameHost host { get; set; }
private readonly string text;
private readonly string url;
protected readonly string Url;
public MarkdownLinkText(string text, LinkInline linkInline)
{
this.text = text;
url = linkInline.Url ?? string.Empty;
Url = linkInline.Url ?? string.Empty;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(GameHost host)
private void load()
{
SpriteText spriteText;
InternalChildren = new Drawable[]
@@ -44,13 +48,15 @@ namespace osu.Framework.Graphics.Containers.Markdown
{
AutoSizeAxes = Axes.Both,
Child = spriteText = CreateSpriteText(),
Action = () => host.OpenUrlExternally(url)
Action = OnLinkPressed,
}
};
spriteText.Text = text;
}
protected virtual void OnLinkPressed() => host.OpenUrlExternally(Url);
public virtual SpriteText CreateSpriteText()
{
var spriteText = parentTextComponent.CreateSpriteText();

View File

@@ -274,9 +274,9 @@ namespace osu.Framework.Graphics.Lines
public Color4 BackgroundColour => new Color4(0, 0, 0, 0);
private readonly BufferedDrawNodeSharedData sharedData = new BufferedDrawNodeSharedData();
private readonly BufferedDrawNodeSharedData sharedData = new BufferedDrawNodeSharedData(new[] { RenderbufferInternalFormat.DepthComponent16 });
protected override DrawNode CreateDrawNode() => new BufferedDrawNode(this, new PathDrawNode(this), sharedData, new[] { RenderbufferInternalFormat.DepthComponent16 });
protected override DrawNode CreateDrawNode() => new BufferedDrawNode(this, new PathDrawNode(this), sharedData);
protected override void Dispose(bool isDisposing)
{

View File

@@ -18,28 +18,15 @@ namespace osu.Framework.Graphics.OpenGL.Buffers
private readonly List<RenderBuffer> attachedRenderBuffers = new List<RenderBuffer>();
public bool IsInitialized { get; private set; }
private bool isInitialised;
public void Initialise(All filteringMode = All.Linear, RenderbufferInternalFormat[] renderBufferFormats = null)
private readonly All filteringMode;
private readonly RenderbufferInternalFormat[] renderBufferFormats;
public FrameBuffer(RenderbufferInternalFormat[] renderBufferFormats = null, All filteringMode = All.Linear)
{
frameBuffer = GL.GenFramebuffer();
Texture = new FrameBufferTexture(filteringMode);
Bind();
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget2d.Texture2D, Texture.TextureId, 0);
GLWrapper.BindTexture(null);
if (renderBufferFormats != null)
{
foreach (var format in renderBufferFormats)
attachedRenderBuffers.Add(new RenderBuffer(format));
}
Unbind();
IsInitialized = true;
this.renderBufferFormats = renderBufferFormats;
this.filteringMode = filteringMode;
}
private Vector2 size = Vector2.One;
@@ -57,13 +44,33 @@ namespace osu.Framework.Graphics.OpenGL.Buffers
size = value;
Texture.Width = (int)Math.Ceiling(size.X);
Texture.Height = (int)Math.Ceiling(size.Y);
Texture.SetData(new TextureUpload());
Texture.Upload();
if (isInitialised)
{
Texture.Width = (int)Math.Ceiling(size.X);
Texture.Height = (int)Math.Ceiling(size.Y);
Texture.SetData(new TextureUpload());
Texture.Upload();
foreach (var buffer in attachedRenderBuffers)
buffer.Size = value;
foreach (var buffer in attachedRenderBuffers)
buffer.Size = value;
}
}
}
private void initialise()
{
frameBuffer = GL.GenFramebuffer();
Texture = new FrameBufferTexture(Vector2.ComponentMax(Vector2.One, Size), filteringMode);
GLWrapper.BindFrameBuffer(frameBuffer);
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget2d.Texture2D, Texture.TextureId, 0);
GLWrapper.BindTexture(null);
if (renderBufferFormats != null)
{
foreach (var format in renderBufferFormats)
attachedRenderBuffers.Add(new RenderBuffer(format) { Size = Size });
}
}
@@ -71,7 +78,19 @@ namespace osu.Framework.Graphics.OpenGL.Buffers
/// Binds the framebuffer.
/// <para>Does not clear the buffer or reset the viewport/ortho.</para>
/// </summary>
public void Bind() => GLWrapper.BindFrameBuffer(frameBuffer);
public void Bind()
{
if (!isInitialised)
{
initialise();
isInitialised = true;
}
else
{
// Buffer is bound during initialisation
GLWrapper.BindFrameBuffer(frameBuffer);
}
}
/// <summary>
/// Unbinds the framebuffer.
@@ -98,13 +117,16 @@ namespace osu.Framework.Graphics.OpenGL.Buffers
if (isDisposed)
return;
Texture?.Dispose();
Texture = null;
if (isInitialised)
{
Texture?.Dispose();
Texture = null;
GLWrapper.DeleteFrameBuffer(frameBuffer);
GLWrapper.DeleteFrameBuffer(frameBuffer);
foreach (var buffer in attachedRenderBuffers)
buffer.Dispose();
foreach (var buffer in attachedRenderBuffers)
buffer.Dispose();
}
isDisposed = true;
}
@@ -113,8 +135,8 @@ namespace osu.Framework.Graphics.OpenGL.Buffers
private class FrameBufferTexture : TextureGLSingle
{
public FrameBufferTexture(All filteringMode = All.Linear)
: base(1, 1, true, filteringMode)
public FrameBufferTexture(Vector2 size, All filteringMode = All.Linear)
: base((int)Math.Ceiling(size.X), (int)Math.Ceiling(size.Y), true, filteringMode)
{
SetData(new TextureUpload());
Upload();

View File

@@ -20,8 +20,13 @@ namespace osu.Framework.Graphics.OpenGL.Buffers
this.format = format;
renderBuffer = GL.GenRenderbuffer();
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, renderBuffer);
// OpenGL docs don't specify that this is required, but seems to be required on some platforms
// to correctly attach in the GL.FramebufferRenderbuffer() call below
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, format, 1, 1);
switch (format)
{
case RenderbufferInternalFormat.DepthComponent16:

View File

@@ -1,8 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Text;
using osu.Framework.Graphics.OpenGL;
using osuTK;
using osuTK.Graphics.ES30;
@@ -11,8 +11,6 @@ namespace osu.Framework.Graphics.Shaders
{
public class Shader : IShader
{
internal StringBuilder Log = new StringBuilder();
public bool IsLoaded { get; private set; }
internal bool IsBound;
@@ -37,7 +35,6 @@ namespace osu.Framework.Graphics.Shaders
parts.RemoveAll(p => p == null);
Uniforms.Clear();
uniformsArray = null;
Log.Clear();
if (parts.Count == 0)
return;
@@ -54,90 +51,79 @@ namespace osu.Framework.Graphics.Shaders
}
GL.LinkProgram(this);
GL.GetProgram(this, GetProgramParameterName.LinkStatus, out int linkResult);
string linkLog = GL.GetProgramInfoLog(this);
Log.AppendLine(string.Format(ShaderPart.BOUNDARY, name));
Log.AppendLine($"Linked: {linkResult == 1}");
if (linkResult == 0)
{
Log.AppendLine("Log:");
Log.AppendLine(linkLog);
}
foreach (var part in parts)
GL.DetachShader(this, part);
IsLoaded = linkResult == 1;
if (IsLoaded)
if (!IsLoaded)
throw new ProgramLinkingFailedException(name, GL.GetProgramInfoLog(this));
// Obtain all the shader uniforms
GL.GetProgram(this, GetProgramParameterName.ActiveUniforms, out int uniformCount);
uniformsArray = new IUniform[uniformCount];
for (int i = 0; i < uniformCount; i++)
{
// Obtain all the shader uniforms
GL.GetProgram(this, GetProgramParameterName.ActiveUniforms, out int uniformCount);
uniformsArray = new IUniform[uniformCount];
GL.GetActiveUniform(this, i, 100, out _, out _, out ActiveUniformType type, out string uniformName);
for (int i = 0; i < uniformCount; i++)
IUniform createUniform<T>(string name)
where T : struct
{
GL.GetActiveUniform(this, i, 100, out _, out _, out ActiveUniformType type, out string uniformName);
int location = GL.GetUniformLocation(this, name);
IUniform createUniform<T>(string name)
where T : struct
{
int location = GL.GetUniformLocation(this, name);
if (GlobalPropertyManager.CheckGlobalExists(name)) return new GlobalUniform<T>(this, name, location);
if (GlobalPropertyManager.CheckGlobalExists(name)) return new GlobalUniform<T>(this, name, location);
return new Uniform<T>(this, name, location);
}
IUniform uniform;
switch (type)
{
case ActiveUniformType.Bool:
uniform = createUniform<bool>(uniformName);
break;
case ActiveUniformType.Float:
uniform = createUniform<float>(uniformName);
break;
case ActiveUniformType.Int:
uniform = createUniform<int>(uniformName);
break;
case ActiveUniformType.FloatMat3:
uniform = createUniform<Matrix3>(uniformName);
break;
case ActiveUniformType.FloatMat4:
uniform = createUniform<Matrix4>(uniformName);
break;
case ActiveUniformType.FloatVec2:
uniform = createUniform<Vector2>(uniformName);
break;
case ActiveUniformType.FloatVec3:
uniform = createUniform<Vector3>(uniformName);
break;
case ActiveUniformType.FloatVec4:
uniform = createUniform<Vector4>(uniformName);
break;
default:
continue;
}
uniformsArray[i] = uniform;
Uniforms.Add(uniformName, uniformsArray[i]);
return new Uniform<T>(this, name, location);
}
GlobalPropertyManager.Register(this);
IUniform uniform;
switch (type)
{
case ActiveUniformType.Bool:
uniform = createUniform<bool>(uniformName);
break;
case ActiveUniformType.Float:
uniform = createUniform<float>(uniformName);
break;
case ActiveUniformType.Int:
uniform = createUniform<int>(uniformName);
break;
case ActiveUniformType.FloatMat3:
uniform = createUniform<Matrix3>(uniformName);
break;
case ActiveUniformType.FloatMat4:
uniform = createUniform<Matrix4>(uniformName);
break;
case ActiveUniformType.FloatVec2:
uniform = createUniform<Vector2>(uniformName);
break;
case ActiveUniformType.FloatVec3:
uniform = createUniform<Vector3>(uniformName);
break;
case ActiveUniformType.FloatVec4:
uniform = createUniform<Vector4>(uniformName);
break;
default:
continue;
}
uniformsArray[i] = uniform;
Uniforms.Add(uniformName, uniformsArray[i]);
}
GlobalPropertyManager.Register(this);
}
internal void EnsureLoaded()
@@ -187,5 +173,21 @@ namespace osu.Framework.Graphics.Shaders
}
public static implicit operator int(Shader shader) => shader.programID;
public class PartCompilationFailedException : Exception
{
public PartCompilationFailedException(string partName, string log)
: base($"A {typeof(ShaderPart)} failed to compile: {partName}:\n{log.Trim()}")
{
}
}
public class ProgramLinkingFailedException : Exception
{
public ProgramLinkingFailedException(string programName, string log)
: base($"A {typeof(Shader)} failed to link: {programName}:\n{log.Trim()}")
{
}
}
}
}

View File

@@ -4,9 +4,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using osu.Framework.IO.Stores;
using osu.Framework.Logging;
using osuTK.Graphics.ES30;
namespace osu.Framework.Graphics.Shaders
@@ -81,21 +79,7 @@ namespace osu.Framework.Graphics.Shaders
createShaderPart(fragment, ShaderType.FragmentShader)
};
shader = new Shader($"{vertex}/{fragment}", parts);
if (!shader.IsLoaded)
{
StringBuilder logContents = new StringBuilder();
logContents.AppendLine($@"Loading shader {vertex}/{fragment}");
logContents.Append(shader.Log);
foreach (ShaderPart p in parts)
logContents.Append(p.Log);
Logger.Log(logContents.ToString().Trim('\n'), LoggingTarget.Runtime, LogLevel.Debug);
}
shaderCache[tuple] = shader;
return shader;
return shaderCache[tuple] = new Shader($"{vertex}/{fragment}", parts);
}
}

View File

@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using osuTK.Graphics.ES30;
@@ -12,9 +11,7 @@ namespace osu.Framework.Graphics.Shaders
{
internal class ShaderPart
{
internal const string BOUNDARY = @"----------------------{0}";
internal StringBuilder Log = new StringBuilder();
internal const string SHADER_ATTRIBUTE_PATTERN = "^\\s*(?>attribute|in)\\s+(?:(?:lowp|mediump|highp)\\s+)?\\w+\\s+(\\w+)";
internal List<ShaderInputInfo> ShaderInputs = new List<ShaderInputInfo>();
@@ -33,7 +30,7 @@ namespace osu.Framework.Graphics.Shaders
private readonly List<string> shaderCodes = new List<string>();
private readonly Regex includeRegex = new Regex("^\\s*#\\s*include\\s+[\"<](.*)[\">]");
private readonly Regex shaderInputRegex = new Regex("^\\s*(?>attribute|in)\\s+[^\\s]+\\s+([^;]+);");
private readonly Regex shaderInputRegex = new Regex(SHADER_ATTRIBUTE_PATTERN);
private readonly ShaderManager manager;
@@ -107,14 +104,19 @@ namespace osu.Framework.Graphics.Shaders
}
}
if (mainFile && isVertexShader)
if (mainFile)
{
string realMainName = "real_main_" + Guid.NewGuid().ToString("N");
code = loadFile(manager.LoadRaw("sh_Precision_Internal.h"), false) + "\n" + code;
string backbufferCode = loadFile(manager.LoadRaw("sh_Backbuffer_Internal.h"), false);
if (isVertexShader)
{
string realMainName = "real_main_" + Guid.NewGuid().ToString("N");
backbufferCode = backbufferCode.Replace("{{ real_main }}", realMainName);
code = Regex.Replace(code, @"void main\((.*)\)", $"void {realMainName}()") + backbufferCode + '\n';
string backbufferCode = loadFile(manager.LoadRaw("sh_Backbuffer_Internal.h"), false);
backbufferCode = backbufferCode.Replace("{{ real_main }}", realMainName);
code = Regex.Replace(code, @"void main\((.*)\)", $"void {realMainName}()") + backbufferCode + '\n';
}
}
return code;
@@ -139,20 +141,8 @@ namespace osu.Framework.Graphics.Shaders
GL.GetShader(this, ShaderParameter.CompileStatus, out int compileResult);
Compiled = compileResult == 1;
#if DEBUG
string compileLog = GL.GetShaderInfoLog(this);
Log.AppendLine(string.Format('\t' + BOUNDARY, Name));
Log.AppendLine($"\tCompiled: {Compiled}");
if (!Compiled)
{
Log.AppendLine("\tLog:");
Log.AppendLine('\t' + compileLog);
}
#endif
if (!Compiled)
delete();
throw new Shader.PartCompilationFailedException(Name, GL.GetShaderInfoLog(this));
return Compiled;
}

View File

@@ -421,6 +421,18 @@ namespace osu.Framework.Graphics.UserInterface
child.Hide();
}
}
public override void Clear(bool disposeChildren)
{
tabVisibility.Clear();
base.Clear(disposeChildren);
}
public override bool Remove(TabItem<T> drawable)
{
tabVisibility.Remove(drawable);
return base.Remove(drawable);
}
}
}
}

View File

@@ -1,6 +1,6 @@
// Automatically included for every vertex shader.
attribute float m_BackbufferDrawDepth;
attribute highp float m_BackbufferDrawDepth;
// Whether the backbuffer is currently being drawn to
uniform bool g_BackbufferDraw;

View File

@@ -1,35 +1,31 @@
#ifdef GL_ES
precision mediump float;
#endif
#include "sh_Utils.h"
#define INV_SQRT_2PI 0.39894
varying vec2 v_TexCoord;
varying mediump vec2 v_TexCoord;
uniform sampler2D m_Sampler;
uniform lowp sampler2D m_Sampler;
uniform vec2 g_TexSize;
uniform mediump vec2 g_TexSize;
uniform int g_Radius;
uniform float g_Sigma;
uniform vec2 g_BlurDirection;
uniform mediump float g_Sigma;
uniform highp vec2 g_BlurDirection;
float computeGauss(in float x, in float sigma)
mediump float computeGauss(in mediump float x, in mediump float sigma)
{
return INV_SQRT_2PI * exp(-0.5*x*x / (sigma*sigma)) / sigma;
}
vec4 blur(sampler2D tex, int radius, vec2 direction, vec2 texCoord, vec2 texSize, float sigma)
lowp vec4 blur(sampler2D tex, int radius, highp vec2 direction, mediump vec2 texCoord, mediump vec2 texSize, mediump float sigma)
{
float factor = computeGauss(0.0, sigma);
vec4 sum = texture2D(tex, texCoord) * factor;
mediump float factor = computeGauss(0.0, sigma);
mediump vec4 sum = texture2D(tex, texCoord) * factor;
float totalFactor = factor;
mediump float totalFactor = factor;
for (int i = 2; i <= 200; i += 2)
{
float x = float(i) - 0.5;
mediump float x = float(i) - 0.5;
factor = computeGauss(x, sigma) * 2.0;
totalFactor += 2.0 * factor;
sum += texture2D(tex, texCoord + direction * x / texSize) * factor;

View File

@@ -1,8 +1,8 @@
attribute vec2 m_Position;
attribute highp vec2 m_Position;
varying vec2 v_Position;
varying highp vec2 v_Position;
uniform mat4 g_ProjMatrix;
uniform highp mat4 g_ProjMatrix;
void main(void)
{

View File

@@ -0,0 +1,10 @@
// This file is automatically included in every shader
#ifndef GL_ES
#define lowp
#define mediump
#define highp
#else
// GL_ES expects a defined precision for every member. Users may miss this requirement, so a default precision is specified.
precision mediump float;
#endif

View File

@@ -1,13 +1,9 @@
#ifdef GL_ES
precision mediump float;
#endif
#include "sh_Utils.h"
varying vec4 v_Colour;
varying vec2 v_TexCoord;
varying lowp vec4 v_Colour;
varying mediump vec2 v_TexCoord;
uniform sampler2D m_Sampler;
uniform lowp sampler2D m_Sampler;
void main(void)
{

View File

@@ -1,24 +1,24 @@
#include "sh_Utils.h"
attribute vec2 m_Position;
attribute vec4 m_Colour;
attribute vec2 m_TexCoord;
attribute vec4 m_TexRect;
attribute vec2 m_BlendRange;
attribute highp vec2 m_Position;
attribute lowp vec4 m_Colour;
attribute mediump vec2 m_TexCoord;
attribute mediump vec4 m_TexRect;
attribute mediump vec2 m_BlendRange;
varying vec2 v_MaskingPosition;
varying vec4 v_Colour;
varying vec2 v_TexCoord;
varying vec4 v_TexRect;
varying vec2 v_BlendRange;
varying highp vec2 v_MaskingPosition;
varying lowp vec4 v_Colour;
varying mediump vec2 v_TexCoord;
varying mediump vec4 v_TexRect;
varying mediump vec2 v_BlendRange;
uniform mat4 g_ProjMatrix;
uniform mat3 g_ToMaskingSpace;
uniform highp mat4 g_ProjMatrix;
uniform highp mat3 g_ToMaskingSpace;
void main(void)
{
// Transform from screen space to masking space.
vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0);
highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0);
v_MaskingPosition = maskingPos.xy / maskingPos.z;
v_Colour = m_Colour;
@@ -27,4 +27,4 @@ void main(void)
v_BlendRange = m_BlendRange;
gl_Position = g_ProjMatrix * vec4(m_Position, 1.0, 1.0);
}
}

View File

@@ -1,14 +1,14 @@
#include "sh_Utils.h"
attribute vec3 m_Position;
attribute vec4 m_Colour;
attribute vec2 m_TexCoord;
attribute highp vec3 m_Position;
attribute lowp vec4 m_Colour;
attribute mediump vec2 m_TexCoord;
varying vec2 v_MaskingPosition;
varying vec4 v_Colour;
varying vec2 v_TexCoord;
varying vec4 v_TexRect;
varying vec2 v_BlendRange;
varying highp vec2 v_MaskingPosition;
varying lowp vec4 v_Colour;
varying mediump vec2 v_TexCoord;
varying mediump vec4 v_TexRect;
varying mediump vec2 v_BlendRange;
uniform mat4 g_ProjMatrix;
uniform mat3 g_ToMaskingSpace;
@@ -25,4 +25,4 @@ void main(void)
v_Colour = m_Colour;
v_TexCoord = m_TexCoord;
gl_Position = g_ProjMatrix * vec4(m_Position, 1.0);
}
}

View File

@@ -1,43 +1,39 @@
#ifdef GL_ES
precision mediump float;
#endif
#include "sh_Utils.h"
varying vec2 v_MaskingPosition;
varying vec4 v_Colour;
varying vec2 v_TexCoord;
varying vec4 v_TexRect;
varying vec2 v_BlendRange;
varying highp vec2 v_MaskingPosition;
varying lowp vec4 v_Colour;
varying mediump vec2 v_TexCoord;
varying mediump vec4 v_TexRect;
varying mediump vec2 v_BlendRange;
uniform sampler2D m_Sampler;
uniform float g_CornerRadius;
uniform vec4 g_MaskingRect;
uniform float g_BorderThickness;
uniform vec4 g_BorderColour;
uniform lowp sampler2D m_Sampler;
uniform highp float g_CornerRadius;
uniform highp vec4 g_MaskingRect;
uniform highp float g_BorderThickness;
uniform lowp vec4 g_BorderColour;
uniform float g_MaskingBlendRange;
uniform mediump float g_MaskingBlendRange;
uniform float g_AlphaExponent;
uniform lowp float g_AlphaExponent;
uniform vec2 g_EdgeOffset;
uniform highp vec2 g_EdgeOffset;
uniform bool g_DiscardInner;
uniform float g_InnerCornerRadius;
uniform highp float g_InnerCornerRadius;
float distanceFromRoundedRect(vec2 offset, float radius)
highp float distanceFromRoundedRect(highp vec2 offset, highp float radius)
{
vec2 maskingPosition = v_MaskingPosition + offset;
highp vec2 maskingPosition = v_MaskingPosition + offset;
// Compute offset distance from masking rect in masking space.
vec2 topLeftOffset = g_MaskingRect.xy - maskingPosition;
vec2 bottomRightOffset = maskingPosition - g_MaskingRect.zw;
highp vec2 topLeftOffset = g_MaskingRect.xy - maskingPosition;
highp vec2 bottomRightOffset = maskingPosition - g_MaskingRect.zw;
vec2 distanceFromShrunkRect = max(
highp vec2 distanceFromShrunkRect = max(
bottomRightOffset + vec2(radius),
topLeftOffset + vec2(radius));
float maxDist = max(distanceFromShrunkRect.x, distanceFromShrunkRect.y);
highp float maxDist = max(distanceFromShrunkRect.x, distanceFromShrunkRect.y);
// Inside the shrunk rectangle
if (maxDist <= 0.0)
@@ -47,38 +43,38 @@ float distanceFromRoundedRect(vec2 offset, float radius)
return length(max(vec2(0.0), distanceFromShrunkRect));
}
float distanceFromDrawingRect()
highp float distanceFromDrawingRect()
{
vec2 topLeftOffset = v_TexRect.xy - v_TexCoord;
highp vec2 topLeftOffset = v_TexRect.xy - v_TexCoord;
topLeftOffset = vec2(
v_BlendRange.x > 0.0 ? topLeftOffset.x / v_BlendRange.x : 0.0,
v_BlendRange.y > 0.0 ? topLeftOffset.y / v_BlendRange.y : 0.0);
vec2 bottomRightOffset = v_TexCoord - v_TexRect.zw;
highp vec2 bottomRightOffset = v_TexCoord - v_TexRect.zw;
bottomRightOffset = vec2(
v_BlendRange.x > 0.0 ? bottomRightOffset.x / v_BlendRange.x : 0.0,
v_BlendRange.y > 0.0 ? bottomRightOffset.y / v_BlendRange.y : 0.0);
vec2 xyDistance = max(topLeftOffset, bottomRightOffset);
highp vec2 xyDistance = max(topLeftOffset, bottomRightOffset);
return max(xyDistance.x, xyDistance.y);
}
void main(void)
{
float dist = distanceFromRoundedRect(vec2(0.0), g_CornerRadius);
float alphaFactor = 1.0;
vec4 texel = texture2D(m_Sampler, v_TexCoord, -0.9);
highp float dist = distanceFromRoundedRect(vec2(0.0), g_CornerRadius);
lowp float alphaFactor = 1.0;
lowp vec4 texel = texture2D(m_Sampler, v_TexCoord, -0.9);
// Discard inner pixels
if (g_DiscardInner)
{
float innerDist = (g_EdgeOffset == vec2(0.0) && g_InnerCornerRadius == g_CornerRadius) ?
highp float innerDist = (g_EdgeOffset == vec2(0.0) && g_InnerCornerRadius == g_CornerRadius) ?
dist : distanceFromRoundedRect(g_EdgeOffset, g_InnerCornerRadius);
// v_BlendRange is set from outside in a hacky way to tell us the g_MaskingBlendRange used for the rounded
// corners of the edge effect container itself. We can then derive the alpha factor for smooth inner edge
// effect from that.
float innerBlendFactor = (g_InnerCornerRadius - g_MaskingBlendRange - innerDist) / v_BlendRange.x;
highp float innerBlendFactor = (g_InnerCornerRadius - g_MaskingBlendRange - innerDist) / v_BlendRange.x;
if (innerBlendFactor > 1.0)
{
gl_FragColor = vec4(0.0);
@@ -92,8 +88,8 @@ void main(void)
dist /= g_MaskingBlendRange;
// This correction is needed to avoid fading of the alpha value for radii below 1px.
float radiusCorrection = g_CornerRadius <= 0.0 ? g_MaskingBlendRange : max(0.0, g_MaskingBlendRange - g_CornerRadius);
float fadeStart = (g_CornerRadius + radiusCorrection) / g_MaskingBlendRange;
highp float radiusCorrection = g_CornerRadius <= 0.0 ? g_MaskingBlendRange : max(0.0, g_MaskingBlendRange - g_CornerRadius);
highp float fadeStart = (g_CornerRadius + radiusCorrection) / g_MaskingBlendRange;
alphaFactor *= min(fadeStart - dist, 1.0);
if (v_BlendRange.x > 0.0 || v_BlendRange.y > 0.0)
@@ -108,8 +104,9 @@ void main(void)
// This ends up softening glow without negatively affecting edge smoothness much.
alphaFactor = pow(alphaFactor, g_AlphaExponent);
float borderStart = 1.0 + fadeStart - g_BorderThickness;
float colourWeight = min(borderStart - dist, 1.0);
highp float borderStart = 1.0 + fadeStart - g_BorderThickness;
lowp float colourWeight = min(borderStart - dist, 1.0);
if (colourWeight <= 0.0)
{
gl_FragColor = toSRGB(vec4(g_BorderColour.rgb, g_BorderColour.a * alphaFactor));

View File

@@ -2,22 +2,22 @@
uniform bool g_GammaCorrection;
float toLinear(float color)
lowp float toLinear(lowp float color)
{
return color <= 0.04045 ? (color / 12.92) : pow((color + 0.055) / 1.055, GAMMA);
}
vec4 toLinear(vec4 colour)
lowp vec4 toLinear(lowp vec4 colour)
{
return vec4(toLinear(colour.r), toLinear(colour.g), toLinear(colour.b), colour.a);
}
float toSRGB(float color)
lowp float toSRGB(lowp float color)
{
return color < 0.0031308 ? (12.92 * color) : (1.055 * pow(color, 1.0 / GAMMA) - 0.055);
}
vec4 toSRGB(vec4 colour)
lowp vec4 toSRGB(lowp vec4 colour)
{
#ifdef GL_ES
return g_GammaCorrection ? vec4(toSRGB(colour.r), toSRGB(colour.g), toSRGB(colour.b), colour.a) : colour;