Created
March 21, 2022 20:26
-
-
Save deccer/e030ef7446be21857968741f07f00d23 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Numerics; | |
using SixLabors.ImageSharp; | |
using SixLabors.ImageSharp.PixelFormats; | |
using Vortice.Direct3D; | |
using Vortice.Direct3D11; | |
using Vortice.DXGI; | |
using Vortice.Mathematics; | |
using Vortice.WIC; | |
using Image = SixLabors.ImageSharp.Image; | |
namespace DummyProject | |
{ | |
public sealed unsafe class GraphicsDevice : IGraphicsDevice | |
{ | |
private static readonly FeatureLevel[] _featureLevels; | |
private readonly IShaderFactory _shaderFactory; | |
private readonly IModelFactory _modelFactory; | |
private readonly IImagingFactory _imagingFactory; | |
private IntPtr _windowHandle; | |
private IDXGIFactory2 _factory; | |
private ID3D11Device1 _device; | |
private ID3D11DeviceContext1 _deviceContext; | |
private ID3D11RasterizerState _wireFrameRasterizerState; | |
private ID3D11RasterizerState _solidRasterizerState; | |
private ID3D11Buffer[] _constantBuffers; | |
private ID3D11InputLayout _inputLayout; | |
private ID3D11Buffer _vertexBuffer; | |
private ID3D11Buffer _indexBuffer; | |
private ID3D11VertexShader _vertexShader; | |
private ID3D11PixelShader _pixelShader; | |
private Viewport _viewport; | |
private Int2 _scissorRectDimensions; | |
private int _vertexCount; | |
private int _indexCount; | |
private ID3D11Texture2D _texture; | |
private ID3D11ShaderResourceView _textureSrv; | |
private ID3D11SamplerState _textureLinearSamplerState; | |
private Matrix4x4 _projectionMatrix; | |
private Matrix4x4 _viewMatrix; | |
private Matrix4x4 _worldMatrix; | |
private readonly Color4 _clearColor; | |
private IDXGISwapChain1 _swapchain; | |
private ID3D11Texture2D _backBuffer; | |
private ID3D11RenderTargetView _backBufferRtv; | |
private ID3D11Texture2D _depthStencilBuffer; | |
private ID3D11DepthStencilView _depthStencilView; | |
private bool _isSolid; | |
static GraphicsDevice() | |
{ | |
_featureLevels = new[] | |
{ | |
FeatureLevel.Level_11_0 | |
}; | |
} | |
public GraphicsDevice( | |
IShaderFactory shaderFactory, | |
IModelFactory modelFactory, | |
IImagingFactory imagingFactory) | |
{ | |
_shaderFactory = shaderFactory; | |
_modelFactory = modelFactory; | |
_imagingFactory = imagingFactory; | |
_clearColor = new Color4(0.05f, 0.05f, 0.05f, 1.0f); | |
} | |
public string CaptureWindow() | |
{ | |
_deviceContext.Flush(); | |
using var staging = CaptureTexture(_backBuffer)!; | |
var filePath = Path.ChangeExtension(Path.GetTempFileName(), ".png"); | |
_imagingFactory.ExportTextureToFile(_deviceContext, staging, filePath); | |
return filePath; | |
} | |
public void Dispose() | |
{ | |
DestroySwapchain(); | |
_vertexShader?.Dispose(); | |
_pixelShader?.Dispose(); | |
_inputLayout?.Dispose(); | |
_vertexBuffer?.Dispose(); | |
foreach (var constantBuffer in _constantBuffers) | |
{ | |
constantBuffer?.Dispose(); | |
} | |
_textureLinearSamplerState?.Dispose(); | |
_textureSrv?.Dispose(); | |
_texture?.Dispose(); | |
_wireFrameRasterizerState?.Dispose(); | |
_solidRasterizerState?.Dispose(); | |
_deviceContext?.Dispose(); | |
_device?.Dispose(); | |
_factory?.Dispose(); | |
_imagingFactory?.Dispose(); | |
} | |
public bool Initialize(IntPtr windowHandle, int width, int height) | |
{ | |
_windowHandle = windowHandle; | |
_factory = DXGI.CreateDXGIFactory1<IDXGIFactory2>(); | |
using var adapter = GetHardwareAdapter(); | |
var deviceCreationFlags = DeviceCreationFlags.BgraSupport | DeviceCreationFlags.Debug; | |
var result = D3D11.D3D11CreateDevice( | |
adapter, | |
DriverType.Unknown, | |
deviceCreationFlags, | |
_featureLevels, | |
out var tempDevice, | |
out var _featureLevel, | |
out var tempDeviceContext); | |
if (result.Failure) | |
{ | |
D3D11.D3D11CreateDevice( | |
IntPtr.Zero, | |
DriverType.Warp, | |
deviceCreationFlags, | |
_featureLevels, | |
out tempDevice, | |
out _featureLevel, | |
out tempDeviceContext); | |
} | |
_device = tempDevice.QueryInterface<ID3D11Device1>(); | |
_deviceContext = tempDeviceContext.QueryInterface<ID3D11DeviceContext1>(); | |
tempDeviceContext.Dispose(); | |
tempDevice.Dispose(); | |
_constantBuffers = new ID3D11Buffer[3]; | |
var initialData = new[] { Matrix4x4.Identity }.AsSpan(); | |
_constantBuffers[0] = _device.CreateBuffer(BindFlags.ConstantBuffer, initialData, initialData.Length * sizeof(Matrix4x4)); | |
_constantBuffers[1] = _device.CreateBuffer(BindFlags.ConstantBuffer, initialData, initialData.Length * sizeof(Matrix4x4)); | |
_constantBuffers[2] = _device.CreateBuffer(BindFlags.ConstantBuffer, initialData, initialData.Length * sizeof(Matrix4x4)); | |
CreateSwapchain(_windowHandle, width, height); | |
var vertexShaderBytes = _shaderFactory.CompileBytecodeFromFile("Shader.hlsl", "VSMain", "vs_5_0"); | |
var pixelShaderBytes = _shaderFactory.CompileBytecodeFromFile("Shader.hlsl", "PSMain", "ps_5_0"); | |
_vertexShader = _device.CreateVertexShader(vertexShaderBytes); | |
_pixelShader = _device.CreatePixelShader(pixelShaderBytes); | |
var inputLayoutDescriptor = new[] | |
{ | |
new InputElementDescription("POSITION", 0, Format.R32G32B32_Float, 0, 0), | |
new InputElementDescription("COLOR", 0, Format.R32G32B32A32_Float, 12, 0), | |
new InputElementDescription("UV", 0, Format.R32G32_Float, 28, 0) | |
}; | |
_inputLayout = _device.CreateInputLayout(inputLayoutDescriptor, vertexShaderBytes); | |
_modelFactory.CreateModelFromFile( | |
"SM_Planet.dae", | |
out var vertices, | |
out var indices); | |
_vertexBuffer = _device.CreateBuffer(BindFlags.VertexBuffer, vertices, vertices.Length * sizeof(VertexPositionColorUv)); | |
_vertexCount = vertices.Length; | |
_indexBuffer = _device.CreateBuffer(BindFlags.IndexBuffer, indices, indices.Length * sizeof(int)); | |
_indexCount = indices.Length; | |
_viewMatrix = Matrix4x4.CreateLookAt(new Vector3(0, 0, 10), Vector3.Zero, Vector3.UnitY); | |
_deviceContext.UpdateSubresource(_viewMatrix, _constantBuffers[1]); | |
_wireFrameRasterizerState = _device.CreateRasterizerState(RasterizerDescription.Wireframe); | |
_solidRasterizerState = _device.CreateRasterizerState(RasterizerDescription.CullFront); | |
_textureLinearSamplerState = _device.CreateSamplerState(SamplerDescription.LinearWrap); | |
SetTextureFromFile("T_Default_D.png"); | |
return true; | |
} | |
private float _counter; | |
public void SetSolid(bool isSolid) | |
{ | |
_isSolid = isSolid; | |
} | |
public void Zoom(float zoom) | |
{ | |
_viewMatrix = Matrix4x4.CreateLookAt(new Vector3(0, 0, 10 - zoom), Vector3.Zero, Vector3.UnitY); | |
_deviceContext.UpdateSubresource(_viewMatrix, _constantBuffers[1]); | |
} | |
public void SetTextureFromFile(string fileName) | |
{ | |
if (!File.Exists(fileName)) | |
{ | |
return; | |
} | |
var configuration = Configuration.Default; | |
configuration.PreferContiguousImageBuffers = true; | |
using var imageStream = File.Open(fileName, FileMode.Open); | |
using var rawImage = Image.Load(imageStream); | |
using var textureImage = rawImage.CloneAs<Rgba32>(configuration); | |
if (textureImage.DangerousTryGetSinglePixelMemory(out var pixels)) | |
{ | |
_texture?.Dispose(); | |
var subResource = new SubresourceData(new IntPtr(pixels.Pin().Pointer), 4 * rawImage.Width, 0); | |
var textureDescriptor = new Texture2DDescription( | |
rawImage.Width, | |
rawImage.Height, | |
Format.R8G8B8A8_UNorm, | |
1, | |
1); | |
_texture = _device.CreateTexture2D( | |
textureDescriptor, | |
new []{ subResource }); | |
_textureSrv?.Dispose(); | |
_textureSrv = _device.CreateShaderResourceView(_texture); | |
} | |
} | |
public void Draw(float speed) | |
{ | |
_counter += speed; | |
_worldMatrix = Matrix4x4.Identity * | |
Matrix4x4.CreateRotationY(MathHelper.ToRadians(_counter), Vector3.UnitY); | |
_deviceContext.UpdateSubresource(_worldMatrix, _constantBuffers[2]); | |
///// | |
_deviceContext.ClearRenderTargetView(_backBufferRtv, _clearColor); | |
_deviceContext.ClearDepthStencilView(_depthStencilView, DepthStencilClearFlags.Depth, 1.0f, 0); | |
_deviceContext.IASetPrimitiveTopology(PrimitiveTopology.TriangleList); | |
_deviceContext.IASetInputLayout(_inputLayout); | |
_deviceContext.IASetVertexBuffer(0, _vertexBuffer, sizeof(VertexPositionColorUv), 0); | |
_deviceContext.IASetIndexBuffer(_indexBuffer, Format.R32_UInt, 0); | |
_deviceContext.VSSetConstantBuffer(0, _constantBuffers[0]); | |
_deviceContext.VSSetConstantBuffer(1, _constantBuffers[1]); | |
_deviceContext.VSSetConstantBuffer(2, _constantBuffers[2]); | |
_deviceContext.VSSetShader(_vertexShader); | |
_deviceContext.PSSetSampler(0, _textureLinearSamplerState); | |
_deviceContext.PSSetShaderResource(0, _textureSrv); | |
_deviceContext.PSSetShader(_pixelShader); | |
_deviceContext.RSSetViewport(_viewport); | |
_deviceContext.RSSetScissorRect(_scissorRectDimensions.X, _scissorRectDimensions.Y); | |
_deviceContext.RSSetState(_isSolid ? _solidRasterizerState : _wireFrameRasterizerState); | |
_deviceContext.OMSetRenderTargets(_backBufferRtv, _depthStencilView); | |
_deviceContext.DrawIndexed(_indexCount, 0, 0); | |
_swapchain.Present(1, PresentFlags.None); | |
} | |
public void Resize(int width, int height) | |
{ | |
if (width > 10 && height > 10) | |
{ | |
DestroySwapchain(); | |
CreateSwapchain(_windowHandle, width, height); | |
} | |
} | |
#if NET6 | |
private ID3D11Texture2D? CaptureTexture(ID3D11Texture2D source) | |
{ | |
ID3D11Texture2D? stagingTexture; | |
#else | |
private ID3D11Texture2D CaptureTexture(ID3D11Texture2D source) | |
{ | |
ID3D11Texture2D stagingTexture; | |
#endif | |
var sourceTextureDescriptor = source.Description; | |
if (sourceTextureDescriptor.ArraySize > 1 || sourceTextureDescriptor.MipLevels > 1) | |
{ | |
return null; | |
} | |
if (sourceTextureDescriptor.SampleDescription.Count > 1) | |
{ | |
// MSAA content must be resolved before being copied to a staging texture | |
sourceTextureDescriptor.SampleDescription.Count = 1; | |
sourceTextureDescriptor.SampleDescription.Quality = 0; | |
var temp = _device.CreateTexture2D(sourceTextureDescriptor); | |
var format = sourceTextureDescriptor.Format; | |
var formatSupport = _device.CheckFormatSupport(format); | |
if ((formatSupport & FormatSupport.MultisampleResolve) == FormatSupport.None) | |
{ | |
return null; | |
} | |
for (var item = 0; item < sourceTextureDescriptor.ArraySize; ++item) | |
{ | |
for (var level = 0; level < sourceTextureDescriptor.MipLevels; ++level) | |
{ | |
int index = ID3D11Resource.CalculateSubResourceIndex( | |
level, | |
item, | |
sourceTextureDescriptor.MipLevels); | |
_deviceContext.ResolveSubresource(temp, index, source, index, format); | |
} | |
} | |
sourceTextureDescriptor.BindFlags = BindFlags.None; | |
sourceTextureDescriptor.OptionFlags &= ResourceOptionFlags.TextureCube; | |
sourceTextureDescriptor.CpuAccessFlags = CpuAccessFlags.Read; | |
sourceTextureDescriptor.Usage = ResourceUsage.Staging; | |
stagingTexture = _device.CreateTexture2D(sourceTextureDescriptor); | |
_deviceContext.CopyResource(stagingTexture, temp); | |
} | |
else if (sourceTextureDescriptor.Usage == ResourceUsage.Staging && | |
(sourceTextureDescriptor.CpuAccessFlags & CpuAccessFlags.Read) != CpuAccessFlags.None) | |
{ | |
// Handle case where the source is already a staging texture we can use directly | |
stagingTexture = source; | |
} | |
else | |
{ | |
// Otherwise, create a staging texture from the non-MSAA source | |
sourceTextureDescriptor.BindFlags = 0; | |
sourceTextureDescriptor.OptionFlags &= ResourceOptionFlags.TextureCube; | |
sourceTextureDescriptor.CpuAccessFlags = CpuAccessFlags.Read; | |
sourceTextureDescriptor.Usage = ResourceUsage.Staging; | |
stagingTexture = _device.CreateTexture2D(sourceTextureDescriptor); | |
_deviceContext.CopyResource(stagingTexture, source); | |
} | |
return stagingTexture; | |
} | |
private void CreateSwapchain(IntPtr windowHandle, int width, int height) | |
{ | |
var swapChainDescriptor = new SwapChainDescription1 | |
{ | |
Width = width, | |
Height = height, | |
Format = Format.R8G8B8A8_UNorm, | |
BufferCount = 2, | |
BufferUsage = Usage.RenderTargetOutput, | |
SampleDescription = new SampleDescription | |
{ | |
Count = 1, | |
Quality = 0 | |
}, | |
Scaling = Scaling.Stretch, | |
SwapEffect = SwapEffect.FlipDiscard, | |
AlphaMode = AlphaMode.Ignore | |
}; | |
var swapChainFullscreenDescriptor = new SwapChainFullscreenDescription | |
{ | |
Windowed = true | |
}; | |
_swapchain = _factory.CreateSwapChainForHwnd( | |
_device, | |
windowHandle, | |
swapChainDescriptor, | |
swapChainFullscreenDescriptor); | |
_factory.MakeWindowAssociation(windowHandle, WindowAssociationFlags.IgnoreAltEnter); | |
_backBuffer = _swapchain.GetBuffer<ID3D11Texture2D>(0); | |
_backBufferRtv = _device.CreateRenderTargetView(_backBuffer); | |
var depthStencilTextureDescriptor = new Texture2DDescription | |
{ | |
ArraySize = 1, | |
BindFlags = BindFlags.DepthStencil, | |
Width = width, | |
Height = height, | |
Format = Format.D24_UNorm_S8_UInt, | |
MipLevels = 1, | |
Usage = ResourceUsage.Default, | |
CpuAccessFlags = CpuAccessFlags.None, | |
SampleDescription = new SampleDescription(1, 0) | |
}; | |
_depthStencilBuffer = _device.CreateTexture2D(depthStencilTextureDescriptor); | |
_depthStencilView = _device.CreateDepthStencilView( | |
_depthStencilBuffer!, | |
new DepthStencilViewDescription( | |
_depthStencilBuffer, | |
DepthStencilViewDimension.Texture2D)); | |
_viewport = new Viewport(width, height); | |
_scissorRectDimensions = new Int2(width, height); | |
_projectionMatrix = Matrix4x4.CreatePerspectiveFieldOfView( | |
MathHelper.ToRadians(60.0f), | |
width / (float)height, | |
0.1f, | |
512.0f); | |
_deviceContext.UpdateSubresource(_projectionMatrix, _constantBuffers[0]); | |
} | |
private void DestroySwapchain() | |
{ | |
_depthStencilView?.Dispose(); | |
_depthStencilBuffer?.Dispose(); | |
_backBufferRtv?.Dispose(); | |
_backBuffer?.Dispose(); | |
_swapchain?.Dispose(); | |
} | |
private IDXGIAdapter GetHardwareAdapter() | |
{ | |
var factory6 = _factory.QueryInterface<IDXGIFactory6>(); | |
if (factory6 != null) | |
{ | |
for (var adapterIndex = 0; | |
factory6.EnumAdapterByGpuPreference( | |
adapterIndex, | |
GpuPreference.HighPerformance, | |
#if NET6 | |
out IDXGIAdapter1? adapter).Success; | |
#else | |
out IDXGIAdapter1 adapter).Success; | |
#endif | |
adapterIndex++) | |
{ | |
if (adapter == null) | |
{ | |
continue; | |
} | |
var adapterDescription = adapter.Description1; | |
if ((adapterDescription.Flags & AdapterFlags.Software) != AdapterFlags.None) | |
{ | |
adapter.Dispose(); | |
continue; | |
} | |
factory6.Dispose(); | |
return adapter; | |
} | |
factory6.Dispose(); | |
} | |
for (var adapterIndex = 0; | |
#if NET6 | |
_factory.EnumAdapters1(adapterIndex, out IDXGIAdapter1? adapter).Success; | |
#else | |
_factory.EnumAdapters1(adapterIndex, out var adapter).Success; | |
#endif | |
adapterIndex++) | |
{ | |
var adapterDescription = adapter.Description1; | |
if ((adapterDescription.Flags & AdapterFlags.Software) != AdapterFlags.None) | |
{ | |
adapter.Dispose(); | |
continue; | |
} | |
return adapter; | |
} | |
throw new InvalidOperationException("Unable to find a D3D11 adapter"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment