Render basic geometry

Added GraphicsContext, SwapChain, Buffer, VertexBuffer, IndexBuffer and a basic DX11 implementation.
This commit is contained in:
Simon Lübeß
2020-11-25 13:03:40 +01:00
parent 0237e0b38d
commit 5954fbc411
20 changed files with 1219 additions and 180 deletions
+181 -3
View File
@@ -2,6 +2,12 @@ using System;
using GlitchyEngine.Events; using GlitchyEngine.Events;
using GlitchyEngine.Platform.DX11; using GlitchyEngine.Platform.DX11;
using GlitchyEngine.ImGui; using GlitchyEngine.ImGui;
using GlitchyEngine.Math;
using DirectX.D3D11;
using DirectX.Common;
using DirectX.D3DCompiler;
using System.Diagnostics;
using GlitchyEngine.Renderer;
namespace GlitchyEngine namespace GlitchyEngine
{ {
@@ -34,6 +40,156 @@ namespace GlitchyEngine
_imGuiLayer = new ImGuiLayer(); _imGuiLayer = new ImGuiLayer();
PushOverlay(_imGuiLayer); PushOverlay(_imGuiLayer);
MakeTestTriangle();
}
struct VertexColor : IVertexData
{
public Vector3 Position;
public Color Color;
public this() => this = default;
public this(Vector3 pos, Color color)
{
Position = pos;
Color = color;
}
//public static readonly InputElementDescription[] InputLayout ~ delete _;
public static readonly VertexLayout VertexLayout ~ delete _;
public static VertexLayout IVertexData.VertexLayout => VertexLayout;
static this()
{
//VertexLayout = new VertexLayout();
}
}
VertexBuffer<VertexColor> _vertexBuffer ~ delete _;
IndexBuffer _indexBuffer ~ delete _;
ID3D11VertexShader* _vertexShader ~ _?.Release();
ID3D11PixelShader* _pixelShader ~ _?.Release();
ID3D11InputLayout* _inputLayout ~ _?.Release();
ID3D11RasterizerState* _rasterizerState ~ _?.Release();
private Vector3 CircleCoord(float angle)
{
return .(Math.Cos(angle), Math.Sin(angle), 0);
}
private void MakeTestTriangle()
{
// Compile vertex shader
ID3DBlob* vsCode = null;
ID3DBlob* errorBlob = null;
var result = D3DCompiler.D3DCompileFromFile("content\\basicShader.hlsl".ToScopedNativeWChar!(), null, .StandardInclude, "VS", "vs_5_0", .Debug, .None, &vsCode, &errorBlob);
if(result.Failed || errorBlob != null)
{
Debug.Write("ERROR: Failed to compile Vertex Shader: {}", result);
//ErrorPrinter.PrintErrorBlob(errorBlob);
Runtime.FatalError("Failed to compile Vertex Shader");
}
result = Dx11Cheater.Device.CreateVertexShader(vsCode.GetBufferPointer(), vsCode.GetBufferSize(), null, &_vertexShader);
if(result.Failed)
{
Debug.Write("ERROR: Failed to create Vertex Shader: {}", result);
Runtime.FatalError("Failed to create Vertex Shader");
}
// Create Input Layout
InputElementDescription[2] elementDescs = .(
InputElementDescription("POSITION", 0, .R32G32B32_Float, 0),
InputElementDescription("COLOR", 0, .R8G8B8A8_UNorm, 0)
);
result = Dx11Cheater.Device.CreateInputLayout(&elementDescs, (.)elementDescs.Count, vsCode.GetBufferPointer(), vsCode.GetBufferSize(), &_inputLayout);
vsCode.Release();
if(result.Failed)
{
Debug.Write("ERROR: Failed to create input layout: {}", result);
Runtime.FatalError("Failed to create input layout");
}
//
// Load pixel shader
//
ID3DBlob* psCode = null;
result = D3DCompiler.D3DCompileFromFile("content\\basicShader.hlsl".ToScopedNativeWChar!(), null, .StandardInclude, "PS", "ps_5_0", .Debug, .None, &psCode, &errorBlob);
if(result.Failed || errorBlob != null)
{
Debug.Write("ERROR: Failed to compile Pixel Shader: {}", result);
//ErrorPrinter.PrintErrorBlob(errorBlob);
Runtime.FatalError("Failed to compile Pixel Shader");
}
result = Dx11Cheater.Device.CreatePixelShader(psCode.GetBufferPointer(), psCode.GetBufferSize(), null, &_pixelShader);
psCode.Release();
if(result.Failed)
{
Debug.Write("ERROR: Failed to create Pixel Shader: {}", result);
Runtime.FatalError("Failed to create Pixel Shader");
}
float pO3 = Math.PI_f / 3.0f;
VertexColor[?] vertices = .(
VertexColor(.Zero, Color(255,255,255)),
VertexColor(CircleCoord(0), Color(255, 0, 0)),
VertexColor(CircleCoord(pO3), Color(255,255, 0)),
VertexColor(CircleCoord(pO3*2), Color( 0,255, 0)),
VertexColor(CircleCoord(Math.PI_f), Color( 0,255,255)),
VertexColor(CircleCoord(-pO3*2), Color( 0, 0,255)),
VertexColor(CircleCoord(-pO3), Color(255, 0,255)),
);
_vertexBuffer = new VertexBuffer<VertexColor>(Window.Context, (.)vertices.Count, .Immutable);
_vertexBuffer.SetData(vertices);
uint16[?] indices = .(
0, 1, 2,
0, 2, 3,
0, 3, 4,
0, 4, 5,
0, 5, 6,
0, 6, 1);
_indexBuffer = new IndexBuffer(Window.Context, (.)indices.Count, .Immutable);
_indexBuffer.SetData(indices);
if(result.Failed)
{
Debug.Write("ERROR: Failed to create Vertex Buffer: {}", result);
Runtime.FatalError();
}
// Create rasterizer state
RasterizerStateDescription rsDesc = .();
rsDesc.CullMode = .Back;
rsDesc.FillMode = .Solid;
rsDesc.FrontCounterClockwise = true;
result = Dx11Cheater.Device.CreateRasterizerState(ref rsDesc, &_rasterizerState);
if(result.Failed)
{
Debug.Write("ERROR: Failed to create Rasterizer State: {}", result);
Runtime.FatalError("Failed to create Rasterizer State");
}
} }
public void OnEvent(Event e) public void OnEvent(Event e)
@@ -57,16 +213,38 @@ namespace GlitchyEngine
Input.NewFrame(); Input.NewFrame();
DirectX.ImmediateContext.ClearRenderTargetView(DirectX.BackBufferTarget, .(1, 0, 1)); _window.Context.ClearRenderTarget(null, .(0.2f, 0.2f, 0.2f));
for(Layer layer in _layerStack) for(Layer layer in _layerStack)
layer.Update(_gameTime); layer.Update(_gameTime);
_window.Update(); _window.Update();
_window.Context.SetRenderTarget(null);
_window.Context.BindRenderTargets();
Window.Context.SetVertexBuffer(0, _vertexBuffer);
Window.Context.SetIndexBuffer(_indexBuffer);
var _immediateContext = Dx11Cheater.ImmediateContext;
_immediateContext.InputAssembler.SetInputLayout(_inputLayout);
_immediateContext.InputAssembler.SetPrimitiveTopology(.TriangleList);
_immediateContext.VertexShader.SetShader(_vertexShader, null, 0);
_immediateContext.Rasterizer.SetState(_rasterizerState);
_immediateContext.Rasterizer.SetViewports(1, &Dx11Cheater.BackbufferViewport);
_immediateContext.PixelShader.SetShader(_pixelShader, null, 0);
Window.Context.DrawIndexed(3 * 6);
_imGuiLayer.ImGuiRender(); _imGuiLayer.ImGuiRender();
DirectX.Present(); Window.Context.SwapChain.Present();
} }
} }
+10 -3
View File
@@ -4,6 +4,7 @@ using GlitchyEngine.Events;
// Temporary // Temporary
using GlitchyEngine.Platform.DX11; using GlitchyEngine.Platform.DX11;
//using GlitchyEngine.Platform.DX11.Renderer;
namespace GlitchyEngine.ImGui namespace GlitchyEngine.ImGui
{ {
@@ -40,8 +41,12 @@ namespace GlitchyEngine.ImGui
#if GE_WINDOWS #if GE_WINDOWS
// Todo: temporary, needs to be platform independent // Todo: temporary, needs to be platform independent
ImGuiImplWin32.Init((Windows.HWnd)(int)Application.Get().Window.NativeWindow); ImGuiImplWin32.Init((Windows.HWnd)(int)Application.Get().Window.NativeWindow);
ImGuiImplDX11.Init(Platform.DX11.DirectX.Device, Platform.DX11.DirectX.ImmediateContext);
var context = Application.Get().Window.Context;
ImGuiImplDX11.Init(context.[Friend]nativeDevice, context.[Friend]nativeContext);
#endif #endif
} }
public override void OnDetach() public override void OnDetach()
@@ -174,8 +179,10 @@ namespace GlitchyEngine.ImGui
public void Begin() public void Begin()
{ {
var v = DirectX.ImmediateContext; // Todo:
v.OutputMerger.SetRenderTargets(1, &DirectX.BackBufferTarget, null); //var v = DirectX.ImmediateContext;
//v.OutputMerger.SetRenderTargets(1, &DirectX.BackBufferTarget, null);
Application.Get().Window.Context.SetRenderTarget(null);
ImGuiImplDX11.NewFrame(); ImGuiImplDX11.NewFrame();
ImGuiImplWin32.NewFrame(); ImGuiImplWin32.NewFrame();
+14
View File
@@ -0,0 +1,14 @@
namespace GlitchyEngine.Math
{
typealias Color = DirectX.Color;
typealias ColorRGB = DirectX.ColorRGB;
typealias ColorRGBA = DirectX.ColorRGBA;
typealias Vector2 = DirectX.Math.Vector2;
typealias Vector3 = DirectX.Math.Vector3;
typealias Vector4 = DirectX.Math.Vector4;
typealias Matrix3x3 = DirectX.Math.Matrix3x3;
typealias Matrix4x3 = DirectX.Math.Matrix4x3;
typealias Matrix = DirectX.Math.Matrix;
}
+2
View File
@@ -2,6 +2,7 @@ using System;
namespace GlitchyEngine.Math namespace GlitchyEngine.Math
{ {
/** /**
* A 2D point represented by two 32bit integers. * A 2D point represented by two 32bit integers.
*/ */
@@ -126,4 +127,5 @@ namespace GlitchyEngine.Math
// //
public override void ToString(String strBuffer) => strBuffer.AppendF("X={0} Y={1}", X, Y); public override void ToString(String strBuffer) => strBuffer.AppendF("X={0} Y={1}", X, Y);
} }
} }
-169
View File
@@ -1,169 +0,0 @@
using System;
using DirectX;
using DirectX.D3D11;
using DirectX.Common;
using System.Diagnostics;
using DirectX.DXGI;
using DirectX.DXGI.DXGI1_2;
using DirectX.D3D11.SDKLayers;
using static System.Windows;
namespace GlitchyEngine.Platform.DX11
{
public static class DirectX
{
public static ID3D11Device* Device;
public static ID3D11DeviceContext* ImmediateContext;
public static ID3D11Debug* DebugDevice;
public static IDXGIDevice* DxgiDevice;
public static IDXGISwapChain1* SwapChain;
public static ID3D11RenderTargetView* BackBufferTarget;
public static Viewport BackBufferViewport;
static HWnd _windowHandle;
static ~this()
{
Shutdown();
}
public static void Init(HWnd windowHandle, uint32 width, uint32 height)
{
_windowHandle = windowHandle;
InitDevice();
UpdateSwapchain(width, height);
}
/**
* Initializes the Device and ImmediateContext.
*/
static void InitDevice()
{
Device?.Release();
Device = null;
ImmediateContext?.Release();
ImmediateContext = null;
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
DeviceCreationFlags deviceFlags = .None;
#if DEBUG
deviceFlags |= .Debug;
#endif
FeatureLevel[] levels = scope .(.Level_11_0);
var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &Device, let deviceLevel, &ImmediateContext);
Debug.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message(0x{(int32)deviceResult}): {deviceResult}");
#if DEBUG
if(Device.QueryInterface<ID3D11Debug>(out DebugDevice).Succeeded)
{
ID3D11InfoQueue* infoQueue;
if(Device.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
{
infoQueue.SetBreakOnSeverity(.Corruption, true);
infoQueue.SetBreakOnSeverity(.Error, true);
infoQueue.Release();
}
}
#endif
Log.EngineLogger.Trace("D3D11 Device and Context created (Feature level: {})", deviceLevel);
}
static uint32 _width, _height;
/**
* Initializes the swapchain.
*/
public static void UpdateSwapchain(uint32 width, uint32 height)
{
if(_width == width && _height == height)
return;
_width = width;
_height = height;
Log.EngineLogger.Trace("Updating swap chain ({}, {})", width, height);
uint32 backBufferCount = 2;
Format backBufferFormat = .R8G8B8A8_UNorm;
Format backBufferViewFormat = .R8G8B8A8_UNorm; // _SRGB
if(SwapChain != null)
{
BackBufferTarget.Release();
var resizeResult = SwapChain.ResizeBuffers(backBufferCount, width, height, backBufferFormat, .None);
Debug.Assert(resizeResult.Succeeded, scope $"Failed to resize swap chain. Message({(int32)resizeResult}):{resizeResult}");
}
else
{
Device.QueryInterface(out DxgiDevice);
DxgiDevice.GetAdapter(let adapter);
adapter.GetParent<IDXGIFactory2>(let factory);
adapter.Release();
SwapChainDescription1 swDesc = .();
swDesc.Width = width;
swDesc.Height = height;
swDesc.Format = backBufferFormat;
swDesc.SampleDescription = .(1, 0);
swDesc.BufferUsage = .RenderTargetOutput;
swDesc.BufferCount = backBufferCount;
swDesc.SwapEffect = .FlipDiscard;
SwapChainFullscreenDescription fsSwapChainDesc = .();
fsSwapChainDesc.Windowed = true;
var createResult = factory.CreateSwapChainForHwnd((.)Device, _windowHandle, ref swDesc, &fsSwapChainDesc, null, &SwapChain);
Debug.Assert(createResult.Succeeded, scope $"Failed to create swap chain. Message({(int32)createResult}):{createResult}");
factory.Release();
}
SwapChain.GetBuffer<ID3D11Texture2D>(0, let backBuffer);
RenderTargetViewDescription rtvDesc = .(backBuffer, .Texture2D, backBufferViewFormat);
Device.CreateRenderTargetView(backBuffer, &rtvDesc, &BackBufferTarget);
BackBufferViewport = Viewport(0, 0, width, height, 0.0f, 1.0f);
backBuffer.Release();
}
public static void Shutdown()
{
Device?.Release();
Device = null;
ImmediateContext?.Release();
ImmediateContext = null;
DxgiDevice?.Release();
DxgiDevice = null;
SwapChain?.Release();
SwapChain = null;
BackBufferTarget?.Release();
BackBufferTarget = null;
DebugDevice.ReportLiveDeviceObjects(.Detail);
DebugDevice?.Release();
DebugDevice = null;
}
public static void Present()
{
SwapChain.Present(Application.Get().Window.IsVSync ? 1 : 0, .None);
}
}
}
@@ -0,0 +1,17 @@
using DirectX.D3D11;
using DirectX.DXGI;
namespace GlitchyEngine.Platform.DX11
{
public static class Dx11Cheater
{
public static ID3D11Device* Device;
public static ID3D11DeviceContext* ImmediateContext;
public static ID3D11DeviceContext* Context => ImmediateContext;
public static IDXGISwapChain* SwapChain;
public static ID3D11RenderTargetView* BackbufferTarget;
public static Viewport BackbufferViewport;
}
}
@@ -0,0 +1,135 @@
using System.Diagnostics;
using System;
using DirectX.D3D11;
using internal GlitchyEngine.Renderer;
namespace GlitchyEngine.Renderer
{
extension CPUAccessFlags
{
public static explicit operator DirectX.D3D11.CpuAccessFlags(Self cpuAccessFlags)
{
DirectX.D3D11.CpuAccessFlags flags = .None;
if(cpuAccessFlags.HasFlag(.Read))
flags |= .Read;
if(cpuAccessFlags.HasFlag(.Write))
flags |= .Write;
return flags;
}
}
extension BufferDescription
{
public static operator DirectX.D3D11.BufferDescription(Self desc)
{
DirectX.D3D11.BufferDescription result;
result.ByteWidth = desc.Size;
result.Usage = (.)desc.Usage;
result.CpuAccessFlags = (.)desc.CPUAccess;
result.BindFlags = .None;
if(desc.BindFlags.HasFlag(.Constant))
{
result.BindFlags |= .ConstantBuffer;
}
if(desc.BindFlags.HasFlag(.Index))
{
result.BindFlags |= .IndexBuffer;
}
if(desc.BindFlags.HasFlag(.Vertex))
{
result.BindFlags |= .VertexBuffer;
}
result.MiscFlags = .None;
result.StructureByteStride = 0;
return result;
}
}
public extension MapType
{
public static explicit operator DirectX.D3D11.MapType(Self mapType)
{
return (.)(uint32)mapType;
}
}
public extension Buffer
{
// Todo: nativeBuffer contains exactly that!
internal DirectX.D3D11.BufferDescription nativeDescription;
internal ID3D11Buffer* nativeBuffer ~ _?.Release();
private Result<void> InternalCreateBuffer(void* data, uint32 byteLength, uint32 dstByteOffset)
{
nativeDescription = (.)_description;
uint8* byteData = (.)data;
if(dstByteOffset == 0)
{
byteData = new uint8[nativeDescription.ByteWidth]*;
defer:: delete byteData;
Internal.MemCpy(byteData + dstByteOffset, data, byteLength);
}
SubresourceData srData = .(byteData, byteLength, 0);
var result = _context.nativeDevice.CreateBuffer(ref nativeDescription, &srData, &nativeBuffer);
if(result.Failed)
{
Log.EngineLogger.Error("Failed to create buffer. Message({}):{}", (int)result, result);
return .Err;
}
return .Ok;
}
protected override Result<void> PlatformSetData(void* data, uint32 byteLength, uint32 dstByteOffset, GlitchyEngine.Renderer.MapType mapType)
{
if(nativeBuffer == null)
{
// We can pass the data while creating the buffer, so we can return here.
return InternalCreateBuffer(data, byteLength, dstByteOffset);
}
Debug.Assert(dstByteOffset + byteLength <= _description.Size, "The destination offset and byte length are too long for the target buffer.");
switch(nativeDescription.Usage)
{
case .Default:
Box dataBox = .(dstByteOffset, 0, 0, dstByteOffset + byteLength, 1, 1);
_context.nativeContext.UpdateSubresource(nativeBuffer, 0, &dataBox, data, byteLength, byteLength);
case .Dynamic:
Debug.Assert(mapType.CanWrite, "The map type has to have write access.");
// Todo: DoNotWaitFlag
MappedSubresource map = ?;
_context.nativeContext.Map(nativeBuffer, 0, (.)mapType, .None, &map);
Internal.MemCpy(((uint8*)map.Data) + dstByteOffset, data, byteLength);
_context.nativeContext.Unmap(nativeBuffer, 0);
Runtime.NotImplemented();
case .Immutable:
Log.EngineLogger.Error("Can't set the data of an immutable resource.");
return .Err;
default:
Log.EngineLogger.Error("Unknown resource usage: engine={}, native={}", _description.Usage, nativeDescription.Usage);
return .Err;
}
return .Ok;
}
}
public extension VertexBuffer<T>
{
}
}
@@ -0,0 +1,159 @@
using System;
using System.Diagnostics;
using DirectX.Common;
using DirectX.D3D11;
using DirectX.D3D11.SDKLayers;
using DirectX.DXGI.DXGI1_2;
using GlitchyEngine.Renderer;
using GlitchyEngine.Math;
using GlitchyEngine.Platform.DX11;
using internal GlitchyEngine.Renderer;
//using internal GlitchyEngine.Platform.DX11.Renderer;
namespace GlitchyEngine.Renderer
//namespace GlitchyEngine.Platform.DX11.Renderer
{
/// DirectX 11 specific implementation of the GraphicsContext
extension GraphicsContext
//public class Dx11Context : GraphicsContext
{
//private Dx11SwapChain _swapChain;
private SwapChain _swapChain;
internal Windows.HWnd nativeWindowHandle;
internal ID3D11Device* nativeDevice;
internal ID3D11DeviceContext* nativeContext;
private ID3D11Debug* _debugDevice;
public override SwapChain SwapChain => _swapChain;
private const uint32 MaxRTVCount = DirectX.D3D11.D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT;
//public static override uint32 MaxRenderTargetCount() => MaxRTVCount;
public this(Windows.HWnd windowHandle)
{
nativeWindowHandle = windowHandle;
_swapChain = new SwapChain(this);
//_swapChain = new Dx11SwapChain(this);
}
public ~this()
{
delete _swapChain;
nativeDevice?.Release();
nativeContext?.Release();
_debugDevice.ReportLiveDeviceObjects(.Detail);
_debugDevice?.Release();
}
public override void Init()
{
InitDevice();
SwapChain.Init();
}
/**
* Initializes the Device and ImmediateContext.
*/
void InitDevice()
{
nativeDevice?.Release();
nativeContext?.Release();
Log.EngineLogger.Trace("Creating D3D11 Device and Context...");
DeviceCreationFlags deviceFlags = .None;
#if DEBUG
deviceFlags |= .Debug;
#endif
FeatureLevel[] levels = scope .(.Level_11_0);
var deviceResult = D3D11.CreateDevice(null, .Hardware, 0, deviceFlags, levels, &nativeDevice, let deviceLevel, &nativeContext);
Debug.Assert(deviceResult.Succeeded, scope $"Failed to create D3D11 Device. Message(0x{(int32)deviceResult}): {deviceResult}");
#if DEBUG
if(nativeDevice.QueryInterface<ID3D11Debug>(out _debugDevice).Succeeded)
{
ID3D11InfoQueue* infoQueue;
if(nativeDevice.QueryInterface<ID3D11InfoQueue>(out infoQueue).Succeeded)
{
infoQueue.SetBreakOnSeverity(.Corruption, true);
infoQueue.SetBreakOnSeverity(.Error, true);
infoQueue.Release();
}
}
#endif
Dx11Cheater.Device = nativeDevice;
Dx11Cheater.ImmediateContext = nativeContext;
Log.EngineLogger.Trace("D3D11 Device and Context created (Feature level: {})", deviceLevel);
}
private ID3D11RenderTargetView*[MaxRTVCount] _renderTargets;
public override void SetRenderTarget(RenderTarget renderTarget, int slot = 0)
{
if(renderTarget == null)
{
_renderTargets[slot] = _swapChain.nativeBackBufferTarget;
//_immediateContext.OutputMerger.SetRenderTargets(1, &_swapChain._backBufferTarget, null);
}
else
{
Runtime.NotImplemented();
}
}
public override void BindRenderTargets()
{
nativeContext.OutputMerger.SetRenderTargets(MaxRTVCount, &_renderTargets, null);
}
public override void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color)
{
if(renderTarget == null)
{
nativeContext.ClearRenderTargetView(_swapChain.nativeBackBufferTarget, color);
}
else
{
Runtime.NotImplemented();
}
}
public override void SetVertexBuffer(uint32 slot, Buffer buffer, uint32 stride, uint32 offset = 0)
{
// make stride and offset mutable so that we can take their pointers.
var stride, offset;
nativeContext.InputAssembler.SetVertexBuffers(slot, 1, &buffer.nativeBuffer, &stride, &offset);
}
public override void Draw(uint32 vertexCount, uint32 startVertexIndex = 0)
{
nativeContext.Draw(vertexCount, startVertexIndex);
}
public override void DrawIndexed(uint32 indexCount, uint32 startIndexLocation = 0, int32 vertexOffset = 0)
{
nativeContext.DrawIndexed(indexCount, startIndexLocation, vertexOffset);
}
public override void SetIndexBuffer(Buffer buffer, IndexFormat indexFormat = .Index16Bit, uint32 byteOffset = 0)
{
nativeContext.InputAssembler.SetIndexBuffer(buffer.nativeBuffer, indexFormat == .Index32Bit ? .R32_UInt : .R16_UInt, byteOffset);
}
}
}
@@ -0,0 +1,155 @@
using DirectX.D3D11;
using DirectX.DXGI.DXGI1_2;
using GlitchyEngine.Platform.DX11;
using System.Diagnostics;
using internal GlitchyEngine.Renderer;
namespace GlitchyEngine.Renderer
{
public extension SwapChain
{
private GraphicsContext _context;
private bool _changed;
private uint32 _width, _height;
internal DirectX.DXGI.IDXGIDevice* nativeDxgiDevice;
internal IDXGISwapChain1* nativeSwapChain;
internal ID3D11RenderTargetView* nativeBackBufferTarget;
private DirectX.D3D11.Viewport _backBufferViewport;
public override uint32 Width
{
get => _width;
set
{
if(_width == value)
return;
_width = value;
_changed = true;
}
}
public override uint32 Height
{
get => _height;
set
{
if(_height == value)
return;
_height = value;
_changed = true;
}
}
public override GraphicsContext Context => _context;
//public this(Dx11Context context)
public this(GraphicsContext context)
{
_context = context;
SetResolutionFromWindow();
}
public ~this()
{
nativeDxgiDevice?.Release();
nativeSwapChain?.Release();
nativeBackBufferTarget?.Release();
}
void SetResolutionFromWindow()
{
DirectX.Windows.Winuser.GetClientRect(_context.nativeWindowHandle, let rect);
Width = (.)(rect.Right - rect.Left);
Height = (.)(rect.Bottom - rect.Top);
}
public override void Init()
{
ApplyChanges();
}
public override void ApplyChanges()
{
if(!_changed)
return;
UpdateSwapchain();
}
/**
* Initializes the swapchain.
*/
public void UpdateSwapchain()
{
Log.EngineLogger.Trace("Updating swap chain ({}, {})", _width, _height);
uint32 backBufferCount = 2;
Format backBufferFormat = .R8G8B8A8_UNorm;
Format backBufferViewFormat = .R8G8B8A8_UNorm; // _SRGB
if(nativeSwapChain != null)
{
nativeBackBufferTarget.Release();
var resizeResult = nativeSwapChain.ResizeBuffers(backBufferCount, _width, _height, backBufferFormat, .None);
Debug.Assert(resizeResult.Succeeded, scope $"Failed to resize swap chain. Message({(int32)resizeResult}):{resizeResult}");
}
else
{
_context.nativeDevice.QueryInterface(out nativeDxgiDevice);
nativeDxgiDevice.GetAdapter(let adapter);
adapter.GetParent<DirectX.DXGI.IDXGIFactory2>(let factory);
adapter.Release();
SwapChainDescription1 swDesc = .();
swDesc.Width = _width;
swDesc.Height = _height;
swDesc.Format = backBufferFormat;
swDesc.SampleDescription = .(1, 0);
swDesc.BufferUsage = .RenderTargetOutput;
swDesc.BufferCount = backBufferCount;
swDesc.SwapEffect = .FlipDiscard;
SwapChainFullscreenDescription fsSwapChainDesc = .();
fsSwapChainDesc.Windowed = true;
var createResult = factory.CreateSwapChainForHwnd((.)_context.nativeDevice, _context.nativeWindowHandle, ref swDesc, &fsSwapChainDesc, null, &nativeSwapChain);
Debug.Assert(createResult.Succeeded, scope $"Failed to create swap chain. Message({(int32)createResult}):{createResult}");
factory.Release();
Dx11Cheater.SwapChain = nativeSwapChain;
}
nativeSwapChain.GetBuffer<ID3D11Texture2D>(0, let backBuffer);
RenderTargetViewDescription rtvDesc = .(backBuffer, .Texture2D, backBufferViewFormat);
_context.nativeDevice.CreateRenderTargetView(backBuffer, &rtvDesc, &nativeBackBufferTarget);
Dx11Cheater.BackbufferTarget = nativeBackBufferTarget;
_backBufferViewport = DirectX.D3D11.Viewport(0, 0, _width, _height, 0.0f, 1.0f);
Dx11Cheater.BackbufferViewport = _backBufferViewport;
backBuffer.Release();
}
public override void Present()
{
nativeSwapChain.Present(Application.Get().Window.IsVSync ? 1 : 0, .None);
}
}
}
@@ -9,6 +9,8 @@ using GlitchyEngine.Events;
using System.Diagnostics; using System.Diagnostics;
using GlitchyEngine.Platform.DX11; using GlitchyEngine.Platform.DX11;
using GlitchyEngine.Math; using GlitchyEngine.Math;
//using GlitchyEngine.Platform.DX11.Renderer;
using GlitchyEngine.Renderer;
using static System.Windows; using static System.Windows;
namespace GlitchyEngine namespace GlitchyEngine
@@ -31,6 +33,10 @@ namespace GlitchyEngine
private bool _isVSync = true; private bool _isVSync = true;
private GraphicsContext _graphicsContext ~ delete _;
public override GraphicsContext Context => _graphicsContext;
public override int32 MinWidth public override int32 MinWidth
{ {
get => _minMaxInfo.MinimumTrackingSize.x; get => _minMaxInfo.MinimumTrackingSize.x;
@@ -191,16 +197,19 @@ namespace GlitchyEngine
_windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE, _windowHandle = CreateWindowExW(.None, _windowClass.ClassName, desc.Title.ToScopedNativeWChar!(), .WS_OVERLAPPEDWINDOW | .WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null); CW_USEDEFAULT, CW_USEDEFAULT, desc.Width, desc.Height, 0, 0, (.)_instanceHandle, null);
//_graphicsContext = new Dx11Context(_windowHandle);
_graphicsContext = new GraphicsContext(_windowHandle);
_graphicsContext.Init();
void* myPtr = Internal.UnsafeCastToPtr(this); void* myPtr = Internal.UnsafeCastToPtr(this);
SetWindowLongPtrW(_windowHandle, GWL_USERDATA, (int)myPtr); SetWindowLongPtrW(_windowHandle, GWL_USERDATA, (int)myPtr);
LoadWindowRectangle(); LoadWindowRectangle();
Log.EngineLogger.Trace("Created window \"{}\" ({}, {})", Title, Width, Height); Log.EngineLogger.Trace("Created window \"{}\" ({}, {})", Title, Width, Height);
DirectX.Init(_windowHandle, (.)Width, (.)Height); //DirectX.Init(_windowHandle, (.)Width, (.)Height);
} }
private bool _isResizingOrMoving; private bool _isResizingOrMoving;
@@ -249,7 +258,11 @@ namespace GlitchyEngine
{ {
SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height); SplitHighAndLowOrder!(lParam, out window._clientRect.Width, out window._clientRect.Height);
DirectX.UpdateSwapchain((.)window._clientRect.Width, (.)window._clientRect.Height); window._graphicsContext.SwapChain.Width = (.)window._clientRect.Width;
window._graphicsContext.SwapChain.Height = (.)window._clientRect.Height;
window._graphicsContext.SwapChain.ApplyChanges();
WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving); WindowResizeEvent event = scope WindowResizeEvent(window._clientRect.Width, window._clientRect.Height, window._isResizingOrMoving);
window._eventCallback(event); window._eventCallback(event);
} }
+279
View File
@@ -0,0 +1,279 @@
using System;
namespace GlitchyEngine.Renderer
{
/**
* Identifies expected resource use during rendering.
* The usage directly reflects whether a resource is accessible by the CPU and/or the graphics processing unit (GPU).
*/
public enum Usage
{
/**
* A resource that requires read and write access by the GPU.
* This is likely to be the most common usage choice.
*/
Default = 0,
/**
* A resource that can only be read by the GPU. It cannot be written by the GPU, and cannot be accessed at all by the CPU.
*/
Immutable = 1,
/**
* A resource that is accessible by both the GPU (read only) and the CPU (write only).
* A dynamic resource is a good choice for a resource that will be updated by the CPU at least once per frame.
* To update a dynamic resource, use a Map method.
*/
Dynamic = 2,
/**
* A resource that supports data transfer (copy) from the GPU to the CPU.
*/
Staging = 3
}
/**
* Defines how the CPU can access a resource.
*/
public enum CPUAccessFlags
{
/// The CPU has no access to the resource.
None = 0,
/// The CPU has read access to the resource.
Read = 1,
/// The CPU has write access to the resource.
Write = 2
}
/**
* Defines how to bind a buffer to the pipeline.
*/
public enum BufferBindFlags
{
/// No binding flags specified
None = 0,
/// The Buffer contains vertex data
Vertex = 1,
/// The Buffer contains index data
Index = 2,
// The Buffer contains constant data
Constant = 4,
// ShaderResource?
// UnorderedAccess?
}
public enum BufferMiscFlags
{
None = 0,
//AllowRawView = 1,
//Structured = 2,
}
typealias Format = DirectX.DXGI.Format;
public struct BufferDescription
{
/**
* The size of the buffer in bytes.
*/
public uint32 Size;
/**
* Identify how the buffer is expected to be read from and written to. Frequency of update is a key factor.
* The most common value is typically Default.
*/
public Usage Usage;
public CPUAccessFlags CPUAccess;
public BufferBindFlags BindFlags;
public BufferMiscFlags MiscFlags;
// Strucutred Byte stride.
public this() => this = default;
public this(uint32 size, BufferBindFlags bindFlags, Usage usage = .Default, CPUAccessFlags cpuAccess = .None, BufferMiscFlags miscFlags = .None)
{
Size = size;
BindFlags = bindFlags;
Usage = usage;
CPUAccess = cpuAccess;
MiscFlags = miscFlags;
}
}
public enum MapType
{
case None;
case Read;
case Write;
case ReadWrite;
case WriteDiscard;
case WriteNoOverwrite;
public bool CanWrite => this == Write ||
this == ReadWrite ||
this == WriteDiscard ||
this == WriteNoOverwrite;
public bool CanRead => this == Read ||
this == ReadWrite;
}
/// Represents a buffer containing binary data on the GPU.
public class Buffer
{
internal GraphicsContext _context;
protected BufferDescription _description;
public GraphicsContext Context => _context;
public BufferDescription Description => _description;
protected this(GraphicsContext context)
{
_context = context;
}
/**
* Creates a new instance of a Buffer.
* @param description The buffer description.
*/
public this(GraphicsContext context, BufferDescription description) : this(context)
{
_description = description;
}
/**
* @param data The span containing the data that will be copied into the buffer.
* @param destinationByteOffset The offset in bytes form the start of the destination buffer.
* @param mapType Only relevant for dynamic buffers...
*/
public Result<void> SetData<T>(Span<T> data, uint32 destinationByteOffset = 0, MapType mapType = .Write) where T : struct
{
return PlatformSetData(data.Ptr, (uint32)(data.Length * sizeof(T)), destinationByteOffset, mapType);
}
/**
* @param data The span containing the data that will be copied into the buffer.
* @param destinationByteOffset The offset in bytes form the start of the destination buffer.
* @param mapType Only relevant for dynamic buffers...
*/
public Result<void> SetData<T>(T* data, uint32 elementCount, uint32 destinationByteOffset = 0, MapType mapType = .Write) where T : struct
{
return PlatformSetData(data, elementCount * (uint32)sizeof(T), destinationByteOffset, mapType);
}
public Result<void> SetData<T, CLength>(T[CLength] data, uint32 destinationByteOffset = 0, MapType mapType = .Write) where T : struct where CLength : const int
{
var data;
return PlatformSetData(&data, (uint32)sizeof(T[CLength]), destinationByteOffset, mapType);
}
/**
* // Todo: as soon as Beef supports generic method override, change to generic?
* Platform specific implementation of SetData.
* @param data The pointer to the source data that will be copied to the buffer.
* @param data The number of bytes that will be copied.
* @param dstByteOffset The offset from the start of the target buffer.
*/
protected extern Result<void> PlatformSetData(void* data, uint32 byteLength, uint32 dstByteOffset, MapType mapType);
}
/**
* Type of data contained in an input slot.
*/
public enum InputClassification
{
/**
* Input data is per-vertex data.
*/
PerVertexData = 0,
/**
* Input data is per-instance data.
*/
PerInstanceData = 1
}
public struct VertexElement
{
/**
* The semantic associated with this element in a shader input-signature.
*/
public String SemanticName;
/**
* The semantic index for the element.
* A semantic index modifies a semantic, with an integer index number.
* A semantic index is only needed in a case where there is more than one element with the same semantic.
* For example, a 4x4 matrix would have four components each with the semantic name "matrix",
* however each of the four component would have different semantic indices (0, 1, 2, and 3).
*/
public uint32 SemanticIndex;
/**
* The data type of the element data.
*/
public Format Format;
/**
* An integer value that identifies the input-assembler (see input slot). Valid values are between 0 and 15.
*/
public uint32 InputSlot;
/**
* Optional. Offset (in bytes) from the start of the vertex. Use AppendAligned for convenience to define the current element directly after the previous one, including any packing if necessary.
*/
public uint32 AlignedByteOffset;
/**
* Identifies the input data class for a single input slot.
*/
public InputClassification InputSlotClass;
/**
* The number of instances to draw using the same per-instance data before advancing in the buffer by one element.
* This value must be 0 for an element that contains per-vertex data (the slot class is set to PerVertexData).
*/
public uint32 InstanceDataStepRate;
public this() => this = default;
public this(String semanticName, uint32 semanticIndex, Format format, uint32 inputSlot, uint32 offset = (.)-1, InputClassification slotClass = .PerVertexData, uint32 instanceStepRate = 0)
{
SemanticName = semanticName;
SemanticIndex = semanticIndex;
Format = format;
InputSlot = inputSlot;
AlignedByteOffset = offset;
InputSlotClass = slotClass;
InstanceDataStepRate = instanceStepRate;
}
/**
* Use AppendAligned for convenience to define the current element directly after the previous one, including any packing if necessary.
*/
public static readonly uint32 AppendAligned = 0xffffffff;
}
public abstract class VertexLayout
{
private GraphicsContext _context;
private VertexElement[] _elements ~ delete _;
public GraphicsContext Context => _context;
public VertexElement[] Elements => _elements;
/// Takes ownership of ownElements!
public this(GraphicsContext context, VertexElement[] ownElements)
{
_context = context;
_elements = ownElements;
//CreateNativeLayout();
}
private extern void CreateNativeLayout();
}
public interface IVertexData
{
static VertexLayout VertexLayout {get;}
}
}
@@ -0,0 +1,80 @@
using GlitchyEngine.Math;
namespace GlitchyEngine.Renderer
{
public class RenderTarget;
//public abstract class GraphicsContext
public class GraphicsContext
{
//public abstract SwapChain SwapChain {get;}
public extern SwapChain SwapChain {get;}
/**
* The maximum number of simultaneous rendertargets supported.
*/
//public static extern uint32 MaxRenderTargetCount();
//public abstract void Init();
public extern void Init();
/**
* Sets a rendertarget.
* @param renderTarget The render target.
* @param slot The slot to which the rendertarget will be bound.
* @remarks When @RenderTarget is null the backbuffer will be bound.
* @BindRenderTargets has to be called in order to bind the rendertargets.
*/
//public abstract void SetRenderTarget(RenderTarget renderTarget, int slot = 0);
public extern void SetRenderTarget(RenderTarget renderTarget, int slot = 0);
/**
* Binds all.
* @param renderTarget The render target.
* @param slot The slot to which the rendertarget will be bound.
* @remarks When @RenderTarget is null the backbuffer will be bound.
*/
//public abstract void BindRenderTargets(); // Todo: maybe do this automatically when a drawcall is issued
public extern void BindRenderTargets(); // Todo: maybe do this automatically when a drawcall is issued
/**
* Sets all elements in the given render target to one value.
* @param renderTarget The render target.
* @param color The color to clear the render target with.
* @remarks When @renderTarget is null the backbuffer will be cleared.
*/
//public abstract void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color);
public extern void ClearRenderTarget(RenderTarget renderTarget, ColorRGBA color);
//public abstract void SetViewport(Viewport viewport);
/**
* Binds a VertexBuffer to the pipeline.
* @param buffer The vertex buffer to bind.
* Note: the buffer has to have BindFlags.Vertex!
* @param slot The slot the vertex buffer will be bound to.
* @param offset The offset in bytes from the start of the buffer.
* @param stride The stride of the
*/
public extern void SetVertexBuffer(uint32 slot, Buffer buffer, uint32 stride, uint32 offset = 0);
/**
* Binds a VertexBuffer to the pipeline.
*/
public void SetVertexBuffer(uint32 slot, VertexBufferBinding binding)
{
SetVertexBuffer(slot, binding.Buffer, binding.Stride, binding.Offset);
}
public extern void Draw(uint32 vertexCount, uint32 startVertexIndex = 0);
public extern void DrawIndexed(uint32 indexCount, uint32 startIndexLocation = 0, int32 vertexOffset = 0);
public void SetIndexBuffer(IndexBuffer indexBuffer, uint32 byteOffset = 0)
{
SetIndexBuffer(indexBuffer, indexBuffer.Format, byteOffset);
}
public extern void SetIndexBuffer(Buffer buffer, IndexFormat indexFormat = .Index16Bit, uint32 byteOffset = 0);
}
}
+32
View File
@@ -0,0 +1,32 @@
namespace GlitchyEngine.Renderer
{
public enum IndexFormat
{
Index16Bit,
Index32Bit,
}
public class IndexBuffer : Buffer
{
private uint32 _indexCount;
private IndexFormat _format;
public uint32 IndexCount => _indexCount;
public IndexFormat Format => _format;
public this(GraphicsContext context, uint32 indexCount, Usage usage = .Default, CPUAccessFlags cpuAccess = .None, IndexFormat indexFormat = .Index16Bit) : base(context)
{
_indexCount = indexCount;
_format = indexFormat;
_description = .()
{
Size = (_format == .Index32Bit ? 4 : 2) * _indexCount,
Usage = usage,
CPUAccess = cpuAccess,
BindFlags = .Index,
MiscFlags = .None
};
}
}
}
+30
View File
@@ -0,0 +1,30 @@
namespace GlitchyEngine.Renderer
{
public class SwapChain
{
public extern GraphicsContext Context {get;}
/**
* Gets or Sets the backbuffers width.
* @remarks @ApplyChanges() needs to be called in order to apply the ganges.
*/
public extern uint32 Width {get; set;}
/**
* Gets or Sets the backbuffers height.
* @remarks @ApplyChanges() needs to be called in order to apply the ganges.
*/
public extern uint32 Height {get; set;}
public extern void Init();
/**
* Applies all changes to the swapchain.
*/
public extern void ApplyChanges();
/**
* Swaps the front- and backbuffer.
*/
public extern void Present();
}
}
@@ -0,0 +1,45 @@
using System;
namespace GlitchyEngine.Renderer
{
public struct VertexBufferBinding
{
public Buffer Buffer;
public uint32 Stride;
public uint32 Offset;
public this(Buffer buffer, uint32 stride, uint32 offset = 0)
{
Buffer = buffer;
Stride = stride;
Offset = offset;
}
}
public class VertexBuffer<T> : Buffer where T: struct, IVertexData
{
private VertexBufferBinding _defaultBinding;
public VertexBufferBinding Binding => _defaultBinding;
public this(GraphicsContext context, uint32 vertexCount, Usage usage = .Default, CPUAccessFlags cpuAccess = .None) : base(context)
{
_description = .(){
Size = ((uint32)sizeof(T) * vertexCount),
Usage = usage,
CPUAccess = cpuAccess,
BindFlags = .Vertex,
MiscFlags = .None
};
_defaultBinding = .(this, (.)sizeof(T), 0);
}
public Result<void> SetData(Span<T> data, uint32 destinationVertexOffset = 0, MapType mapType = .Write)
{
return SetData<T>(data, destinationVertexOffset * (uint32)sizeof(T));
}
[Inline]
public static implicit operator VertexBufferBinding(Self buffer) => buffer._defaultBinding;
}
}
+30
View File
@@ -0,0 +1,30 @@
namespace GlitchyEngine.Renderer
{
public struct Viewport
{
/// X position of the left hand side of the viewport.
public float Left;
/// Y position of the top of the viewport. Ranges between BoundsMin and BoundsMax.
public float Top;
/// Width of the viewport.
public float Width;
/// Height of the viewport.
public float Height;
/// Minimum depth of the viewport. Ranges between 0 and 1.
public float MinDepth;
/// Maximum depth of the viewport. Ranges between 0 and 1.
public float MaxDepth;
public this() => this = default;
public this(float left, float top, float width, float height, float minDepth = 0.0f, float maxDepth = 1.0f)
{
Left = left;
Top = top;
Width = width;
Height = height;
MinDepth = minDepth;
MaxDepth = maxDepth;
}
}
}
+6
View File
@@ -1,6 +1,7 @@
using System; using System;
using GlitchyEngine.Events; using GlitchyEngine.Events;
using GlitchyEngine.Math; using GlitchyEngine.Math;
using GlitchyEngine.Renderer;
namespace GlitchyEngine namespace GlitchyEngine
{ {
@@ -86,6 +87,11 @@ namespace GlitchyEngine
* Gets or Sets whether or not the application uses VSync * Gets or Sets whether or not the application uses VSync
*/ */
public extern bool IsVSync {get; set;} public extern bool IsVSync {get; set;}
/**
* Gets the windows graphics context.
*/
public extern GraphicsContext Context {get;}
/** /**
* Gets a pointer to the platform specific window representation. * Gets a pointer to the platform specific window representation.
+25
View File
@@ -0,0 +1,25 @@
struct VS_IN
{
float3 Position : POSITION;
float4 Color : COLOR;
};
struct PS_IN
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
};
PS_IN VS(VS_IN input)
{
PS_IN output;
output.Position = float4(input.Position, 1);
output.Color = input.Color;
return output;
}
float4 PS(PS_IN input) : SV_TARGET
{
return input.Color;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

+3 -2
View File
@@ -4,6 +4,7 @@ using GlitchyEngine.Events;
using System.Diagnostics; using System.Diagnostics;
using GlitchLog; using GlitchLog;
using GlitchyEngine.ImGui; using GlitchyEngine.ImGui;
using ImGui;
namespace Sandbox namespace Sandbox
{ {
@@ -29,9 +30,9 @@ namespace Sandbox
private bool OnImGuiRender(ImGuiRenderEvent e) private bool OnImGuiRender(ImGuiRenderEvent e)
{ {
ImGui.ImGui.Begin("Test"); ImGui.Begin("Test");
ImGui.ImGui.End(); ImGui.End();
return false; return false;
} }