wyvern010
Topic Author
Posts: 19
Joined: 18 Apr 2019, 13:41

Two Instances of the same App class

06 Oct 2019, 19:52

As title implies: I'm trying to create two instances of the same app from one executable.
Basically i want 2 separate windows with the same view and put them on 2 monitors respectively.

The following code runs ok, but when uncommenting T.Start(), the error pops up: "An Application was already created"
 class Apper
    {

        static Thread T = null;
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            T = new Thread(new ThreadStart(SecondWindow));
           // T.Start();

            App app = new App();
            app.Run();
        }

        static void SecondWindow()
        {
            App app = new App(2);
            app.Run();
        }
    }
I also changed the App class from examples as:
  public partial class App : Application
    {
        public App()
        {
            Uri = "App.xaml";
        }

        public App(int WindowNumber)
        {
            Uri = "App.xaml";
        }

        protected override Display CreateDisplay()
        {
            return new Win32Display();
        }

        protected override RenderContext CreateRenderContext()
        {
            return new RenderContextD3D11();
        }
        
        protected override bool VSync => true;
    }

Tags:
 
User avatar
jsantos
Site Admin
Posts: 2584
Joined: 20 Jan 2012, 17:18
Contact:

Re: Two Instances of the same App class

08 Oct 2019, 06:13

It doesn't make sense to have two App classes for the same executable. What you probably need is one App class and several Window instances. That scenario should be supported although our examples do not use it, so probably minor changes may be needed.
 
wyvern010
Topic Author
Posts: 19
Joined: 18 Apr 2019, 13:41

Re: Two Instances of the same App class

08 Oct 2019, 21:58

I thought, cus of the App class seems to be the starting point of the application and the overriden function.
Is there an quick sample on how to do this?

I tried Creating 2 MainWindows, but the Display property is only Get.
Then furthermore i think i also should create an render context? or is this all handles internaly.
Currently im trying to get the openGL integration to work with just pure noesis classes.
This is what i got so far:
namespace HelloWorld
{
    public class Apper
    {
        static TimeSpan start = TimeSpan.FromTicks(DateTime.Now.Ticks);
        static View _view = null;
        static RenderContextD3D11 context = null;

