示例#1
0
        /// <summary>
        /// Our present hook that will grab a copy of the backbuffer when requested. Note: this supports multi-sampling (anti-aliasing)
        /// </summary>
        /// <param name="swapChainPtr"></param>
        /// <param name="syncInterval"></param>
        /// <param name="flags"></param>
        /// <returns>The HRESULT of the original method</returns>
        int PresentHook(IntPtr swapChainPtr, int syncInterval, SharpDX.DXGI.PresentFlags flags)
        {
            this.Frame();
            SwapChain swapChain = (SharpDX.DXGI.SwapChain)swapChainPtr;

            try
            {
                #region Screenshot Request
                if (this.Request != null)
                {
                    this.DebugMessage("PresentHook: Request Start");
                    DateTime startTime = DateTime.Now;
                    using (Texture2D currentRT = Texture2D.FromSwapChain <Texture2D>(swapChain, 0))
                    {
                        #region Determine region to capture
                        Rectangle captureRegion = new Rectangle(0, 0, currentRT.Description.Width, currentRT.Description.Height);

                        if (this.Request.RegionToCapture.Width > 0)
                        {
                            captureRegion = new Rectangle(this.Request.RegionToCapture.Left, this.Request.RegionToCapture.Top, this.Request.RegionToCapture.Right, this.Request.RegionToCapture.Bottom);
                        }
                        else if (this.Request.Resize.HasValue)
                        {
                            captureRegion = new Rectangle(0, 0, this.Request.Resize.Value.Width, this.Request.Resize.Value.Height);
                        }
                        #endregion

                        // Create / Recreate resources as necessary
                        EnsureResources(currentRT.Device, currentRT.Description, captureRegion, Request);

                        Texture2D sourceTexture = null;

                        // If texture is multisampled, then we can use ResolveSubresource to copy it into a non-multisampled texture
                        if (currentRT.Description.SampleDescription.Count > 1 || Request.Resize.HasValue)
                        {
                            if (Request.Resize.HasValue)
                            {
                                this.DebugMessage("PresentHook: resizing texture");
                            }
                            else
                            {
                                this.DebugMessage("PresentHook: resolving multi-sampled texture");
                            }

                            // Resolve into _resolvedRT
                            if (_resolvedRTKeyedMutex != null)
                            {
                                _resolvedRTKeyedMutex.Acquire(0, int.MaxValue);
                            }
                            currentRT.Device.ImmediateContext.ResolveSubresource(currentRT, 0, _resolvedRT, 0, _resolvedRT.Description.Format);
                            if (_resolvedRTKeyedMutex != null)
                            {
                                _resolvedRTKeyedMutex.Release(1);
                            }

                            if (Request.Resize.HasValue)
                            {
                                lock (_lock)
                                {
                                    if (_resolvedRTKeyedMutex_Dev2 != null)
                                    {
                                        _resolvedRTKeyedMutex_Dev2.Acquire(1, int.MaxValue);
                                    }
                                    _saQuad.ShaderResource   = _resolvedSharedSRV;
                                    _saQuad.RenderTargetView = _resizedRTV;
                                    _saQuad.RenderTarget     = _resizedRT;
                                    _saQuad.Render();
                                    if (_resolvedRTKeyedMutex_Dev2 != null)
                                    {
                                        _resolvedRTKeyedMutex_Dev2.Release(0);
                                    }
                                }

                                // set sourceTexture to the resized RT
                                sourceTexture = _resizedRT;
                            }
                            else
                            {
                                // Make sourceTexture be the resolved texture
                                sourceTexture = _resolvedRTShared;
                            }
                        }
                        else
                        {
                            // Copy the resource into the shared texture
                            if (_resolvedRTKeyedMutex != null)
                            {
                                _resolvedRTKeyedMutex.Acquire(0, int.MaxValue);
                            }
                            currentRT.Device.ImmediateContext.CopySubresourceRegion(currentRT, 0, null, _resolvedRT, 0);
                            if (_resolvedRTKeyedMutex != null)
                            {
                                _resolvedRTKeyedMutex.Release(1);
                            }
                            sourceTexture = _resolvedRTShared;
                        }

                        // Copy to memory and send back to host process on a background thread so that we do not cause any delay in the rendering pipeline
                        _requestCopy = this.Request.Clone(); // this.Request gets set to null, so copy the Request for use in the thread

                        // Prevent the request from being processed a second time
                        this.Request = null;

                        bool acquireLock = sourceTexture == _resolvedRTShared;

                        ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
                        {
                            // Acquire lock on second device
                            if (acquireLock && _resolvedRTKeyedMutex_Dev2 != null)
                            {
                                _resolvedRTKeyedMutex_Dev2.Acquire(1, int.MaxValue);
                            }

                            lock (_lock)
                            {
                                // Copy the subresource region, we are dealing with a flat 2D texture with no MipMapping, so 0 is the subresource index
                                sourceTexture.Device.ImmediateContext.CopySubresourceRegion(sourceTexture, 0, new ResourceRegion()
                                {
                                    Top    = captureRegion.Top,
                                    Bottom = captureRegion.Bottom,
                                    Left   = captureRegion.Left,
                                    Right  = captureRegion.Right,
                                    Front  = 0,
                                    Back   = 1 // Must be 1 or only black will be copied
                                }, _finalRT, 0, 0, 0, 0);

                                // Release lock upon shared surface on second device
                                if (acquireLock && _resolvedRTKeyedMutex_Dev2 != null)
                                {
                                    _resolvedRTKeyedMutex_Dev2.Release(0);
                                }

                                _finalRT.Device.ImmediateContext.End(_query);
                                _queryIssued = true;
                                while (!_finalRT.Device.ImmediateContext.GetData(_query).ReadBoolean())
                                {
                                    // Spin (usually no spin takes place)
                                }

                                DateTime startCopyToSystemMemory = DateTime.Now;
                                try
                                {
                                    DataBox db = default(DataBox);
                                    //Arvy added to support features required by average color capture
                                    if (_requestCopy.Format == ImageFormat.PixelData || _requestCopy.Format == ImageFormat.AverageColor)
                                    {
                                        db             = _finalRT.Device.ImmediateContext.MapSubresource(_finalRT, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.DoNotWait);
                                        _finalRTMapped = true;
                                    }
                                    _queryIssued = false;

                                    try
                                    {
                                        using (MemoryStream ms = new MemoryStream())
                                        {
                                            switch (_requestCopy.Format)
                                            {
                                            case ImageFormat.Bitmap:
                                                Texture2D.ToStream(_finalRT.Device.ImmediateContext, _finalRT, ImageFileFormat.Bmp, ms);
                                                break;

                                            case ImageFormat.Jpeg:
                                                Texture2D.ToStream(_finalRT.Device.ImmediateContext, _finalRT, ImageFileFormat.Jpg, ms);
                                                break;

                                            case ImageFormat.Png:
                                                Texture2D.ToStream(_finalRT.Device.ImmediateContext, _finalRT, ImageFileFormat.Png, ms);
                                                break;

                                            case ImageFormat.PixelData:
                                            //Arvy added to support features required by average color capture
                                            case ImageFormat.AverageColor:
                                                if (db.DataPointer != IntPtr.Zero)
                                                {
                                                    ProcessCapture(_finalRT.Description.Width, _finalRT.Description.Height, db.RowPitch, System.Drawing.Imaging.PixelFormat.Format32bppArgb, db.DataPointer, _requestCopy);
                                                }
                                                return;
                                            }
                                            ms.Position = 0;
                                            ProcessCapture(ms, _requestCopy);
                                        }
                                    }
                                    finally
                                    {
                                        this.DebugMessage("PresentHook: Copy to System Memory time: " + (DateTime.Now - startCopyToSystemMemory).ToString());
                                    }

                                    if (_finalRTMapped)
                                    {
                                        lock (_lock)
                                        {
                                            _finalRT.Device.ImmediateContext.UnmapSubresource(_finalRT, 0);
                                            _finalRTMapped = false;
                                        }
                                    }
                                }
                                catch (SharpDX.SharpDXException exc)
                                {
                                    // Catch DXGI_ERROR_WAS_STILL_DRAWING and ignore - the data isn't available yet
                                }
                            }
                        }));


                        // Note: it would be possible to capture multiple frames and process them in a background thread
                    }
                    this.DebugMessage("PresentHook: Copy BackBuffer time: " + (DateTime.Now - startTime).ToString());
                    this.DebugMessage("PresentHook: Request End");
                }
                #endregion

                #region Draw overlay (after screenshot so we don't capture overlay as well)
                if (this.Config.ShowOverlay)
                {
                    // Initialise Overlay Engine
                    if (_swapChainPointer != swapChain.NativePointer || _overlayEngine == null)
                    {
                        if (_overlayEngine != null)
                        {
                            _overlayEngine.Dispose();
                        }

                        _overlayEngine = new DX11.DXOverlayEngine();
                        _overlayEngine.Overlays.Add(new Capture.Hook.Common.Overlay
                        {
                            Elements =
                            {
                                //new Capture.Hook.Common.TextElement(new System.Drawing.Font("Times New Roman", 22)) { Text = "Test", Location = new System.Drawing.Point(200, 200), Color = System.Drawing.Color.Yellow, AntiAliased = false},
                                new Capture.Hook.Common.FramesPerSecond(new System.Drawing.Font("Arial",                               16))
                                {
                                    Location = new System.Drawing.Point(5, 5), Color = System.Drawing.Color.Red, AntiAliased = true
                                },
                                //new Capture.Hook.Common.ImageElement(@"C:\Temp\test.bmp") { Location = new System.Drawing.Point(20, 20) }
                            }
                        });
                        _overlayEngine.Initialise(swapChain);

                        _swapChainPointer = swapChain.NativePointer;
                    }
                    // Draw Overlay(s)
                    else if (_overlayEngine != null)
                    {
                        foreach (var overlay in _overlayEngine.Overlays)
                        {
                            overlay.Frame();
                        }
                        _overlayEngine.Draw();
                    }
                }
                #endregion
            }
            catch (Exception e)
            {
                // If there is an error we do not want to crash the hooked application, so swallow the exception
                this.DebugMessage("PresentHook: Exeception: " + e.GetType().FullName + ": " + e.ToString());
                //return unchecked((int)0x8000FFFF); //E_UNEXPECTED
            }

            // As always we need to call the original method, note that EasyHook will automatically skip the hook and call the original method
            // i.e. calling it here will not cause a stack overflow into this function
            return(DXGISwapChain_PresentHook.Original(swapChainPtr, syncInterval, flags));
        }
