C# Render Visual naar Bmp, sneller dan RenderTargetBitmap

Pagina: 1
Acties:

Vraag


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 25-05 11:39
Ik heb een WPF applicatie die een redelijk complexe view laat zien met verschillende animaties etc. Mijn doel is om deze view in real-time, met ~60 fps, om te zetten naar een BitmapSource. Deze bitmap wordt vervolgens over het netwerk doorgestuurd als video source voor NDI.

Momenteel gebruik ik de RenderTargetBitmap Render method om de Visual om te zetten naar een BitmapSource. Hoewel dit prima werkt, is het helaas niet snel genoeg in veel gevallen. Enkel met een simpele view met een paar simpele elementen haal ik iets wat richting de 60 fps lijkt, maar met iets complexe scene is het al snel ~5 fps of nog minder.

Ik heb zitten timen waar het probleem ligt en het zit overduidelijk in de RenderTargetBitmap call. Deze duurt voor een simpele scene < 1 ms, maar zodra ik mijn iets complexe scene laat zien gaat het al richting ~250 ms en is de richtlijn van 60 fps dus compleet onhaalbaar.


Ik ben dus op zoek naar een andere manier om een willekeurige Visual / UIElement naar een BitmapSource te renderen, welke sneller is dan de RenderTargetBitmap. Daarnaast moet het ook werken in een transparent window en moet het ook de semi-transparency van elementen kunnen opnemen.


Na wat zoeken heb ik twee mogelijke opties gevonden:

1. https://stackoverflow.com...-a-wpf-visual-to-a-bitmap
Dit topic laat zien hoe je een Visual naar een Bitmap kan renderen. Het werkt ook nog Async maar dat is verder niet zo belangrijk.

