Building a Windows.Forms + GLControl based application
Note 1: This tutorial is somewhat dated. For example, no "garbage" is seen anymore in the design view of Visual Studio. And the default color is beige, not black. Apart from those (minor) issues, this tutorial will get you started using OpenTK+Windows.Forms.
Note 2: If you have some spare time and want to contribute to the OpenTK project, please update the below page (by clicking the "Edit" link above when logged in) to reflect the current state of OpenTK.
This tutorial assumes familiarity with Windows.Forms application development in Visual Studio 2005/C#, and at least basic knowledge of OpenGL. It also assumes a top-to-bottom readthrough; it is a guide and not a reference.
To begin with, it is quite a different approach one has to take when designing a game/application using the GLControl in a Windows.Form compared to using the GameWindow. GLControl is more low-level than GameWindow so you'll have to pull the strings on for example time measurements by yourself. In GameWindow, you get more for free!
Just as in the GameWindow case, GLControl uses the default OpenGL driver of the system, so with the right drivers installed it will be hardware accelerated. However, with large windows it will be slower than the corresponding fullscreen GameWindow, because of how the underlying windowing system works [someone with more detailed knowledge than me may want to elaborate on this..].
If you come from a "main-loop-background" (C/SDL/Allegro etc.) when it comes to coding games, you'll have to rethink that fundamentally. You'll have to change into a mindset of "what event should I hook into, and what events should I trigger, and when?" instead.
Why use Windows.Forms+GLControl instead of GameWindow?
The first thing you'll have to decide is:
"Do I really need the added complexity of Windows.Forms + embedded GLControl compared to a windowed GameWindow?"
Here are some reasons why you would like to add that complexity:
- You want to build a GUI-rich/RAD kind of application using the
Windows.Formscontrols. Eg. level editors or model viewers/editors may be in this category while a windowed game leans more towards aGameWindowkind of application. - You want to have an embedded OpenGL rendering panel inside an already existing
Windows.Formsapplication. - You want to be able to do drag-and-drop into the rendering panel, for example dropping a model file into a model viewer.
Assuming you've got at least one of those reasons to build a Windows.Forms+GLControl based application, here's the steps, gotchas and whys for you.
Adding the GLControl to your Windows.Form
(I am assuming you are using Visual Studio 2005 Express Edition. Your mileage may vary if using VS2008 or Monodevelop -- I don't know the details for them -- but the follow sections should be the same no matter how you add the GLControl)
To begin with, create a Form on which you will place your GLControl. Right click in some empty space of the Toolbox, pick "Choose Items..." and browse for OpenTK.dll. Make sure you can find the "GLControl" listed in the ".NET Framework Components", as in the image below.
Then you can add the GLControl to your form as any .NET control. A GLControl named glControl1 will be added to your Form.
The first thing you'll notice is the "junk graphics" inside glControl1. Under the hood, GLControl uses a so called GLContext to do the actual GL-rendering and so on, and this context is only created in runtime, not in design time. So no worries.
Order of creation
The fact that glControl1's GLContext is created in runtime is important to remember however, since you cannot access or change glControl1's properties reliably until the GLContext has been created. The same is true for any GL.* commands (or Glu for that matter!). The conceptual order is this:
- First the Windows.Form constructor runs. Don't touch
glControl/GL - Then the
Loadevent of the form is triggered. Don't touchglControl/GL - Then the
Loadevent of the GLControl is triggered. OK to touchglControl/GL - After the
Loadevent handler has run, any event handler may touchglControl/GL.
So one approach to address this problem is having a bool loaded=false; member variable in your Form, which is set to true in the Load event handler of the GLControl:
using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; public partial class Form1 : Form { bool loaded = false; public Form1() { InitializeComponent(); } private void glControl1_Load(object sender, EventArgs e) { loaded = true; } }
Note: the GLControl.Load event is never fired -- please use the Form.Load event instead until this issue of OpenTK is fixed.
Then in any event handler where you are going to access glControl1/GL you can put a guard first of all:
private void glControl1_Resize(object sender, EventArgs e) { if (!loaded) return; }
A popular way of adding a Load event handler to a Form is via the Properties window of Visual Studio, something like this:
- Click anywhere on the GLControl in the Designer
- Make sure glControl1 is listed in the Properties window
- Click the "Events" button to list all events of glControl1
- Double-click the empty cell right of the Load event to create and hook an event handler for the Load event
Hello World!
The absolutely minimal code you can add at this stage to see something is adding an event handler to the Paint event of glControl1 and filling it with this:
private void glControl1_Paint(object sender, PaintEventArgs e) { if (!loaded) // Play nice return; GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); glControl1.SwapBuffers(); }

Yes! A black viewport. Notice that the GLControl provides a color- and a depth buffer, which we have to clear using GL.Clear(). [TODO: how to control which buffers and formats the GLControl has? Possible at all?]
Next thing would be setting the clear color. An appropriate place to do GL initialization is in the form's Load event handler:
private void glControl1_Load(object sender, EventArgs e) { loaded = true; GL.ClearColor(Color.SkyBlue); // Yey! .NET Colors can be used directly! }

Viewport initialization
Next thing we want to do is draw a single yellow triangle.
First we need to be good OpenGL citizen and setup an orthographic projection matrix using GL.Ortho(). We need to call GL.Viewport() also.
For now we'll add this in the Load event handler by the other initialization code -- ignoring the fact that we may want to allow the user to resize the window/GLControl. We'll look into window resizing later.
I put the viewport initialization in a separate method to make it a little more readable.
private void glControl1_Load(object sender, EventArgs e) { loaded = true; GL.ClearColor(Color.SkyBlue); SetupViewport(); } private void SetupViewport() { int w = glControl1.Width; int h = glControl1.Height; GL.MatrixMode(MatrixMode.Projection); GL.LoadIdentity(); GL.Ortho(0, w, 0, h, -1, 1); // Bottom-left corner pixel has coordinate (0, 0) GL.Viewport(0, 0, w, h); // Use all of the glControl painting area }
And between Clear() and SwapBuffers() our yellow triangle:
private void glControl1_Paint(object sender, PaintEventArgs e) { if (!loaded) return; GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.MatrixMode(MatrixMode.Modelview); GL.LoadIdentity(); GL.Color3(Color.Yellow); GL.Begin(BeginMode.Triangles); GL.Vertex2(10, 20); GL.Vertex2(100, 20); GL.Vertex2(100, 50); GL.End(); glControl1.SwapBuffers(); }
Voila!

Keyboard input
Next thing we want to do is animate the triangle via user interaction. Every time Space is pressed, we want the triangle to move one pixel right.
The two general approaches to keyboard input in a GLControl scenario is using Windows.Forms key events and using the OpenTK KeyboardDevice. Since the rest of our program resides in the Windows.Forms world (our window might be a very small part of a large GUI) we'll play nice and use Windows.Forms key events in this guide.
We'll have an int x=0; variable that we'll increment in a KeyDown event handler. Adding it to the glControl1 and not the Form, means the glControl has to be focused, ie. clicked by the user for key events to be sent to our handler.
int x = 0; private void glControl1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Space) x++; }
We add GL.Translate() to our Paint event handler:
private void glControl1_Paint(object sender, PaintEventArgs e) { if (!loaded) return; GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.MatrixMode(MatrixMode.Modelview); GL.LoadIdentity(); GL.Translate(x, 0, 0); // position triangle according to our x variable ... }
.. and we run our program. But wait! Nothing happens when we push Space! The reason is, glControl1 is not painted all the time; the operating systems window manager (Windows/X/OSX) makes sure as few Paint events as possible happens. Only on resize, obscured window and a couple of more situations do Paint events actually get triggered.
What we would like to do is have a way of telling the window manager "This control needs to be repainted since the data it relies on has changed". We want to notify the window manager that our glControl1 should be repainted. Easy, Invalidate() to the resque:
private void glControl1_KeyDown(object sender, KeyEventArgs e) { if (!loaded) return; if (e.KeyCode == Keys.Space) { x++; glControl1.Invalidate(); } }
Focus behaviour
If you're anything like me you're wondering a little how this focusing behaves; let's find out!
A simple way is painting the triangle yellow when glControl1 is focused and blue when it is not. Right:
private void glControl1_Paint(object sender, PaintEventArgs e) { ... if (glControl1.Focused) // Simple enough :) GL.Color3(Color.Yellow); else GL.Color3(Color.Blue); GL.Begin(BeginMode.Triangles); ... }
So now anytime the triangle is yellow, Space should work alright, and when it's blue any keyboard input will be ignored.
Freedom at last: resizing the window
We will now turn our attention to a sore teeth: window resizing.
Anytime a Windows.Forms control is resized, it's Resize event is triggered. That is true for glControl1 too. That's one piece in the puzzle.
The other piece to find is "What do we need to update when a GLControl is resized?" and the answer is "The viewport and the projection matrix".
Well it seems our SetupViewport() will come in handy once more! Add an event handler to the Resize event of glControl1 and fill it in:
private void glControl1_Resize(object sender, EventArgs e) { SetupViewport(); }

There is still one problem though: if you shrink the window using eg. the bottom-right resize grip of the window, no repaint will trigger automatically. That's because the window manager makes assumptions about where the (0, 0) pixel of a control resides, namely in the top-left corner of the control. (Try resizing using the top-left grip instead - the triangle is repainted continously!). Our general fix to alleviate this problem is instructing the window manager that we really want the repaint to occur upon any resize event:
private void glControl1_Resize(object sender, EventArgs e) { SetupViewport(); glControl1.Invalidate(); }
I want my main loop: driving animation using Application.Idle
So, what if we wanted our triangle to rotate continously? This would be childs play in a main loop scenario: just increment a rotation variable in the main loop, before we render the triangle.
But we don't have any loop. We only have events!
To mend for this lack of continuity we have to force Windows.Forms to do it our way. We want an event triggered every now and then, fast enough to get that realtime interactive feeling.
Now there are several ways to achieve this. One is adding a Timer control to our form, changing rotation in the timers Tick event handler. Another is adding a wild Thread to the soup. The first is a little too high-level and slow while the second is really low-level and a bit harder to get right.
We will take a third path and use a Windows.Forms event designed just for the purpose of being executed "when nothing else is going on": meet the Application.Idle event.
This event is special in a number of ways as you may have guessed already. It is not associated with any Form or other control, but with the program as a whole. You cannot hook into it from the GUI Designer; you'll have to add it manually -- for example in the Load event:
private void glControl1_Load(object sender, EventArgs e) { loaded = true; GL.ClearColor(Color.SkyBlue); SetupViewport(); Application.Idle += new EventHandler(Application_Idle); // press TAB twice after += } void Application_Idle(object sender, EventArgs e) { }
One good thing about the Idle event is that the corresponding event handlers are executed on the Windows.Forms thread. That is good since it means we can access all GUI controls without having to worry about threading issues, a pain we would have to deal with if we cooked our own thread.
So we simply increment our rotation variable in the Idle event handler and Invalidate() glControl1 -- business as usual.
float rotation = 0; void Application_Idle(object sender, EventArgs e) { // no guard needed -- we hooked into the event in Load handler while (glControl1.IsIdle) { rotation += 1; Render(); } }
Let's update our rendering code while we're at it:
private void Render() { ... if (glControl1.Focused) GL.Color3(Color.Yellow); else GL.Color3(Color.Blue); GL.Rotate(rotation, Vector3.UnitZ); // OpenTK has this nice Vector3 class! GL.Begin(BeginMode.Triangles); ... } private void glControl1_Paint(object sender, PaintEventArgs e) { Render(); }
Happy wonders! Look:

The triangle rotates slower when the window is big! How come?
(This might not be true if you have a super-fast-computer with a super-fast-graphics-card; but you want your game to run on your neighbours computer too, don't you?)
The reason is that windowed 3d rendering just is a lot slower than full-screen rendering, in general.
But you can reduce the damage by using a technique called frame-rate independent animation. The idea is simple: increment the rotation variable not with 1 but with an amount that depends on the current rendering speed (if the speed is slow, increment rotation with a larger amount than if the speed is high).
But you need to be able to measure the current rendering speed or, equivalently, how long time it takes to render a frame.
Since .NET2.0 there is a class available to do high-precision time measurements called Stopwatch. Here's how to use it:
Stopwatch sw = new Stopwatch(); sw.Start(); MyAdvancedAlgorithm(); sw.Stop(); double milliseconds = sw.Elapsed.TotalMilliseconds;
(don't try with DateTime.Now -- it has a granularity of 10 or more milliseconds, which is in the same size as typical frame rendering -- worthless..)
Now, if we could measure the time it takes to perform the glControl painting, we would be close to making some kind of frame-rate-independent animation. But there is an even more elegant way: let's measure all time that is not Application.Idle time! Then we'll be sure it is not just the painting that is measured, but everything that has been going on since our last Idle run:
Stopwatch sw = new Stopwatch(); // available to all event handlers private void glControl1_Load(object sender, EventArgs e) { ... sw.Start(); // start at application boot } float rotation = 0; void Application_Idle(object sender, EventArgs e) { // no guard needed -- we hooked into the event in Load handler sw.Stop(); // we've measured everything since last Idle run double milliseconds = sw.Elapsed.TotalMilliseconds; sw.Reset(); // reset stopwatch sw.Start(); // restart stopwatch // increase rotation by an amount proportional to the // total time since last Idle run float deltaRotation = (float)milliseconds / 20.0f; rotation += deltaRotation; glControl1.Invalidate(); }
Cool! The triangle spins with the same speed regardless of window size.
I want an FPS counter!
Yeah me too. It's quite simple now that we've got a Stopwatch.
The idea is just counting the Idle runs, and every second or so, update a Label control with the counter! But we'll have to know when a second has passed, so we need an accumulator variable adding all time slices together.
Quite a lot of logic started to add up in the Idle event handler, so I split it up a little:
void Application_Idle(object sender, EventArgs e) { double milliseconds = ComputeTimeSlice(); Accumulate(milliseconds); Animate(milliseconds); } private double ComputeTimeSlice() { sw.Stop(); double timeslice = sw.Elapsed.TotalMilliseconds; sw.Reset(); sw.Start(); return timeslice; } float rotation = 0; private void Animate(double milliseconds) { float deltaRotation = (float)milliseconds / 20.0f; rotation += deltaRotation; glControl1.Invalidate(); } double accumulator = 0; int idleCounter = 0; private void Accumulate(double milliseconds) { idleCounter++; accumulator += milliseconds; if (accumulator > 1000) { label1.Text = idleCounter.ToString(); accumulator -= 1000; idleCounter = 0; // don't forget to reset the counter! } }
Our FPS counter in all its glory:

Can't you put the complete source code somewhere?
Sure, here it is:
using System; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; using System.Diagnostics; namespace GLControlApp { public partial class Form1 : Form { bool loaded = false; public Form1() { InitializeComponent(); } Stopwatch sw = new Stopwatch(); // available to all event handlers private void glControl1_Load(object sender, EventArgs e) { loaded = true; GL.ClearColor(Color.SkyBlue); // Yey! .NET Colors can be used directly! SetupViewport(); Application.Idle += new EventHandler(Application_Idle); // press TAB twice after += sw.Start(); // start at application boot } void Application_Idle(object sender, EventArgs e) { double milliseconds = ComputeTimeSlice(); Accumulate(milliseconds); Animate(milliseconds); } float rotation = 0; private void Animate(double milliseconds) { float deltaRotation = (float)milliseconds / 20.0f; rotation += deltaRotation; glControl1.Invalidate(); } double accumulator = 0; int idleCounter = 0; private void Accumulate(double milliseconds) { idleCounter++; accumulator += milliseconds; if (accumulator > 1000) { label1.Text = idleCounter.ToString(); accumulator -= 1000; idleCounter = 0; // don't forget to reset the counter! } } private double ComputeTimeSlice() { sw.Stop(); double timeslice = sw.Elapsed.TotalMilliseconds; sw.Reset(); sw.Start(); return timeslice; } private void SetupViewport() { int w = glControl1.Width; int h = glControl1.Height; GL.MatrixMode(MatrixMode.Projection); GL.LoadIdentity(); GL.Ortho(0, w, 0, h, -1, 1); // Bottom-left corner pixel has coordinate (0, 0) GL.Viewport(0, 0, w, h); // Use all of the glControl painting area } private void glControl1_Paint(object sender, PaintEventArgs e) { if (!loaded) return; GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.MatrixMode(MatrixMode.Modelview); GL.LoadIdentity(); GL.Translate(x, 0, 0); if (glControl1.Focused) GL.Color3(Color.Yellow); else GL.Color3(Color.Blue); GL.Rotate(rotation, Vector3.UnitZ); // OpenTK has this nice Vector3 class! GL.Begin(BeginMode.Triangles); GL.Vertex2(10, 20); GL.Vertex2(100, 20); GL.Vertex2(100, 50); GL.End(); glControl1.SwapBuffers(); } int x = 0; private void glControl1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Space) { x++; glControl1.Invalidate(); } } private void glControl1_Resize(object sender, EventArgs e) { SetupViewport(); glControl1.Invalidate(); } } }
Next step: What about multiple GLControls at once?
Yeah it is possible. It is even simple!
All you have to do is "make the appropriate GLControl current".
Let's say you have one GLControl named glCtrl1 and one named glCtrl2. And you have added handlers to the Paint event of both. This is what you do in the event handler of glCtrl1 (of course you do something similar in the event handler of glCtrl2!):
private void glCtrl1_Paint(object sender, PaintEventArgs e) { if (!loaded) return; glCtrl1.MakeCurrent(); // Ohh.. It's that simple? GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); ... }
The same is true for any code calling a GL.* or Glu.* method!
Although each GLControl has its own GraphicsContext, OpenTK will share OpenGL resources by default. This means, any context can use textures, display lists, etc. created on any other context. You can disable this behavior via the GraphicsContext.ShareContexts property.
- Printer-friendly version
- Login or register to post comments



Comments
Re: Building a Windows.Forms+GLControl based application
At the end, you say :
"Technically, each GLControl has it's own "OpenGL context and OpenGL state" -- so for example the clear color may be different in the controls/contexts - and if you want to use the same texture in both you'll have to upload it twice!"
It's false, with OpenGL, one shares contexts with wglShareLists().
Re: Building a Windows.Forms+GLControl based application
Thanks. OpenTK 0.9.0+ will share resources by default - I have updated the page to reflect that.
Re: Building a Windows.Forms+GLControl based application
Thanks!
One question: wglShareLists() does seem to be about Display Lists, according to your link.
What about textures, clear color and other GL state?
Update: re-read Fiddlers clarification in the end. But the ShareContexts flag, when is the user supposed to set it to false? Is it ok to toggle the flag at any time / in any thread of the application? A little example would be great, or at least a whole sentence.
Re: Building a Windows.Forms+GLControl based application
Agreed, a book page about this would be nice. Will see to it.
The gist is that you can use it at any time and it only affects newly created contexts. The thread should not matter as it only sets an internal flag.
Also, wglShareLists shares everything, not only lists (it was created in a time when texture objects weren't even available). The same is true on the Linux and MacOS side, too.
Edit: you should also be aware that sharing is not always possible. For example, you cannot share data between contexts with different renderers (e.g. microsoft's GDI renderer and a hardware accelerated one) or when the visual/pixel format of the contexts is different.
Re: Building a Windows.Forms+GLControl based application
Thanks.
1. So after a context has been created, it's locked to a specific resource "pool"? A pool which it might share with other contexts, and which may get extended with more contexts, to but not shrunk?
2. Is it at all possible to have two different renderers in the same process in OpenTK?
Re: Building a Windows.Forms+GLControl based application
1. Yes and no. There is a single resource pool and contexts either belong to it or not. You cannot create a second pool, but the pool itself can be shrunk by destroying contexts.
I cannot think of any use case for >1 resource pools (or context chains as we used to call them). Even simple resource sharing is considered arcane, so no need to overcomplicate things!
2. Yes.
There's nothing stopping you from creating a second context that cannot be hardware accelerated (by requesting requesting an indexed pixel format for example), but that's not really useful. It is not possible, however, to have two contexts accelerated by different video cards. I haven't tested this, mind you, but it is said this is a driver/OS limitation.
Re: Building a Windows.Forms+GLControl based application
1. "Yes and no. There is a single resource pool and contexts either belong to it or not. You cannot create a second pool, but the pool itself can be shrunk by destroying contexts. I cannot think of any use case for >1 resource pools (or context chains as we used to call them). 99% of times you'll want to share contexts anyway, so that's the default option."
I'm not sure I'm following you here. If there is only one resource pool, what happens in this case:
a) glCtrl1 created in designer
b) on Form_Load, ShareContexts is set to false and a new GLControl is instancianted and put somewhere on the Form (dynamically).
c) texture1 is uploaded to glCtrl1
d) texture2 is uploaded to the new GLControl.
If there is only one resource pool, where is texture2 stored?
Re: Building a Windows.Forms+GLControl based application
Ah, I misunderstood what you meant by "resource pool". In my post, I should have said "context pool" or "context chain".
Every context holds its own resources. In your example, texture1 belongs glCtrl1 and texture2 to glCtrl2. If the contexts were shared, both textures would belong to both contexts.
One could say that there OpenTK supports only one shared resource pool (used by all shared contexts). Non-shared contexts of course contain their own distinct resource pools.
Re: Building a Windows.Forms+GLControl based application
One could say that there OpenTK supports only one shared resource pool (used by all shared contexts).
So if I understand you correct:
a) While SharedContexts is true two GLControls are created dynamically, A and B
b) .. then SharedContexts is set to false an a third GLControl is created dynamically, C
c) Again SharedContexts is set to true, and a fourth GLControl is created, D.
.. Then A,B,D would share textures etc while context C would have its own separate resource pool?
Technologically speaking, how much space is reserved for each resource pool? I guess it is on the gfx-card-memory, is it fixed or changing dynamically?
Re: Building a Windows.Forms+GLControl based application
Yes, that's the idea.
Resource allocation is performed by the driver and, unless you are a driver developer or a high-profile customer, you can only guess. We know that the resources are created dynamically and allocated lazily (i.e. on first use); we also know that OpenGL resources are backed by system memory; last, we know that drivers provide (or rely on) complex memory managers and resource allocators to keep the hardware well fed with data.
I guess there is a lot to learn by studying the mesa3d and intel's linux driver implementations, but that's not exactly bedtime reading material.
Edit: Note that you can allocate more than the total amount of video memory - the driver will take care of swapping data in/out. (Of course, if your running memory needs exceed the video memory, performance will fall way down).
There's also a trend towards virtualizing video memory at the OS level, much like the main memory is virtualized. I'm not fully aware of the implications this will have on drivers, but it's one of those things deemed important for further development.
Re: Building a Windows.Forms+GLControl based application
OK now that I think I understand the mainstream ideas of GL-state-sharing, I'd like to say that I consider letting SharedContexts be true by default is very reasonable.
Exactly what is the constituents of the GL-state? I know of display lists and textures of course, but what about light positions? Clear color? Blend function? Etc.
Re: Building a Windows.Forms+GLControl based application
Bump!
Any info on the shared state of GL? see my prev. post..
Re: Building a Windows.Forms+GLControl based application
I found the KeyDown couldn't catch arrow keys. You can repeat my situation by adding
MessageBox.Show("KeyDown called");
to the first line of glControl1_KeyDown().
It's quite weird that function keys, PgUp PgDn Ins Del, even ScrollLock NumLock all work well, just except the Up Down Left Right Tab keys which can't be caught by the KeyDown handler.
Any ideas?
Environment:
Windows XP + DotNet 3.5 SP1 + Visual Studio 2008 SP1
Re: Building a Windows.Forms+GLControl based application
@sidetalker:
Thanks for noticing ... I have a feeling it has something to do with windows focusing. Arrow keys are special in that regard, just as Tab.
Anyway I googled and found this:
http://bytes.com/forum/thread555102.html
If you find a simple solution, please post it and I will update the tutorial above.
Re: Building a Windows.Forms+GLControl based application
I think there is a simple property you can set to change the default behavior, but I can't seem to find anything relevant. Still looking...
Edit: Ok, I've found a good analysis of what is going on and the possible solutions here.
Re: Building a Windows.Forms+GLControl based application
Coolness... I'm not sure I have the energy to update the tutorial just yet, so please just leave all comments here and I'll "eat them up" when the GLControl spirit is back :)
.. or, if anyone wants to update, go ahead!
Syncing BufferSwaps
As an absolute newbie to OpenGl I have a question, which I hope is not too stupid.
In a window form with two GlControls I want both controls to swap their buffers simultaneously. How can one do this?
If someone could point me in the right direction, I would be very grateful. This should demonstrate what the problem is, in VB.Net:
Re: Building a Windows.Forms+GLControl based application
To the best of my knowledge, you'll have to use the NV_swap_group extension, but I don't know if this is supported on consumer-grade hardware.
I have also seen attempts to synchronize contexts without driver support (e.g. by running contexts on different threads and calling SwapBuffers at the same time), but the results were always less than perfect.
Re: Building a Windows.Forms+GLControl based application
I just looked at the link which you provided - It sounds like it might be a solution but also like a function which is not universal.
Thanks!
Re: Building a Windows.Forms+GLControl based application
Despite the name, Ati supports this extension in the 8.12 drivers (released a couple of days ago). In any case, synchronizing the rendering of different contexts is not a typical operation outside of VR applications (where you tend to know your hardware beforehand).
Without any knowledge of your specific application, would it be possible to achieve similar results with a single GLControl (partitioned in different viewports)?
Edit: Please open a separate forum topic for this discussion, the book pages are not the best place for this :)
Re: Building a Windows.Forms+GLControl based application
> partitioned in different viewports
It already occurred to me, and once I'm a bit further down the road of learning OpenGl, I'll try doing this.
Sorry about posting in the wrong place.
Re: Building a Windows.Forms+GLControl based application
No problem, it's just that we clean the book pages comments from time to time, so it's best to keep the discussion at a place where it won't get lost in the future.
Re: Building a Windows.Forms+GLControl based application
I am trying made similar app, but with floating text instead rotating triange. In glControl1_Paint i change drawing triange routing with my:
//-----------------------------------------------------
tp.Begin();
tp.Print(
r_text,
this.Font, Color.Black,
new RectangleF(float_p, new SizeF(200f, 200f))
);
tp.End();
//-----------------------------------------------------
declare some global vars:
tp = new TextPrinter(TextQuality.Medium);
string r_text = "Hello World! \nOpenGL - way to freedom!";
and write some code changing float_p of rect, so, that my text float from one corner form to another, and from top to bottom and back. As result i get app, wich render text with 650-700 fps, and "eating" 50% of cpu in task manager.
After some failed tricks as control variable "milliseconds" in Application_Idle() and paste Application.DoEvents() in many places, i found that Thread.Sleep(10); in first line in proc Application_Idle() works excelent. Now app make 70-80 fps, and in task manager has 00-01% of cpu loading. So if you gone in situation, where you'll need decrease rendering fps and weight on hardware - you can try something look like this.
Sorry for my bad English)
Re: Building a Windows.Forms+GLControl based application
Another solution is to turn on vsync:
Context.VSync = trueThis will ensure that you get very smooth motion and also decrease CPU usage. On the other hand, it may not work very well with some drivers and operating systems (haven't had such problems myself, but you never know).
Re: Building a Windows.Forms + GLControl based application
MY mistake. Please see next post.
Re: Building a Windows.Forms + GLControl based application
I have tried this tutorial several times in Visual Studio 2008. The color in the GLControl object is beige and remains unchanged after
in the Load event handler.
Is this problem peculiar to Visual Studio 2008? Could it be because I am not using the most current release of OpenTK?
Re: Building a Windows.Forms + GLControl based application
You are probably seeing bug #1071: Not firing Load event for GLControl..
Workaround: place the call to GL.ClearColor inside the main Form constructor:
Re: Building a Windows.Forms + GLControl based application
I think I have tried all the suggestions. Unfortunately, I am still unsuccessful. This is my added code:
In addition I tried to start an entirely new project using version 0.9.9-1. I got an error message:
"There was an error loading files from assembly ...Could not load type 'OpenTK.Platform.Egl.EGLContext' from Assembly 'OpenTK, version 0.9.9.1, Culture=neutral, PublicKeyToken=Null' "
I was therefore unable to use the GLControl on the form in the new project.
Is there anyone who has used this example on Visual Studio 2008? I will be grateful if I can get any simple example of OpenTK on this platform. Do I need to add any special hardware?
Re: Building a Windows.Forms + GLControl based application
I am using Visual Studio 2008 on Windows 7. Could this be the source of my problem?
Re: Building a Windows.Forms + GLControl based application
OpenTK supports VS2005, VS2008 and VS2010 and runs without issues on Windows 7.
This problem you are seeing ("could not load type...") has been identified as a potential compiler issue and has been forwarded to Microsoft. You can either rollback to version 0.9.8-2 or use TortoiseSVN to download the latest version from SVN, which fixes the issue.