示例#2
0
        /// <summary>
        /// Our present hook that will grab a copy of the backbuffer when requested. Note: this supports multi-sampling (anti-aliasing)
        /// </summary>
        /// <param name="swapChainPtr"></param>
        /// <param name="syncInterval"></param>
        /// <param name="flags"></param>
        /// <returns>The HRESULT of the original method</returns>
        int PresentHook(IntPtr swapChainPtr, int syncInterval, SharpDX.DXGI.PresentFlags flags)
        {
            this.Frame();
            SwapChain swapChain = (SharpDX.DXGI.SwapChain)swapChainPtr;

            try
            {
                #region Screenshot Request
                if (this.Request != null)
                {
                    this.DebugMessage("PresentHook: Request Start");
                    DateTime startTime = DateTime.Now;
                    using (Texture2D texture = Texture2D.FromSwapChain <Texture2D>(swapChain, 0))
                    {
                        #region Determine region to capture
                        System.Drawing.Rectangle regionToCapture = new System.Drawing.Rectangle(0, 0, texture.Description.Width, texture.Description.Height);

                        if (this.Request.RegionToCapture.Width > 0)
                        {
                            regionToCapture = this.Request.RegionToCapture;
                        }
                        #endregion

                        var theTexture = texture;

                        // If texture is multisampled, then we can use ResolveSubresource to copy it into a non-multisampled texture
                        Texture2D textureResolved = null;
                        if (texture.Description.SampleDescription.Count > 1)
                        {
                            this.DebugMessage("PresentHook: resolving multi-sampled texture");
                            // texture is multi-sampled, lets resolve it down to single sample
                            textureResolved = new Texture2D(texture.Device, new Texture2DDescription()
                            {
                                CpuAccessFlags    = CpuAccessFlags.None,
                                Format            = texture.Description.Format,
                                Height            = texture.Description.Height,
                                Usage             = ResourceUsage.Default,
                                Width             = texture.Description.Width,
                                ArraySize         = 1,
                                SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0), // Ensure single sample
                                BindFlags         = BindFlags.None,
                                MipLevels         = 1,
                                OptionFlags       = texture.Description.OptionFlags
                            });
                            // Resolve into textureResolved
                            texture.Device.ImmediateContext.ResolveSubresource(texture, 0, textureResolved, 0, texture.Description.Format);

                            // Make "theTexture" be the resolved texture
                            theTexture = textureResolved;
                        }

                        // Create destination texture
                        Texture2D textureDest = new Texture2D(texture.Device, new Texture2DDescription()
                        {
                            CpuAccessFlags    = CpuAccessFlags.None,                      // CpuAccessFlags.Write | CpuAccessFlags.Read,
                            Format            = SharpDX.DXGI.Format.R8G8B8A8_UNorm,       // Supports BMP/PNG
                            Height            = regionToCapture.Height,
                            Usage             = ResourceUsage.Default,                    // ResourceUsage.Staging,
                            Width             = regionToCapture.Width,
                            ArraySize         = 1,                                        //texture.Description.ArraySize,
                            SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0), // texture.Description.SampleDescription,
                            BindFlags         = BindFlags.None,
                            MipLevels         = 1,                                        //texture.Description.MipLevels,
                            OptionFlags       = texture.Description.OptionFlags
                        });

                        // Copy the subresource region, we are dealing with a flat 2D texture with no MipMapping, so 0 is the subresource index
                        theTexture.Device.ImmediateContext.CopySubresourceRegion(theTexture, 0, new ResourceRegion()
                        {
                            Top    = regionToCapture.Top,
                            Bottom = regionToCapture.Bottom,
                            Left   = regionToCapture.Left,
                            Right  = regionToCapture.Right,
                            Front  = 0,
                            Back   = 1 // Must be 1 or only black will be copied
                        }, textureDest, 0, 0, 0, 0);

                        // Note: it would be possible to capture multiple frames and process them in a background thread

                        // Copy to memory and send back to host process on a background thread so that we do not cause any delay in the rendering pipeline
                        Guid requestId = this.Request.RequestId; // this.Request gets set to null, so copy the RequestId for use in the thread
                        ThreadPool.QueueUserWorkItem(delegate
                        {
                            //FileStream fs = new FileStream(@"c:\temp\temp.bmp", FileMode.Create);
                            //Texture2D.ToStream(testSubResourceCopy, ImageFileFormat.Bmp, fs);

                            DateTime startCopyToSystemMemory = DateTime.Now;
                            using (MemoryStream ms = new MemoryStream())
                            {
                                Texture2D.ToStream(textureDest.Device.ImmediateContext, textureDest, ImageFileFormat.Bmp, ms);
                                ms.Position = 0;
                                this.DebugMessage("PresentHook: Copy to System Memory time: " + (DateTime.Now - startCopyToSystemMemory).ToString());

                                DateTime startSendResponse = DateTime.Now;
                                ProcessCapture(ms, requestId);
                                this.DebugMessage("PresentHook: Send response time: " + (DateTime.Now - startSendResponse).ToString());
                            }

                            // Free the textureDest as we no longer need it.
                            textureDest.Dispose();
                            textureDest = null;
                            this.DebugMessage("PresentHook: Full Capture time: " + (DateTime.Now - startTime).ToString());
                        });

                        // Prevent the request from being processed a second time
                        this.Request = null;

                        // Make sure we free up the resolved texture if it was created
                        if (textureResolved != null)
                        {
                            textureResolved.Dispose();
                            textureResolved = null;
                        }
                    }
                    this.DebugMessage("PresentHook: Copy BackBuffer time: " + (DateTime.Now - startTime).ToString());
                    this.DebugMessage("PresentHook: Request End");
                }
                #endregion

                #region Draw overlay (after screenshot so we don't capture overlay as well)
                if (this.Config.ShowOverlay)
                {
                    // Initialise Overlay Engine
                    if (_swapChainPointer != swapChain.NativePointer || _overlayEngine == null)
                    {
                        if (_overlayEngine != null)
                        {
                            _overlayEngine.Dispose();
                        }

                        _overlayEngine = new DX11.DXOverlayEngine();
                        _overlayEngine.Overlays.Add(new Capture.Hook.Common.Overlay
                        {
                            Elements =
                            {
                                //new Capture.Hook.Common.TextElement(new System.Drawing.Font("Times New Roman", 22)) { Text = "Test", Location = new System.Drawing.Point(200, 200), Color = System.Drawing.Color.Yellow, AntiAliased = false},
                                new Capture.Hook.Common.FramesPerSecond(new System.Drawing.Font("Arial",                               16))
                                {
                                    Location = new System.Drawing.Point(5, 5), Color = System.Drawing.Color.Red, AntiAliased = true
                                }
                            }
                        });
                        _overlayEngine.Initialise(swapChain);

                        _swapChainPointer = swapChain.NativePointer;
                    }
                    // Draw Overlay(s)
                    else if (_overlayEngine != null)
                    {
                        foreach (var overlay in _overlayEngine.Overlays)
                        {
                            overlay.Frame();
                        }
                        _overlayEngine.Draw();
                    }
                }
                #endregion
            }
            catch (Exception e)
            {
                // If there is an error we do not want to crash the hooked application, so swallow the exception
                this.DebugMessage("PresentHook: Exeception: " + e.GetType().FullName + ": " + e.ToString());
                //return unchecked((int)0x8000FFFF); //E_UNEXPECTED
            }

            // As always we need to call the original method, note that EasyHook has already repatched the original method
            // so calling it here will not cause an endless recursion to this function
            swapChain.Present(syncInterval, flags);
            return(SharpDX.Result.Ok.Code);
        }
示例#3
0
        /// <summary>
        /// Our present hook that will grab a copy of the backbuffer when requested. Note: this supports multi-sampling (anti-aliasing)
        /// </summary>
        /// <param name="swapChainPtr"></param>
        /// <param name="syncInterval"></param>
        /// <param name="flags"></param>
        /// <returns>The HRESULT of the original method</returns>
        int PresentHook(IntPtr swapChainPtr, int syncInterval, SharpDX.DXGI.PresentFlags flags)
        {
            this.Frame();
            SwapChain swapChain = (SharpDX.DXGI.SwapChain)swapChainPtr;
            try
            {
                #region Screenshot Request
                if (this.Request != null)
                {
                    this.DebugMessage("PresentHook: Request Start");
                    DateTime startTime = DateTime.Now;
                    using (Texture2D texture = Texture2D.FromSwapChain<Texture2D>(swapChain, 0))
                    {
                        #region Determine region to capture
                        System.Drawing.Rectangle regionToCapture = new System.Drawing.Rectangle(0, 0, texture.Description.Width, texture.Description.Height);

                        if (this.Request.RegionToCapture.Width > 0)
                        {
                            regionToCapture = this.Request.RegionToCapture;
                        }
                        #endregion

                        var theTexture = texture;

                        // If texture is multisampled, then we can use ResolveSubresource to copy it into a non-multisampled texture
                        Texture2D textureResolved = null;
                        if (texture.Description.SampleDescription.Count > 1)
                        {
                            this.DebugMessage("PresentHook: resolving multi-sampled texture");
                            // texture is multi-sampled, lets resolve it down to single sample
                            textureResolved = new Texture2D(texture.Device, new Texture2DDescription()
                            {
                                CpuAccessFlags = CpuAccessFlags.None,
                                Format = texture.Description.Format,
                                Height = texture.Description.Height,
                                Usage = ResourceUsage.Default,
                                Width = texture.Description.Width,
                                ArraySize = 1,
                                SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0), // Ensure single sample
                                BindFlags = BindFlags.None,
                                MipLevels = 1,
                                OptionFlags = texture.Description.OptionFlags
                            });
                            // Resolve into textureResolved
                            texture.Device.ImmediateContext.ResolveSubresource(texture, 0, textureResolved, 0, texture.Description.Format);

                            // Make "theTexture" be the resolved texture
                            theTexture = textureResolved;
                        }

                        // Create destination texture
                        Texture2D textureDest = new Texture2D(texture.Device, new Texture2DDescription()
                        {
                            CpuAccessFlags = CpuAccessFlags.None,// CpuAccessFlags.Write | CpuAccessFlags.Read,
                            Format = SharpDX.DXGI.Format.R8G8B8A8_UNorm, // Supports BMP/PNG
                            Height = regionToCapture.Height,
                            Usage = ResourceUsage.Default,// ResourceUsage.Staging,
                            Width = regionToCapture.Width,
                            ArraySize = 1,//texture.Description.ArraySize,
                            SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0),// texture.Description.SampleDescription,
                            BindFlags = BindFlags.None,
                            MipLevels = 1,//texture.Description.MipLevels,
                            OptionFlags = texture.Description.OptionFlags
                        });

                        // Copy the subresource region, we are dealing with a flat 2D texture with no MipMapping, so 0 is the subresource index
                        theTexture.Device.ImmediateContext.CopySubresourceRegion(theTexture, 0, new ResourceRegion()
                        {
                            Top = regionToCapture.Top,
                            Bottom = regionToCapture.Bottom,
                            Left = regionToCapture.Left,
                            Right = regionToCapture.Right,
                            Front = 0,
                            Back = 1 // Must be 1 or only black will be copied
                        }, textureDest, 0, 0, 0, 0);

                        // Note: it would be possible to capture multiple frames and process them in a background thread

                        // Copy to memory and send back to host process on a background thread so that we do not cause any delay in the rendering pipeline
                        Guid requestId = this.Request.RequestId; // this.Request gets set to null, so copy the RequestId for use in the thread
                        ThreadPool.QueueUserWorkItem(delegate
                        {
                            //FileStream fs = new FileStream(@"c:\temp\temp.bmp", FileMode.Create);
                            //Texture2D.ToStream(testSubResourceCopy, ImageFileFormat.Bmp, fs);

                            DateTime startCopyToSystemMemory = DateTime.Now;
                            using (MemoryStream ms = new MemoryStream())
                            {
                                Texture2D.ToStream(textureDest.Device.ImmediateContext, textureDest, ImageFileFormat.Bmp, ms);
                                ms.Position = 0;
                                this.DebugMessage("PresentHook: Copy to System Memory time: " + (DateTime.Now - startCopyToSystemMemory).ToString());

                                DateTime startSendResponse = DateTime.Now;
                                ProcessCapture(ms, requestId);
                                this.DebugMessage("PresentHook: Send response time: " + (DateTime.Now - startSendResponse).ToString());
                            }

                            // Free the textureDest as we no longer need it.
                            textureDest.Dispose();
                            textureDest = null;
                            this.DebugMessage("PresentHook: Full Capture time: " + (DateTime.Now - startTime).ToString());
                        });

                        // Prevent the request from being processed a second time
                        this.Request = null;

                        // Make sure we free up the resolved texture if it was created
                        if (textureResolved != null)
                        {
                            textureResolved.Dispose();
                            textureResolved = null;
                        }
                    }
                    this.DebugMessage("PresentHook: Copy BackBuffer time: " + (DateTime.Now - startTime).ToString());
                    this.DebugMessage("PresentHook: Request End");
                }
                #endregion

#if OVERLAYENGINE
                #region Draw overlay (after screenshot so we don't capture overlay as well)
                if (this.Config.ShowOverlay)
                {
                    // Initialise Overlay Engine
                    if (_swapChainPointer != swapChain.NativePointer || _overlayEngine == null)
                    {
                        if (_overlayEngine != null)
                            _overlayEngine.Dispose();

                        _overlayEngine = new DX11.DXOverlayEngine();
                        _overlayEngine.Overlays.Add(new Capture.Hook.Common.Overlay
                        {
                            Elements =
                            {
                                //new Capture.Hook.Common.TextElement(new System.Drawing.Font("Times New Roman", 22)) { Text = "Test", Location = new System.Drawing.Point(200, 200), Color = System.Drawing.Color.Yellow, AntiAliased = false},
                                new Capture.Hook.Common.FramesPerSecond(new System.Drawing.Font("Arial", 16)) { Location = new System.Drawing.Point(5,5), Color = System.Drawing.Color.Red, AntiAliased = true }
                            }
                        });
                        _overlayEngine.Initialise(swapChain);

                        _swapChainPointer = swapChain.NativePointer;
                    }
                    // Draw Overlay(s)
                    else if (_overlayEngine != null)
                    {
                        foreach (var overlay in _overlayEngine.Overlays)
                            overlay.Frame();
                        _overlayEngine.Draw();
                    }
                }
                #endregion