Dit heb ik min of meer werkend gekregen na wat aanpassingen om het iets simpeler te maken (bijv async eruit):
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
        public static BitmapSource Render(this UIElement visual, int width, int height)
        {
            var src = (HwndSource)PresentationSource.FromVisual(visual);
            if (src == null)
                return null;

            BitmapSource bitmapSourceT = Render(src, width, height);
            bitmapSourceT.Freeze();
            return bitmapSourceT;
        }
        
        private static BitmapSource Render(HwndSource src, int width, int height)
        {
            Bitmap bitmap = VisualToBitmapConverter.GetBitmap(src.Handle, width, height);

            return Convert(bitmap);
        }
        
        
        public static class VisualToBitmapConverter
        {
            private enum TernaryRasterOperations : uint
            {
                SRCCOPY = 0x00CC0020,
                SRCPAINT = 0x00EE0086,
                SRCAND = 0x008800C6,
                SRCINVERT = 0x00660046,
                SRCERASE = 0x00440328,
                NOTSRCCOPY = 0x00330008,
                NOTSRCERASE = 0x001100A6,
                MERGECOPY = 0x00C000CA,
                MERGEPAINT = 0x00BB0226,
                PATCOPY = 0x00F00021,
                PATPAINT = 0x00FB0A09,
                PATINVERT = 0x005A0049,
                DSTINVERT = 0x00550009,
                BLACKNESS = 0x00000042,
                WHITENESS = 0x00FF0062,
                CAPTUREBLT = 0x40000000
            }

            [DllImport("gdi32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);

            public static Bitmap GetBitmap(IntPtr hwnd, int width, int height)
            {
                var bitmap = new Bitmap(width, height);
                using (Graphics graphicsFromVisual = Graphics.FromHwnd(hwnd))
                {
                    using (Graphics graphicsFromImage = Graphics.FromImage(bitmap))
                    {
                        IntPtr source = graphicsFromVisual.GetHdc();
                        IntPtr destination = graphicsFromImage.GetHdc();

                        BitBlt(destination, 0, 0, bitmap.Width, bitmap.Height, source, 0, 0, TernaryRasterOperations.SRCCOPY);

                        graphicsFromVisual.ReleaseHdc(source);
                        graphicsFromImage.ReleaseHdc(destination);
                    }
                }

                return bitmap;
            }
        }

        [DllImport("gdi32")]
        static extern int DeleteObject(IntPtr o);

        public static BitmapSource Convert(System.Drawing.Bitmap source)
        {
            IntPtr ip = source.GetHbitmap();
            BitmapSource bs = null;
            try
            {
                bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ip,
                    IntPtr.Zero, Int32Rect.Empty,
                    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(ip);
            }

            return bs;
        }
    }


Dit lijkt OK te werken in een normaal window maar twee problemen:
- Hij neemt de complete Window op en niet alleen de Visual in de Window. Dit is nog wel te overzien want de Window waarin mijn Visual zit is in principe toch leeg.
- Groter probleem: het werkt niet meer zodra ik mijn Window transparent maak via "AllowsTransparency=True". Na deze aanpassing krijg ik enkel een zwart beeld terug. Zonder transparency werkt het wel en is het ook veel sneller dan RenderTargetBitmap


2. Ik zie hier en daar wat dingen langs komen om DirectX renderer te gebruiken voor een soort screen capture, ipv RenderTargetBitmap. Maar ik kan nergens een complete source of uitleg vinden en dit soort dingen heb ik absoluut geen ervaring mee, dus hier loop ik al snel vast. Maar het klinkt ergens wel veelbelovend...
Een aantal opties worden hier genoemd: MSDN: RenderTargetBitmap and Hardware WPF Rendering


Mijn specifieke vraag is dus eigenlijk of iemand een alternatief voor RenderTargetBitmap kent, welke sneller is en ook nog werkt met transparent windows? Mijn eerste gevonden oplossing hierboven lijkt wel snel genoeg maar werkt helaas niet met een transparent window, wellicht weet iemand daar een oplossing voor?
Een commercieel product is ook te overwegen (afhankelijk van de prijs) maar ook daar kan ik niks van vinden helaas.

Bedankt voor enige input!

Mijn iRacing profiel

Alle reacties


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 25-05 11:39
Ik heb vanavond het volgende gevonden:
https://github.com/Micros.../cpp/ScreenCaptureforHWND

Met deze samples kan ik mijn transparent window zonder enige lag als een 'screen capture' opvangen en in hun sample application dupliceren. Dit lijkt te gebeuren via DirectX / UWP waar ik helaas geen ervaring mee heb. Het lijkt echter wel de goeie richting op te gaan. Ik hoop dat er via deze weg een manier is om de "capture" weer om te zetten in een bitmap zodat ik die verder kan gebruiken. Hier zit ik helaas momenteel weer vast.

Heeft iemand ervaring met deze techniek en weet mij in de goeie richting te sturen?

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Sandor_Clegane
  • Registratie: Januari 2012
  • Niet online

Sandor_Clegane

Fancy plans and pants to match

NickThissen schreef op zaterdag 17 augustus 2019 @ 23:12:
Ik heb vanavond het volgende gevonden:
https://github.com/Micros.../cpp/ScreenCaptureforHWND

Met deze samples kan ik mijn transparent window zonder enige lag als een 'screen capture' opvangen en in hun sample application dupliceren. Dit lijkt te gebeuren via DirectX / UWP waar ik helaas geen ervaring mee heb. Het lijkt echter wel de goeie richting op te gaan. Ik hoop dat er via deze weg een manier is om de "capture" weer om te zetten in een bitmap zodat ik die verder kan gebruiken. Hier zit ik helaas momenteel weer vast.

Heeft iemand ervaring met deze techniek en weet mij in de goeie richting te sturen?
NewTek? Man, zijn dat niet die lui van Lightwave? Dat is een blast from the past. :)

Ik weet niet of dit kan, maar zoiets als het streamen van RDP of Steam of Shadow play van NVidia kan je dat als inputsource voor dat NDI gebruiken?

Steam is goed geoptimaliseerd lijkt me, daar haal je wel 60 FPS mee.

Tis maar een idee hoor.

[ Voor 60% gewijzigd door Sandor_Clegane op 18-08-2019 17:23 ]

Less alienation, more cooperation.


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 25-05 11:39
Sandor_Clegane schreef op zondag 18 augustus 2019 @ 17:20:
[...]


NewTek? Man, zijn dat niet die lui van Lightwave? Dat is een blast from the past. :)

Ik weet niet of dit kan, maar zoiets als het streamen van RDP of Steam of Shadow play van NVidia kan je dat als inputsource voor dat NDI gebruiken?

Steam is goed geoptimaliseerd lijkt me, daar haal je wel 60 FPS mee.

Tis maar een idee hoor.
Ik zou niet weten hoe ik dat werkend zou moeten krijgen. Voor zover ik bekend ben met dit soort streaming (niet veel) kun je enkel een game of de hele desktop opnemen, niet een specifiek window? En ik kan ook niks vinden over doorsturen via NDI helaas.


Inmiddels ben ik wel iets verder met mijn eigen oplossing. De Screen Capture sample hierboven gelinkt heb ik weten aan te passen zodat ik elke frame:
- De 'captured screen' (een SharpDX Texture2D object) omzet naar een Sharp2D Bitmap object
- Het Bitmap object encode naar een png stream in memory
- De stream weer omzet naar een WPF BitmapImage
- Deze BitmapImage uiteindelijk via NDI over het netwerk sturen.

Op deze manier kan ik het window dat ik wil opnemen inderdaad via NDI doorsturen. Helaas lukt het nog niet om transparency werkend te krijgen, het lijkt dat de screen capture momenteel een zwarte achtergrond opneemt ipv transparency. Ik heb zitten spelen met de verschillende pixel formats maar krijg het niet aan de gang. Hopelijk is het geen restrictie van de screen capture.

Het geeft me enige hoop dat de transparency wel te zien is in de ingebakken "GraphicsCapturePicker", deze opent een view waarin je kan kiezen welke window je wil opnemen met een preview voor elk window, en als ik mijn transparent window daar selecteer zie ik de selectie kleur door het window heen dus daar lijkt hij wel semi-transparent. Ik gok dat deze ingebakken capture picker eenzelfde technologie gebruikt dus het zou mogelijk moeten zijn 8)7


Mijn code als volgt:

Hier zet ik de Texture2D om naar een memory stream:
C#:
1
2
3
4
5
6
7
8
          using (var backBuffer = swapChain.GetBackBuffer<SharpDX.Direct3D11.Texture2D>(0))
            using (var bitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface))
            {
                d3dDevice.ImmediateContext.CopyResource(bitmap, backBuffer);

                // My addition:
                GetBitmap(bitmap);
            }


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    private void GetBitmap(Texture2D texture)
    {
        // Create texture copy
        var copy = new Texture2D(d3dDevice, new Texture2DDescription
        {
            Width = texture.Description.Width,
            Height = texture.Description.Height,
            MipLevels = 1,
            ArraySize = 1,
            Format = texture.Description.Format,
            Usage = ResourceUsage.Staging,
            SampleDescription = new SampleDescription(1, 0),
            BindFlags = BindFlags.None,
            CpuAccessFlags = CpuAccessFlags.Read,
            OptionFlags = ResourceOptionFlags.None
        });

        // Copy data
        d3dDevice.ImmediateContext.CopyResource(texture, copy);

        var dataBox = d3dDevice.ImmediateContext.MapSubresource(copy, 0, 0, MapMode.Read, MapFlags.None,
            out DataStream stream);
        var rect = new DataRectangle
        {
            DataPointer = stream.DataPointer,
            Pitch = dataBox.RowPitch
        };
        
        var format = PixelFormat.Format32bppPBGRA;
        Bitmap bmp = new Bitmap(factory, copy.Description.Width, copy.Description.Height, format, rect);
        
        using (var ras = new InMemoryRandomAccessStream())
        {
            var ms = ras.AsStream(); // Do not dispose here
            using (var wic = new WICStream(factory, ms))
            using (var encoder = new PngBitmapEncoder(factory, wic))
            using (var frame = new BitmapFrameEncode(encoder))
            {
                frame.Initialize();
                frame.SetSize(bmp.Size.Width, bmp.Size.Height);
                frame.SetPixelFormat(ref format);
                frame.WriteSource(bmp);
                frame.Commit();
                encoder.Commit();
            }

            BitmapCaptured?.Invoke(this, new CaptureEventArgs(ms, bmp.Size.Width, bmp.Size.Height));
        }

        d3dDevice.ImmediateContext.UnmapSubresource(copy, 0);
        copy.Dispose();
        bmp.Dispose();
    }


