From c62449704fc02eb05dc82450f9e3ba8bdca357cb Mon Sep 17 00:00:00 2001 From: Aze Date: Fri, 24 Oct 2025 19:45:56 +0200 Subject: [PATCH 1/3] sample using accelerated renderer on offscreen environment --- .gitignore | 1 + .../AcceleratedRenderHandler.cs | 31 ++ .../Application.cs | 26 ++ ...CefSharp.Offscreen.Direct3D.Example.csproj | 18 + .../D3D11Renderer.cs | 408 ++++++++++++++++++ .../OffscreenBrowser.cs | 29 ++ .../Program.cs | 24 ++ CefSharp.Offscreen.Direct3D.Example/README.md | 4 + OnAcceleratedPaint.Example.sln | 10 + 9 files changed, 551 insertions(+) create mode 100644 CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs create mode 100644 CefSharp.Offscreen.Direct3D.Example/Application.cs create mode 100644 CefSharp.Offscreen.Direct3D.Example/CefSharp.Offscreen.Direct3D.Example.csproj create mode 100644 CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs create mode 100644 CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs create mode 100644 CefSharp.Offscreen.Direct3D.Example/Program.cs create mode 100644 CefSharp.Offscreen.Direct3D.Example/README.md diff --git a/.gitignore b/.gitignore index d37b44a..fc68cef 100644 --- a/.gitignore +++ b/.gitignore @@ -397,3 +397,4 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml /CefSharp.WinForms.Direct3D.Example/obj.netcore +.idea/ \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs b/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs new file mode 100644 index 0000000..7b548a0 --- /dev/null +++ b/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs @@ -0,0 +1,31 @@ +using CefSharp.OffScreen; +using CefSharp.Structs; + +namespace CefSharp.Offscreen.Direct3D.Example; + +public class AcceleratedRenderHandler(ChromiumWebBrowser browser, int windowWidth, int windowHeight) : DefaultRenderHandler(browser) +{ + private D3D11Renderer _renderer = null!; + + public override void OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, AcceleratedPaintInfo acceleratedPaintInfo) + { + base.OnAcceleratedPaint(type, dirtyRect, acceleratedPaintInfo); + _renderer ??= new D3D11Renderer(windowWidth, windowHeight); + switch (type) + { + case PaintElementType.View: + _renderer?.OnAcceleratedPaint(acceleratedPaintInfo.SharedTextureHandle, PopupOpen); + break; + case PaintElementType.Popup: + _renderer?.CreatePopupLayer(acceleratedPaintInfo.SharedTextureHandle, new Rect(PopupPosition.X, PopupPosition.Y, PopupSize.Width, PopupSize.Height)); + break; + } + } + + public override void OnPopupShow(bool show) + { + base.OnPopupShow(show); + if(show) return; + _renderer?.RemovePopupLayer(); + } +} \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/Application.cs b/CefSharp.Offscreen.Direct3D.Example/Application.cs new file mode 100644 index 0000000..98060d3 --- /dev/null +++ b/CefSharp.Offscreen.Direct3D.Example/Application.cs @@ -0,0 +1,26 @@ +namespace CefSharp.Offscreen.Direct3D.Example; + +public class Application +{ + public static Uri Url { get; set; } = new("https://github.com/cefsharp/CefSharp.OnAcceleratedPaint.Example"); + private OffscreenBrowser browser; + public Application() + { + var cefBrowserSettings = new BrowserSettings + { + BackgroundColor = Cef.ColorSetARGB(255, 255, 255, 255), + WebGl = CefState.Enabled, + WindowlessFrameRate = 60, + LocalStorage = CefState.Default + }; + + browser = new OffscreenBrowser(1920, 1080, Url.AbsoluteUri, cefBrowserSettings); + } + + public void Close() + { + browser.Stop(); + browser.Dispose(); + Cef.Shutdown(); + } +} \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/CefSharp.Offscreen.Direct3D.Example.csproj b/CefSharp.Offscreen.Direct3D.Example/CefSharp.Offscreen.Direct3D.Example.csproj new file mode 100644 index 0000000..29ef826 --- /dev/null +++ b/CefSharp.Offscreen.Direct3D.Example/CefSharp.Offscreen.Direct3D.Example.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + enable + disable + + + + + + + + + + + diff --git a/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs b/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs new file mode 100644 index 0000000..7a77a6f --- /dev/null +++ b/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs @@ -0,0 +1,408 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using CefSharp.Structs; +using SharpDX; +using SharpDX.D3DCompiler; +using SharpDX.Direct3D; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using SharpDX.WIC; +using Adapter = SharpDX.DXGI.Adapter; +using Buffer = SharpDX.Direct3D11.Buffer; +using Device = SharpDX.Direct3D11.Device; +using Device1 = SharpDX.Direct3D11.Device1; +using Device2 = SharpDX.Direct3D11.Device2; +using Device3 = SharpDX.Direct3D11.Device3; +using Device4 = SharpDX.Direct3D11.Device4; +using MapFlags = SharpDX.Direct3D11.MapFlags; +using Point = SharpDX.Point; +using SampleDescription = SharpDX.DXGI.SampleDescription; +using Vector2 = SharpDX.Vector2; +using Vector4 = SharpDX.Vector4; +using Viewport = SharpDX.Viewport; + +namespace CefSharp.Offscreen.Direct3D.Example; + +/// +/// D3D11 renderer for CefSharp.Offscreen.Direct3D.Example +/// +public class D3D11Renderer +{ + private Texture2D _sharedTexture; + private Device _device; + private Adapter _adapter; + private static VertexDX11[] s_vertices; + private int _width; + private int _height; + private VertexBufferBinding _binding; + private Texture2D _popupLayer; + private Point _popupPosition; + private const string ShaderSrc = "Texture2D tex; \n" + + " \n" + + "SamplerState texSampler \n" + + "{ \n" + + " Texture = ; \n" + + " Filter = MIN_MAG_MIP_POINT; \n" + + "}; \n" + + " \n" + + "struct PS_IN \n" + + "{ \n" + + " float4 pos : SV_POSITION; \n" + + " float2 Tex : TEXCOORD; \n" + + "}; \n" + + " \n" + + "PS_IN vertex( PS_IN input ) \n" + + "{ \n" + + " return input; \n" + + "} \n" + + " \n" + + "float4 pixel( PS_IN input ) : SV_Target \n" + + "{ \n" + + " return tex.Sample( texSampler, input.Tex ); \n" + + "} \n" + + " \n"; + + [StructLayout(LayoutKind.Sequential)] + private struct VertexDX11 + { + public Vector4 Position; + public Vector2 TexCoord0; + + public static InputElement[] GetInputElements() + { + InputElement[] inputElements = [new("SV_POSITION", 0, Format.R32G32B32A32_Float, 0, 0), new("TEXCOORD", 0, Format.R32G32_Float, 16, 0)]; + return inputElements; + } + } + + private void CreateDevice() + { + CreateAdapter(); + UpgradeAdapter(); + _device = new Device(_adapter, DeviceCreationFlags.BgraSupport, FeatureLevel.Level_11_1); + + UpgradeDevice(); + } + + private void CreateAdapter() + { + using var f = new Factory1(); + _adapter = f.GetAdapter(0); + } + + public D3D11Renderer(int windowWidth, int windowHeight) + { + Cef.ShutdownStarted += (_, _) => Destroy(); + _width = windowWidth; + _height = windowHeight; + s_vertices = [new VertexDX11 {Position = new Vector4(-1, 1, 0, 1), TexCoord0 = new Vector2(0, 0)}, new VertexDX11 {Position = new Vector4(3, 1, 0, 1), TexCoord0 = new Vector2(2, 0)}, new VertexDX11 {Position = new Vector4(-1, -3, 0, 1), TexCoord0 = new Vector2(0, 2)}]; + //order is important + CreateDevice(); + CreateSharedTexture(); + CreateRenderTarget(); + CreateShaders(); + + _device.ImmediateContext.Flush(); + } + + private void CreateSharedTexture() + { + var sharedTextureDescription = new Texture2DDescription + { + ArraySize = 1, + BindFlags = BindFlags.ShaderResource | BindFlags.RenderTarget, + CpuAccessFlags = CpuAccessFlags.None, + Format = Format.B8G8R8A8_UNorm, + Height = _height, + MipLevels = 1, + OptionFlags = ResourceOptionFlags.Shared | ResourceOptionFlags.SharedNthandle, + SampleDescription = new SampleDescription(1, 0), + Usage = ResourceUsage.Default, + Width = _width + }; + _sharedTexture = new Texture2D(_device as Device1, sharedTextureDescription); + } + + private void CreateRenderTarget() + { + using var renderTarget = new RenderTargetView(_device as Device1, _sharedTexture); + _device.ImmediateContext.ClearRenderTargetView(renderTarget, Color4.Black); + _device.ImmediateContext.OutputMerger.SetRenderTargets(renderTarget); + var viewPort = new Viewport(0, 0, _width, _height); + _device.ImmediateContext.Rasterizer.SetViewport(viewPort); + } + + private void UpgradeDevice() + { + var device5 = _device.QueryInterfaceOrNull(); + if (device5 != null) + { + _device.Dispose(); + _device = device5; + return; + } + + var device4 = _device.QueryInterfaceOrNull(); + if (device4 != null) + { + _device.Dispose(); + _device = device4; + return; + } + + var device3 = _device.QueryInterfaceOrNull(); + if (device3 != null) + { + _device = device3; + return; + } + + var device2 = _device.QueryInterfaceOrNull(); + if (device2 != null) + { + _device = device2; + return; + } + + var device1 = _device.QueryInterfaceOrNull(); + if (device1 != null) + { + _device.Dispose(); + _device = device1; + } + } + + private void UpgradeAdapter() + { + var adapter4 = _adapter.QueryInterfaceOrNull(); + if (adapter4 != null) + { + _adapter.Dispose(); + _adapter = adapter4; + return; + } + + var adapter3 = _adapter.QueryInterfaceOrNull(); + if (adapter3 != null) + { + _adapter.Dispose(); + _adapter = adapter3; + return; + } + + var adapter2 = _adapter.QueryInterfaceOrNull(); + if (adapter2 != null) + { + _adapter.Dispose(); + _adapter = adapter2; + return; + } + + var adapter1 = _adapter.QueryInterfaceOrNull(); + if (adapter1 != null) + { + _adapter.Dispose(); + _adapter = adapter1; + } + } + + private void CreateShaders() + { + using (ShaderBytecode byteCode = ShaderBytecode.Compile(ShaderSrc, "vertex", "vs_4_0_level_9_1")) + { + using (var vs = new VertexShader(_device, byteCode)) + { + _device.ImmediateContext.VertexShader.Set(vs); + } + + var signature = ShaderSignature.GetInputSignature(byteCode); + using (var layout = new InputLayout(_device, signature, VertexDX11.GetInputElements())) + { + _device.ImmediateContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleStrip; + _device.ImmediateContext.InputAssembler.InputLayout = layout; + } + } + + using (ShaderBytecode byteCode = ShaderBytecode.Compile(ShaderSrc, "pixel", "ps_4_0_level_9_1")) + { + using (var ps = new PixelShader(_device, byteCode)) + { + _device.ImmediateContext.PixelShader.Set(ps); + } + } + + using (var data = DataStream.Create(s_vertices, false, false)) + { + var bufferDesc = new BufferDescription {BindFlags = BindFlags.VertexBuffer, SizeInBytes = s_vertices.Length * Marshal.SizeOf(), }; + using (var vb = new Buffer(_device, data, bufferDesc)) + { + _binding = new VertexBufferBinding {Buffer = vb, Offset = 0, Stride = Marshal.SizeOf()}; + } + } + } + + public void OnAcceleratedPaint(IntPtr sharedTextureHandle, bool popupShown = false) + { + try + { + if (_device == null || _sharedTexture == null || sharedTextureHandle == IntPtr.Zero) return; + if (_device is not Device1 {ImmediateContext: not null} dev1) return; + using var cefTex = dev1.OpenSharedResource1(sharedTextureHandle); + if (cefTex == null) return; + dev1.ImmediateContext.CopyResource(cefTex, _sharedTexture); + if (popupShown && _popupLayer != null)//if we have a popup widget layer being shown, we need to copy it to the shared texture + { + dev1.ImmediateContext.CopySubresourceRegion(_popupLayer, 0, null, _sharedTexture, 0, _popupPosition.X, _popupPosition.Y); + } + dev1.ImmediateContext.Flush(); +#if DEBUG + SaveTextureToFile(dev1, _sharedTexture, _width, _height);//to actually show that the browser is working, let's create a texture from the paint event and save it to a file +#endif + } + catch (Exception e) + { + Console.WriteLine($"Error copying resources from cef texture to d3d11: {e.Message}"); + } + } + + /// + /// Popup widget texture creation to draw over the next OnAcceleratedPaint + /// + /// + /// + public void CreatePopupLayer(IntPtr popupHandle, Rect popupRect) + { + try + { + var sharedTextureDescription = new Texture2DDescription + { + ArraySize = 1, + BindFlags = BindFlags.ShaderResource | BindFlags.RenderTarget, + CpuAccessFlags = CpuAccessFlags.None, + Format = Format.B8G8R8A8_UNorm, + Height = popupRect.Height, + MipLevels = 1, + OptionFlags = ResourceOptionFlags.Shared, + SampleDescription = new SampleDescription(1, 0), + Usage = ResourceUsage.Default, + Width = popupRect.Width, + }; + _popupLayer = new Texture2D(_device as Device1, sharedTextureDescription); + _popupPosition = new Point(popupRect.X, popupRect.Y); + + var dev1 = _device as Device1; + using var popupTex = dev1.OpenSharedResource1(popupHandle); + dev1.ImmediateContext.CopyResource(popupTex, _popupLayer); + } + catch (Exception e) + { + Console.WriteLine($"Error handling popup view: {e.Message}"); + } + } + + public void RemovePopupLayer() + { + try + { + _popupLayer?.Dispose(); + } + catch (Exception e) + { + Console.WriteLine($"Error disposing popup widget layer {e.Message}"); + } + } + + +#if DEBUG + private void SaveTextureToFile(Device1 device1, Texture2D source, int width, int height) + { + var readableTextureDescription = new Texture2DDescription + { + Width = width, + Height = height, + MipLevels = 1, + ArraySize = 1, + Format = Format.B8G8R8A8_UNorm, + SampleDescription = new SampleDescription(1, 0), + CpuAccessFlags = CpuAccessFlags.Read, + Usage = ResourceUsage.Staging, + BindFlags = BindFlags.None, + OptionFlags = ResourceOptionFlags.None + }; + + using var readableTexture = new Texture2D(device1, readableTextureDescription); + device1.ImmediateContext.CopyResource(source, readableTexture); + + var dataBox = device1.ImmediateContext.MapSubresource(readableTexture, 0, MapMode.Read, MapFlags.None, out var dataStream); + + if (dataStream != null) + { + var dataRectangle = new DataRectangle {DataPointer = dataStream.DataPointer, Pitch = dataBox.RowPitch}; + + using var factory = new ImagingFactory(); + using var bitmap = new Bitmap(factory, readableTexture.Description.Width, readableTexture.Description.Height, PixelFormat.Format32bppBGRA, dataRectangle); + using var fileStream = new FileStream(@$"{Application.Url.Host}.png", FileMode.OpenOrCreate); + fileStream.Position = 0; + using var bitmapEncoder = new PngBitmapEncoder(factory, fileStream); + using var bitmapFrameEncode = new BitmapFrameEncode(bitmapEncoder); + bitmapFrameEncode.Initialize(); + bitmapFrameEncode.SetSize(bitmap.Size.Width, bitmap.Size.Height); + var pixelFormat = PixelFormat.FormatDontCare; + bitmapFrameEncode.SetPixelFormat(ref pixelFormat); + bitmapFrameEncode.WriteSource(bitmap); + bitmapFrameEncode.Commit(); + bitmapEncoder.Commit(); + } + + device1.ImmediateContext.UnmapSubresource(readableTexture, 0); + } +#endif + + /// + /// If we need to resize the window we need to recreate the shared texture and the render target + /// + /// + /// + public void ResizeWindow(int width, int height) + { + try + { + if (_width != width || _height != height) + { + _sharedTexture?.Dispose(); + _device.ImmediateContext.OutputMerger.GetRenderTargets(out var depthStencilViewRef); + var targets = _device.ImmediateContext.OutputMerger.GetRenderTargets(1); + foreach (var target in targets) + { + target.Dispose(); + } + depthStencilViewRef?.Dispose(); + _device.ImmediateContext.OutputMerger.ResetTargets(); + _width = width; + _height = height; + CreateSharedTexture(); + CreateRenderTarget(); + _device.ImmediateContext.Flush(); + } + Debug.WriteLine($"Resized window {width}x{height}"); + } + catch (Exception e) + { + Console.WriteLine($"Failed to resize window: {e}"); + } + } + + /// + /// Resources cleanup + /// + private void Destroy() + { + Debug.WriteLine("Destroying D3D11 renderer"); + _device?.Dispose(); + _device = null; + _adapter?.Dispose(); + _adapter = null; + _sharedTexture?.Dispose(); + _sharedTexture = null; + } +} \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs b/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs new file mode 100644 index 0000000..3051203 --- /dev/null +++ b/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs @@ -0,0 +1,29 @@ +using System.Drawing; +using CefSharp.OffScreen; + +namespace CefSharp.Offscreen.Direct3D.Example; + +public class OffscreenBrowser : ChromiumWebBrowser +{ + public OffscreenBrowser(int width, int height, string startUrl, IBrowserSettings cefBrowserSettings) : base(startUrl, cefBrowserSettings, automaticallyCreateBrowser: false, useLegacyRenderHandler: false) + { + Initialize(width, height, cefBrowserSettings); + } + + private void Initialize(int width, int height, IBrowserSettings cefBrowserSettings) + { + try + { + var windowInfo = new WindowInfo {WindowlessRenderingEnabled = true, Width = width, Height = height}; + windowInfo.SharedTextureEnabled = true; + windowInfo.SetAsWindowless(IntPtr.Zero); + CreateBrowser(windowInfo, cefBrowserSettings); + Size = new Size(width, height); + RenderHandler = new AcceleratedRenderHandler(this, width, height); + } + catch (Exception e) + { + Console.WriteLine($"Error creating browser {e}"); + } + } +} \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/Program.cs b/CefSharp.Offscreen.Direct3D.Example/Program.cs new file mode 100644 index 0000000..f7e50c3 --- /dev/null +++ b/CefSharp.Offscreen.Direct3D.Example/Program.cs @@ -0,0 +1,24 @@ +using CefSharp; +using CefSharp.OffScreen; +using CefSharp.Offscreen.Direct3D.Example; + +Console.WriteLine("Hello, fellow dev!"); + +var settings = new CefSettings +{ + BackgroundColor = Cef.ColorSetARGB(255, 255, 255, 255),//let's set a white background + WindowlessRenderingEnabled = true +}; + +var success = Cef.Initialize(settings, true); +if (!success) +{ + var exitCode = Cef.GetExitCode(); + + throw new Exception($"Cef.Initialize failed with {exitCode}, check the log file for more details."); +} + +var app = new Application(); +Console.ReadKey(); +app.Close(); +Console.WriteLine("Bye!"); \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/README.md b/CefSharp.Offscreen.Direct3D.Example/README.md new file mode 100644 index 0000000..e5fcff4 --- /dev/null +++ b/CefSharp.Offscreen.Direct3D.Example/README.md @@ -0,0 +1,4 @@ +## Offscreen Direct3D +This project is a sample of using CefSharp in an **offscreen** mode with the usage of **OnAcceleratedPaint**. + +The sample creates an offscreen window and renders a simple HTML page. By running it in debug mode you can see the OnAcceleratedPaint texture being created in the application running path. \ No newline at end of file diff --git a/OnAcceleratedPaint.Example.sln b/OnAcceleratedPaint.Example.sln index ab72a4e..8020912 100644 --- a/OnAcceleratedPaint.Example.sln +++ b/OnAcceleratedPaint.Example.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.10.35122.118 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CefSharp.WinForms.Direct3D.Example.netcore", "CefSharp.WinForms.Direct3D.Example\CefSharp.WinForms.Direct3D.Example.netcore.csproj", "{D4541C3D-E6F8-4174-9D7D-1B3D3CDBF94C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CefSharp.Offscreen.Direct3D.Example", "CefSharp.Offscreen.Direct3D.Example\CefSharp.Offscreen.Direct3D.Example.csproj", "{42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -21,6 +23,14 @@ Global {D4541C3D-E6F8-4174-9D7D-1B3D3CDBF94C}.Release|x64.Build.0 = Release|x64 {D4541C3D-E6F8-4174-9D7D-1B3D3CDBF94C}.Release|x86.ActiveCfg = Release|x86 {D4541C3D-E6F8-4174-9D7D-1B3D3CDBF94C}.Release|x86.Build.0 = Release|x86 + {42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}.Debug|x64.ActiveCfg = Debug|Any CPU + {42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}.Debug|x64.Build.0 = Debug|Any CPU + {42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}.Debug|x86.ActiveCfg = Debug|Any CPU + {42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}.Debug|x86.Build.0 = Debug|Any CPU + {42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}.Release|x64.ActiveCfg = Release|Any CPU + {42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}.Release|x64.Build.0 = Release|Any CPU + {42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}.Release|x86.ActiveCfg = Release|Any CPU + {42090FAF-A5A7-4C6F-A77F-8384AF2A65AF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From e7af26decd025e1a9370233aaad0bdd39be04731 Mon Sep 17 00:00:00 2001 From: Aze Date: Sat, 25 Oct 2025 23:50:35 +0200 Subject: [PATCH 2/3] disposable pattern optimization in render handler --- .../AcceleratedRenderHandler.cs | 6 +++ .../Application.cs | 8 +-- .../D3D11Renderer.cs | 53 +++---------------- .../OffscreenBrowser.cs | 7 +++ 4 files changed, 23 insertions(+), 51 deletions(-) diff --git a/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs b/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs index 7b548a0..fec5d96 100644 --- a/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs +++ b/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs @@ -28,4 +28,10 @@ public override void OnPopupShow(bool show) if(show) return; _renderer?.RemovePopupLayer(); } + + public new void Dispose() + { + base.Dispose(); + _renderer?.Dispose(); + } } \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/Application.cs b/CefSharp.Offscreen.Direct3D.Example/Application.cs index 98060d3..be08f5b 100644 --- a/CefSharp.Offscreen.Direct3D.Example/Application.cs +++ b/CefSharp.Offscreen.Direct3D.Example/Application.cs @@ -3,7 +3,7 @@ public class Application { public static Uri Url { get; set; } = new("https://github.com/cefsharp/CefSharp.OnAcceleratedPaint.Example"); - private OffscreenBrowser browser; + private readonly OffscreenBrowser _browser; public Application() { var cefBrowserSettings = new BrowserSettings @@ -14,13 +14,13 @@ public Application() LocalStorage = CefState.Default }; - browser = new OffscreenBrowser(1920, 1080, Url.AbsoluteUri, cefBrowserSettings); + _browser = new OffscreenBrowser(1920, 1080, Url.AbsoluteUri, cefBrowserSettings); } public void Close() { - browser.Stop(); - browser.Dispose(); + _browser.Stop(); + _browser.Dispose(); Cef.Shutdown(); } } \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs b/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs index 7a77a6f..0cdeb80 100644 --- a/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs +++ b/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using CefSharp.Structs; using SharpDX; using SharpDX.D3DCompiler; @@ -26,7 +25,7 @@ namespace CefSharp.Offscreen.Direct3D.Example; /// /// D3D11 renderer for CefSharp.Offscreen.Direct3D.Example /// -public class D3D11Renderer +public class D3D11Renderer : IDisposable { private Texture2D _sharedTexture; private Device _device; @@ -92,7 +91,6 @@ private void CreateAdapter() public D3D11Renderer(int windowWidth, int windowHeight) { - Cef.ShutdownStarted += (_, _) => Destroy(); _width = windowWidth; _height = windowHeight; s_vertices = [new VertexDX11 {Position = new Vector4(-1, 1, 0, 1), TexCoord0 = new Vector2(0, 0)}, new VertexDX11 {Position = new Vector4(3, 1, 0, 1), TexCoord0 = new Vector2(2, 0)}, new VertexDX11 {Position = new Vector4(-1, -3, 0, 1), TexCoord0 = new Vector2(0, 2)}]; @@ -358,51 +356,12 @@ private void SaveTextureToFile(Device1 device1, Texture2D source, int width, int } #endif - /// - /// If we need to resize the window we need to recreate the shared texture and the render target - /// - /// - /// - public void ResizeWindow(int width, int height) + public void Dispose() { - try - { - if (_width != width || _height != height) - { - _sharedTexture?.Dispose(); - _device.ImmediateContext.OutputMerger.GetRenderTargets(out var depthStencilViewRef); - var targets = _device.ImmediateContext.OutputMerger.GetRenderTargets(1); - foreach (var target in targets) - { - target.Dispose(); - } - depthStencilViewRef?.Dispose(); - _device.ImmediateContext.OutputMerger.ResetTargets(); - _width = width; - _height = height; - CreateSharedTexture(); - CreateRenderTarget(); - _device.ImmediateContext.Flush(); - } - Debug.WriteLine($"Resized window {width}x{height}"); - } - catch (Exception e) - { - Console.WriteLine($"Failed to resize window: {e}"); - } - } - - /// - /// Resources cleanup - /// - private void Destroy() - { - Debug.WriteLine("Destroying D3D11 renderer"); + _sharedTexture?.Dispose(); _device?.Dispose(); - _device = null; _adapter?.Dispose(); - _adapter = null; - _sharedTexture?.Dispose(); - _sharedTexture = null; + _popupLayer?.Dispose(); + GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs b/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs index 3051203..f3ead37 100644 --- a/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs +++ b/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs @@ -26,4 +26,11 @@ private void Initialize(int width, int height, IBrowserSettings cefBrowserSettin Console.WriteLine($"Error creating browser {e}"); } } + + protected override void Dispose(bool disposing) + { + if (RenderHandler is AcceleratedRenderHandler renderHandler) + renderHandler.Dispose(); + base.Dispose(disposing); + } } \ No newline at end of file From 997268bfbedfa5f85abd9ed65f4e6af6a3a0aa12 Mon Sep 17 00:00:00 2001 From: Aze Date: Sun, 26 Oct 2025 01:20:48 +0200 Subject: [PATCH 3/3] adding resize functionality --- .../AcceleratedRenderHandler.cs | 5 ++++ .../D3D11Renderer.cs | 28 +++++++++++++++++++ .../OffscreenBrowser.cs | 7 +++++ 3 files changed, 40 insertions(+) diff --git a/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs b/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs index fec5d96..995def0 100644 --- a/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs +++ b/CefSharp.Offscreen.Direct3D.Example/AcceleratedRenderHandler.cs @@ -21,6 +21,11 @@ public override void OnAcceleratedPaint(PaintElementType type, Rect dirtyRect, A break; } } + + public void BrowserWasResized(int width, int height) + { + _renderer?.ResizeWindow(width, height); + } public override void OnPopupShow(bool show) { diff --git a/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs b/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs index 0cdeb80..7f3d33e 100644 --- a/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs +++ b/CefSharp.Offscreen.Direct3D.Example/D3D11Renderer.cs @@ -310,6 +310,34 @@ public void RemovePopupLayer() } } + public void ResizeWindow(int width, int height) + { + try + { + if (_width != width || _height != height) + { + _sharedTexture?.Dispose(); + _device.ImmediateContext.OutputMerger.GetRenderTargets(out var depthStencilViewRef); + var targets = _device.ImmediateContext.OutputMerger.GetRenderTargets(1); + foreach (var target in targets) + { + target.Dispose(); + } + depthStencilViewRef?.Dispose(); + _device.ImmediateContext.OutputMerger.ResetTargets(); + _width = width; + _height = height; + CreateSharedTexture(); + CreateRenderTarget(); + _device.ImmediateContext.Flush(); + } + Console.WriteLine($"Resized window {width}x{height}"); + } + catch (Exception e) + { + Console.WriteLine($"Failed to resize window: {e}"); + } + } #if DEBUG private void SaveTextureToFile(Device1 device1, Texture2D source, int width, int height) diff --git a/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs b/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs index f3ead37..d217e08 100644 --- a/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs +++ b/CefSharp.Offscreen.Direct3D.Example/OffscreenBrowser.cs @@ -27,6 +27,13 @@ private void Initialize(int width, int height, IBrowserSettings cefBrowserSettin } } + public void ResizeBrowser(int width, int height) + { + Size = new Size(width, height); + if (RenderHandler is AcceleratedRenderHandler renderHandler) + renderHandler.BrowserWasResized(width, height); + } + protected override void Dispose(bool disposing) { if (RenderHandler is AcceleratedRenderHandler renderHandler)