Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImVector types #48

Closed
xposure opened this issue Mar 1, 2018 · 13 comments
Closed

ImVector types #48

xposure opened this issue Mar 1, 2018 · 13 comments

Comments

@xposure
Copy link

xposure commented Mar 1, 2018

Since as far as I know this isn't possible...

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct ImVector<T>
        where T: struct
    {
        public int Size;
        public int Capacity;
        ///<summary>T* Data</summary>
        public T* Data;
    }

Would there be consideration to clean some of these up with something like T4 Text Templates and auto generate a series of ImVectors as something like ...

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct ImVector
    {
        public int Size;
        public int Capacity;
        ///<summary>DrawVert* Data</summary>
        public DrawVert* Data;
    }

Could even implement something like...

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct ImVector
    {
        public int Size;
        public int Capacity;
        ///<summary>T* Data</summary>
        public DrawVert* Data;

        public ref DrawVert this[int index]
        {
            get
            {
                ////could save a lot of protected memory errors
                //if(index <= 0 || index >= Size)
                //    throw new ArgumentOutOfRangeException(nameof(index));
                return ref Data[index];
            }
        }
    }

I'm not sure on the level of where you want to take this project if its simply just "its good enough" then thats fine and I can start work in my own fork to tidy things up. I think this sort of goes with this issue #23 though and would be the start in implementing more friendly things.

EDIT: I had a bit of a hassle using this project (and some others last time) due to inconsistencies between the projects and in the end I ported ImGui to straight .NET here https://github.com/xposure/ImGuiSharp this didn't feel well received so I haven't expanded on it. Now I'm on a new project and circling back around to this.

@mellinoe
Copy link
Collaborator

mellinoe commented Mar 1, 2018

I'd rather do something like this:

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct ImVector<T> where T : struct
    {
        private static readonly int s_sizeofT = Unsafe.SizeOf<T>();

        public int Size;
        public int Capacity;
        ///<summary>T* Data</summary>
        public void* Data;

        public ref T this[int index]
        {
            get
            {
                if (index < 0 || index >= Size)
                {
                    throw new ArgumentOutOfRangeException(nameof(index));
                }
                return ref Unsafe.AsRef<T>((byte*)Data + (s_sizeofT * index));
            }
        }
    }

When I originally wrote this stuff, I don't think ref returns, or the Unsafe library existed yet.

This gets you typed access to the contents, and we don't need to blow up the number of types in the library.

@xposure
Copy link
Author

xposure commented Mar 1, 2018

I'm not familiar with Unsafe so I'll look in to that. My only concern here is the extra level of indirection being added. I assume the JIT in release mode would inline the indexer out but if I remember right this can be tricky with generics and adding a function call inside here would add to the overhead of the call.

PS Yes, return ref has given some of my code syntactical sugar while keeping performance up, I love it. It also has a lot of other benefits as well.

@mellinoe
Copy link
Collaborator

mellinoe commented Mar 1, 2018

I wouldn't worry too much about the overhead of it. I don't think there's much that can be done to reduce it, and essentially all that is being done is re-interpreting a pointer as a managed reference. It doesn't get much simpler than that. I think the indexer call is unavoidable.

BTW, what are you interested in using this for? Generally the stuff contained in ImVectors is internal or otherwise not that interesting. Usually you don't need to poke around in them.

@xposure
Copy link
Author

xposure commented Mar 1, 2018

I've been working on an editor of sorts and seeing how you have recently started bringing in some of the docking code from imgui, it brought me back to here. I'm not directly trying to use it as of now, but this project can be a bit of a head ache to get setup outside of the example in another ecosystem. I have experience in this area but I know some don't and I thought if I could help simplify some of this stuff it would help others get setup quicker.

For example, creating a DrawVert ImVector would ease the readability of the sample code for people not experienced with pointers. DrawVert is something you really have to access here because you need to push something to the GPU. I know its a one time setup, but was just looking to lower the entry for some.

@mellinoe
Copy link
Collaborator

mellinoe commented Mar 1, 2018

You just need the pointer and the data size to push to the GPU, though -- you don't need to iterate through individual elements. We can definitely add this since it's not going to hurt anything, but I think it will still be an advanced scenario to read individual elements of an ImVector.

@mellinoe
Copy link
Collaborator

mellinoe commented Mar 1, 2018

you have recently started bringing in some of the docking code from imgui

I'm using the docking code from Lumix Engine in the editor for my own engine and it is working well. I've been meaning to revamp the sample code here and to provide the docking stuff as an extra helper, but lots of stuff has gotten in the way of that...

@xposure
Copy link
Author

xposure commented Mar 2, 2018