Vervolgens maak ik er weer een BitmapImage van en gebruik ik CopyPixels (volgens het standaard NDI example) om de data op de goeie plek te krijgen:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
                private void OnBitmapCaptured(object sender, CaptureEventArgs e)
        {
            SendData(e.Stream, e.Width, e.Height);
        }

public void SendData(Stream ms, int width, int height)
        {
            var image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.StreamSource = ms;
            image.EndInit();
            ms.Dispose();

            int xres = width; // NdiWidth;
            int yres = height; //NdiHeight;

            int frNum = NdiFrameRateNumerator;
            int frDen = NdiFrameRateDenominator;

            // sanity
            if (sendInstancePtr == IntPtr.Zero || xres < 8 || yres < 8)
                return;

            stride = (xres * 32/*BGRA bpp*/ + 7) / 8;
            bufferSize = yres * stride;
            aspectRatio = (float)xres / (float)yres;

            // allocate some memory for a video buffer
            IntPtr bufferPtr = Marshal.AllocHGlobal(bufferSize);

            // We are going to create a progressive frame at 60Hz.
            NDIlib.video_frame_v2_t videoFrame = new NDIlib.video_frame_v2_t()
            {
                // Resolution
                xres = xres,
                yres = yres,
                // Use BGRA video
                FourCC = NDIlib.FourCC_type_e.FourCC_type_BGRA,
                // The frame-eate
                frame_rate_N = frNum,
                frame_rate_D = frDen,
                // The aspect ratio
                picture_aspect_ratio = aspectRatio,
                // This is a progressive frame
                frame_format_type = NDIlib.frame_format_type_e.frame_format_type_progressive,
                // Timecode.
                timecode = NDIlib.send_timecode_synthesize,
                // The video memory used for this frame
                p_data = bufferPtr,
                // The line to line stride of this image
                line_stride_in_bytes = stride,
                // no metadata
                p_metadata = IntPtr.Zero,
                // only valid on received frames
                timestamp = 0
            };

            fmtConvertedBmp = new FormatConvertedBitmap();
            fmtConvertedBmp.BeginInit();
            fmtConvertedBmp.Source = image;
            fmtConvertedBmp.DestinationFormat = PixelFormats.Bgra32;
            fmtConvertedBmp.EndInit();

            fmtConvertedBmp.CopyPixels(new Int32Rect(0, 0, xres, yres), bufferPtr, bufferSize, stride);
            
            // add it to the output queue
            AddFrame(videoFrame);
        }

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Sandor_Clegane
  • Registratie: Januari 2012
  • Niet online

Sandor_Clegane

Fancy plans and pants to match

NickThissen schreef op zondag 18 augustus 2019 @ 22:15:
[...]

Ik zou niet weten hoe ik dat werkend zou moeten krijgen. Voor zover ik bekend ben met dit soort streaming (niet veel) kun je enkel een game of de hele desktop opnemen, niet een specifiek window? En ik kan ook niks vinden over doorsturen via NDI helaas.


Inmiddels ben ik wel iets verder met mijn eigen oplossing. De Screen Capture sample hierboven gelinkt heb ik weten aan te passen zodat ik elke frame:
- De 'captured screen' (een SharpDX Texture2D object) omzet naar een Sharp2D Bitmap object
- Het Bitmap object encode naar een png stream in memory
- De stream weer omzet naar een WPF BitmapImage
- Deze BitmapImage uiteindelijk via NDI over het netwerk sturen.

Op deze manier kan ik het window dat ik wil opnemen inderdaad via NDI doorsturen. Helaas lukt het nog niet om transparency werkend te krijgen, het lijkt dat de screen capture momenteel een zwarte achtergrond opneemt ipv transparency. Ik heb zitten spelen met de verschillende pixel formats maar krijg het niet aan de gang. Hopelijk is het geen restrictie van de screen capture.

Het geeft me enige hoop dat de transparency wel te zien is in de ingebakken "GraphicsCapturePicker", deze opent een view waarin je kan kiezen welke window je wil opnemen met een preview voor elk window, en als ik mijn transparent window daar selecteer zie ik de selectie kleur door het window heen dus daar lijkt hij wel semi-transparent. Ik gok dat deze ingebakken capture picker eenzelfde technologie gebruikt dus het zou mogelijk moeten zijn 8)7


