private void InitializeDevice() { device = new Dx11.Device(Dx11.DriverType.Hardware, Dx11.DeviceCreationFlags.None); for (int i = 0; i < 8; i++) { qual = device.CheckMultisampleQualityLevels(Dxgi.Format.R8G8B8A8_UNorm, i); if (qual > 0) { count = i; } } factor = new Dxgi.Factory(); swapChain = new Dxgi.SwapChain(factor, device, new Dxgi.SwapChainDescription() { BufferCount = 1, OutputHandle = this.Handle, IsWindowed = true, SampleDescription = new Dxgi.SampleDescription() { Count = count, Quality = qual - 1, }, ModeDescription = new Dxgi.ModeDescription() { Width = ClientSize.Width, Height = ClientSize.Height, RefreshRate = new Rational(60, 1), Format = Dxgi.Format.R8G8B8A8_UNorm, }, Usage = Dxgi.Usage.RenderTargetOutput }); InitializeRenderTarget(); InitializeDepthStencil(); InitializeViewport(); LoadContent(); InitializeInputDevice(); }
static void CreateDeviceAndSwapChain( System.Windows.Forms.Form form, out SlimDX.Direct3D11.Device device, out SlimDX.DXGI.SwapChain swapChain ) { SlimDX.Direct3D11.Device.CreateWithSwapChain( SlimDX.Direct3D11.DriverType.Hardware, SlimDX.Direct3D11.DeviceCreationFlags.None, new SlimDX.DXGI.SwapChainDescription { BufferCount = 1, OutputHandle = form.Handle, IsWindowed = true, SampleDescription = new SlimDX.DXGI.SampleDescription { Count = 1, Quality = 0 }, ModeDescription = new SlimDX.DXGI.ModeDescription { Width = form.ClientSize.Width, Height = form.ClientSize.Height, RefreshRate = new SlimDX.Rational(60, 1), Format = SlimDX.DXGI.Format.R8G8B8A8_UNorm }, Usage = SlimDX.DXGI.Usage.RenderTargetOutput }, out device, out swapChain ); }
/// <summary> /// Hooked to allow resizing a texture/surface that is reused. Currently not in use as we create the texture for each request /// to support different sizes each time (as we use DirectX to copy only the region we are after rather than the entire backbuffer) /// </summary> /// <param name="swapChainPtr"></param> /// <param name="newTargetParameters"></param> /// <returns></returns> int ResizeTargetHook(IntPtr swapChainPtr, ref DXGI.DXGI_MODE_DESC newTargetParameters) { using (SlimDX.DXGI.SwapChain swapChain = SlimDX.DXGI.SwapChain.FromPointer(swapChainPtr)) { // This version creates a new texture for each request so there is nothing to resize. // IF the size of the texture is known each time, we could create it once, and then possibly need to resize it here if (bitmap != null) { bitmap.Dispose(); bitmap = null; } return(swapChain.ResizeTarget( new SlimDX.DXGI.ModeDescription() { Format = newTargetParameters.Format, Height = newTargetParameters.Height, RefreshRate = newTargetParameters.RefreshRate, Scaling = newTargetParameters.Scaling, ScanlineOrdering = newTargetParameters.ScanlineOrdering, Width = newTargetParameters.Width } ).Code); } }
public override void Hook() { this.DebugMessage("Hook: Begin"); // Determine method addresses in Direct3D10.Device, and DXGI.SwapChain if (_d3d10VTblAddresses == null) { _d3d10VTblAddresses = new List <IntPtr>(); _dxgiSwapChainVTblAddresses = new List <IntPtr>(); this.DebugMessage("Hook: Before device creation"); using (SlimDX.DXGI.Factory factory = new SlimDX.DXGI.Factory()) { using (SlimDX.Direct3D10.Device device = new Device(factory.GetAdapter(0), DriverType.Hardware, DeviceCreationFlags.None)) { this.DebugMessage("Hook: Device created"); _d3d10VTblAddresses.AddRange(GetVTblAddresses(device.ComPointer, D3D10_DEVICE_METHOD_COUNT)); using (SlimDX.Windows.RenderForm renderForm = new SlimDX.Windows.RenderForm()) { using (SlimDX.DXGI.SwapChain sc = new SlimDX.DXGI.SwapChain(factory, device, DXGI.CreateSwapChainDescription(renderForm.Handle))) { _dxgiSwapChainVTblAddresses.AddRange(GetVTblAddresses(sc.ComPointer, DXGI.DXGI_SWAPCHAIN_METHOD_COUNT)); } } } } } // We will capture the backbuffer here DXGISwapChain_PresentHook = LocalHook.Create( _dxgiSwapChainVTblAddresses[(int)DXGI.DXGISwapChainVTbl.Present], new DXGISwapChain_PresentDelegate(PresentHook), this); // We will capture target/window resizes here DXGISwapChain_ResizeTargetHook = LocalHook.Create( _dxgiSwapChainVTblAddresses[(int)DXGI.DXGISwapChainVTbl.ResizeTarget], new DXGISwapChain_ResizeTargetDelegate(ResizeTargetHook), this); /* * Don't forget that all hooks will start deactivated... * The following ensures that all threads are intercepted: * Note: you must do this for each hook. */ DXGISwapChain_PresentHook.ThreadACL.SetExclusiveACL(new Int32[1]); DXGISwapChain_ResizeTargetHook.ThreadACL.SetExclusiveACL(new Int32[1]); }
private void DestroySwapChain() { if (_swapChain != null) { if (_isFullScreen) { _swapChain.SetFullScreenState(false, null); } if (_renderer != null) { _renderer.Resources.RemoveTrackedObject(_swapChain.ComPointer); } _swapChain.Dispose(); _swapChain = null; } }
private void Destroy() { mOffScreenWindow?.Close(); mSwapChain?.Dispose(); mPixelShader?.Dispose(); mVertexShader?.Dispose(); mVertexBuffer?.Dispose(); mVertices?.Dispose(); mRenderTarget?.Dispose(); mOffScreenWindow = null; mSwapChain = null; mPixelShader = null; mVertexShader = null; mVertexBuffer = null; mVertices = null; mRenderTarget = null; }
/// <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); } }
private void CreateSwapChain(IntPtr windowHandle, PresentationParameters presentParams) { DXGI.Format surfaceFormat = D3D10Helper.ToD3DSurfaceFormat(presentParams.BackBufferFormat); switch (presentParams.PresentInterval) { case PresentInterval.Immediate: _interval = 0; break; case PresentInterval.One: _interval = 1; break; case PresentInterval.Two: _interval = 2; break; } DXGI.SampleDescription sampleDesc = new DXGI.SampleDescription(1, 0); bool msEnabled = false; if (presentParams.MultiSampleCount > 1) { sampleDesc.Count = presentParams.MultiSampleCount; int q = _graphicsDevice.CheckMultisampleQualityLevels(surfaceFormat, presentParams.MultiSampleCount); if (presentParams.MultiSampleQuality < q) { sampleDesc.Quality = presentParams.MultiSampleQuality; } else { sampleDesc.Quality = q - 1; } msEnabled = true; } DXGI.SwapEffect swap; if (presentParams.RenderTargetUsage == RenderTargetUsage.PlatformDefault) { if (msEnabled) { swap = DXGI.SwapEffect.Discard; } else { swap = DXGI.SwapEffect.Sequential; } } else if (presentParams.RenderTargetUsage == RenderTargetUsage.DiscardContents) { swap = DXGI.SwapEffect.Discard; } else { swap = DXGI.SwapEffect.Sequential; } DXGI.ModeDescription modeDesc = new DXGI.ModeDescription(presentParams.BackBufferWidth, presentParams.BackBufferHeight, new SDX.Rational(60, 1), surfaceFormat); modeDesc.Scaling = DXGI.DisplayModeScaling.Stretched; _swapChain = new DXGI.SwapChain(_factory, _graphicsDevice, new DXGI.SwapChainDescription { BufferCount = 1, Flags = DXGI.SwapChainFlags.AllowModeSwitch, IsWindowed = true, ModeDescription = modeDesc, OutputHandle = windowHandle, SampleDescription = sampleDesc, SwapEffect = swap, Usage = DXGI.Usage.RenderTargetOutput }); //Add to tracker _renderer.Resources.AddTrackedObject(_swapChain.ComPointer, this); _factory.SetWindowAssociation(windowHandle, DXGI.WindowAssociationFlags.IgnoreAll | DXGI.WindowAssociationFlags.IgnoreAltEnter); }
public override void Hook() { this.DebugMessage("Hook: Begin"); // Determine method addresses in Direct3D10.Device, and DXGI.SwapChain if (_d3d10VTblAddresses == null) { _d3d10VTblAddresses = new List<IntPtr>(); _dxgiSwapChainVTblAddresses = new List<IntPtr>(); this.DebugMessage("Hook: Before device creation"); using (SlimDX.DXGI.Factory factory = new SlimDX.DXGI.Factory()) { using (SlimDX.Direct3D10.Device device = new Device(factory.GetAdapter(0), DriverType.Hardware, DeviceCreationFlags.None)) { this.DebugMessage("Hook: Device created"); _d3d10VTblAddresses.AddRange(GetVTblAddresses(device.ComPointer, D3D10_DEVICE_METHOD_COUNT)); using (SlimDX.Windows.RenderForm renderForm = new SlimDX.Windows.RenderForm()) { using (SlimDX.DXGI.SwapChain sc = new SlimDX.DXGI.SwapChain(factory, device, DXGI.CreateSwapChainDescription(renderForm.Handle))) { _dxgiSwapChainVTblAddresses.AddRange(GetVTblAddresses(sc.ComPointer, DXGI.DXGI_SWAPCHAIN_METHOD_COUNT)); } } } } } // We will capture the backbuffer here DXGISwapChain_PresentHook = LocalHook.Create( _dxgiSwapChainVTblAddresses[(int)DXGI.DXGISwapChainVTbl.Present], new DXGISwapChain_PresentDelegate(PresentHook), this); // We will capture target/window resizes here DXGISwapChain_ResizeTargetHook = LocalHook.Create( _dxgiSwapChainVTblAddresses[(int)DXGI.DXGISwapChainVTbl.ResizeTarget], new DXGISwapChain_ResizeTargetDelegate(ResizeTargetHook), this); /* * Don't forget that all hooks will start deactivated... * The following ensures that all threads are intercepted: * Note: you must do this for each hook. */ DXGISwapChain_PresentHook.ThreadACL.SetExclusiveACL(new Int32[1]); DXGISwapChain_ResizeTargetHook.ThreadACL.SetExclusiveACL(new Int32[1]); }
private void CreateView(int width, int height) { if (width <= 0) { throw new ArgumentOutOfRangeException(nameof(width)); } if (height <= 0) { throw new ArgumentOutOfRangeException(nameof(height)); } Destroy(); mWidth = width; mHeight = height; Execution.ExecuteOnUiThread(() => { mOffScreenWindow = new Form(); mOffScreenWindow.Left = -65535; mOffScreenWindow.Top = -65535; mOffScreenWindow.FormBorderStyle = FormBorderStyle.None; mOffScreenWindow.Width = 1; mOffScreenWindow.Height = 1; mOffScreenWindow.ShowInTaskbar = false; mOffScreenWindow.Show(); mOffScreenWindow.Hide(); }); var description = new SwapChainDescription { BufferCount = 1, IsWindowed = true, ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format.B8G8R8A8_UNorm), SampleDescription = new SampleDescription(1, 0), Usage = Usage.RenderTargetOutput | Usage.Shared, OutputHandle = mOffScreenWindow.Handle }; mSwapChain = mDevice.CreateSwapChain(description); mPixelShader = new PixelShader(Device, Bytecode); mVertexShader = new VertexShader(Device, mVertexShaderBytecode); mVertexBuffer = CreateVertexBuffer(out mVertices); Execution.ExecuteOnUiThread(() => { mOffScreenWindow.Width = mWidth; mOffScreenWindow.Height = mHeight; }); mSwapChain.ResizeBuffers(1, mWidth, mHeight, Format.B8G8R8A8_UNorm, SwapChainFlags.None); Device.ImmediateContext.Rasterizer.SetViewports(new Viewport(0.0f, 0.0f, mWidth, mHeight)); Device.ImmediateContext.Rasterizer.State = RasterizerState.FromDescription(Device, new RasterizerStateDescription { CullMode = CullMode.None, FillMode = FillMode.Solid }); mRenderTarget = new HardwareRenderTarget(mDevice, mSwapChain); }