Summary (TL:DR version)
Ultimately our goal is to be able to utilize OpenGL ES code in a WPF application natively (i.e. not SharpGL, etc.) and without Airspace or driver issues, possible using Google's ANGLE project.
Background:
One of the things I like about OpenGL over DirectX is its cross-platform capability. It has excellent support on both OS X and Linux, and also on Android and iOS via ES. However, on Windows, using it is marred with driver issues, or worse, a lot of cards simply don't implement it properly.
Enter Google's ANGLE project, or Almost-Native-Graphics-Layer-Engine.
ANGLE is an OpenGL ES 2.0 wrapper around a Direct3D implementation, meaning you can write OpenGL ES 2.0 code to be run on Windows without the need for actual OpenGL drivers. It not only passes the ES compatibility tests, but it's actually how Chrome does all of its graphics rendering, including WebGL so it definitely is a proven technology.
The Question:
We know WPF has a D3DImage control which lets you host Direct3D rendering within WPF and it supposedly does away with the airspace issues by properly composting its output to the WPF render thread. My question is since ANGLE is implemented via Direct3D, and D3DImage is a target for Direct3D rendering, is it possible to combine the two, allowing us to write OpenGL ES code and host it in a WPF application on Windows, all without driver or airspace issues?
This would be the 'Holy Grail' for us.
However, I keep hitting a wall around getting ANGLE to target its rendering on the D3D surface created by the D3DImage control since ANGLE wants to use its own. I'm not sure this is even possible. I can't find a single article or reference anywhere of anyone even discussing this, let alone attempting it.
Again to be clear though, the goal is to get our shared, cross-platform OpenGL (or ES) code to work in a WPF application without airspace issues or OpenGL driver requirements. My suggestion of using ANGLE/D3DImage is just that... an attempt. It's the 'means' I've come up with so far, but that's just a potential means to our goal, not the goal itself. Anything else that would get us to the same solution would be more than welcome.
I have uploaded a github project that demonstrates how to integrate OpenGL rendering into a WPF application via OpenTK.GLControl.
The code is surprisingly simple:
WindowsFormsHost
to the WPF applicationOpenTK.GLControl
and attach it to the WindowsFormsHost
GraphicsContextFlags.Default
for a desktop OpenGL contextGraphicsContextFlags.Embedded
for an OpenGL ES (ANGLE) contextHint: OpenTK and OpenTK.GLControl are also available as NuGet packages. There is a new release due tomorrow with improved ANGLE support.
Note that the WindowsFormsHost approach is subject to airspace restrictions. If this is an issue, see my answer here for a solution.
// This code is public domain
#define USE_ANGLE
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Forms.Integration;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using OpenTK;
using OpenTK.Graphics;
#if USE_ANGLE
using OpenTK.Graphics.ES20;
#else
using OpenTK.Graphics.OpenGL;
#endif
namespace WPF.Angle
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
GLControl glControl;
public MainWindow()
{
InitializeComponent();
}
private void WindowsFormsHost_Initialized(object sender, EventArgs e)
{
var flags = GraphicsContextFlags.Default;
#if USE_ANGLE
flags = GraphicsContextFlags.Embedded;
#endif
glControl = new GLControl(new GraphicsMode(32, 24), 2, 0, flags);
glControl.MakeCurrent();
glControl.Paint += GLControl_Paint;
glControl.Dock = DockStyle.Fill;
(sender as WindowsFormsHost).Child = glControl;
}
private void GLControl_Paint(object sender, PaintEventArgs e)
{
GL.ClearColor(
(float)Red.Value,
(float)Green.Value,
(float)Blue.Value,
1);
GL.Clear(
ClearBufferMask.ColorBufferBit |
ClearBufferMask.DepthBufferBit |
ClearBufferMask.StencilBufferBit);
glControl.SwapBuffers();
}
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
glControl.Invalidate();
}
}
}