Mijn code als volgt:

Hier zet ik de Texture2D om naar een memory stream:
C#:
1
2
3
4
5
6
7
8
          using (var backBuffer = swapChain.GetBackBuffer<SharpDX.Direct3D11.Texture2D>(0))
            using (var bitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface))
            {
                d3dDevice.ImmediateContext.CopyResource(bitmap, backBuffer);

                // My addition:
                GetBitmap(bitmap);
            }


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    private void GetBitmap(Texture2D texture)
    {
        // Create texture copy
        var copy = new Texture2D(d3dDevice, new Texture2DDescription
        {
            Width = texture.Description.Width,
            Height = texture.Description.Height,
            MipLevels = 1,
            ArraySize = 1,
            Format = texture.Description.Format,
            Usage = ResourceUsage.Staging,
            SampleDescription = new SampleDescription(1, 0),
            BindFlags = BindFlags.None,
            CpuAccessFlags = CpuAccessFlags.Read,
            OptionFlags = ResourceOptionFlags.None
        });

        // Copy data
        d3dDevice.ImmediateContext.CopyResource(texture, copy);

        var dataBox = d3dDevice.ImmediateContext.MapSubresource(copy, 0, 0, MapMode.Read, MapFlags.None,
            out DataStream stream);
        var rect = new DataRectangle
        {
            DataPointer = stream.DataPointer,
            Pitch = dataBox.RowPitch
        };
        
        var format = PixelFormat.Format32bppPBGRA;
        Bitmap bmp = new Bitmap(factory, copy.Description.Width, copy.Description.Height, format, rect);
        
        using (var ras = new InMemoryRandomAccessStream())
        {
            var ms = ras.AsStream(); // Do not dispose here
            using (var wic = new WICStream(factory, ms))
            using (var encoder = new PngBitmapEncoder(factory, wic))
            using (var frame = new BitmapFrameEncode(encoder))
            {
                frame.Initialize();
                frame.SetSize(bmp.Size.Width, bmp.Size.Height);
                frame.SetPixelFormat(ref format);
                frame.WriteSource(bmp);
                frame.Commit();
                encoder.Commit();
            }

            BitmapCaptured?.Invoke(this, new CaptureEventArgs(ms, bmp.Size.Width, bmp.Size.Height));
        }

        d3dDevice.ImmediateContext.UnmapSubresource(copy, 0);
        copy.Dispose();
        bmp.Dispose();
    }


Vervolgens maak ik er weer een BitmapImage van en gebruik ik CopyPixels (volgens het standaard NDI example) om de data op de goeie plek te krijgen:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
                private void OnBitmapCaptured(object sender, CaptureEventArgs e)
        {
            SendData(e.Stream, e.Width, e.Height);
        }

public void SendData(Stream ms, int width, int height)
        {
            var image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.StreamSource = ms;
            image.EndInit();
            ms.Dispose();

            int xres = width; // NdiWidth;
            int yres = height; //NdiHeight;

            int frNum = NdiFrameRateNumerator;
            int frDen = NdiFrameRateDenominator;

            // sanity
            if (sendInstancePtr == IntPtr.Zero || xres < 8 || yres < 8)
                return;

            stride = (xres * 32/*BGRA bpp*/ + 7) / 8;
            bufferSize = yres * stride;
            aspectRatio = (float)xres / (float)yres;

            // allocate some memory for a video buffer
            IntPtr bufferPtr = Marshal.AllocHGlobal(bufferSize);

            // We are going to create a progressive frame at 60Hz.
            NDIlib.video_frame_v2_t videoFrame = new NDIlib.video_frame_v2_t()
            {
                // Resolution
                xres = xres,
                yres = yres,
                // Use BGRA video
                FourCC = NDIlib.FourCC_type_e.FourCC_type_BGRA,
                // The frame-eate
                frame_rate_N = frNum,
                frame_rate_D = frDen,
                // The aspect ratio
                picture_aspect_ratio = aspectRatio,
                // This is a progressive frame
                frame_format_type = NDIlib.frame_format_type_e.frame_format_type_progressive,
                // Timecode.
                timecode = NDIlib.send_timecode_synthesize,
                // The video memory used for this frame
                p_data = bufferPtr,
                // The line to line stride of this image
                line_stride_in_bytes = stride,
                // no metadata
                p_metadata = IntPtr.Zero,
                // only valid on received frames
                timestamp = 0
            };

            fmtConvertedBmp = new FormatConvertedBitmap();
            fmtConvertedBmp.BeginInit();
            fmtConvertedBmp.Source = image;
            fmtConvertedBmp.DestinationFormat = PixelFormats.Bgra32;
            fmtConvertedBmp.EndInit();

            fmtConvertedBmp.CopyPixels(new Int32Rect(0, 0, xres, yres), bufferPtr, bufferSize, stride);
            
            // add it to the output queue
            AddFrame(videoFrame);
        }