You just need the pointer and the data size to push to the GPU, though -- you don't need to iterate through individual elements. We can definitely add this since it's not going to hurt anything, but I think it will still be an advanced scenario to read individual elements of an ImVector.

There is currently no way to submit a pointer of data in MonoGame, it expects an array of T. Now that you have shown me Unsafe, I imagine there might be a way. Going to mess with this later tonight.

I'm using the docking code from Lumix Engine in the editor for my own engine and it is working well. I've been meaning to revamp the sample code here and to provide the docking stuff as an extra helper, but lots of stuff has gotten in the way of that...

Are you using this in .NET? I seen the project but never considered to check for .NET bindings.

@mellinoe
Copy link
Collaborator

mellinoe commented Mar 2, 2018

There is currently no way to submit a pointer of data in MonoGame, it expects an array of T. Now that you have shown me Unsafe, I imagine there might be a way. Going to mess with this later tonight.

Seems like a major hole in the API to me, but I'm not familiar with MonoGame. There might be another way to do it.

Are you using this in .NET? I seen the project but never considered to check for .NET bindings.

Yes, I've converted the code to C#. https://gist.github.com/mellinoe/4b8f8886d864d1d1eb00ec5f69c465a5

@dmitsuki
Copy link

Hi Mellinoe, do you have a basic usage example of that imguidock class? If not that's fine, I will just have to figure it out myself.

@mellinoe
Copy link
Collaborator

@dmitsuki I don't have anything public, no. It's fairly simple, though:

DockContext.RootDock(Vector2.Zero, ImGui.GetIO().DisplaySize);
if (DockContext.BeginDock("Panel A", WindowFlags.Default, new Vector2(500, 300)))
{
    // Draw Stuff
    DockContext.EndDock();
}
if (DockContext.BeginDock("Panel B", WindowFlags.Default, new Vector2(500, 300)))
{
    // Draw Other Stuff
    DockContext.EndDock();
}

@hypeartist
Copy link

@xposure

Since as far as I know this isn't possible...

Anything like this, maybe?

	public unsafe struct PodPointer<T> where T : struct
	{
		// ReSharper disable once InconsistentNaming
		private static readonly int TSize = Unsafe.SizeOf<T>();

		private readonly byte* _pointer;

		public PodPointer(void* unsafePtr)
		{
			_pointer = (byte*) unsafePtr;
		}

		public ref T this[int pos]
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			get => ref Unsafe.AsRef<T>(_pointer + pos * TSize);
		}

		public ref T Ref
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			get => ref Unsafe.AsRef<T>(_pointer);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public void Fill(T value, int length)
		{
			var l = length;
			var p = _pointer;
			do
			{
				Unsafe.Write(p, value);
				p += TSize;
			} while (--l != 0);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public ref TOther AsRef<TOther>() => ref Unsafe.AsRef<TOther>(_pointer);

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public PodPointer<TOther> AsPtr<TOther>() where TOther : struct => new PodPointer<TOther>(_pointer);

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static PodPointer<T> operator ++(PodPointer<T> p) => new PodPointer<T>(p._pointer + TSize);

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static PodPointer<T> operator --(PodPointer<T> p) => new PodPointer<T>(p._pointer - TSize);

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static PodPointer<T> operator +(PodPointer<T> p, int o) => new PodPointer<T>(p._pointer + o * TSize);

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static PodPointer<T> operator -(PodPointer<T> p, int o) => new PodPointer<T>(p._pointer - o * TSize);

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator <(PodPointer<T> p1, PodPointer<T> p2) => p1._pointer < p2._pointer;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator <=(PodPointer<T> p1, PodPointer<T> p2) => p1._pointer <= p2._pointer;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator >=(PodPointer<T> p1, PodPointer<T> p2) => p1._pointer >= p2._pointer;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator >(PodPointer<T> p1, PodPointer<T> p2) => p1._pointer > p2._pointer;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator ==(PodPointer<T> p1, PodPointer<T> p2) => p1._pointer == p2._pointer;

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static bool operator !=(PodPointer<T> p1, PodPointer<T> p2) => !(p1 == p2);

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static long operator -(PodPointer<T> p1, PodPointer<T> p2) => (p1._pointer - p2._pointer) / TSize;

		public static implicit operator void*(PodPointer<T> p) => p._pointer;

		public static implicit operator IntPtr(PodPointer<T> p) => (IntPtr) p._pointer;
	}

@mellinoe
Copy link
Collaborator

mellinoe commented Jul 6, 2018

I will likely make a breaking change for this improvement at the next native version bump.

@mellinoe
Copy link
Collaborator

The new 1.65.0 version has a significantly better abstraction for ImVector which gives you type-safe by-ref access to native structures.

https://github.com/mellinoe/ImGui.NET/blob/master/src/ImGui.NET/ImVector.cs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants