void game_FrameEnd(object sender, EventArgs e) { SlimDX.DXGI.PresentFlags flags = SlimDX.DXGI.PresentFlags.None; if (renderingOccluded) { flags = SlimDX.DXGI.PresentFlags.Test; } else { flags = CurrentSettings.Direct3D10.PresentFlags; } Result result = Direct3D10.SwapChain.Present(CurrentSettings.Direct3D10.SyncInterval, flags); if (result == SlimDX.DXGI.ResultCode.Occluded) { renderingOccluded = true; } else if (result == SlimDX.DXGI.ResultCode.DeviceReset) { ResetDevice(); } else { renderingOccluded = false; } }
/// <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, SlimDX.DXGI.PresentFlags flags) { if (swapChainPtr != _swapChainPointer) { _swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr); } SwapChain swapChain = _swapChain; //using (SlimDX.DXGI.SwapChain swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr)) { try { #region Screenshot Request if (this.Request != null) { try { this.DebugMessage("PresentHook: Request Start"); DateTime startTime = DateTime.Now; using (Texture2D texture = Texture2D.FromSwapChain <SlimDX.Direct3D10.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 SlimDX.DXGI.SampleDescription(1, 0), // Ensure single sample BindFlags = BindFlags.None, MipLevels = 1, OptionFlags = texture.Description.OptionFlags }); // Resolve into textureResolved texture.Device.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 = SlimDX.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 SlimDX.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.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, ImageFileFormat.Bmp, ms); ms.Position = 0; this.DebugMessage("PresentHook: Copy to System Memory time: " + (DateTime.Now - startCopyToSystemMemory).ToString()); DateTime startSendResponse = DateTime.Now; SendResponse(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()); }); // 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"); } finally { // Prevent the request from being processed a second time this.Request = null; } } #endregion #region Example: Draw overlay (after screenshot so we don't capture overlay as well) if (this.ShowOverlay) { using (Texture2D texture = Texture2D.FromSwapChain <SlimDX.Direct3D10.Texture2D>(swapChain, 0)) { if (_lastFrame != null) { FontDescription fd = new SlimDX.Direct3D10.FontDescription() { Height = 16, FaceName = "Times New Roman", IsItalic = false, Width = 0, MipLevels = 1, CharacterSet = SlimDX.Direct3D10.FontCharacterSet.Default, Precision = SlimDX.Direct3D10.FontPrecision.Default, Quality = SlimDX.Direct3D10.FontQuality.Antialiased, PitchAndFamily = FontPitchAndFamily.Default | FontPitchAndFamily.DontCare }; using (Font font = new Font(texture.Device, fd)) { DrawText(font, new Vector2(100, 100), String.Format("{0}", DateTime.Now), new Color4(System.Drawing.Color.Red)); } } _lastFrame = DateTime.Now; } } #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.Message); } // 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 return(swapChain.Present(syncInterval, flags).Code); } }
/// <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, SlimDX.DXGI.PresentFlags flags) { using (SlimDX.DXGI.SwapChain swapChain = SlimDX.DXGI.SwapChain.FromPointer(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 SlimDX.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 = SlimDX.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 SlimDX.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; SendResponse(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 TODO: Draw overlay (after screenshot so we don't capture overlay as well) // Note: Direct3D 11 doesn't have font support, so I believe the approach is to use // a Direct3D 10.1 device with Direct2d, render to a texture, and then blend // this into the Direct3D 11 backbuffer - hmm sounds like fun. // http://forums.create.msdn.com/forums/t/38961.aspx // http://www.gamedev.net/topic/547920-how-to-use-d2d-with-d3d11/ #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.Message); //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 return(swapChain.Present(syncInterval, flags).Code); } }
/// <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, SlimDX.DXGI.PresentFlags flags) { try { if (_swapChain == null) { _swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr); } SwapChain swapChain = _swapChain; { CheckAuto(); UpdateFPS(); try { #region Screenshot Request if (this.Request != null) { try { this.DebugMessage("PresentHook: Request Start"); LastRequestTime = DateTime.Now; DateTime startTime = DateTime.Now; using (Texture2D texture = Texture2D.FromSwapChain <SlimDX.Direct3D10.Texture2D>(swapChain, 0)) { #region Determine region to capture System.Drawing.Rectangle regionToCapture = new System.Drawing.Rectangle(0, 0, texture.Description.Width, texture.Description.Height); System.Drawing.Rectangle regionToCaptureDest = new System.Drawing.Rectangle(0, 0, texture.Description.Width, texture.Description.Height); DebugMessage("from " + regionToCapture.ToString() + " to " + regionToCaptureDest.ToString()); //DebugMessage("Flags: "+flags.ToString()); //if (this.Request.RegionToCapture.Width > 0) //{ // regionToCapture = this.Request.RegionToCapture; //} #endregion ////start old //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 SlimDX.DXGI.SampleDescription(1, 0), // Ensure single sample // BindFlags = BindFlags.None, // MipLevels = 1, // OptionFlags = texture.Description.OptionFlags // }); // // Resolve into textureResolved // texture.Device.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 = SlimDX.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 SlimDX.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.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); ////end old var theTexture = texture; DebugMessage(texture.Description.Format.ToString()); // 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 SlimDX.DXGI.SampleDescription(1, 0), // Ensure single sample BindFlags = BindFlags.None, MipLevels = 1, OptionFlags = texture.Description.OptionFlags }); // Resolve into textureResolved texture.Device.ResolveSubresource(texture, 0, textureResolved, 0, texture.Description.Format); // Make "theTexture" be the resolved texture theTexture = textureResolved; } //SlimDX.Direct3D10.Device d2 = new SlimDX.Direct3D10_1.Device1(DriverType.Hardware, SlimDX.Direct3D10.DeviceCreationFlags.None, FeatureLevel.Level_10_1); // Create destination texture if (textureDest == null) { textureDest = new Texture2D(texture.Device, new Texture2DDescription() { CpuAccessFlags = CpuAccessFlags.Read, //CpuAccessFlags.Read, Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm, // Supports BMP/PNG Height = regionToCaptureDest.Height, Usage = ResourceUsage.Staging, // ResourceUsage.Default, Width = regionToCaptureDest.Width, ArraySize = 1, //texture.Description.ArraySize, SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0), // texture.Description.SampleDescription, BindFlags = BindFlags.None, MipLevels = 1, //texture.Description.MipLevels, OptionFlags = texture.Description.OptionFlags //| ResourceOptionFlags.GdiCompatible }); } Texture2D theTexture2 = new Texture2D(texture.Device, new Texture2DDescription() { CpuAccessFlags = CpuAccessFlags.None, Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm,//texture.Description.Format, Height = regionToCaptureDest.Height, Usage = ResourceUsage.Default, Width = regionToCaptureDest.Width, ArraySize = 1, SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0), // Ensure single sample BindFlags = BindFlags.None, MipLevels = 1, OptionFlags = texture.Description.OptionFlags //| ResourceOptionFlags.GdiCompatible }); Result r = SlimDX.Direct3D10.Resource.LoadTextureFromTexture(theTexture, theTexture2, new TextureLoadInformation() { SourceRegion = 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 }, DestinationRegion = new ResourceRegion() { Top = regionToCaptureDest.Top, Bottom = regionToCaptureDest.Bottom, Left = regionToCaptureDest.Left, Right = regionToCaptureDest.Right, Front = 0, Back = 1 // Must be 1 or only black will be copied }, ElementCount = 1, Filter = FilterFlags.Linear, FirstDestinationElement = 0, FirstDestinationMip = 0, FirstSourceElement = 0, FirstSourceMip = 0, MipCount = 1, MipFilter = FilterFlags.Linear }); DebugMessage("Result: " + r.ToString()); // Copy the subresource region, we are dealing with a flat 2D texture with no MipMapping, so 0 is the subresource index theTexture.Device.CopySubresourceRegion(theTexture2, 0, new ResourceRegion() { Top = regionToCaptureDest.Top, Bottom = regionToCaptureDest.Bottom, Left = regionToCaptureDest.Left, Right = regionToCaptureDest.Right, Front = 0, Back = 1 // Must be 1 or only black will be copied }, textureDest, 0, 0, 0, 0); theTexture.Device.CopyResource(theTexture, textureDest); #region copy f*****g texture to f*****g bitmap and save it DataRectangle dr = textureDest.Map(0, MapMode.Read, SlimDX.Direct3D10.MapFlags.None); IntPtr data = dr.Data.DataPointer; int width = textureDest.Description.Width; int height = textureDest.Description.Height; //WriteableBitmap wbmap = new WriteableBitmap(width, height, 300, 300, PixelFormats.Bgra32, null); //CopyMemory(wbmap.BackBuffer, data, (uint)(width * height * 4)); if (bitmap == null) { bitmap = new Bitmap(width, height, PixelFormat.Format32bppRgb); dd = new byte[width * height * 4]; } Marshal.Copy(data, dd, 0, dd.Length); textureDest.Unmap(0); for (int i = 0; i < dd.Length; i += 4) { byte tmp = dd[i]; dd[i] = dd[i + 2]; dd[i + 2] = tmp; } BitmapData bd = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); Marshal.Copy(dd, 0, bd.Scan0, dd.Length); //CopyMemory(bd.Scan0, data, (uint)(width * height * 4)); bitmap.UnlockBits(bd); //textureDest.Unmap(0); //FileStream stream = new FileStream(@"c:\temp\new.png", FileMode.Create); //PngBitmapEncoder encoder = new PngBitmapEncoder(); ////TextBlock myTextBlock = new TextBlock(); ////myTextBlock.Text = "Codec Author is: " + encoder.CodecInfo.Author.ToString(); //encoder.Interlace = PngInterlaceOption.On; //encoder.Frames.Add(BitmapFrame.Create(wbmap)); //encoder.Save(stream); SaveFile(); #endregion //Texture2D.ToFile(textureDest, ImageFileFormat.Png, @"c:\temp\dx10.png"); //Texture2D.ToFile(textureDest, GetImageFileFormat(this.Request.Format), this.Request.FileName); // 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"); } finally { // Prevent the request from being processed a second time this.Request = null; } } #endregion #region Draw the f*****g overlay using (Texture2D texture = Texture2D.FromSwapChain <SlimDX.Direct3D10.Texture2D>(swapChain, 0)) { DrawOverlay(texture); //DrawSprite(texture.Device); } #endregion #region Example: Draw overlay (after screenshot so we don't capture overlay as well) //if (this.ShowOverlay) //{ // using (Texture2D texture = Texture2D.FromSwapChain<SlimDX.Direct3D10.Texture2D>(swapChain, 0)) // { // if (_lastFrame != null) // { // FontDescription fd = new SlimDX.Direct3D10.FontDescription() // { // Height = 16, // FaceName = "Times New Roman", // IsItalic = false, // Width = 0, // MipLevels = 1, // CharacterSet = SlimDX.Direct3D10.FontCharacterSet.Default, // Precision = SlimDX.Direct3D10.FontPrecision.Default, // Quality = SlimDX.Direct3D10.FontQuality.Antialiased, // PitchAndFamily = FontPitchAndFamily.Default | FontPitchAndFamily.DontCare // }; // using (Font font = new Font(texture.Device, fd)) // { // DrawText(font, new Vector2(100, 100), String.Format("{0}", DateTime.Now), new Color4(System.Drawing.Color.Red.R, System.Drawing.Color.Red.G, System.Drawing.Color.Red.B, System.Drawing.Color.Red.A)); // } // } // _lastFrame = DateTime.Now; // } //} #endregion } catch (Exception e) { // If there is an error we do not want to crash the hooked application, so swallow the exception this.DebugMessage(e.ToString()); } // 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 return(swapChain.Present(syncInterval, flags).Code); } } catch (Exception ex) { DebugMessage(ex.ToString()); return(System.Runtime.InteropServices.Marshal.GetHRForException(ex)); } }