Blijkbaar kan OBS naar NDI streamen, ik weet dat OBS wel een specifiek window kan streamen: https://obsproject.com/forum/resources/obs-ndi-newtek-ndi™-integration-into-obs-studio.528/

Voor het transparancy verhaal, kun je niet zelf de "rest" pixels transparant maken? Door bijvoorbeeld eerst alle pixels in de texture op een opacity van 0 te zetten?

[ Voor 90% gewijzigd door Sandor_Clegane op 19-08-2019 07:07 ]

Less alienation, more cooperation.


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 25-05 11:39
OBS kan geen transparent window streamen.

We gebruiken normaal OBS in de volgende manier. Mijn applicatie genereert overlay graphics die op een spel geprojecteerd moeten worden. We draaien het spel in windowed mode en zetten mijn transparent window er bovenop. Dan streamen we met OBS een deel van de desktop, en zo streamen we dus zowel het spel als de overlay tegelijk. Normaal gezien zou je in OBS het spel en de overlay als aparte scènes kunnen doen maar ons transparent window kan niet opgenomen worden met OBS.


Wat ik nu wil veranderen is om de overlay graphics en het spel apart te draaien. Het spel wordt dan op 1 (of meerdere) PCs gedraaid en de overlay graphics op een andere. Dan wordt alles via NDI over het netwerk gestuurd, en uiteindelijk via een hardware mixer (tricaster) over elkaar gelegd.

Hiervoor moet de overlay met semi transparency werken, zodat hij uiteindelijk over de andere NDI streams kan.

Zoals ik al zei werkt het prima als ik de bitmap via RenderTargetBitmap genereer, alleen is dat helaas te langzaam.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 09:51

Haan

dotnetter

Ik ben totaal niet thuis in deze materie, maar wat ik denk ik zou doen, is in de .NET source duiken van de RenderTargetBitmap method om te achterhalen hoe deze precies werkt. Wellicht is het te reverse engineeren op een manier die sneller werkt (doordat je misschien bepaalde zaken die gedaan worden kunt skippen of efficiënter implementeren).

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • boe2
  • Registratie: November 2002
  • Niet online

boe2

'-')/

*knip*

[ Voor 120% gewijzigd door boe2 op 19-08-2019 11:56 ]

'Multiple exclamation marks,' he went on, shaking his head, 'are a sure sign of a diseased mind.' - Pratchett.


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 25-05 11:39
Misschien begrijp ik je verkeerd, maar is dit niet andersom? Je lijkt een renderer te hebben (die verder los staat van WPF?) waarmee je een WriteableBitmap vult, en die bitmap toon je daarna in een Canvas.

Ik wil het andersom: ik heb een Grid (of wat voor container element dan ook) met een aantal WPF elements (borders, itemcontrols, etc). Die container wil ik naar een bitmap renderen.
Haan schreef op maandag 19 augustus 2019 @ 09:52:
Ik ben totaal niet thuis in deze materie, maar wat ik denk ik zou doen, is in de .NET source duiken van de RenderTargetBitmap method om te achterhalen hoe deze precies werkt. Wellicht is het te reverse engineeren op een manier die sneller werkt (doordat je misschien bepaalde zaken die gedaan worden kunt skippen of efficiënter implementeren).
Even doorheen gebladerd maar het gaat al vrij rap richting de interne WPF renderer en daar ga ik toch echt geen verbetering vinden ben ik bang.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Gomez12
  • Registratie: Maart 2001
  • Laatst online: 17-10-2023
Tja, ik denk dat je keuzes moet maken...

