diff --git a/src/GLWpfControl/DXGLContext.cs b/src/GLWpfControl/DXGLContext.cs
index 1795c94..af32c7c 100644
--- a/src/GLWpfControl/DXGLContext.cs
+++ b/src/GLWpfControl/DXGLContext.cs
@@ -117,6 +117,7 @@ private static IGraphicsContext GetOrCreateSharedOpenGLContext(GLWpfControlSetti
var hwndSource = new HwndSource(0, 0, 0, 0, 0, "GLWpfControl", baseHandle);
_sharedContext = glfwWindow.Context;
+ _sharedContextSettings = settings;
_sharedContextResources = new IDisposable[] {hwndSource, glfwWindow};
// GL init
// var mode = new GraphicsMode(ColorFormat.Empty, 0, 0, 0, 0, 0, false);
diff --git a/src/GLWpfControl/DXGLRenderSurface.cs b/src/GLWpfControl/DXGLRenderSurface.cs
deleted file mode 100644
index a8c5763..0000000
--- a/src/GLWpfControl/DXGLRenderSurface.cs
+++ /dev/null
@@ -1,194 +0,0 @@
-using OpenTK.Graphics.OpenGL;
-using OpenTK.Graphics.Wgl;
-using OpenTK.Platform.Windows;
-using OpenTK.Wpf.Interop;
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Windows;
-using System.Windows.Interop;
-
-namespace OpenTK.Wpf
-{
- class DXGLRenderSurface : IDisposable
- {
-
- private readonly GLWpfControlRendererDx _device;
-
- private readonly bool _hasSyncFenceAvailable;
-
- private IntPtr _syncFence;
-
- private int _glFrameBuffer;
-
- private int _glDepthRenderBuffer;
-
- private int _glSharedTexture;
-
- private IntPtr _dxSurfaceHandle;
-
- private IntPtr _dxSharedHandle;
-
- private IntPtr[] _glDxInteropSharedHandles;
-
- public int Width { get; }
-
- public int Height { get; }
-
- public int FrameBuffer
- {
- get
- {
- EnsureSurfaceCreated();
- return _glFrameBuffer;
- }
- }
-
- public DXGLRenderSurface(GLWpfControlRendererDx device, int width, int height, bool hasSyncFenceAvailable)
- {
- Width = width;
- Height = height;
-
- _device = device;
- _hasSyncFenceAvailable = hasSyncFenceAvailable;
- }
-
- public void Render(D3DImage image, Action render, Action asyncRender)
- {
- if (disposedValue)
- throw new ObjectDisposedException("DXGLRenderSurface");
-
- EnsureSurfaceCreated();
-
- image.Lock();
- Wgl.DXLockObjectsNV(_device.GLHandle, 1, _glDxInteropSharedHandles);
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, _glFrameBuffer);
- GL.Viewport(0, 0, image.PixelWidth, image.PixelHeight);
-
- render?.Invoke();
-
- // post-render
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
-
- asyncRender?.Invoke();
- SyncOperations();
-
- // update image
- Wgl.DXUnlockObjectsNV(_device.GLHandle, 1, _glDxInteropSharedHandles);
- image.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _dxSurfaceHandle);
- image.AddDirtyRect(new Int32Rect(0, 0, image.PixelWidth, image.PixelHeight));
- image.Unlock();
-
- }
-
- private void EnsureSurfaceCreated()
- {
- if (disposedValue)
- throw new ObjectDisposedException("DXGLRenderSurface");
-
- if (_glFrameBuffer != 0)
- return;
-
- DXInterop.CreateRenderTarget(
- _device.DeviceHandle,
- Width,
- Height,
- Format.X8R8G8B8,// this is like A8 R8 G8 B8, but avoids issues with Gamma correction being applied twice.
- MultisampleType.None,
- 0,
- false,
- out _dxSurfaceHandle,
- ref _dxSharedHandle);
-
- Wgl.DXSetResourceShareHandleNV(_dxSurfaceHandle, _dxSharedHandle);
-
- _glFrameBuffer = GL.GenFramebuffer();
- _glSharedTexture = GL.GenTexture();
-
- var genHandle = Wgl.DXRegisterObjectNV(
- _device.GLHandle,
- _dxSurfaceHandle,
- (uint)_glSharedTexture,
- (uint)TextureTarget.Texture2D,
- WGL_NV_DX_interop.AccessReadWrite);
-
- _glDxInteropSharedHandles = new[] { genHandle };
-
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, _glFrameBuffer);
- GL.FramebufferTexture2D(
- FramebufferTarget.Framebuffer,
- FramebufferAttachment.ColorAttachment0,
- TextureTarget.Texture2D,
- _glSharedTexture, 0);
-
- _glDepthRenderBuffer = GL.GenRenderbuffer();
- GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, _glDepthRenderBuffer);
- GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent24, Width, Height);
- GL.FramebufferRenderbuffer(
- FramebufferTarget.Framebuffer,
- FramebufferAttachment.DepthAttachment,
- RenderbufferTarget.Renderbuffer,
- _glDepthRenderBuffer);
-
- GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
- }
-
- private void SyncOperations()
- {
- // // wait 10 seconds for the sync to complete.
- // if (_hasSyncFenceAvailable) {
- // // timeout is in nanoseconds
- // var syncRes = GL.ClientWaitSync(_syncFence, ClientWaitSyncFlags.None, 10_000_000);
- // if (syncRes != WaitSyncStatus.ConditionSatisfied) {
- // throw new TimeoutException("Synchronization failed because the sync could not be completed in a reasonable time.");
- // }
- // }
- // else {
- // GL.Flush();
- // }
- //
- GL.Flush();
- }
-
- private bool disposedValue;
-
- protected virtual void Dispose(bool disposing)
- {
- if (!disposedValue)
- {
- if (_dxSurfaceHandle != IntPtr.Zero)
- {
- Wgl.DXUnregisterObjectNV(_device.GLHandle, _glDxInteropSharedHandles[0]);
- }
-
- if (_glFrameBuffer != 0)
- {
- GL.DeleteFramebuffer(_glFrameBuffer);
- }
-
- if (_glDepthRenderBuffer != 0)
- {
- GL.DeleteRenderbuffer(_glDepthRenderBuffer);
- }
-
- if (_glSharedTexture != 0)
- {
- GL.DeleteTexture(_glSharedTexture);
- }
-
- disposedValue = true;
- }
- }
-
- ~DXGLRenderSurface()
- {
- Dispose(disposing: false);
- }
-
- public void Dispose()
- {
- Dispose(disposing: true);
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/src/GLWpfControl/DxGLFramebuffer.cs b/src/GLWpfControl/DxGLFramebuffer.cs
index bf533ae..c2d226b 100644
--- a/src/GLWpfControl/DxGLFramebuffer.cs
+++ b/src/GLWpfControl/DxGLFramebuffer.cs
@@ -1,6 +1,10 @@
using System;
+using System.Windows.Interop;
+using System.Windows.Media;
+using JetBrains.Annotations;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics.Wgl;
+using OpenTK.Platform.Windows;
using OpenTK.Wpf.Interop;
namespace OpenTK.Wpf {
@@ -12,6 +16,8 @@ namespace OpenTK.Wpf {
/// The calling class must correctly dispose of this by calling
/// Prior to releasing references.
internal sealed class DxGLFramebuffer : IDisposable {
+
+ private DxGlContext DxGlContext { get; }
/// The width of this buffer in pixels
public int Width { get; }
@@ -19,21 +25,93 @@ internal sealed class DxGLFramebuffer : IDisposable {
/// The height of this buffer in pixels
public int Height { get; }
+ /// The DirectX Render target (framebuffer) handle.
+ public IntPtr DxRenderTargetHandle { get; }
+
/// The OpenGL Framebuffer handle
- public int GLFramebuffer { get; }
+ public int GLFramebufferHandle { get; }
+
+ /// The OpenGL shared texture handle (with DX)
+ private int GLSharedTextureHandle { get; }
+
+ /// The OpenGL depth render buffer handle.
+ private int GLDepthRenderBufferHandle { get; }
+
+ /// Specific wgl_dx_interop handle that marks the framebuffer as ready for interop.
+ public IntPtr DxInteropRegisteredHandle { get; }
+
+
+ public D3DImage D3dImage { get; }
+
+ public TranslateTransform TranslateTransform { get; }
+ public ScaleTransform FlipYTransform { get; }
+
+
+ public DxGLFramebuffer([NotNull] DxGlContext context, int width, int height, double dpiScaleX, double dpiScaleY) {
+ DxGlContext = context;
+ Width = width;
+ Height = height;
+
+ var dxSharedHandle = IntPtr.Zero; // Unused windows-vista legacy sharing handle. Must always be null.
+ DXInterop.CreateRenderTarget(
+ context.DxDeviceHandle,
+ Width,
+ Height,
+ Format.X8R8G8B8,// this is like A8 R8 G8 B8, but avoids issues with Gamma correction being applied twice.
+ MultisampleType.None,
+ 0,
+ false,
+ out var dxRenderTargetHandle,
+ ref dxSharedHandle);
+
+ DxRenderTargetHandle = dxRenderTargetHandle;
+
+ Wgl.DXSetResourceShareHandleNV(dxRenderTargetHandle, dxSharedHandle);
+
+ GLFramebufferHandle = GL.GenFramebuffer();
+ GLSharedTextureHandle = GL.GenTexture();
+
+ var genHandle = Wgl.DXRegisterObjectNV(
+ context.GlDeviceHandle,
+ dxRenderTargetHandle,
+ (uint)GLSharedTextureHandle,
+ (uint)TextureTarget.Texture2D,
+ WGL_NV_DX_interop.AccessReadWrite);
- /// The shared texture handle
- private int GLSharedTexture { get; }
+ DxInteropRegisteredHandle = genHandle;
+ GL.BindFramebuffer(FramebufferTarget.Framebuffer, GLFramebufferHandle);
+ GL.FramebufferTexture2D(
+ FramebufferTarget.Framebuffer,
+ FramebufferAttachment.ColorAttachment0,
+ TextureTarget.Texture2D,
+ GLSharedTextureHandle, 0);
- public DxGLFramebuffer() {
+ GLDepthRenderBufferHandle = GL.GenRenderbuffer();
+ GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, GLDepthRenderBufferHandle);
+ GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent24, Width, Height);
+ GL.FramebufferRenderbuffer(
+ FramebufferTarget.Framebuffer,
+ FramebufferAttachment.DepthAttachment,
+ RenderbufferTarget.Renderbuffer,
+ GLDepthRenderBufferHandle);
+ GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
+
+
+ D3dImage = new D3DImage(96.0 * dpiScaleX, 96.0 * dpiScaleY);
+
+ TranslateTransform = new TranslateTransform(0, height);
+ FlipYTransform = new ScaleTransform(1, -1);
}
public void Dispose() {
- GL.DeleteBuffer(GLFramebuffer);
- GL.DeleteTexture(GLSharedTexture);
+ GL.DeleteBuffer(GLFramebufferHandle);
+ GL.DeleteRenderbuffer(GLDepthRenderBufferHandle);
+ GL.DeleteTexture(GLSharedTextureHandle);
+ Wgl.DXUnregisterObjectNV(DxGlContext.GlDeviceHandle, DxInteropRegisteredHandle);
+ DXInterop.Release(DxRenderTargetHandle);
}
}
}
diff --git a/src/GLWpfControl/GLWpfControl.cs b/src/GLWpfControl/GLWpfControl.cs
index d197825..0665d0e 100644
--- a/src/GLWpfControl/GLWpfControl.cs
+++ b/src/GLWpfControl/GLWpfControl.cs
@@ -1,16 +1,11 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
-using System.Threading;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
-using OpenTK.Graphics.Wgl;
-using OpenTK.Windowing.Common;
+using JetBrains.Annotations;
using OpenTK.Windowing.Desktop;
-using OpenTK.Windowing.GraphicsLibraryFramework;
-using Window = System.Windows.Window;
-using WindowState = OpenTK.Windowing.Common.WindowState;
namespace OpenTK.Wpf
{
@@ -21,23 +16,9 @@ namespace OpenTK.Wpf
///
public sealed class GLWpfControl : FrameworkElement
{
-
- private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
- private TimeSpan _lastFrameStamp;
-
- // ReSharper disable once NotAccessedField.Local
- private static IGraphicsContext _commonContext;
- private static int _activeControlCount = 0;
- private IGraphicsContext _context;
- private bool _hasSyncFenceAvailable;
-
- private volatile bool _needsRedraw = true;
-
-
- private GLWpfControlSettings _settings;
- private GLWpfControlRendererDx _renderer;
- private DXGLRenderSurface _currentSurface;
- private HwndSource _hwnd;
+ // -----------------------------------
+ // EVENTS
+ // -----------------------------------
/// Called whenever rendering should occur.
public event Action Render;
@@ -48,27 +29,28 @@ public sealed class GLWpfControl : FrameworkElement
/// If you do not know what these are, do not use this function.
public event Action AsyncRender;
-
///
/// Gets called after the control has finished initializing and is ready to render
///
public event Action Ready;
+
- // The image that the control uses to update stuff
- private D3DImage _d3dImage;
-
- // Transformations and size
- private Rect _imageRectangle;
- private readonly TranslateTransform _translateTransform = new TranslateTransform();
- private readonly ScaleTransform _flipYTransform = new ScaleTransform(1, -1);
- private NativeWindow _glfwWindow;
+ // -----------------------------------
+ // Fields
+ // -----------------------------------
+
+ [CanBeNull] private GLWpfControlSettings _settings;
+ [CanBeNull] private GLWpfControlRenderer _renderer;
+ private bool _needsRedraw;
- // Flag to check if ready event has been already triggered
- private bool readyEventTriggered = false;
+ // -----------------------------------
+ // Properties
+ // -----------------------------------
/// The OpenGL Framebuffer Object used internally by this component.
/// Bind to this instead of the default framebuffer when using this component along with other FrameBuffers for the final pass.
- public int Framebuffer => _currentSurface?.FrameBuffer ?? 0;
+ /// If no framebuffer is available (because this control is not visible, etc etc, then it should be 0).
+ public int Framebuffer => _renderer?.FrameBufferHandle ?? 0;
/// If this control is rendering continuously.
@@ -78,13 +60,15 @@ public bool RenderContinuously {
set => _settings.RenderContinuously = value;
}
- /// Pixel size of the underlying OpenGL framebuffer.
+ /// Pixel width of the underlying OpenGL framebuffer.
/// It could differ from UIElement.RenderSize if UseDeviceDpi setting is set.
/// To be used for operations related to OpenGL viewport calls (glViewport, glScissor, ...).
- public Size FramebufferSize {
- get;
- private set;
- }
+ public int FrameBufferWidth => _renderer?.Width ?? 0;
+
+ /// Pixel height of the underlying OpenGL framebuffer.
+ /// It could differ from UIElement.RenderSize if UseDeviceDpi setting is set.
+ /// To be used for operations related to OpenGL viewport calls (glViewport, glScissor, ...).
+ public int FrameBufferHeight => _renderer?.Height ?? 0;
///
/// Used to create a new control. Before rendering can take place, must be called.
@@ -96,7 +80,7 @@ public GLWpfControl() {
public void Start(GLWpfControlSettings settings)
{
_settings = settings.Copy();
- _hasSyncFenceAvailable = _settings.MajorVersion >= 4 || (_settings.MajorVersion == 3 && _settings.MinorVersion >= 2);
+ _renderer = new GLWpfControlRenderer(_settings);
IsVisibleChanged += (_, args) => {
if ((bool) args.NewValue) {
CompositionTarget.Rendering += OnCompTargetRender;
@@ -106,118 +90,33 @@ public void Start(GLWpfControlSettings settings)
}
};
- Loaded += (a, b) => SetupRendererIfRequired();
+ Loaded += (a, b) => SetupRenderSize();
Unloaded += (a, b) => OnUnloaded();
-
- // we have two cases:
- var shouldSetupContexts = _context == null;
- if (shouldSetupContexts)
- {
- if (_settings.ContextToUse == null)
- {
- InitOpenGLContext();
- }
- else
- {
- _context = _settings.ContextToUse;
- }
- }
-
- }
-
- private void SetupRendererIfRequired() {
-
- // if we actually have a surface we can render onto...
- var presentationSource = PresentationSource.FromVisual(this);
- // presentationSource must be checked for null: when the window is closed, IsVisibleChanged event is triggered
- // and FromVisual method returns null due to disposal of visual.
- var shouldSetupRenderer = RenderSize.Width > 0 && RenderSize.Height > 0 && presentationSource != null;
- if (shouldSetupRenderer)
- {
- EnsureD3DImage(presentationSource);
- var deviceSize = GetDevicePixelSize(RenderSize.Width, RenderSize.Height);
- var deviceWidth = (int)deviceSize.Width;
- var deviceHeight = (int)deviceSize.Height;
-
- if (_renderer == null)
- {
- _renderer = new GLWpfControlRendererDx(_hasSyncFenceAvailable);
- }
-
- if (_currentSurface == null || _currentSurface.Width != deviceWidth || _currentSurface.Height != deviceHeight)
- {
- _currentSurface?.Dispose();
- _currentSurface = _renderer.CreateRenderSurface(deviceWidth, deviceHeight);
- }
-
- FramebufferSize = new Size(deviceWidth, deviceHeight);
- _imageRectangle = new Rect(0, 0, RenderSize.Width, RenderSize.Height);
- _translateTransform.Y = RenderSize.Height;
- }
-
- if (_renderer != null && _context != null && !readyEventTriggered) {
- readyEventTriggered = true;
- Ready?.Invoke();
- }
}
-
- private void EnsureD3DImage(PresentationSource presentationSource)
- {
- if (_d3dImage == null)
- {
- if (_settings.UseDeviceDpi)
- {
- var transformToDevice = presentationSource.CompositionTarget.TransformToDevice;
- _d3dImage = new D3DImage(96.0 * transformToDevice.M11, 96.0 * transformToDevice.M22);
- }
- else
- {
- _d3dImage = new D3DImage(96.0, 96.0);
- }
+
+ private void SetupRenderSize() {
+ if (_renderer == null || _settings == null) {
+ return;
}
- }
- private void InitOpenGLContext() {
- if (_commonContext == null) {
- var nws = NativeWindowSettings.Default;
- nws.StartFocused = false;
- nws.StartVisible = false;
- nws.NumberOfSamples = 0;
- nws.APIVersion = new Version(_settings.MajorVersion,_settings.MinorVersion);
- nws.Flags = ContextFlags.Offscreen;
- nws.Profile = _settings.GraphicsProfile;
- nws.WindowBorder = WindowBorder.Hidden;
- nws.WindowState = WindowState.Minimized;
- _glfwWindow = new NativeWindow(nws) {IsVisible = false};
- var provider = new GLFWBindingsContext();
- Wgl.LoadBindings(provider);
- // retrieve window handle/info
- var window = Window.GetWindow(this);
- var baseHandle = window is null ? IntPtr.Zero : new WindowInteropHelper(window).Handle;
- _hwnd = new HwndSource(0, 0, 0, 0, 0, "GLWpfControl", baseHandle);
+ var dpiScaleX = 1.0;
+ var dpiScaleY = 1.0;
- _commonContext = _glfwWindow.Context;
- // GL init
- // var mode = new GraphicsMode(ColorFormat.Empty, 0, 0, 0, 0, 0, false);
- // _commonContext = new GraphicsContext(mode, _windowInfo, _settings.MajorVersion, _settings.MinorVersion,
- // _settings.GraphicsContextFlags);
- // _commonContext.LoadAll();
- _commonContext.MakeCurrent();
+ if (_settings.UseDeviceDpi) {
+ var presentationSource = PresentationSource.FromVisual(this);
+ Debug.Assert(presentationSource != null, nameof(presentationSource) + " != null");
+ Debug.Assert(presentationSource.CompositionTarget != null, "presentationSource.CompositionTarget != null");
+
+ var transformToDevice = presentationSource.CompositionTarget.TransformToDevice;
+ dpiScaleX = transformToDevice.M11;
+ dpiScaleY = transformToDevice.M22;
}
- _context = _commonContext;
- Interlocked.Increment(ref _activeControlCount);
+ _renderer?.SetSize((int) RenderSize.Width, (int) RenderSize.Height, dpiScaleX, dpiScaleY);
}
private void OnUnloaded()
{
- if (_context == null)
- {
- return;
- }
-
- ReleaseOpenGLResources();
- _glfwWindow?.Dispose();
- _hwnd?.Dispose();
+ _renderer?.SetSize(0,0, 1, 1);
}
private void OnCompTargetRender(object sender, EventArgs e)
@@ -229,22 +128,7 @@ private void OnCompTargetRender(object sender, EventArgs e)
}
protected override void OnRender(DrawingContext drawingContext) {
- var curFrameStamp = _stopwatch.Elapsed;
- var deltaT = curFrameStamp - _lastFrameStamp;
- _lastFrameStamp = curFrameStamp;
- if (_currentSurface != null) {
- _currentSurface.Render(_d3dImage, () => Render?.Invoke(deltaT), AsyncRender);
- // Transforms are applied in reverse order
- drawingContext.PushTransform(_translateTransform); // Apply translation to the image on the Y axis by the height. This assures that in the next step, where we apply a negative scale the image is still inside of the window
- drawingContext.PushTransform(_flipYTransform); // Apply a scale where the Y axis is -1. This will rotate the image by 180 deg
-
- drawingContext.DrawImage(_d3dImage, _imageRectangle); // Draw the image source
-
- drawingContext.Pop(); // Remove the scale transform
- drawingContext.Pop(); // Remove the translation transform
-
- }
-
+ _renderer?.Render(drawingContext);
base.OnRender(drawingContext);
}
@@ -257,41 +141,10 @@ protected override void OnRenderSizeChanged(SizeChangedInfo info)
if ((info.WidthChanged || info.HeightChanged) && (info.NewSize.Width > 0 && info.NewSize.Height > 0))
{
- SetupRendererIfRequired();
+ SetupRenderSize();
InvalidateVisual();
}
base.OnRenderSizeChanged(info);
}
-
- private void ReleaseOpenGLResources()
- {
- _currentSurface?.Dispose();
- if (!_settings.IsUsingExternalContext) {
- _context = null;
- var newCount = Interlocked.Decrement(ref _activeControlCount);
- if (newCount == 0) {
- _glfwWindow?.Dispose();
- }
- }
- }
-
- private Size GetDevicePixelSize(double width, double height)
- {
- if (!_settings.UseDeviceDpi) {
- return new Size(width, height);
- }
-
- // inspired from https://stackoverflow.com/questions/3286175/how-do-i-convert-a-wpf-size-to-physical-pixels
- Matrix transformToDevice;
- var source = PresentationSource.FromVisual(this);
- if (source != null)
- transformToDevice = source.CompositionTarget.TransformToDevice;
- else
- using (var s = new HwndSource(new HwndSourceParameters()))
- transformToDevice = s.CompositionTarget.TransformToDevice;
-
- return (Size)transformToDevice.Transform(new Vector(width, height));
- }
-
}
}
diff --git a/src/GLWpfControl/GLWpfControlRenderer.cs b/src/GLWpfControl/GLWpfControlRenderer.cs
new file mode 100644
index 0000000..eaee52f
--- /dev/null
+++ b/src/GLWpfControl/GLWpfControlRenderer.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Interop;
+using System.Windows.Media;
+using OpenTK.Graphics.OpenGL;
+using OpenTK.Graphics.Wgl;
+using OpenTK.Wpf.Interop;
+
+namespace OpenTK.Wpf
+{
+
+ /// Renderer that uses DX_Interop for a fast-path.
+ internal sealed class GLWpfControlRenderer {
+
+ private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
+ private readonly DxGlContext _context;
+
+ public event Action GLRender;
+
+
+ private DxGLFramebuffer _framebuffer;
+
+ /// The OpenGL framebuffer handle.
+ public int FrameBufferHandle => _framebuffer.GLFramebufferHandle;
+
+ /// The OpenGL Framebuffer width
+ public int Width => _framebuffer?.Width ?? 0;
+
+ /// The OpenGL Framebuffer height
+ public int Height => _framebuffer?.Width ?? 0;
+
+ private TimeSpan _lastFrameStamp;
+ private GLWpfControlSettings _settings;
+
+
+ public GLWpfControlRenderer(GLWpfControlSettings settings)
+ {
+ _context = new DxGlContext(settings);
+ _settings = settings;
+ }
+
+
+ public void SetSize(int width, int height, double dpiScaleX, double dpiScaleY) {
+ if (_framebuffer == null || _framebuffer.Width != width && _framebuffer.Height != height) {
+ _framebuffer?.Dispose();
+ _framebuffer = null;
+ if (width > 0 && height > 0) {
+ _framebuffer = new DxGLFramebuffer(_context, width, height, dpiScaleX, dpiScaleX);
+ }
+ }
+ }
+
+ public void Render(DrawingContext drawingContext) {
+ if (_framebuffer == null) {
+ return;
+ }
+ var curFrameStamp = _stopwatch.Elapsed;
+ var deltaT = curFrameStamp - _lastFrameStamp;
+ _lastFrameStamp = curFrameStamp;
+ PreRender();
+ GLRender?.Invoke(deltaT);
+ PostRender();
+
+ // Transforms are applied in reverse order
+ drawingContext.PushTransform(_framebuffer.TranslateTransform); // Apply translation to the image on the Y axis by the height. This assures that in the next step, where we apply a negative scale the image is still inside of the window
+ drawingContext.PushTransform(_framebuffer.FlipYTransform); // Apply a scale where the Y axis is -1. This will rotate the image by 180 deg
+
+ // dpi scaled rectangle from the image
+ var rect = new Rect(0, 0, _framebuffer.D3dImage.Width, _framebuffer.D3dImage.Height);
+ drawingContext.DrawImage(_framebuffer.D3dImage, rect); // Draw the image source
+
+ drawingContext.Pop(); // Remove the scale transform
+ drawingContext.Pop(); // Remove the translation transform
+ }
+
+ /// Sets up the framebuffer, directx stuff for rendering.
+ public void PreRender()
+ {
+ _framebuffer.D3dImage.Lock();
+ Wgl.DXLockObjectsNV(_context.GlDeviceHandle, 1, new [] {_framebuffer.DxInteropRegisteredHandle});
+ GL.BindFramebuffer(FramebufferTarget.Framebuffer, _framebuffer.GLFramebufferHandle);
+ GL.Viewport(0, 0, _framebuffer.Width, _framebuffer.Height);
+ }
+
+ /// Completes the rendering, unbinds, and gets stuff ready for blitting the image over.
+ public void PostRender()
+ {
+ GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
+ GL.Flush();
+ Wgl.DXUnlockObjectsNV(_context.GlDeviceHandle, 1, new [] {_framebuffer.DxInteropRegisteredHandle});
+ _framebuffer.D3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _framebuffer.DxRenderTargetHandle);
+ _framebuffer.D3dImage.AddDirtyRect(new Int32Rect(0, 0, _framebuffer.Width, _framebuffer.Height));
+ _framebuffer.D3dImage.Unlock();
+ }
+
+
+ }
+}
diff --git a/src/GLWpfControl/GLWpfControlRendererDX.cs b/src/GLWpfControl/GLWpfControlRendererDX.cs
deleted file mode 100644
index 7ad66d1..0000000
--- a/src/GLWpfControl/GLWpfControlRendererDX.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using OpenTK.Graphics.Wgl;
-using OpenTK.Wpf.Interop;
-
-namespace OpenTK.Wpf
-{
-
- /// Renderer that uses DX_Interop for a fast-path.
- internal sealed class GLWpfControlRendererDx {
-
- private readonly IntPtr _glHandle;
-
- private readonly IntPtr _dxContextHandle;
-
- private readonly IntPtr _dxDeviceHandle;
-
- public IntPtr DeviceHandle => _dxDeviceHandle;
-
- // private readonly WGLInterop _wglInterop;
-
- private readonly bool _hasSyncFenceAvailable;
-
-
- public IntPtr GLHandle => _glHandle;
-
-
- public GLWpfControlRendererDx(bool hasSyncFenceAvailable)
- {
- // _wglInterop = new WGLInterop();
- _hasSyncFenceAvailable = hasSyncFenceAvailable;
-
- _glHandle = IntPtr.Zero;
-
- DXInterop.Direct3DCreate9Ex(DXInterop.DefaultSdkVersion, out _dxContextHandle);
-
- var deviceParameters = new PresentationParameters
- {
- Windowed = 1,
- SwapEffect = SwapEffect.Discard,
- DeviceWindowHandle = IntPtr.Zero,
- PresentationInterval = 0,
- BackBufferFormat = Format.X8R8G8B8, // this is like A8 R8 G8 B8, but avoids issues with Gamma correction being applied twice.
- BackBufferWidth = 1,
- BackBufferHeight = 1,
- AutoDepthStencilFormat = Format.Unknown,
- BackBufferCount = 1,
- EnableAutoDepthStencil = 0,
- Flags = 0,
- FullScreen_RefreshRateInHz = 0,
- MultiSampleQuality = 0,
- MultiSampleType = MultisampleType.None
- };
-
- DXInterop.CreateDeviceEx(
- _dxContextHandle,
- 0,
- DeviceType.HAL, // use hardware rasterization
- IntPtr.Zero,
- CreateFlags.HardwareVertexProcessing |
- CreateFlags.Multithreaded |
- CreateFlags.PureDevice,
- ref deviceParameters,
- IntPtr.Zero,
- out _dxDeviceHandle);
-
- _glHandle = Wgl.DXOpenDeviceNV(_dxDeviceHandle);
- }
-
- public DXGLRenderSurface CreateRenderSurface(int width, int height)
- {
- return new DXGLRenderSurface(this, width, height, _hasSyncFenceAvailable);
- }
-
- }
-}