osu-framework icon indicating copy to clipboard operation
osu-framework copied to clipboard

NullReferenceException when accessing ScreenSpaceDrawQuad

Open nobbele opened this issue 1 year ago • 3 comments

not being able to access ScreenSpaceDrawQuad when IsLoaded == true seems like a bug to me. Parent is null, I assume it gets initialized too late?

Relevant stacktrace:

System.NullReferenceException: Object reference not set to an instance of an object.
at osu.Framework.Graphics.Containers.CompositeDrawable.get_BoundingBox()
at osu.Framework.Graphics.Drawable.computeRequiredParentSizeToFit()
at osu.Framework.Graphics.Containers.CompositeDrawable.computeAutoSize()
at osu.Framework.Graphics.Containers.CompositeDrawable.updateAutoSize()
at osu.Framework.Graphics.Containers.CompositeDrawable.updateChildrenSizeDependencies()
at osu.Framework.Graphics.Containers.CompositeDrawable.get_Size()
at osu.Framework.Graphics.Drawable.get_DrawSize()
at osu.Framework.Graphics.Drawable.ComputeScreenSpaceDrawQuad()
at osu.Framework.Graphics.Drawable.get_ScreenSpaceDrawQuad()

nobbele avatar May 14 '23 16:05 nobbele

How/where are you seeing this come up?

smoogipoo avatar May 15 '23 07:05 smoogipoo

It's running inside Scheduler.AddDelayed(f, dt, repeat: true)'s handler f The Drawable gets added to the list used inside f at the end of it's [BackgroundDependencyLoader] load

Can f be ran after IsLoaded = true but before Parent = xx due to threading perhaps?

nobbele avatar May 15 '23 16:05 nobbele

I'm not entirely seeing how this is possible. Are you working on a personal project with osu!framework and hitting this? If so, can you post a trimmed down example that causes it to happen?

I haven't been able to reproduce with this simple test:

public partial class TestSceneTesting : FrameworkTestScene
{
    [Test]
    public void TestAddDrawable()
    {
        AddStep("add drawable", () => Add(new TestDrawable()));
    }

    private partial class TestDrawable : CompositeDrawable
    {
        [BackgroundDependencyLoader]
        private void load()
        {
            Drawable autoSizedDrawable;
            InternalChild = autoSizedDrawable = new Container
            {
                AutoSizeAxes = Axes.Both,
                Child = new Container
                {
                    AutoSizeAxes = Axes.Both,
                    Child = new Box { Size = new Vector2(100) }
                }
            };

            Scheduler.Add(() => Trace.Assert(autoSizedDrawable.DrawSize.X == 100));
        }
    }
}

Can f be ran after IsLoaded = true but before Parent = xx due to threading perhaps?

I don't believe so. There's definitely some weirdness around when o!f considers a Drawable to be IsLoaded = true - a parent is always IsLoaded = true and invokes LoadComplete() before its children, however children receive a Parent during that parent's load.

This would happen here: https://github.com/ppy/osu-framework/blob/9f689c541b7c58459a63caf6487e36c82b34fd2b/osu.Framework/Graphics/Containers/CompositeDrawable.cs#L247-L262

smoogipoo avatar May 22 '23 00:05 smoogipoo