#endif
            }
            catch (Exception e)
            {
                // If there is an error we do not want to crash the hooked application, so swallow the exception
                this.DebugMessage("PresentHook: Exeception: " + e.GetType().FullName + ": " + e.ToString());
                //return unchecked((int)0x8000FFFF); //E_UNEXPECTED
            }

            // As always we need to call the original method, note that EasyHook has already repatched the original method
            // so calling it here will not cause an endless recursion to this function
            swapChain.Present(syncInterval, flags);
            return SharpDX.Result.Ok.Code;
        }
示例#4
0
        /// <summary>
        /// Our present hook that will grab a copy of the backbuffer when requested. Note: this supports multi-sampling (anti-aliasing)
        /// </summary>
        /// <param name="swapChainPtr"></param>
        /// <param name="syncInterval"></param>
        /// <param name="flags"></param>
        /// <returns>The HRESULT of the original method</returns>
        int PresentHook(IntPtr swapChainPtr, int syncInterval, SharpDX.DXGI.PresentFlags flags)
        {
            this.Frame();
            SwapChain swapChain = (SharpDX.DXGI.SwapChain)swapChainPtr;
            try
            {
                #region Screenshot Request
                if (this.Request != null)
                {
                    this.DebugMessage("PresentHook: Request Start");
                    DateTime startTime = DateTime.Now;
                    using (Texture2D currentRT = Texture2D.FromSwapChain<Texture2D>(swapChain, 0))
                    {
                        #region Determine region to capture
                        Rectangle captureRegion = new Rectangle(0, 0, currentRT.Description.Width, currentRT.Description.Height);

                        if (this.Request.RegionToCapture.Width > 0)
                        {
                            captureRegion = new Rectangle(this.Request.RegionToCapture.Left, this.Request.RegionToCapture.Top, this.Request.RegionToCapture.Right, this.Request.RegionToCapture.Bottom);
                        }
                        else if (this.Request.Resize.HasValue)
                        {
                            captureRegion = new Rectangle(0, 0, this.Request.Resize.Value.Width, this.Request.Resize.Value.Height);
                        }
                        #endregion

                        // Create / Recreate resources as necessary
                        EnsureResources(currentRT.Device, currentRT.Description, captureRegion, Request);

                        Texture2D sourceTexture = null;

                        // If texture is multisampled, then we can use ResolveSubresource to copy it into a non-multisampled texture
                        if (currentRT.Description.SampleDescription.Count > 1 || Request.Resize.HasValue)
                        {
                            if (Request.Resize.HasValue)
                                this.DebugMessage("PresentHook: resizing texture");
                            else
                                this.DebugMessage("PresentHook: resolving multi-sampled texture");

                            // Resolve into _resolvedRT
                            if (_resolvedRTKeyedMutex != null)
                                _resolvedRTKeyedMutex.Acquire(0, int.MaxValue);
                            currentRT.Device.ImmediateContext.ResolveSubresource(currentRT, 0, _resolvedRT, 0, _resolvedRT.Description.Format);
                            if (_resolvedRTKeyedMutex != null)
                                _resolvedRTKeyedMutex.Release(1);

                            if (Request.Resize.HasValue)
                            {
                                lock(_lock)
                                {
                                    if (_resolvedRTKeyedMutex_Dev2 != null)
                                        _resolvedRTKeyedMutex_Dev2.Acquire(1, int.MaxValue);
                                    _saQuad.ShaderResource = _resolvedSharedSRV;
                                    _saQuad.RenderTargetView = _resizedRTV;
                                    _saQuad.RenderTarget = _resizedRT;
                                    _saQuad.Render();
                                    if (_resolvedRTKeyedMutex_Dev2 != null)
                                        _resolvedRTKeyedMutex_Dev2.Release(0);
                                }

                                // set sourceTexture to the resized RT
                                sourceTexture = _resizedRT;
                            }
                            else
                            {
                                // Make sourceTexture be the resolved texture
                                sourceTexture = _resolvedRTShared;
                            }
                        }
                        else
                        {
                            // Copy the resource into the shared texture
                            if (_resolvedRTKeyedMutex != null) _resolvedRTKeyedMutex.Acquire(0, int.MaxValue);
                            currentRT.Device.ImmediateContext.CopySubresourceRegion(currentRT, 0, null, _resolvedRT, 0);
                            if (_resolvedRTKeyedMutex != null) _resolvedRTKeyedMutex.Release(1);
                            sourceTexture = _resolvedRTShared;
                        }

                        // Copy to memory and send back to host process on a background thread so that we do not cause any delay in the rendering pipeline
                        _requestCopy = this.Request.Clone(); // this.Request gets set to null, so copy the Request for use in the thread

                        // Prevent the request from being processed a second time
                        this.Request = null;

                        bool acquireLock = sourceTexture == _resolvedRTShared;

                        ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
                        {
                            // Acquire lock on second device
                            if (acquireLock && _resolvedRTKeyedMutex_Dev2 != null)
                                _resolvedRTKeyedMutex_Dev2.Acquire(1, int.MaxValue);

                            lock (_lock)
                            {
                                // Copy the subresource region, we are dealing with a flat 2D texture with no MipMapping, so 0 is the subresource index
                                sourceTexture.Device.ImmediateContext.CopySubresourceRegion(sourceTexture, 0, new ResourceRegion()
                                {
                                    Top = captureRegion.Top,
                                    Bottom = captureRegion.Bottom,
                                    Left = captureRegion.Left,
                                    Right = captureRegion.Right,
                                    Front = 0,
                                    Back = 1 // Must be 1 or only black will be copied
                                }, _finalRT, 0, 0, 0, 0);

                                // Release lock upon shared surface on second device
                                if (acquireLock && _resolvedRTKeyedMutex_Dev2 != null)
                                    _resolvedRTKeyedMutex_Dev2.Release(0);

                                _finalRT.Device.ImmediateContext.End(_query);
                                _queryIssued = true;
                                while (!_finalRT.Device.ImmediateContext.GetData(_query).ReadBoolean())
                                {
                                    // Spin (usually no spin takes place)
                                }

                                DateTime startCopyToSystemMemory = DateTime.Now;
                                try
                                {
                                    DataBox db = default(DataBox);
                                    if (_requestCopy.Format == ImageFormat.PixelData)
                                    {
                                        db = _finalRT.Device.ImmediateContext.MapSubresource(_finalRT, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.DoNotWait);
                                        _finalRTMapped = true;
                                    }
                                    _queryIssued = false;

                                    try
                                    {
                                        using (MemoryStream ms = new MemoryStream())
                                        {
                                            switch (_requestCopy.Format)
                                            {
                                                case ImageFormat.Bitmap:
                                                    Texture2D.ToStream(_finalRT.Device.ImmediateContext, _finalRT, ImageFileFormat.Bmp, ms);
                                                    break;
                                                case ImageFormat.Jpeg:
                                                    Texture2D.ToStream(_finalRT.Device.ImmediateContext, _finalRT, ImageFileFormat.Jpg, ms);
                                                    break;
                                                case ImageFormat.Png:
                                                    Texture2D.ToStream(_finalRT.Device.ImmediateContext, _finalRT, ImageFileFormat.Png, ms);
                                                    break;
                                                case ImageFormat.PixelData:
                                                    if (db.DataPointer != IntPtr.Zero)
                                                    {
                                                        ProcessCapture(_finalRT.Description.Width, _finalRT.Description.Height, db.RowPitch, System.Drawing.Imaging.PixelFormat.Format32bppArgb, db.DataPointer, _requestCopy);
                                                    }
                                                    return;
                                            }
                                            ms.Position = 0;
                                            ProcessCapture(ms, _requestCopy);
                                        }
                                    }
                                    finally
                                    {
                                        this.DebugMessage("PresentHook: Copy to System Memory time: " + (DateTime.Now - startCopyToSystemMemory).ToString());
                                    }

                                    if (_finalRTMapped)
                                    {
                                        lock (_lock)
                                        {
                                            _finalRT.Device.ImmediateContext.UnmapSubresource(_finalRT, 0);
                                            _finalRTMapped = false;
                                        }
                                    }
                                }
                                catch (SharpDX.SharpDXException exc)
                                {
                                    // Catch DXGI_ERROR_WAS_STILL_DRAWING and ignore - the data isn't available yet
                                }
                            }
                        }));
                        

                        // Note: it would be possible to capture multiple frames and process them in a background thread
                    }
                    this.DebugMessage("PresentHook: Copy BackBuffer time: " + (DateTime.Now - startTime).ToString());
                    this.DebugMessage("PresentHook: Request End");
                }
                #endregion

                #region Draw overlay (after screenshot so we don't capture overlay as well)
                if (this.Config.ShowOverlay)
                {
                    // Initialise Overlay Engine
                    if (_swapChainPointer != swapChain.NativePointer || _overlayEngine == null)
                    {
                        if (_overlayEngine != null)
                            _overlayEngine.Dispose();

                        _overlayEngine = new DX11.DXOverlayEngine();
                        _overlayEngine.Overlays.Add(new Capture.Hook.Common.Overlay
                        {
                            Elements =
                            {
                                //new Capture.Hook.Common.TextElement(new System.Drawing.Font("Times New Roman", 22)) { Text = "Test", Location = new System.Drawing.Point(200, 200), Color = System.Drawing.Color.Yellow, AntiAliased = false},
                                new Capture.Hook.Common.FramesPerSecond(new System.Drawing.Font("Arial", 16)) { Location = new System.Drawing.Point(5,5), Color = System.Drawing.Color.Red, AntiAliased = true },
                                //new Capture.Hook.Common.ImageElement(@"C:\Temp\test.bmp") { Location = new System.Drawing.Point(20, 20) }
                            }
                        });
                        _overlayEngine.Initialise(swapChain);

                        _swapChainPointer = swapChain.NativePointer;
                    }
                    // Draw Overlay(s)
                    else if (_overlayEngine != null)
                    {
                        foreach (var overlay in _overlayEngine.Overlays)
                            overlay.Frame();
                        _overlayEngine.Draw();
                    }
                }
                #endregion
            }
            catch (Exception e)
            {
                // If there is an error we do not want to crash the hooked application, so swallow the exception
                this.DebugMessage("PresentHook: Exeception: " + e.GetType().FullName + ": " + e.ToString());
                //return unchecked((int)0x8000FFFF); //E_UNEXPECTED
            }

            // As always we need to call the original method, note that EasyHook will automatically skip the hook and call the original method
            // i.e. calling it here will not cause a stack overflow into this function
            return DXGISwapChain_PresentHook.Original(swapChainPtr, syncInterval, flags);
        }