        [STAThread]
        static void Main()
        {
            //App app = new App();
            // app.Uri = "App.xaml";
            // app.Run();

            Noesis.Log.SetLogCallback((level, channel, message) =>
            {
                if (channel == "")
                {
                    // [TRACE] [DEBUG] [INFO] [WARNING] [ERROR]
                    string[] prefixes = new string[] { "T", "D", "I", "W", "E" };
                    string prefix = (int)level < prefixes.Length ? prefixes[(int)level] : " ";
                    Console.WriteLine("[NOESIS/" + prefix + "] " + message);
                }
            });

            Noesis.GUI.Init();
            Win32Display disp = new Win32Display();
            context = new RenderContextD3D11();
           // context.SetWindow(disp.NativeWindow);
            context.Init(disp.NativeHandle, disp.NativeWindow, 1, false, false);
           // window.Init(disp, context, 1, false);

            _view = GUI.CreateView((Noesis.Grid)Noesis.GUI.ParseXaml(@"
                <Grid xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
                    <Grid.Background>
                        <LinearGradientBrush StartPoint=""0,0"" EndPoint=""0,1"">
                            <GradientStop Offset=""0"" Color=""#FF123F61""/>
                            <GradientStop Offset=""0.6"" Color=""#FF0E4B79""/>
                            <GradientStop Offset=""0.7"" Color=""#FF106097""/>
                        </LinearGradientBrush>
                    </Grid.Background>
                    <Viewbox>
                        <StackPanel Margin=""50"">
                            <Button Content=""Hello World!"" Margin=""0,30,0,0""/>
                            <Rectangle Height=""5"" Margin=""-10,20,-10,0"">
                                <Rectangle.Fill>
                                    <RadialGradientBrush>
                                        <GradientStop Offset=""0"" Color=""#40000000""/>
                                        <GradientStop Offset=""1"" Color=""#00000000""/>
                                    </RadialGradientBrush>
                                </Rectangle.Fill>
                            </Rectangle>
                        </StackPanel>
                    </Viewbox>
                </Grid>"));
            _view.Renderer.Init(context.Device);
            _view.SetSize(1920, 1080);
            _view.Activate();

            disp.Show();
            disp.Render += new RenderEventHandler(Rndr);
            disp.EnterMessageLoop(false);
        }

        private static void Rndr(Display disp)
        {
            double time = TimeSpan.FromTicks(DateTime.Now.Ticks).TotalSeconds - start.TotalSeconds;
            context.BeginRender();
            _view.Update(TimeSpan.FromTicks(DateTime.Now.Ticks).TotalSeconds);
            _view.Renderer.UpdateRenderTree();
            _view.Renderer.RenderOffscreen();
            _view.Renderer.Render();
            context.EndRender();
            context.Swap();
        }
    }
}
All i see is a black window, and with msi afterburner i see 2 "stat" overlays. but not the xaml....
 
User avatar
jsantos
Site Admin
Posts: 2584
Joined: 20 Jan 2012, 17:18
Contact:

Re: Two Instances of the same App class

09 Oct 2019, 05:40

Our application layer is just he minimum code for our samples. We tried to be as simple as possible to improve readability of this layer.

My recommendation, avoid the App layer and just create everything on top of the Core layer as we are doing in the GLUT sample.

Even if you are using two windows, you want the same rendercontext. I will find time later to give you a better explanation.
 
User avatar
sfernandez
Site Admin
Posts: 1652
Joined: 22 Dec 2011, 19:20

Re: Two Instances of the same App class

09 Oct 2019, 11:23

You can only have 1 application, but you don't need more. Your own application class can handle multiple windows (base Application class only provides you the MainWindow) by having a List of opened windows.

If you use our application framework you will need a Display and a RenderContext for each Window, and they have to run in separate threads to have their own message/update loop.

But if you have your own windowing system (as you can see in our integration sample, not using the application framework) you can handle everything in a single thread with a single RenderDevice that will be shared across all the Views (one or more for each window).
 
User avatar
sfernandez
Site Admin
Posts: 1652
Joined: 22 Dec 2011, 19:20

Re: Two Instances of the same App class

09 Oct 2019, 12:05

Some pseudo code:
namespace MultiWindow
{
    internal static class Program
    {
        [STAThread]
        private static void Main()
        {
            Noesis.GUI.Init();

            // ... create SO windows (window1, window2)
            // ... create device context
            Noesis.RenderDeviceD3D11 device = new Noesis.RenderDeviceD3D11(context.NativePointer);

            Noesis.UserControl xaml1 = Noesis.GUI.LoadXaml("Window1.xaml");
            Noesis.View view1 = Noesis.GUI.CreateView(xaml1);
            view1.Renderer.Init(device);
            view1.SetSize(window1.Width, window1.Height);

            Noesis.UserControl xaml2 = Noesis.GUI.LoadXaml("Window2.xaml");
            Noesis.View view2 = Noesis.GUI.CreateView(xaml2);
            view2.Renderer.Init(device);
            view2.SetSize(window2.Width, window2.Height);
            
            //...
            
            // Render loop
            while (running)
            {
                view1.Update(totalSeconds);
                view2.Update(totalSeconds);
                
                view1.Renderer.UpdateRenderTree();
                view2.Renderer.UpdateRenderTree();
                
                view1.Renderer.RenderOffscreen();
                view2.Renderer.RenderOffscreen();
                
                // Set render buffers and clear each window...
                
                view1.Renderer.Render();
                view2.Renderer.Render();
                
                // Swap each window...
            }
        }
    }
}
 
wyvern010
Topic Author
Posts: 19
Joined: 18 Apr 2019, 13:41

Re: Two Instances of the same App class

09 Oct 2019, 22:30

So have been fiddling around and went for the:
If you use our application framework you will need a Display and a RenderContext for each Window, and they have to run in separate threads to have their own message/update loop.
way like this:
 public class AppDisplay
    {
        DateTime start = DateTime.Now;
        Display _display = null;
        Window _window = null;
        RenderContext _context = null;
        Thread RenderThread = null;

        public AppDisplay()
        {

        }

        public AppDisplay(Display display, Window window, RenderContext context)
        {
            _window = window;
            _display = display;
            _display.SetResizeMode(ResizeMode.CanResize);
            _context = context;
            _context.Init(_display.NativeHandle, _display.NativeWindow, 1, true, false);
            _window.Init(display, context, 1, true);
            _display.Show();
            RenderThread = new Thread(new ThreadStart(RenderFunction));
            RenderThread.Start();
            _display.EnterMessageLoop(false);
            RenderThread.Abort();
            RenderThread.Join();
        }

