From 4bb40643a8ef197c392ed7b93690b105ebd7e0f8 Mon Sep 17 00:00:00 2001 From: Varon Date: Tue, 24 Nov 2020 21:01:49 +0200 Subject: [PATCH] Compiling but broken --- src/GLWpfControl/DXGLContext.cs | 1 + src/GLWpfControl/DXGLRenderSurface.cs | 194 ----------------- src/GLWpfControl/DxGLFramebuffer.cs | 90 +++++++- src/GLWpfControl/GLWpfControl.cs | 235 ++++----------------- src/GLWpfControl/GLWpfControlRenderer.cs | 99 +++++++++ src/GLWpfControl/GLWpfControlRendererDX.cs | 75 ------- 6 files changed, 228 insertions(+), 466 deletions(-) delete mode 100644 src/GLWpfControl/DXGLRenderSurface.cs create mode 100644 src/GLWpfControl/GLWpfControlRenderer.cs delete mode 100644 src/GLWpfControl/GLWpfControlRendererDX.cs 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 @@ internal sealed class DxGlContext : IDisposable { 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 sealed class GLWpfControl : FrameworkElement 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 sealed class GLWpfControl : FrameworkElement 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); - } - - } -}