Thanks for the tips!
Why don't you resize the view like you are supposed to do?
I don't know how I missed that section of the View tutorial, but that would probably do the trick. Ideally the RenderControl would work when its DockStyle was set to fill, but that caused a
lot of trouble initially... I'll give this a try and report back.
You never dispose the old window
Good catch. Ran into trouble when testing before I'd finished the class, and must have missed that.
Not to mention SFML doesn't exactly play nicely with threads in some situations, so I don't understand why you have all these monitor calls all over
This control will eventually be incorporated into an application where rendering runs in a separate thread than the GUI, as is necessary to keep the GUI from hanging when using a rendering loop. After I shifted construction of all OpenGL-related objects to the rendering thread I haven't had any trouble when using SFML in my multithreaded app... if you have/know of any specific concerns, I'd be happy to hear them, though!
As for their purpose: if the GUI thread calls Destroy/Create at the same time the rendering thread calls Draw (or something) then we might have a race - if m_Window.Dispose had been called but m_Window hadn't yet been set to null, for example, we might try to draw to the disposed object (right...?). These will probably be converted into something more elegant, but during testing they will stay there.
Why don't you just store a context setting struct as a member variable
I had been considering making these changeable, but then I noticed that SFML doesn't let you do this and figured I shouldn't either. In retrospect I don't really know why you'd want to change them anyway. That will be fixed as soon as the more important bits are taken care of.
EDIT: Altering the view as suggested in that tutorial works wonderfully - thanks for the link.
At this point, zsbzsb, I practically owe you a co-author credit on my application
As it stands I believe the class to be fully functional - if there are other concerns to be considered in a multithreaded application, though, I might need to make some changes (e.g. I am not entirely sure which of the built-in RenderWindow functions
won't lead to a race, so some uses of Monitor might be unnecessary). I might just do away with the Monitor stuff entirely and assume that it will be handled externally (if needed), as this is more in line with what is expected of SFML.
Feel free to toss any other suggestions my way - revised code below.
using SFML.Graphics;using SFML.Window;using System;using System.Threading;using System.Windows.Forms;namespace RenderControl
{ public partial class RenderControl
: UserControl
{ private RenderWindow m_Window
= null; private ContextSettings m_Context
; public RenderControl
(uint depth
= 0,
uint stencil
= 0,
uint antialiasing
= 0,
uint major
= 2,
uint minor
= 0) { InitializeComponent
(); this.m_Context = new ContextSettings
(depth, stencil, antialiasing, major, minor
); this.SizeChanged += new EventHandler
(RenderControl_SizeChanged
); } ~RenderControl
() { this.m_Window.Dispose(); } #region interface public void Create
() { Monitor
.Enter(this); if(this.m_Window != null) this.m_Window.Dispose(); this.m_Window = new RenderWindow
(this.Handle,
this.m_Context); this.m_Window.SetVerticalSyncEnabled(true); Monitor
.Exit(this); } public void Destroy
() { Monitor
.Enter(this); if (this.m_Window != null) { this.m_Window.Close(); this.m_Window.Dispose(); this.m_Window = null; } Monitor
.Exit(this); } public void DispatchEvents
() { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.DispatchEvents(); Monitor
.Exit(this); } public void SetActive
(bool active
= true) { Monitor
.Enter(this); if(this.m_Window != null) this.m_Window.SetActive(active
); Monitor
.Exit(this); } public void Clear
() { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Clear(Color
.Black); Monitor
.Exit(this); } public void Clear
(Color color
) { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Clear(color
); Monitor
.Exit(this); } public void Draw
(Drawable drawable
) { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Draw(drawable
); Monitor
.Exit(this); } public void Draw
(Drawable drawable, RenderStates states
) { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Draw(drawable, states
); Monitor
.Exit(this); } public void Draw
(Vertex
[] vertices, PrimitiveType type
) { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Draw(vertices, type
); Monitor
.Exit(this); } public void Draw
(Vertex
[] vertices, PrimitiveType type, RenderStates states
) { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Draw(vertices, type, states
); Monitor
.Exit(this); } public void Draw
(Vertex
[] vertices,
uint start,
uint count, PrimitiveType type
) { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Draw(vertices, start, count, type
); Monitor
.Exit(this); } public void Draw
(Vertex
[] vertices,
uint start,
uint count, PrimitiveType type, RenderStates states
) { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Draw(vertices, start, count, type, states
); Monitor
.Exit(this); } public void Display
() { Monitor
.Enter(this); if (this.m_Window != null) this.m_Window.Display(); this.Refresh(); Monitor
.Exit(this); } #endregion #region paint overrides protected override void OnPaint
(PaintEventArgs e
) { //don't call base method! //base.OnPaint(e); } protected override void OnPaintBackground
(PaintEventArgs e
) { //don't call base method! //base.OnPaintBackground(e); } #endregion void RenderControl_SizeChanged
(object sender, EventArgs e
) { Monitor
.Enter(this); //need to recreate view when the window changes if(this.m_Window != null) { this.m_Window.SetView(new SFML
.Graphics.View(new FloatRect
(0f, 0f,
this.Size.Width,
this.Size.Height))); } Monitor
.Exit(this); } }}