        private void RenderFunction()
        {
            while (true)
            {
                _window.Render((DateTime.Now - start).TotalSeconds);
            }
        }
    }
    
And use it like this:

static void Main()
{
	    GUI.Init();
            GUI.SetXamlProvider(new EmbeddedXamlProvider(System.Reflection.Assembly.GetCallingAssembly(),"Slidecrew"));

            Thread T = new Thread(new ParameterizedThreadStart(SecondWindow));
            T.Start();
            AppDisplay WindowOne = new AppDisplay(new Win32Display(), new MainWindow(), new RenderContextD3D11());
            T.Join();
 }
            
        static void SecondWindow(object sender)
        {
            SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
            AppDisplay WindowTwo = new AppDisplay(new Win32Display(),new MainWindow(), new RenderContextD3D11());
        }
I get a lot of errors reading like:
The calling thread (17724) cannot access this object (.?AVView@Noesis@@) because a different thread owns it (14176)
Obviously its an threading issue, but why?
Cus im creating separate instances of MainWindow wich loads .xaml using: GUI.LoadComponent(this, "MainWindow.xaml");
Wich should return an new instance of that file?....

If you want i can make a small compilable example to check out?
 
User avatar
sfernandez
Site Admin
Posts: 1652
Joined: 22 Dec 2011, 19:20

Re: Two Instances of the same App class

10 Oct 2019, 10:27

Those threading errors are because you are creating the Window in one thread and updating it in another thread.
You should do all the creation code inside the thread that will update the window:
RenderThread = new Thread(new ThreadStart(WindowProc));
RenderThread.Start();
...
private void WindowProc()
{
  _display = new Win32Display();
  _display.SetResizeMode(ResizeMode.CanResize);
  _context = new RenderContextD3D11();
  _context.Init(_display.NativeHandle, _display.NativeWindow, 1, true, false);
  _window = new MainWindow();
  _window.Init(display, context, 1, true);
  
  DateTime startTime = DateTime.Now;
  _display.Render += (d) =>
  {
    _window.Render((DateTime.Now - startTime).TotalSeconds);
  };
  
  _display.Show();
  _display.EnterMessageLoop(false);
}
 
wyvern010
Topic Author
Posts: 19
Joined: 18 Apr 2019, 13:41

Re: Two Instances of the same App class

11 Oct 2019, 21:44

Alright, so i've set it up like you said.
static void FirstWindow()
        {
            Window _window = new MainWindow();
            Display _display = new Win32Display();
            _display.SetResizeMode(ResizeMode.CanResize);
            RenderContext _context = new RenderContextD3D11();
            _context.Init(_display.NativeHandle, _display.NativeWindow, 1, true, false);
            _window.Init(_display, _context, 1, true);
            DateTime start = DateTime.Now;
            _display.Render += (s) =>
            {
                _window.Render((DateTime.Now - start).TotalSeconds);
            };
            _display.Show();
            _display.EnterMessageLoop(false);
        }
and in Main()
            GUI.Init();
            GUI.SetXamlProvider(new EmbeddedXamlProvider(System.Reflection.Assembly.GetExecutingAssembly(), "RssReader"));
            
            Thread T = new Thread(new ThreadStart(FirstWindow));
            T.Start();
           // Thread T2 = new Thread(new ThreadStart(FirstWindow));
          //  T2.Start();
            T.Join();
          //  T2.Join();
This runs ok when only one thread is fired.
When the second thread is called into the mix, it crashed all over the place.
Sometimes it crashes at start-up, other times only one window is rendered, some other time i get an MemoryAccessViolation at:
  protected override bool ConnectEvent(object source, string eventName, string handlerName)
        {
            if (eventName == "Click" && handlerName == "OnPrevClicked")
            {
                ((Button)source).Click += OnPrevClicked;
                return true;
            }
            if (eventName == "Click" && handlerName == "OnNextClicked")
            {
                ((Button)source).Click += OnNextClicked;
                return true;
            }
            return false;
        }
        
So i opened an example, Application tutorial to be exact.
Implemented the change i did in my own project and got the same results.

I understand the threading issue in the Render call.
But since we basically, or so seems it, create a new instance on MainWindow in every thread for every window i don't know why it crashes even in the example project.

Here is a link to my dropbox with the project i edited.
https://www.dropbox.com/s/z87argdm2td82 ... l.rar?dl=0
 
User avatar
sfernandez
Site Admin
Posts: 1652
Joined: 22 Dec 2011, 19:20

Re: Two Instances of the same App class

14 Oct 2019, 12:44

Could you please open a ticket in our bugtracker, it seems there are some parts in our code that are not thread-safe and are causing those crashes.

While that is being addressed, have you tried the other approach I suggested, by using a single RenderDevice shared among several Views (in the same thread)?

Who is online

Users browsing this forum: No registered users and 3 guests