Transparant bestaat er niet voor een compleet gerenderd iets, tenminste ik kijk nooit tegen de binnenkant van mijn monitor aan.
Normaliter wordt simpelweg je hele scherm gerenderd en wil je delen gaan renderen dan zul je idd moeten terugvallen op software rendering / rendertargetbitmap die niet snel genoeg is.

Om probleem 2 is nog wel heen te komen door simpelweg alles te renderen en slechts jouw deel naar bitmap om te zetten...
Echter je blijft denk ik met probleem 1 zitten... Er is geen rendermodus (buiten software rendering) die transparantie laat behouden... Want er zou gewoon weinig nut voor zijn...

Acties:
  • 0 Henk 'm!

  • Knopsje
  • Registratie: November 2006
  • Laatst online: 14:59
Even outside de box gedacht, maar aangezien je met een tricaster werkt: kun je het geheel niet renderen met een solid background (groen) en vervolgens die achtergrond er uit keyen met behulp van een Chroma Key?

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 25-05 11:39
Gomez12 schreef op maandag 19 augustus 2019 @ 13:10:
Tja, ik denk dat je keuzes moet maken...

Transparant bestaat er niet voor een compleet gerenderd iets, tenminste ik kijk nooit tegen de binnenkant van mijn monitor aan.
Normaliter wordt simpelweg je hele scherm gerenderd en wil je delen gaan renderen dan zul je idd moeten terugvallen op software rendering / rendertargetbitmap die niet snel genoeg is.

Om probleem 2 is nog wel heen te komen door simpelweg alles te renderen en slechts jouw deel naar bitmap om te zetten...
Echter je blijft denk ik met probleem 1 zitten... Er is geen rendermodus (buiten software rendering) die transparantie laat behouden... Want er zou gewoon weinig nut voor zijn...
Ik geef toe dat ik hier helemaal niet in thuis ben maar ik begrijp niet waarom "transparency = software rendering". Waarom is dat perse zo? Of begrijp ik je verkeerd.

Daarnaast ben ik niet perse uit op hardware rendering, zolang het maar sneller is dan RenderTargetBitmap zodat ik iets wat richting de 60fps gaat kan halen.

Het probleem met RenderTargetBitmap is dat hij niet zozeer kopieert wat er op het scherm staat, maar daadwerkelijk de hele WPF visual tree gaat renderen. Stel dat er 800 elements op elkaar geplakt zijn, uiteindelijk is er maar 1 zichtbaar, maar toch duurt RenderTargetBitmap 800 keer langer dan een enkel element. Ik snap dat hier niet zo gemakkelijk omheen te komen is maar ik denk dat ik wel dichtbij zit met dit Screen Capture verhaal in UWP Composition. Tenzij er dus absoluut geen transparency kan maar ik heb nog steeds hoop dat het wel kan op een of andere manier.
Knopsje schreef op maandag 19 augustus 2019 @ 13:48:
Even outside de box gedacht, maar aangezien je met een tricaster werkt: kun je het geheel niet renderen met een solid background (groen) en vervolgens die achtergrond er uit keyen met behulp van een Chroma Key?
Dat is prima voor een achtergrond weg te halen maar zo krijg ik geen semi-transparency helaas. Eventueel een last resort oplossing maar ik wil eigenlijk wel gebruik maken van semi-transparente elementen.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Sandor_Clegane
  • Registratie: Januari 2012
  • Niet online

Sandor_Clegane

Fancy plans and pants to match

Chromakey is waar ik ook aan zat te denken.

Less alienation, more cooperation.


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 05-06 23:25
Misschien heb je hier wat aan....ik vermoed dat je hetzelfde probleem hebt.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • +1 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 25-05 11:39
Sorry voor de bump maar wellicht vinden mensen het interessant of wordt dit later gevonden.

Inmiddels heb ik een reply op Github gekregen, en het lijkt alsof dit gewoon had moeten werken zoals ik vanaf het begin al aan het proberen was, echter door een bug werkte de transparency niet.

Met de nieuwe W10 update is dit verholpen en werkt het wel! Zojuist getest op een VM via insider build, helaas nog even afwachten tot hij op de normale release komt.

Mijn iRacing profiel

Pagina: 1