Skip to content

Instantly share code, notes, and snippets.

@technoir42
Created September 29, 2012 18:40
Show Gist options
  • Save technoir42/3804858 to your computer and use it in GitHub Desktop.
Save technoir42/3804858 to your computer and use it in GitHub Desktop.
module Render.D3D11;
import Core;
import Core.Logging;
import std.array;
import std.c.stdlib;
import std.comptr;
import std.conv;
import std.format;
import std.string;
import win32.directx.dxgi;
import win32.directx.d3d11;
import win32.directx.d3dcompiler;
import win32.windows;
pragma(lib, "dxgi.lib");
pragma(lib, "d3d11.lib");
pragma(lib, "d3dcompiler.lib");
public enum BlendArg
{
Zero,
One,
SrcColor,
InvSrcColor,
SrcAlpha,
InvSrcAlpha,
DestAlpha,
InvDestAlpha,
DestColor,
InvDestColor,
}
public enum BlendOp
{
Add,
Subtract,
RevSubtract,
Min,
Max,
}
public enum ComparisonFunc
{
Never,
Less,
Equal,
LessEqual,
Greater,
NotEqual,
GreaterEqual,
Always,
}
public enum CullMode
{
None,
Back,
Front,
}
public enum FillMode
{
Solid,
Wireframe,
}
public enum TextureAddressMode
{
Clamp,
Wrap,
Mirror,
Border,
}
public enum TextureFilter
{
Point,
Linear,
}
public enum IndexFormat
{
I16,
I32,
}
public enum PrimitiveType : ubyte
{
Unknown,
PointList,
LineList,
LineStrip,
TriangleList,
TriangleStrip,
}
public enum ResourceUsage
{
Default,
Immutable,
Dynamic
}
public enum ShaderType
{
VertexShader,
FragmentShader,
GeometryShader,
HullShader,
DomainShader,
}
public enum SurfaceFormat
{
Unknown,
// Standard formats
R8,
RG8,
RGB8,
RGBA8,
R16,
R16F,
RG16,
RG16F,
RGBA16,
RGBA16F,
R32F,
RG32F,
RGB32F,
RGBA32F,
// Compressed formats
DXT1,
DXT2,
DXT3,
DXT4,
DXT5,
// Depth-Stencil formats
D16,
D24S8,
D32F,
}
public enum VertexElementType : ubyte
{
Float,
Float2,
Float3,
Float4,
Half2,
Half4,
UByte4,
UByte4N,
}
public enum VertexElementUsage : ubyte
{
Position,
Normal,
Tangent,
Binormal,
TextureCoord,
Color,
BlendWeight,
BlendIndices,
}
public struct SamplerStateDesc
{
TextureAddressMode AddressU = TextureAddressMode.Clamp;
TextureAddressMode AddressV = TextureAddressMode.Clamp;
TextureAddressMode AddressW = TextureAddressMode.Clamp;
TextureFilter MinFilter = TextureFilter.Point;
TextureFilter MagFilter = TextureFilter.Point;
TextureFilter MipFilter = TextureFilter.Point;
ubyte MaxAnisotropy = 1;
}
public struct DepthStencilStateDesc
{
bool DepthEnable = true;
bool DepthWriteEnable = true;
bool StencilEnable = false;
ComparisonFunc DepthFunc = ComparisonFunc.Less;
}
public struct BlendStateDesc
{
bool alphaToCoverageEnable = false;
bool blendEnable = false;
BlendArg srcBlend = BlendArg.SrcAlpha;
BlendArg destBlend = BlendArg.One;
BlendOp blendOp = BlendOp.Add;
BlendArg srcBlendAlpha = BlendArg.Zero;
BlendArg destBlendAlpha = BlendArg.Zero;
BlendOp blendOpAlpha = BlendOp.Add;
//ubyte RenderTargetWriteMask = 0x0F;
}
public struct RasterizerStateDesc
{
CullMode cullMode = CullMode.Back;
FillMode fillMode = FillMode.Solid;
}
public struct VertexElement
{
align(1):
VertexElementType type;
VertexElementUsage usage;
ubyte index;
}
public struct MultiSampleType
{
public dword SampleCount = 1;
public dword Quality = 0;
public this(dword sampleCount, dword quality)
{
this.SampleCount = sampleCount;
this.Quality = quality;
}
public const MultiSampleType None = MultiSampleType(1, 0);
}
public final class Viewport
{
public dword x;
public dword y;
public dword width;
public dword height;
public float minDepth;
public float maxDepth;
public this(dword x, dword y, dword width, dword height, float minDepth = 0.0f, float maxDepth = 1.0f)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.minDepth = minDepth;
this.maxDepth = maxDepth;
}
}
public class RenderException : Exception
{
public this(string msg, string file = __FILE__, size_t line = __LINE__)
{
super(msg, file, line);
}
}
public final class DeviceManager
{
private IDXGIFactory m_factory;
private GraphicsAdapter m_defaultAdapter;
private GraphicsAdapter[] m_adapters;
public this()
{
try
{
HRESULT hr = CreateDXGIFactory(&IID_IDXGIFactory, m_factory);
if(FAILED(hr))
throw new RenderException("Failed to create DXGI factory");
IDXGIAdapter adapter;
hr = m_factory.EnumAdapters(0, adapter);
if(FAILED(hr))
throw new RenderException("Default graphics adapter is not accessible");
m_defaultAdapter = new GraphicsAdapter(adapter);
}
catch(RenderException e)
{
Log.Error("Device Manager: %s", e.toString());
throw e;
}
}
public ~this()
{
delete m_defaultAdapter;
foreach(adapter; m_adapters)
delete adapter;
m_adapters.length = 0;
SafeRelease(m_factory);
}
@property
public GraphicsAdapter[] Adapters()
{
if(m_adapters.length == 0)
{
try
{
dword index = 0;
IDXGIAdapter adapter;
while(m_factory.EnumAdapters(index++, adapter) != DXGI_ERROR_NOT_FOUND)
{
m_adapters.length++;
m_adapters[$-1] = new GraphicsAdapter(adapter);
}
}
catch(RenderException e)
{
Log.Error("Device Manager: %s", e.toString());
throw e;
}
}
return m_adapters;
}
@property
public GraphicsAdapter DefaultAdapter()
{
return m_defaultAdapter;
}
public GraphicsDevice CreateDevice(GraphicsAdapter adapter)
{
assert(adapter !is null);
Log.Info("Device Manager: Creating device on adapter %s (%u MB RAM)", adapter.Name, adapter.VideoMemory);
try
{
return new GraphicsDevice(m_factory, adapter.m_adapter, adapter.m_output);
}
catch(RenderException e)
{
Log.Error("Device Manager: %s", e.toString());
throw e;
}
}
}
public struct DisplayMode
{
dword width;
dword height;
dword refreshRate;
}
public final class GraphicsAdapter
{
private IDXGIAdapter m_adapter;
private IDXGIOutput m_output;
private DisplayMode[] m_modes;
private string m_name;
private dword m_videoMemory;
private this(IDXGIAdapter adapter)
{
m_adapter = adapter;
HRESULT hr = m_adapter.EnumOutputs(0, m_output);
if(FAILED(hr))
throw new RenderException("Failed to obtain primary output for adapter");
DXGI_ADAPTER_DESC desc;
hr = m_adapter.GetDesc(desc);
if(FAILED(hr))
throw new RenderException("Failed to retrieve adapter information");
char[128] buf;
size_t len = wcstombs(buf, desc.Description, buf.length);
m_name = buf[0..len].idup;
m_videoMemory = desc.DedicatedVideoMemory / 1024 / 1024;
// round to multiple of 16
if(m_videoMemory > 16 && m_videoMemory % 16 != 0)
m_videoMemory = (m_videoMemory / 16 + 1) * 16;
}
public ~this()
{
SafeRelease(m_output);
SafeRelease(m_adapter);
}
@property
public string Name()
{
return m_name;
}
@property
public dword VideoMemory()
{
return m_videoMemory;
}
@property
public const(DisplayMode[]) DisplayModes()
{
if(m_modes.length == 0)
{
try
{
DXGI_MODE_DESC[] modes = GetDXGIModeList(m_output, DXGI_FORMAT_R8G8B8A8_UNORM);
m_modes.length = modes.length;
for(size_t i = 0; i < modes.length; i++)
{
m_modes[i].width = modes[i].Width;
m_modes[i].height = modes[i].Height;
m_modes[i].refreshRate = toHz(modes[i].RefreshRate);
}
}
catch(RenderException e)
{
Log.Error("Graphics Adapter: %s", e.toString());
throw e;
}
}
return m_modes;
}
}
public final class GraphicsDevice
{
private IDXGIFactory m_factory;
private IDXGIAdapter m_adapter;
private IDXGIOutput m_output;
private ID3D11Device m_device;
private ID3D11DeviceContext m_mainContext;
private this(IDXGIFactory factory, IDXGIAdapter adapter, IDXGIOutput output)
{
m_factory = factory;
m_adapter = adapter;
m_output = output;
m_factory.AddRef();
m_adapter.AddRef();
m_output.AddRef();
immutable D3D_FEATURE_LEVEL[3] featureLevels =
[
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
];
dword flags = 0;
debug
{
flags |= D3D11_CREATE_DEVICE_DEBUG;
Log.Info("Graphics Device: Setting debug flag");
}
D3D_FEATURE_LEVEL featureLevel;
HRESULT hr = D3D11CreateDevice(
m_adapter,
D3D_DRIVER_TYPE_UNKNOWN,
null,
flags,
featureLevels,
featureLevels.length,
D3D11_SDK_VERSION,
m_device,
&featureLevel,
m_mainContext);
if(FAILED(hr))
throw new RenderException("Failed to create Direct3D device");
Log.Info("Graphics Device: Available Direct3D feature level: %s", to!string(featureLevel));
}
public ~this()
{
if(m_mainContext)
m_mainContext.ClearState();
SafeRelease(m_mainContext);
SafeRelease(m_device);
SafeRelease(m_output);
SafeRelease(m_adapter);
SafeRelease(m_factory);
}
public BackBuffer CreateBackBuffer(HWND outputWindow, dword width, dword height)
{
return CreateBackBuffer(outputWindow, width, height, MultiSampleType.None);
}
public BackBuffer CreateBackBuffer(HWND outputWindow, dword width, dword height, ref const(MultiSampleType) multiSampleType)
{
DisplayMode mode = { width, height, 0 };
return CreateBackBuffer(outputWindow, mode, false, multiSampleType);
}
public BackBuffer CreateBackBuffer(HWND outputWindow, ref const(DisplayMode) mode, bool fullscreen)
{
return CreateBackBuffer(outputWindow, mode, fullscreen, MultiSampleType.None);
}
public BackBuffer CreateBackBuffer(HWND outputWindow, ref const(DisplayMode) mode, bool fullscreen, ref const(MultiSampleType) multiSampleType)
{
Log.Info("Graphics Device: Creating back buffer: %ux%u %uHz (%s), samples: %u, quality: %u", mode.width, mode.height, mode.refreshRate, fullscreen ? "fullscreen" : "windowed", multiSampleType.SampleCount, multiSampleType.Quality);
assert(mode.width > 0 && mode.height > 0);
assert(outputWindow != null);
try
{
SurfaceFormat format = SurfaceFormat.RGBA8;
DXGI_SWAP_CHAIN_DESC desc =
{
BufferCount : 1,
BufferDesc : GetMatchingDXGIMode(m_device, m_output, format, mode, fullscreen),
BufferUsage : DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT,
OutputWindow : outputWindow,
Windowed : !fullscreen,
Flags : DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,
SwapEffect : DXGI_SWAP_EFFECT_DISCARD,
SampleDesc :
{
Count : multiSampleType.SampleCount,
Quality : multiSampleType.Quality,
}
};
if(CheckFormatSupport(format, multiSampleType))
{
desc.SampleDesc.Count = multiSampleType.SampleCount;
desc.SampleDesc.Quality = multiSampleType.Quality;
}
else
{
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
Log.Warning("Combination of specified surface format and multisample type is not supported. Falling back to no multisampling");
}
return new BackBuffer(m_factory, m_device, m_output, desc, desc.BufferDesc.Width, desc.BufferDesc.Height, format, multiSampleType);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public RenderTarget CreateRenderTarget(SurfaceFormat format, dword width, dword height)
{
return CreateRenderTarget(format, width, height, MultiSampleType.None);
}
public RenderTarget CreateRenderTarget(SurfaceFormat format, dword width, dword height, ref const(MultiSampleType) multiSampleType)
{
Log.Info("Graphics Device: Creating render target: %ux%u %s, samples: %u, quality: %u", width, height, to!string(format), multiSampleType.SampleCount, multiSampleType.Quality);
assert(width > 0 && height > 0);
assert(format != SurfaceFormat.Unknown);
try
{
if(!IsColorFormat(format))
throw new RenderException("Specified surface format cannot be used to create render target");
if(!CheckFormatSupport(format, multiSampleType))
throw new RenderException("Combination of specified surface format and multisample type is not supported");
return new RenderTarget(m_device, width, height, format, multiSampleType);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public DepthStencilBuffer CreateDepthStencilBuffer(SurfaceFormat format, dword width, dword height, ref const(MultiSampleType) multiSampleType)
{
Log.Info("Graphics Device: Creating depth-stencil buffer: %ux%u %s, samples: %u, quality: %u", width, height, to!string(format), multiSampleType.SampleCount, multiSampleType.Quality);
assert(width > 0 && height > 0);
assert(format != SurfaceFormat.Unknown);
try
{
if(!IsDepthStencilFormat(format))
throw new RenderException("Specified surface format cannot be used to create depth-stencil buffer");
if(!CheckFormatSupport(format, multiSampleType))
throw new RenderException("Combination of specified surface format and multisample type is not supported");
return new DepthStencilBuffer(m_device, width, height, format, multiSampleType);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public RenderContext CreateMainContext()
{
Log.Info("Graphics Device: Creating main rendering context");
return new RenderContext(m_mainContext);
}
public DeferredContext CreateRenderContext()
{
Log.Info("Graphics Device: Creating deferred rendering context");
try
{
return new DeferredContext(m_device);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
/**
* Creates constant buffer and fills it with specified data.
*
* @param size size of constant buffer in bytes
* @param data pointer to data used to fill constant buffer (optional)
*
* @throws RenderException if any error occurs
*/
public ConstantBuffer CreateConstantBuffer(dword size, const void* data)
{
Log.Info("Graphics Device: Creating constant buffer (size: %u bytes)", size);
try
{
return new ConstantBuffer(m_device, size, data);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
/**
* Creates vertex buffer and fills it with specified data.
*
* @param size number of vertices in vertex buffer
* @param vertexSize size of single vertex in bytes
* @param data pointer to data used to fill vertex buffer (optional)
*
* @throws RenderException if any error occurs
*/
public VertexBuffer CreateVertexBuffer(dword size, dword vertexSize, const void* data)
{
Log.Info("Graphics Device: Creating vertex buffer (size: %u elements, stride: %u bytes)", size, vertexSize);
try
{
return new VertexBuffer(m_device, size, vertexSize, data);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
/**
* Creates index buffer and fills it with specified data.
*
* @param size number of indices in index buffer
* @param format format of indices in index buffer
* @param data pointer to data used to fill index buffer (optional)
*
* @throws RenderException if any error occurs
*/
public IndexBuffer CreateIndexBuffer(dword size, IndexFormat format, const void* data)
{
Log.Info("Graphics Device: Creating index buffer (size: %u elements, format: %s)", size, to!string(format));
try
{
return new IndexBuffer(m_device, size, format, data);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public VertexLayout CreateVertexLayout(const VertexElement[] elements)
{
Log.Info("Graphics Device: Creating vertex layout (%u elements)", elements.length);
assert(elements != null);
try
{
return new VertexLayout(m_device, elements);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public Texture1D CreateTexture1D(dword size, SurfaceFormat format, dword mipLevels, ResourceUsage usage, const void* data = null)
{
Log.Info("Graphics Device: Creating 1D texture (width: %u, format: %s, mip levels: %u)", size, to!string(format), mipLevels);
assert(size > 0);
assert(format != SurfaceFormat.Unknown);
try
{
return new Texture1D(m_device, size, format, mipLevels, usage, data);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public Texture2D CreateTexture2D(dword width, dword height, SurfaceFormat format, dword mipLevels, ResourceUsage usage, const void* data = null)
{
return CreateTexture2D(width, height, format, mipLevels, usage, MultiSampleType.None, data);
}
public Texture2D CreateTexture2D(dword width, dword height, SurfaceFormat format, dword mipLevels, ResourceUsage usage, ref const(MultiSampleType) multiSampleType, const void* data = null)
{
Log.Info("Graphics Device: Creating 2D texture (width: %u, height: %u, format: %s, mip levels: %u, samples: %u, quality: %u)", width, height, to!string(format), mipLevels, multiSampleType.SampleCount, multiSampleType.Quality);
assert(width > 0 && height > 0);
assert(format != SurfaceFormat.Unknown);
try
{
return new Texture2D(m_device, width, height, format, mipLevels, usage, multiSampleType, data);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public Texture3D CreateTexture3D(dword width, dword height, dword depth, SurfaceFormat format, dword mipLevels, ResourceUsage usage, const void* data = null)
{
Log.Info("Graphics Device: Creating 3D texture (width: %u, height: %u, depth: %u, format: %s, mip levels: %u)", width, height, depth, to!string(format), mipLevels);
assert(width > 0 && height > 0 && depth > 0);
assert(format != SurfaceFormat.Unknown);
try
{
return new Texture3D(m_device, width, height, depth, format, mipLevels, usage, data);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public TextureCube CreateTextureCube(dword size, SurfaceFormat format, dword mipLevels, ResourceUsage usage, const void* data = null)
{
return CreateTextureCube(size, format, mipLevels, usage, MultiSampleType.None, data);
}
public TextureCube CreateTextureCube(dword size, SurfaceFormat format, dword mipLevels, ResourceUsage usage, ref const(MultiSampleType) multiSampleType, const void* data = null)
{
Log.Info("Graphics Device: Creating cube texture (size: %u, format: %s, mip levels: %u, samples: %u, quality: %u)", size, to!string(format), mipLevels, multiSampleType.SampleCount, multiSampleType.Quality);
assert(size > 0);
assert(format != SurfaceFormat.Unknown);
try
{
return new TextureCube(m_device, size, format, mipLevels, usage, multiSampleType, data);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public VertexShader CreateVertexShader(string code)
{
Log.Info("Graphics Device: Creating vertex shader");
assert(code != null);
try
{
ShaderCompiler compiler;
if(!compiler.Compile(code, "vs_4_1", "vs_main"))
throw new RenderException("Failed to compile vertex shader");
return new VertexShader(m_device, compiler.CompiledCode, compiler.CompiledSize);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public FragmentShader CreateFragmentShader(string code)
{
Log.Info("Graphics Device: Creating fragment shader");
assert(code != null);
try
{
ShaderCompiler compiler;
if(!compiler.Compile(code, "ps_4_1", "fs_main"))
throw new RenderException("Failed to compile fragment shader");
return new FragmentShader(m_device, compiler.CompiledCode, compiler.CompiledSize);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public GeometryShader CreateGeometryShader(string code)
{
Log.Info("Graphics Device: Creating geometry shader");
assert(code != null);
try
{
ShaderCompiler compiler;
if(!compiler.Compile(code, "gs_4_1", "gs_main"))
throw new RenderException("Failed to compile geometry shader");
return new GeometryShader(m_device, compiler.CompiledCode, compiler.CompiledSize);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public HullShader CreateHullShader(string code)
{
Log.Info("Graphics Device: Creating hull shader");
assert(code != null);
try
{
ShaderCompiler compiler;
if(!compiler.Compile(code, "hs_5_0", "hs_main"))
throw new RenderException("Failed to compile hull shader");
return new HullShader(m_device, compiler.CompiledCode, compiler.CompiledSize);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public DomainShader CreateDomainShader(string code)
{
Log.Info("Graphics Device: Creating domain shader");
assert(code != null);
try
{
ShaderCompiler compiler;
if(!compiler.Compile(code, "ds_5_0", "ds_main"))
throw new RenderException("Failed to compile domain shader");
return new DomainShader(m_device, compiler.CompiledCode, compiler.CompiledSize);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public SamplerState CreateSamplerState(const ref SamplerStateDesc samplerState)
{
Log.Info("Graphics Device: Creating sampler state");
try
{
return new SamplerState(m_device, samplerState);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public DepthStencilState CreateDepthStencilState(const ref DepthStencilStateDesc desc)
{
Log.Info("Graphics Device: Creating depth-stencil state");
try
{
return new DepthStencilState(m_device, desc);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public RasterizerState CreateRasterizerState(const ref RasterizerStateDesc desc)
{
Log.Info("Graphics Device: Creating rasterizer state");
try
{
return new RasterizerState(m_device, desc);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public BlendState CreateBlendState(const ref BlendStateDesc desc)
{
Log.Info("Graphics Device: Creating blend state");
try
{
return new BlendState(m_device, desc);
}
catch(RenderException e)
{
Log.Error("Graphics Device: %s", e.toString());
throw e;
}
}
public bool CheckFormatSupport(SurfaceFormat format, ref const(MultiSampleType) multiSampleType)
{
return .CheckFormatSupport(m_device, format, multiSampleType);
}
}
public class RenderContext
{
private PrimitiveType m_primitiveType;
private VertexShader m_vertexShader;
private FragmentShader m_fragmentShader;
private GeometryShader m_geometryShader;
private VertexBuffer m_vertexBuffer;
private IndexBuffer m_indexBuffer;
private VertexLayout m_layout;
private DepthStencilState m_dsState;
private RasterizerState m_rsState;
private BlendState m_blendState;
protected ID3D11DeviceContext m_context;
protected this()
{
}
private this(ID3D11DeviceContext context)
{
assert(context !is null);
m_context = context;
m_context.AddRef();
}
public ~this()
{
ClearState();
SafeRelease(m_context);
}
public final void ClearState()
{
m_primitiveType = PrimitiveType.Unknown;
m_vertexShader = null;
m_fragmentShader = null;
m_geometryShader = null;
m_vertexBuffer = null;
m_indexBuffer = null;
m_layout = null;
m_dsState = null;
m_rsState = null;
m_blendState = null;
if(m_context)
m_context.ClearState();
}
public final void SetViewport(Viewport viewport)
{
D3D11_VIEWPORT vp =
{
TopLeftX: viewport.x,
TopLeftY: viewport.y,
Width: viewport.width,
Height: viewport.height,
MinDepth: viewport.minDepth,
MaxDepth: viewport.maxDepth
};
m_context.RSSetViewports(1, &vp);
}
public final void SetRenderTarget(RenderTarget renderTarget)
{
ID3D11DepthStencilView dsView;
m_context.OMGetRenderTargets(0, null, &dsView);
ID3D11RenderTargetView view = (renderTarget !is null) ? renderTarget.m_rtView : null;
m_context.OMSetRenderTargets(1, &view, dsView);
SafeRelease(dsView);
}
public final void SetRenderTarget(RenderTarget renderTarget, DepthStencilBuffer depthStencil)
{
ID3D11RenderTargetView view = (renderTarget !is null) ? renderTarget.m_rtView : null;
ID3D11DepthStencilView dsView = (depthStencil !is null) ? depthStencil.m_dsView : null;
m_context.OMSetRenderTargets(1, &view, dsView);
}
public final void SetDepthStencilBuffer(DepthStencilBuffer depthStencil)
{
ID3D11RenderTargetView[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] rtViews;
m_context.OMGetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, rtViews, null);
ID3D11DepthStencilView dsView = (depthStencil !is null) ? depthStencil.m_dsView : null;
m_context.OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, rtViews, dsView);
for(int i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
SafeRelease(rtViews[i]);
}
public final void DetachRenderTargets()
{
m_context.OMSetRenderTargets(0, null, null);
}
public final void SetVertexShader(VertexShader shader)
{
if(m_vertexShader != shader)
{
m_vertexShader = shader;
m_context.VSSetShader(shader ? shader.m_shader : null, null, 0);
}
}
public final void SetFragmentShader(FragmentShader shader)
{
if(m_fragmentShader != shader)
{
m_fragmentShader = shader;
m_context.PSSetShader(shader ? shader.m_shader : null, null, 0);
}
}
public final void SetGeometryShader(GeometryShader shader)
{
if(m_geometryShader != shader)
{
m_geometryShader = shader;
m_context.GSSetShader(shader ? shader.m_shader : null, null, 0);
}
}
public final void BindResource(ShaderType stage, ubyte slot, ShaderResource resource)
{
assert(resource !is null);
assert(slot < D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT);
final switch(stage)
{
case ShaderType.VertexShader:
m_context.VSSetShaderResources(slot, 1, &resource.m_resourceView);
break;
case ShaderType.FragmentShader:
m_context.PSSetShaderResources(slot, 1, &resource.m_resourceView);
break;
case ShaderType.GeometryShader:
m_context.GSSetShaderResources(slot, 1, &resource.m_resourceView);
break;
case ShaderType.HullShader:
m_context.HSSetShaderResources(slot, 1, &resource.m_resourceView);
break;
case ShaderType.DomainShader:
m_context.DSSetShaderResources(slot, 1, &resource.m_resourceView);
break;
}
}
public final void BindConstantBuffer(ShaderType stage, ubyte slot, ConstantBuffer buffer)
{
assert(buffer !is null);
assert(slot < D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT);
final switch(stage)
{
case ShaderType.VertexShader:
m_context.VSSetConstantBuffers(slot, 1, &buffer.m_buffer);
break;
case ShaderType.FragmentShader:
m_context.PSSetConstantBuffers(slot, 1, &buffer.m_buffer);
break;
case ShaderType.GeometryShader:
m_context.GSSetConstantBuffers(slot, 1, &buffer.m_buffer);
break;
case ShaderType.HullShader:
m_context.HSSetConstantBuffers(slot, 1, &buffer.m_buffer);
break;
case ShaderType.DomainShader:
m_context.DSSetConstantBuffers(slot, 1, &buffer.m_buffer);
break;
}
}
public final void BindVertexBuffer(VertexBuffer buffer)
{
assert(buffer !is null);
if(m_vertexBuffer != buffer)
{
m_vertexBuffer = buffer;
dword vertexSize = buffer.VertexSize;
dword offset = 0;
m_context.IASetVertexBuffers(0, 1, &buffer.m_buffer, &vertexSize, &offset);
}
}
public final void BindIndexBuffer(IndexBuffer buffer)
{
assert(buffer !is null);
if(m_indexBuffer != buffer)
{
m_indexBuffer = buffer;
m_context.IASetIndexBuffer(buffer.m_buffer, type_cast!DXGI_FORMAT(buffer.Format), 0);
}
}
public final void SetPrimitiveType(PrimitiveType primitiveType)
{
if(m_primitiveType != primitiveType)
{
m_primitiveType = primitiveType;
m_context.IASetPrimitiveTopology(type_cast!D3D11_PRIMITIVE_TOPOLOGY(primitiveType));
}
}
public final void SetVertexLayout(VertexLayout layout)
{
assert(layout !is null);
if(m_layout != layout)
{
m_layout = layout;
m_context.IASetInputLayout(layout.m_layout);
}
}
public final void SetSamplerState(ShaderType stage, ubyte slot, SamplerState state)
{
assert(state !is null);
assert(slot < D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT);
final switch(stage)
{
case ShaderType.VertexShader:
m_context.VSSetSamplers(slot, 1, &state.m_state);
break;
case ShaderType.FragmentShader:
m_context.PSSetSamplers(slot, 1, &state.m_state);
break;
case ShaderType.GeometryShader:
m_context.GSSetSamplers(slot, 1, &state.m_state);
break;
case ShaderType.HullShader:
m_context.HSSetSamplers(slot, 1, &state.m_state);
break;
case ShaderType.DomainShader:
m_context.DSSetSamplers(slot, 1, &state.m_state);
break;
}
}
public final void SetDepthStencilState(DepthStencilState state)
{
assert(state !is null);
if(m_dsState != state)
{
m_dsState = state;
m_context.OMSetDepthStencilState(state.m_state, 0);
}
}
public final void SetRasterizerState(RasterizerState state)
{
assert(state !is null);
if(m_rsState != state)
{
m_rsState = state;
m_context.RSSetState(state.m_state);
}
}
public final void SetBlendState(BlendState state)
{
assert(state !is null);
if(m_blendState != state)
{
m_blendState = state;
immutable float[4] coef = [ 1.0f, 1.0f, 1.0f, 1.0f ];
m_context.OMSetBlendState(state.m_state, coef, 0xffffffff);
}
}
public final void ClearRenderTarget(RenderTarget renderTarget, const ref float[4] color)
{
assert(renderTarget !is null);
m_context.ClearRenderTargetView(renderTarget.m_rtView, color);
}
public final void ClearDepthStencilBuffer(DepthStencilBuffer depthStencil, float depth)
{
assert(depthStencil !is null);
m_context.ClearDepthStencilView(depthStencil.m_dsView, D3D11_CLEAR_DEPTH, depth, 0);
}
public final void Draw(dword vertexCount, dword startVertex)
{
m_context.Draw(vertexCount, startVertex);
}
public final void DrawIndexed(dword indexCount, dword startIndex)
{
m_context.DrawIndexed(indexCount, startIndex, 0);
}
public final void DrawInstances(dword instanceCount, dword vertexCount, dword startVertex)
{
m_context.DrawInstanced(vertexCount, instanceCount, startVertex, 0);
}
public final void DrawIndexedInstances(dword instanceCount, dword indexCount, dword startIndex)
{
m_context.DrawIndexedInstanced(indexCount, instanceCount, startIndex, 0, 0);
}
}
public final class DeferredContext : RenderContext
{
private this(ID3D11Device device)
{
HRESULT hr = device.CreateDeferredContext(0, m_context);
if(FAILED(hr))
throw new RenderException("Failed to create deferred device context");
}
public void Flush()
{
com_ptr!ID3D11CommandList commandList;
if(FAILED(m_context.FinishCommandList(true, commandList)))
return;
m_context.ExecuteCommandList(commandList, true);
}
}
public class ShaderResource
{
protected ID3D11ShaderResourceView m_resourceView;
public ~this()
{
SafeRelease(m_resourceView);
}
}
public class Texture1D : ShaderResource
{
protected dword m_size;
protected dword m_mipLevels;
protected SurfaceFormat m_format;
protected ResourceUsage m_usage;
protected ID3D11Texture1D m_texture;
protected this(dword size, SurfaceFormat format, dword mipLevels, ResourceUsage usage)
{
m_size = size;
m_format = format;
m_mipLevels = mipLevels;
m_usage = usage;
}
private this(ID3D11Device device, dword size, SurfaceFormat format, dword mipLevels, ResourceUsage usage, const void* data)
{
this(size, format, mipLevels, usage);
D3D11_TEXTURE1D_DESC desc =
{
ArraySize : 1,
BindFlags : D3D11_BIND_SHADER_RESOURCE,
Width : size,
MipLevels : mipLevels,
Usage : type_cast!D3D11_USAGE(usage),
Format : type_cast!DXGI_FORMAT(format),
MiscFlags : 0,
CPUAccessFlags : 0
};
D3D11_SUBRESOURCE_DATA[] initData = data ? FillInitialData(size, 1, 1, format, mipLevels, 1, data) : null;
HRESULT hr = device.CreateTexture1D(desc, initData ? initData.ptr : null, m_texture);
if(FAILED(hr))
throw new RenderException("Failed to create 1D texture");
hr = device.CreateShaderResourceView(m_texture, null, m_resourceView);
if(FAILED(hr))
throw new RenderException("Failed to create shader resource view");
}
public ~this()
{
SafeRelease(m_texture);
}
@property
public dword Size() const
{
return m_size;
}
@property
public dword MipLevels() const
{
return m_mipLevels;
}
@property
public SurfaceFormat Format() const
{
return m_format;
}
@property
public ResourceUsage Usage() const
{
return m_usage;
}
public void Update(RenderContext context, const void* data, int mip)
{
assert(data != null);
assert(m_usage != ResourceUsage.Immutable);
dword k = 0;
if(mip != -1)
k = D3D11CalcSubresource(mip, 0, MipLevels);
context.m_context.UpdateSubresource(
m_texture,
k,
null,
data,
0,
0);
}
}
public class Texture2D : ShaderResource
{
protected dword m_width;
protected dword m_height;
protected dword m_mipLevels;
protected SurfaceFormat m_format;
protected ResourceUsage m_usage;
protected MultiSampleType m_multiSampleType;
protected ID3D11Texture2D m_texture;
protected this(dword width, dword height, SurfaceFormat format, dword mipLevels, ResourceUsage usage, ref const(MultiSampleType) multiSampleType)
{
m_width = width;
m_height = height;
m_format = format;
m_mipLevels = mipLevels;
m_usage = usage;
m_multiSampleType = multiSampleType;
}
private this(ID3D11Device device, dword width, dword height, SurfaceFormat format, dword mipLevels, ResourceUsage usage, ref const(MultiSampleType) multiSampleType, const void* data)
{
this(width, height, format, mipLevels, usage, multiSampleType);
D3D11_TEXTURE2D_DESC desc =
{
ArraySize : 1,
BindFlags : D3D11_BIND_SHADER_RESOURCE,
Width : width,
Height : height,
MipLevels : mipLevels,
Usage : type_cast!D3D11_USAGE(usage),
Format : type_cast!DXGI_FORMAT(format),
MiscFlags : 0,
CPUAccessFlags : 0,
SampleDesc :
{
Count : multiSampleType.SampleCount,
Quality : multiSampleType.Quality
}
};
D3D11_SUBRESOURCE_DATA[] initData = data ? FillInitialData(width, height, 1, format, mipLevels, 1, data) : null;
HRESULT hr = device.CreateTexture2D(desc, initData ? initData.ptr : null, m_texture);
if(FAILED(hr))
throw new RenderException("Failed to create 2D texture");
hr = device.CreateShaderResourceView(m_texture, null, m_resourceView);
if(FAILED(hr))
throw new RenderException("Failed to create shader resource view");
}
public ~this()
{
SafeRelease(m_texture);
}
@property
public dword Width() const
{
return m_width;
}
@property
public dword Height() const
{
return m_height;
}
@property
public dword MipLevels() const
{
return m_mipLevels;
}
@property
public SurfaceFormat Format() const
{
return m_format;
}
@property
public ref const(MultiSampleType) GetMultiSampleType() const
{
return m_multiSampleType;
}
@property
public ResourceUsage Usage() const
{
return m_usage;
}
public void Update(RenderContext context, const void* data, int mip)
{
assert(data != null);
assert(m_usage != ResourceUsage.Immutable);
dword k = 0;
if(mip != -1)
k = D3D11CalcSubresource(mip, 0, MipLevels);
context.m_context.UpdateSubresource(
m_texture,
k,
null,
data,
CalculateImageSize(Width, 1, 1, Format),
0);
}
}
public class Texture3D : ShaderResource
{
protected dword m_width;
protected dword m_height;
protected dword m_depth;
protected dword m_mipLevels;
protected SurfaceFormat m_format;
protected ResourceUsage m_usage;
protected ID3D11Texture3D m_texture;
protected this(dword width, dword height, dword depth, SurfaceFormat format, dword mipLevels, ResourceUsage usage)
{
m_width = width;
m_height = height;
m_depth = depth;
m_format = format;
m_mipLevels = mipLevels;
m_usage = usage;
}
private this(ID3D11Device device, dword width, dword height, dword depth, SurfaceFormat format, dword mipLevels, ResourceUsage usage, const void* data)
{
this(width, height, depth, format, mipLevels, usage);
D3D11_TEXTURE3D_DESC desc =
{
BindFlags : D3D11_BIND_SHADER_RESOURCE,
Width : width,
Height : height,
Depth : depth,
MipLevels : mipLevels,
Usage : type_cast!D3D11_USAGE(usage),
Format : type_cast!DXGI_FORMAT(format),
MiscFlags : 0,
CPUAccessFlags : 0
};
D3D11_SUBRESOURCE_DATA[] initData = data ? FillInitialData(width, height, depth, format, mipLevels, 1, data) : null;
HRESULT hr = device.CreateTexture3D(desc, initData ? initData.ptr : null, m_texture);
if(FAILED(hr))
throw new RenderException("Failed to create 3D texture");
hr = device.CreateShaderResourceView(m_texture, null, m_resourceView);
if(FAILED(hr))
throw new RenderException("Failed to create shader resource view");
}
public ~this()
{
SafeRelease(m_texture);
}
public dword Width() const
{
return m_width;
}
@property
public dword Height() const
{
return m_height;
}
@property
public dword Depth() const
{
return m_depth;
}
@property
public dword MipLevels() const
{
return m_mipLevels;
}
@property
public SurfaceFormat Format() const
{
return m_format;
}
@property
public ResourceUsage Usage() const
{
return m_usage;
}
}
public class TextureCube : ShaderResource
{
protected dword m_size;
protected dword m_mipLevels;
protected SurfaceFormat m_format;
protected ResourceUsage m_usage;
protected MultiSampleType m_multiSampleType;
protected ID3D11Texture2D m_texture;
protected this(dword size, SurfaceFormat format, dword mipLevels, ResourceUsage usage, ref const(MultiSampleType) multiSampleType)
{
m_size = size;
m_format = format;
m_mipLevels = mipLevels;
m_usage = usage;
m_multiSampleType = multiSampleType;
}
private this(ID3D11Device device, dword size, SurfaceFormat format, dword mipLevels, ResourceUsage usage, ref const(MultiSampleType) multiSampleType, const void* data)
{
this(size, format, mipLevels, usage, multiSampleType);
D3D11_TEXTURE2D_DESC desc =
{
ArraySize : 6,
BindFlags : D3D11_BIND_SHADER_RESOURCE,
Width : size,
Height : size,
MipLevels : mipLevels,
Usage : type_cast!D3D11_USAGE(usage),
Format : type_cast!DXGI_FORMAT(format),
MiscFlags : D3D11_RESOURCE_MISC_TEXTURECUBE,
CPUAccessFlags : 0,
SampleDesc :
{
Count : multiSampleType.SampleCount,
Quality : multiSampleType.Quality
}
};
D3D11_SUBRESOURCE_DATA[] initData = data ? FillInitialData(size, size, 1, format, mipLevels, 6, data) : null;
HRESULT hr = device.CreateTexture2D(desc, initData ? initData.ptr : null, m_texture);
if(FAILED(hr))
throw new RenderException("Failed to create cube texture");
hr = device.CreateShaderResourceView(m_texture, null, m_resourceView);
if(FAILED(hr))
throw new RenderException("Failed to create shader resource view");
}
public ~this()
{
SafeRelease(m_texture);
}
@property
public dword Size() const
{
return m_size;
}
@property
public SurfaceFormat Format() const
{
return m_format;
}
@property
public ref const(MultiSampleType) GetMultiSampleType() const
{
return m_multiSampleType;
}
@property
public ResourceUsage Usage() const
{
return m_usage;
}
}
public class RenderTarget : Texture2D
{
private ID3D11RenderTargetView m_rtView;
protected this(dword width, dword height, SurfaceFormat format, ref const(MultiSampleType) multiSampleType)
{
super(width, height, format, 1, ResourceUsage.Default, multiSampleType);
}
private this(ID3D11Device device, dword width, dword height, SurfaceFormat format, ref const(MultiSampleType) multiSampleType)
{
this(width, height, format, multiSampleType);
D3D11_TEXTURE2D_DESC desc =
{
ArraySize : 1,
BindFlags : D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE,
Width : width,
Height : height,
MipLevels : 1,
Usage : D3D11_USAGE_DEFAULT,
Format : type_cast!DXGI_FORMAT(format),
CPUAccessFlags : 0,
MiscFlags : 0,
SampleDesc :
{
Count : multiSampleType.SampleCount,
Quality : multiSampleType.Quality
}
};
HRESULT hr = device.CreateTexture2D(desc, null, m_texture);
if(FAILED(hr))
throw new RenderException("Failed to create 2D texture");
hr = device.CreateRenderTargetView(m_texture, null, m_rtView);
if(FAILED(hr))
throw new RenderException("Failed to create render target view");
hr = device.CreateShaderResourceView(m_texture, null, m_resourceView);
if(FAILED(hr))
throw new RenderException("Failed to create shader resource view");
}
public ~this()
{
SafeRelease(m_rtView);
}
}
public final class BackBuffer : RenderTarget
{
private IDXGIFactory m_factory;
private IDXGISwapChain m_swapChain;
private IDXGIOutput m_output;
private ID3D11Device m_device;
private bool m_vsync;
private this(IDXGIFactory factory, ID3D11Device device, IDXGIOutput output, ref const(DXGI_SWAP_CHAIN_DESC) desc, dword width, dword height, SurfaceFormat format, ref const(MultiSampleType) multiSampleType)
{
assert(output !is null);
super(width, height, format, multiSampleType);
m_factory = factory;
m_device = device;
m_output = output;
m_factory.AddRef();
m_device.AddRef();
m_output.AddRef();
HRESULT hr = factory.CreateSwapChain(device, desc, m_swapChain);
if(FAILED(hr))
throw new RenderException("Failed to create swap chain");
CreateViews();
}
public ~this()
{
if(m_swapChain)
m_swapChain.SetFullscreenState(false, null);
SafeRelease(m_swapChain);
SafeRelease(m_output);
SafeRelease(m_device);
SafeRelease(m_factory);
}
public void Present(bool vsync = false)
{
assert(m_swapChain !is null);
m_swapChain.Present(vsync ? 1 : 0, 0);
}
public void ChangeDisplayMode(ref const(DisplayMode) mode, bool fullscreen)
{
ChangeDisplayMode(mode, fullscreen, GetMultiSampleType());
}
public void ChangeDisplayMode(ref const(DisplayMode) mode, bool fullscreen, ref const(MultiSampleType) multiSampleType)
{
try
{
Log.Info("Back Buffer: Changing display mode to %ux%u %uHz (%s), samples: %u, quality: %u", mode.width, mode.height, mode.refreshRate, fullscreen ? "fullscreen" : "windowed", multiSampleType.SampleCount, multiSampleType.Quality);
DXGI_SWAP_CHAIN_DESC desc = void;
HRESULT hr = m_swapChain.GetDesc(desc);
if(FAILED(hr))
throw new RenderException("Failed to retrieve swap chain information");
desc.Windowed = !fullscreen;
desc.BufferDesc = GetMatchingDXGIMode(m_device, m_output, Format, mode, fullscreen);
if(CheckFormatSupport(m_device, Format, multiSampleType))
{
desc.SampleDesc.Count = multiSampleType.SampleCount;
desc.SampleDesc.Quality = multiSampleType.Quality;
}
else
{
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
Log.Warning("Combination of specified surface format and multisample type is not supported. Falling back to no multisampling");
}
m_swapChain.SetFullscreenState(false, null);
SafeRelease(m_rtView);
SafeRelease(m_resourceView);
SafeRelease(m_texture);
SafeRelease(m_swapChain);
hr = m_factory.CreateSwapChain(m_device, desc, m_swapChain);
if(FAILED(hr))
throw new RenderException("Failed to create swap chain");
CreateViews();
m_width = desc.BufferDesc.Width;
m_height = desc.BufferDesc.Height;
}
catch(RenderException e)
{
Log.Error("Back Buffer: %s", e.toString());
throw e;
}
}
public void Resize(dword width, dword height)
{
if(m_width == width && m_height == height)
return;
Log.Info("Back Buffer: Resizing to %ux%u", width, height);
try
{
DXGI_SWAP_CHAIN_DESC desc = void;
HRESULT hr = m_swapChain.GetDesc(desc);
if(FAILED(hr))
throw new RenderException("Failed to retrieve swap chain information");
desc.BufferDesc.Width = width;
desc.BufferDesc.Height = height;
SafeRelease(m_rtView);
SafeRelease(m_resourceView);
SafeRelease(m_texture);
hr = m_swapChain.ResizeBuffers(
desc.BufferCount,
desc.BufferDesc.Width,
desc.BufferDesc.Height,
desc.BufferDesc.Format,
desc.Flags);
if(FAILED(hr))
throw new RenderException("Failed to resize swap chain buffers");
CreateViews();
m_width = desc.BufferDesc.Width;
m_height = desc.BufferDesc.Height;
}
catch(RenderException e)
{
Log.Error("Back Buffer: %s", e.toString());
throw e;
}
}
@property
public bool VSync() const
{
return m_vsync;
}
@property
public bool VSync(bool vsync)
{
return m_vsync = vsync;
}
public void Present()
{
m_swapChain.Present(m_vsync ? 1 : 0, 0);
}
private void CreateViews()
{
assert(m_texture is null);
assert(m_resourceView is null);
assert(m_rtView is null);
HRESULT hr = m_swapChain.GetBuffer(0, &IID_ID3D11Texture2D, cast(void**)&m_texture);
if(FAILED(hr))
throw new RenderException("Failed to obtain back buffer texture");
hr = m_device.CreateRenderTargetView(m_texture, null, m_rtView);
if(FAILED(hr))
throw new RenderException("Failed to create render target view");
hr = m_device.CreateShaderResourceView(m_texture, null, m_resourceView);
if(FAILED(hr))
throw new RenderException("Failed to create shader resource view");
}
}
public final class DepthStencilBuffer : public Texture2D
{
private ID3D11DepthStencilView m_dsView;
private this(ID3D11Device device, dword width, dword height, SurfaceFormat format, ref const(MultiSampleType) multiSampleType)
{
super(width, height, format, 1, ResourceUsage.Default, multiSampleType);
immutable(DepthStencilFormatDesc)* formats;
switch(format)
{
case SurfaceFormat.D16: formats = &internalFormats[0]; break;
case SurfaceFormat.D24S8: formats = &internalFormats[1]; break;
case SurfaceFormat.D32F: formats = &internalFormats[2]; break;
default:
break;
}
D3D11_TEXTURE2D_DESC desc =
{
ArraySize : 1,
BindFlags : D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE,
Width : width,
Height : height,
MipLevels : 1,
Usage : D3D11_USAGE_DEFAULT,
Format : formats.surfaceFormat,
CPUAccessFlags : 0,
MiscFlags : 0,
SampleDesc :
{
Count : multiSampleType.SampleCount,
Quality : multiSampleType.Quality
}
};
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = { Format : formats.DSVFormat };
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = { Format : formats.SRVFormat };
if(multiSampleType != MultiSampleType.None)
{
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS;
srvDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2DMS;
}
else
{
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
srvDesc.ViewDimension = D3D_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
}
HRESULT hr = device.CreateTexture2D(desc, null, m_texture);
if(FAILED(hr))
throw new RenderException("Failed to create 2D texture");
hr = device.CreateDepthStencilView(m_texture, &dsvDesc, m_dsView);
if(FAILED(hr))
throw new RenderException("Failed to create depth-stencil view");
hr = device.CreateShaderResourceView(m_texture, &srvDesc, m_resourceView);
if(FAILED(hr))
throw new RenderException("Failed to create shader resource view");
}
public ~this()
{
SafeRelease(m_dsView);
}
}
public final class ConstantBuffer
{
private ID3D11Buffer m_buffer;
private dword m_size;
private this(ID3D11Device device, dword size, const void* data)
{
D3D11_BUFFER_DESC desc =
{
BindFlags : D3D11_BIND_CONSTANT_BUFFER,
ByteWidth : size,
CPUAccessFlags : 0,
MiscFlags : 0,
Usage : D3D11_USAGE_DEFAULT,
StructureByteStride : 0
};
D3D11_SUBRESOURCE_DATA dataDesc = { data, 0, 0 };
HRESULT hr = device.CreateBuffer(desc, data ? &dataDesc : null, m_buffer);
if(FAILED(hr))
throw new RenderException("Failed to create constant buffer");
m_size = size;
}
public ~this()
{
SafeRelease(m_buffer);
}
@property
public dword Size() const
{
return m_size;
}
public void Update(RenderContext context, const void* data)
{
assert(context !is null);
context.m_context.UpdateSubresource(m_buffer, 0, null, data, 0, 0);
}
}
public final class VertexBuffer
{
private ID3D11Buffer m_buffer;
private dword m_count;
private dword m_vertexSize;
private this(ID3D11Device device, dword count, dword vertexSize, const void* data)
{
D3D11_BUFFER_DESC desc =
{
BindFlags : D3D11_BIND_VERTEX_BUFFER,
ByteWidth : count * vertexSize,
CPUAccessFlags : 0,
MiscFlags : 0,
Usage : D3D11_USAGE_DEFAULT,
StructureByteStride : 0
};
D3D11_SUBRESOURCE_DATA dataDesc = { data, 0, 0 };
HRESULT hr = device.CreateBuffer(desc, data ? &dataDesc : null, m_buffer);
if(FAILED(hr))
throw new RenderException("Failed to create vertex buffer");
m_count = count;
m_vertexSize = vertexSize;
}
public ~this()
{
SafeRelease(m_buffer);
}
@property
public dword Count() const
{
return m_count;
}
@property
public dword VertexSize() const
{
return m_vertexSize;
}
public void Update(RenderContext context, const void* data)
{
assert(context !is null);
context.m_context.UpdateSubresource(m_buffer, 0, null, data, 0, 0);
}
}
public final class IndexBuffer
{
private ID3D11Buffer m_buffer;
private dword m_count;
private IndexFormat m_format;
private this(ID3D11Device device, dword count, IndexFormat format, const void* data)
{
D3D11_BUFFER_DESC desc =
{
BindFlags : D3D11_BIND_INDEX_BUFFER,
ByteWidth : count * GetIndexSize(format),
CPUAccessFlags : 0,
MiscFlags : 0,
Usage : D3D11_USAGE_DEFAULT,
StructureByteStride : 0
};
D3D11_SUBRESOURCE_DATA dataDesc = { data, 0, 0 };
HRESULT hr = device.CreateBuffer(desc, data ? &dataDesc : null, m_buffer);
if(FAILED(hr))
throw new RenderException("Failed to create index buffer");
m_count = count;
m_format = format;
}
public ~this()
{
SafeRelease(m_buffer);
}
@property
public dword Count() const
{
return m_count;
}
@property
public IndexFormat Format() const
{
return m_format;
}
public void Update(RenderContext context, const void* data)
{
assert(context !is null);
context.m_context.UpdateSubresource(m_buffer, 0, null, data, 0, 0);
}
}
public enum ShaderVariableType
{
CBuffer,
Texture,
Sampler,
}
public struct ShaderVariable
{
ShaderVariableType type;
dword slot;
}
private class Shader
{
protected ShaderVariable[string] m_variables;
public ShaderVariable* GetVariable(string name)
{
return name in m_variables;
}
protected final void ExtractVariables(const void* data, size_t size)
{
com_ptr!ID3D11ShaderReflection reflector;
HRESULT hr = D3DReflect(
data,
size,
&IID_ID3D11ShaderReflection,
cast(void**)&reflector);
if(FAILED(hr))
throw new RenderException("Failed to retrieve shader reflection data");
D3D11_SHADER_DESC desc = void;
hr = reflector.GetDesc(desc);
if(FAILED(hr))
throw new RenderException("Failed to retrieve shader information");
for(dword i = 0; i < desc.BoundResources; i++)
{
D3D11_SHADER_INPUT_BIND_DESC resourceDesc = void;
hr = reflector.GetResourceBindingDesc(i, resourceDesc);
if(FAILED(hr))
throw new RenderException("Failed to retrieve shader resource binding information");
ShaderVariableType type;
switch(resourceDesc.Type)
{
case D3D_SIT_CBUFFER:
type = ShaderVariableType.CBuffer;
break;
case D3D_SIT_TEXTURE:
type = ShaderVariableType.Texture;
break;
case D3D_SIT_SAMPLER:
type = ShaderVariableType.Sampler;
break;
default:
continue;
}
m_variables[to!string(resourceDesc.Name)] = ShaderVariable(type, resourceDesc.BindPoint);
}
}
}
public final class VertexShader : Shader
{
private ID3D11VertexShader m_shader;
private this(ID3D11Device device, const void* data, size_t size)
{
assert(data != null);
HRESULT hr = device.CreateVertexShader(data, size, null, m_shader);
if(FAILED(hr))
throw new RenderException("Failed to create vertex shader");
ExtractVariables(data, size);
}
public ~this()
{
SafeRelease(m_shader);
}
}
public final class FragmentShader : Shader
{
private ID3D11PixelShader m_shader;
private this(ID3D11Device device, const void* data, size_t size)
{
assert(data != null);
HRESULT hr = device.CreatePixelShader(data, size, null, m_shader);
if(FAILED(hr))
throw new RenderException("Failed to create fragment shader");
ExtractVariables(data, size);
}
public ~this()
{
SafeRelease(m_shader);
}
}
public final class GeometryShader : Shader
{
private ID3D11GeometryShader m_shader;
private this(ID3D11Device device, const void* data, size_t size)
{
assert(data != null);
HRESULT hr = device.CreateGeometryShader(data, size, null, m_shader);
if(FAILED(hr))
throw new RenderException("Failed to create geometry shader");
ExtractVariables(data, size);
}
public ~this()
{
SafeRelease(m_shader);
}
}
public final class HullShader : Shader
{
private ID3D11HullShader m_shader;
private this(ID3D11Device device, const void* data, size_t size)
{
assert(data != null);
HRESULT hr = device.CreateHullShader(data, size, null, m_shader);
if(FAILED(hr))
throw new RenderException("Failed to create hull shader");
ExtractVariables(data, size);
}
public ~this()
{
SafeRelease(m_shader);
}
}
public final class DomainShader : Shader
{
private ID3D11DomainShader m_shader;
private this(ID3D11Device device, const void* data, size_t size)
{
assert(data != null);
HRESULT hr = device.CreateDomainShader(data, size, null, m_shader);
if(FAILED(hr))
throw new RenderException("Failed to create domain shader");
ExtractVariables(data, size);
}
public ~this()
{
SafeRelease(m_shader);
}
}
public final class VertexLayout
{
private dword m_vertexSize;
private ID3D11InputLayout m_layout;
private this(ID3D11Device device, const VertexElement[] elements)
{
D3D11_INPUT_ELEMENT_DESC[] d3dElements;
d3dElements.length = elements.length;
auto buf = appender!string();
buf.put("struct VS_INPUT\n");
buf.put("{\n");
dword offset = 0;
for(dword i = 0; i < elements.length; i++)
{
d3dElements[i].AlignedByteOffset = offset;
d3dElements[i].Format = type_cast!DXGI_FORMAT(elements[i].type);
d3dElements[i].SemanticIndex = elements[i].index;
d3dElements[i].SemanticName = GetSemanticName(elements[i].usage);
offset += GetSizeInBytes(elements[i].type);
formattedWrite(buf, "\t%s element%u : %s%u;\n", GetHLSLTypeName(elements[i].type), i, to!string(d3dElements[i].SemanticName), d3dElements[i].SemanticIndex);
}
buf.put("};\n\n");
buf.put("void vs_main(VS_INPUT In)\n");
buf.put("{\n");
buf.put("}\n");
ShaderCompiler compiler;
if(!compiler.Compile(buf.data, "vs_4_0", "vs_main"))
throw new RenderException("Failed to compile vertex shader");
HRESULT hr = device.CreateInputLayout(
d3dElements.ptr,
d3dElements.length,
compiler.InputSignature,
compiler.InputSignatureSize,
m_layout);
if(FAILED(hr))
throw new RenderException("Failed to create input layout");
m_vertexSize = offset;
}
public ~this()
{
SafeRelease(m_layout);
}
@property
public dword GetVertexSize() const
{
return m_vertexSize;
}
}
/**
* Describes a sampler state.
*/
public final class SamplerState
{
private ID3D11SamplerState m_state;
private this(ID3D11Device device, const ref SamplerStateDesc desc)
{
D3D11_SAMPLER_DESC desc =
{
Filter : D3D11_ENCODE_BASIC_FILTER(type_cast!D3D11_FILTER_TYPE(desc.MinFilter),
type_cast!D3D11_FILTER_TYPE(desc.MagFilter),
type_cast!D3D11_FILTER_TYPE(desc.MipFilter),
false),
AddressU : type_cast!D3D11_TEXTURE_ADDRESS_MODE(desc.AddressU),
AddressV : type_cast!D3D11_TEXTURE_ADDRESS_MODE(desc.AddressV),
AddressW : type_cast!D3D11_TEXTURE_ADDRESS_MODE(desc.AddressW),
MipLODBias : 0.0f,
MaxAnisotropy : desc.MaxAnisotropy,
ComparisonFunc : D3D11_COMPARISON_NEVER,
BorderColor : [ 1.0f, 1.0f, 1.0f, 1.0f ],
MinLOD : -float.max,
MaxLOD : float.max
};
HRESULT hr = device.CreateSamplerState(desc, m_state);
if(FAILED(hr))
throw new RenderException("Failed to create sampler state object");
}
}
/**
* Describes depth-stencil state.
*/
public final class DepthStencilState
{
private ID3D11DepthStencilState m_state;
private this(ID3D11Device device, const ref DepthStencilStateDesc desc)
{
D3D11_DEPTH_STENCIL_DESC desc =
{
DepthEnable : desc.DepthEnable,
DepthWriteMask : desc.DepthWriteEnable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO,
DepthFunc : type_cast!D3D11_COMPARISON_FUNC(desc.DepthFunc),
StencilEnable : desc.StencilEnable,
StencilReadMask : D3D11_DEFAULT_STENCIL_READ_MASK,
StencilWriteMask : D3D11_DEFAULT_STENCIL_WRITE_MASK,
FrontFace :
{
StencilFailOp : D3D11_STENCIL_OP_KEEP,
StencilDepthFailOp : D3D11_STENCIL_OP_KEEP,
StencilPassOp : D3D11_STENCIL_OP_KEEP,
StencilFunc : D3D11_COMPARISON_ALWAYS
},
BackFace :
{
StencilFailOp : D3D11_STENCIL_OP_KEEP,
StencilDepthFailOp : D3D11_STENCIL_OP_KEEP,
StencilPassOp : D3D11_STENCIL_OP_KEEP,
StencilFunc : D3D11_COMPARISON_ALWAYS
}
};
HRESULT hr = device.CreateDepthStencilState(desc, m_state);
if(FAILED(hr))
throw new RenderException("Failed to create depth-stencil state object");
}
}
/**
* Describes rasterizer state.
*/
public final class RasterizerState
{
private ID3D11RasterizerState m_state;
private this(ID3D11Device device, const ref RasterizerStateDesc desc)
{
D3D11_RASTERIZER_DESC desc =
{
FillMode : type_cast!D3D11_FILL_MODE(desc.fillMode),
CullMode : type_cast!D3D11_CULL_MODE(desc.cullMode),
FrontCounterClockwise : false,
DepthBias : D3D11_DEFAULT_DEPTH_BIAS,
DepthBiasClamp : D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
SlopeScaledDepthBias : D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
DepthClipEnable : true,
ScissorEnable : false,
MultisampleEnable : false,
AntialiasedLineEnable : false
};
HRESULT hr = device.CreateRasterizerState(desc, m_state);
if(FAILED(hr))
throw new RenderException("Failed to create rasterizer state object");
}
}
/**
* Describes blend state.
*/
public final class BlendState
{
private ID3D11BlendState m_state;
private this(ID3D11Device device, const ref BlendStateDesc desc)
{
D3D11_BLEND_DESC desc =
{
AlphaToCoverageEnable : desc.alphaToCoverageEnable,
IndependentBlendEnable : false,
RenderTarget :
[
{
BlendEnable : desc.blendEnable,
SrcBlend : type_cast!D3D11_BLEND(desc.srcBlend),
DestBlend : type_cast!D3D11_BLEND(desc.destBlend),
BlendOp : type_cast!D3D11_BLEND_OP(desc.blendOp),
SrcBlendAlpha : type_cast!D3D11_BLEND(desc.srcBlendAlpha),
DestBlendAlpha : type_cast!D3D11_BLEND(desc.destBlendAlpha),
BlendOpAlpha : type_cast!D3D11_BLEND_OP(desc.blendOpAlpha),
RenderTargetWriteMask : D3D11_COLOR_WRITE_ENABLE_ALL
}
]
};
HRESULT hr = device.CreateBlendState(desc, m_state);
if(FAILED(hr))
throw new RenderException("Failed to create blend state object");
}
}
public dword GetIndexSize(IndexFormat format)
{
final switch(format)
{
case IndexFormat.I16: return word.sizeof;
case IndexFormat.I32: return dword.sizeof;
}
}
public bool IsColorFormat(SurfaceFormat format)
{
switch(format)
{
case SurfaceFormat.R8:
case SurfaceFormat.RG8:
case SurfaceFormat.RGB8:
case SurfaceFormat.RGBA8:
case SurfaceFormat.R16:
case SurfaceFormat.R16F:
case SurfaceFormat.RG16:
case SurfaceFormat.RG16F:
case SurfaceFormat.RGBA16:
case SurfaceFormat.RGBA16F:
case SurfaceFormat.R32F:
case SurfaceFormat.RG32F:
case SurfaceFormat.RGB32F:
case SurfaceFormat.RGBA32F:
return true;
default:
return false;
}
}
public bool IsDepthStencilFormat(SurfaceFormat format)
{
switch(format)
{
case SurfaceFormat.D16:
case SurfaceFormat.D24S8:
case SurfaceFormat.D32F:
return true;
default:
return false;
}
}
public bool IsCompressedFormat(SurfaceFormat format)
{
switch(format)
{
case SurfaceFormat.DXT1:
case SurfaceFormat.DXT2:
case SurfaceFormat.DXT3:
case SurfaceFormat.DXT4:
case SurfaceFormat.DXT5:
return true;
default:
return false;
}
}
// Returns pixel size for not compressed formats, block size - for compressed
public dword GetNumBytesPerUnit(SurfaceFormat format)
{
final switch(format)
{
case SurfaceFormat.Unknown: return 0;
// Color formats
case SurfaceFormat.R8: return 1;
case SurfaceFormat.RG8: return 2;
case SurfaceFormat.RGB8: return 3;
case SurfaceFormat.RGBA8: return 4;
case SurfaceFormat.R16: return 2;
case SurfaceFormat.R16F: return 2;
case SurfaceFormat.RG16: return 4;
case SurfaceFormat.RG16F: return 4;
case SurfaceFormat.RGBA16: return 8;
case SurfaceFormat.RGBA16F: return 8;
case SurfaceFormat.R32F: return 4;
case SurfaceFormat.RG32F: return 8;
case SurfaceFormat.RGB32F: return 12;
case SurfaceFormat.RGBA32F: return 16;
// Compressed formats
case SurfaceFormat.DXT1: return 8;
case SurfaceFormat.DXT2:
case SurfaceFormat.DXT3:
case SurfaceFormat.DXT4:
case SurfaceFormat.DXT5: return 16;
// Depth-Stencil formats
case SurfaceFormat.D16: return 2;
case SurfaceFormat.D24S8: return 4;
case SurfaceFormat.D32F: return 4;
}
}
public dword CalculateImageSize(dword width, dword height, dword depth, SurfaceFormat format)
{
assert(width > 0);
assert(height > 0);
assert(depth > 0);
assert(format != SurfaceFormat.Unknown);
dword size;
if(IsCompressedFormat(format))
size = ((width + 3) >> 2) * ((height + 3) >> 2) * depth;
else
size = width * height * depth;
return size * GetNumBytesPerUnit(format);
}
public dword CalculateImageSize(dword width, dword height, dword depth, SurfaceFormat format, dword mipMaps)
{
assert(width > 0);
assert(height > 0);
assert(depth > 0);
assert(format != SurfaceFormat.Unknown);
dword size = 0;
for(dword i = 0; i < mipMaps; ++i)
{
size += CalculateImageSize(width, height, depth, format);
if(width > 1) width >>= 1;
if(height > 1) height >>= 1;
if(depth > 1) depth >>= 1;
}
return size;
}
public dword CalculateNumMipLevels(dword width, dword height, dword depth)
{
assert(width > 0);
assert(height > 0);
assert(depth > 0);
dword count = 1;
while(width > 1 || height > 1 || depth > 1)
{
++count;
if(width > 1) width >>= 1;
if(height > 1) height >>= 1;
if(depth > 1) depth >>= 1;
}
return count;
}
private:
struct ShaderCompiler
{
private ID3DBlob m_errors;
private ID3DBlob m_compiledCode;
private ID3DBlob m_inputSignature;
public ~this()
{
SafeRelease(m_errors);
SafeRelease(m_compiledCode);
SafeRelease(m_inputSignature);
}
public bool Compile(string code, string profile, string entryPoint)
{
SafeRelease(m_errors);
SafeRelease(m_compiledCode);
SafeRelease(m_inputSignature);
HRESULT hr = D3DCompile(
toStringz(code),
code.length,
null,
null,
null,
toStringz(entryPoint),
toStringz(profile),
0,
0,
m_compiledCode,
&m_errors);
if(FAILED(hr))
{
Log.Error("Shader Compiler: Failed to compile shader using profile \"%s\"", profile);
if(m_errors !is null)
Log.Error("Shader Compiler: %s", to!string(cast(const char*)m_errors.GetBufferPointer()));
return false;
}
hr = D3DGetInputSignatureBlob(
m_compiledCode.GetBufferPointer(),
m_compiledCode.GetBufferSize(),
m_inputSignature);
return SUCCEEDED(hr);
}
@property
public size_t CompiledSize()
{
assert(m_compiledCode !is null);
return m_compiledCode.GetBufferSize();
}
@property
public const(void)* CompiledCode()
{
assert(m_compiledCode !is null);
return m_compiledCode.GetBufferPointer();
}
@property
public size_t InputSignatureSize()
{
assert(m_inputSignature !is null);
return m_inputSignature.GetBufferSize();
}
@property
public const(void)* InputSignature()
{
assert(m_inputSignature !is null);
return m_inputSignature.GetBufferPointer();
}
}
bool CheckFormatSupport(ID3D11Device device, SurfaceFormat format, ref const(MultiSampleType) multiSampleType)
{
assert(device !is null);
DXGI_FORMAT internalFormat = type_cast!DXGI_FORMAT(format);
dword supportCaps;
HRESULT hr = device.CheckFormatSupport(internalFormat, supportCaps);
if(FAILED(hr))
return false;
if(!(supportCaps & (D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_DEPTH_STENCIL)))
return false;
if(multiSampleType != MultiSampleType.None)
{
if(!(supportCaps & (D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET)))
return false;
dword numLevels;
hr = device.CheckMultisampleQualityLevels(internalFormat, multiSampleType.SampleCount, numLevels);
if(FAILED(hr))
return false;
if(multiSampleType.Quality >= numLevels)
return false;
}
return true;
}
D3D11_SUBRESOURCE_DATA[] FillInitialData(dword width, dword height, dword depth, SurfaceFormat format, dword mipLevels, dword arraySize, const void* data)
{
D3D11_SUBRESOURCE_DATA[] initData;
initData.length = arraySize * mipLevels;
const(ubyte)* src = cast(const(ubyte)*)data;
dword k = 0;
for(dword i = 0; i < arraySize; i++)
{
dword layerWidth = width;
dword layerHeight = height;
dword layerDepth = depth;
for(dword j = 0; j < mipLevels; j++)
{
initData[k].pSysMem = src;
initData[k].SysMemPitch = height > 1 ? CalculateImageSize(layerWidth, 1, 1, format) : 0;
initData[k].SysMemSlicePitch = depth > 1 ? CalculateImageSize(layerWidth, layerHeight, 1, format) : 0;
src += CalculateImageSize(layerWidth, layerHeight, layerDepth, format);
if(layerWidth > 1) layerWidth >>= 1;
if(layerHeight > 1) layerHeight >>= 1;
if(layerDepth > 1) layerDepth >>= 1;
k++;
}
}
return initData;
}
struct DepthStencilFormatDesc
{
DXGI_FORMAT surfaceFormat;
DXGI_FORMAT DSVFormat;
DXGI_FORMAT SRVFormat;
}
immutable DepthStencilFormatDesc[3] internalFormats =
[
// surface format DSV format SRV format
{ DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_D16_UNORM, DXGI_FORMAT_R16_UNORM },
{ DXGI_FORMAT_R24G8_TYPELESS, DXGI_FORMAT_D24_UNORM_S8_UINT, DXGI_FORMAT_R24_UNORM_X8_TYPELESS },
{ DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_D32_FLOAT, DXGI_FORMAT_R32_FLOAT },
];
dword GetSizeInBytes(VertexElementType type)
{
final switch(type)
{
case VertexElementType.Float: return float.sizeof;
case VertexElementType.Float2: return float.sizeof * 2;
case VertexElementType.Float3: return float.sizeof * 3;
case VertexElementType.Float4: return float.sizeof * 4;
case VertexElementType.Half2: return 2 * 2;
case VertexElementType.Half4: return 2 * 4;
case VertexElementType.UByte4: return ubyte.sizeof * 4;
case VertexElementType.UByte4N: return ubyte.sizeof * 4;
}
}
const(char)* GetSemanticName(VertexElementUsage usage)
{
final switch(usage)
{
case VertexElementUsage.Position: return "POSITION";
case VertexElementUsage.Normal: return "NORMAL";
case VertexElementUsage.Tangent: return "TANGENT";
case VertexElementUsage.Binormal: return "BINORMAL";
case VertexElementUsage.TextureCoord: return "TEXCOORD";
case VertexElementUsage.Color: return "COLOR";
case VertexElementUsage.BlendWeight: return "BLENDWEIGHT";
case VertexElementUsage.BlendIndices: return "BLENDINDICES";
}
}
string GetHLSLTypeName(VertexElementType type)
{
final switch(type)
{
case VertexElementType.Float: return "float";
case VertexElementType.Float2: return "float2";
case VertexElementType.Float3: return "float3";
case VertexElementType.Float4: return "float4";
case VertexElementType.Half2: return "half2";
case VertexElementType.Half4: return "half4";
case VertexElementType.UByte4: return "uint4";
case VertexElementType.UByte4N: return "float4";
}
}
D3D11_BLEND type_cast(T : D3D11_BLEND)(BlendArg value)
{
final switch(value)
{
case BlendArg.Zero: return D3D11_BLEND_ZERO;
case BlendArg.One: return D3D11_BLEND_ONE;
case BlendArg.SrcColor: return D3D11_BLEND_SRC_COLOR;
case BlendArg.InvSrcColor: return D3D11_BLEND_INV_SRC_COLOR;
case BlendArg.SrcAlpha: return D3D11_BLEND_SRC_ALPHA;
case BlendArg.InvSrcAlpha: return D3D11_BLEND_INV_SRC_ALPHA;
case BlendArg.DestAlpha: return D3D11_BLEND_DEST_ALPHA;
case BlendArg.InvDestAlpha: return D3D11_BLEND_INV_DEST_ALPHA;
case BlendArg.DestColor: return D3D11_BLEND_DEST_COLOR;
case BlendArg.InvDestColor: return D3D11_BLEND_INV_DEST_COLOR;
}
}
D3D11_BLEND_OP type_cast(T : D3D11_BLEND_OP)(BlendOp value)
{
final switch(value)
{
case BlendOp.Add: return D3D11_BLEND_OP_ADD;
case BlendOp.Subtract: return D3D11_BLEND_OP_SUBTRACT;
case BlendOp.RevSubtract: return D3D11_BLEND_OP_REV_SUBTRACT;
case BlendOp.Min: return D3D11_BLEND_OP_MIN;
case BlendOp.Max: return D3D11_BLEND_OP_MAX;
}
}
D3D11_COMPARISON_FUNC type_cast(T : D3D11_COMPARISON_FUNC)(ComparisonFunc value)
{
final switch(value)
{
case ComparisonFunc.Never: return D3D11_COMPARISON_NEVER;
case ComparisonFunc.Less: return D3D11_COMPARISON_LESS;
case ComparisonFunc.Equal: return D3D11_COMPARISON_EQUAL;
case ComparisonFunc.LessEqual: return D3D11_COMPARISON_LESS_EQUAL;
case ComparisonFunc.Greater: return D3D11_COMPARISON_GREATER;
case ComparisonFunc.NotEqual: return D3D11_COMPARISON_NOT_EQUAL;
case ComparisonFunc.GreaterEqual: return D3D11_COMPARISON_GREATER_EQUAL;
case ComparisonFunc.Always: return D3D11_COMPARISON_ALWAYS;
}
}
D3D11_CULL_MODE type_cast(T : D3D11_CULL_MODE)(CullMode value)
{
final switch(value)
{
case CullMode.None: return D3D11_CULL_NONE;
case CullMode.Back: return D3D11_CULL_BACK;
case CullMode.Front: return D3D11_CULL_FRONT;
}
}
D3D11_FILL_MODE type_cast(T : D3D11_FILL_MODE)(FillMode value)
{
final switch(value)
{
case FillMode.Solid: return D3D11_FILL_SOLID;
case FillMode.Wireframe: return D3D11_FILL_WIREFRAME;
}
}
DXGI_FORMAT type_cast(T : DXGI_FORMAT)(IndexFormat value)
{
final switch(value)
{
case IndexFormat.I16: return DXGI_FORMAT_R16_UINT;
case IndexFormat.I32: return DXGI_FORMAT_R32_UINT;
}
}
D3D11_PRIMITIVE_TOPOLOGY type_cast(T : D3D11_PRIMITIVE_TOPOLOGY)(PrimitiveType value)
{
final switch(value)
{
case PrimitiveType.Unknown: return D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED;
case PrimitiveType.PointList: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
case PrimitiveType.LineList: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
case PrimitiveType.LineStrip: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
case PrimitiveType.TriangleList: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
case PrimitiveType.TriangleStrip: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
}
}
D3D11_USAGE type_cast(T : D3D11_USAGE)(ResourceUsage value)
{
final switch(value)
{
case ResourceUsage.Default: return D3D11_USAGE_DEFAULT;
case ResourceUsage.Immutable: return D3D11_USAGE_IMMUTABLE;
case ResourceUsage.Dynamic: return D3D11_USAGE_DYNAMIC;
}
}
DXGI_FORMAT type_cast(T : DXGI_FORMAT)(SurfaceFormat value)
{
final switch(value)
{
case SurfaceFormat.Unknown: return DXGI_FORMAT_UNKNOWN;
// Color formats
case SurfaceFormat.R8: return DXGI_FORMAT_R8_UNORM;
case SurfaceFormat.RG8: return DXGI_FORMAT_R8G8_UNORM;
case SurfaceFormat.RGB8: return DXGI_FORMAT_UNKNOWN; // No equivalent
case SurfaceFormat.RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM;
case SurfaceFormat.R16: return DXGI_FORMAT_R16_UNORM;
case SurfaceFormat.R16F: return DXGI_FORMAT_R16_FLOAT;
case SurfaceFormat.RG16: return DXGI_FORMAT_R16G16_UNORM;
case SurfaceFormat.RG16F: return DXGI_FORMAT_R16G16_FLOAT;
case SurfaceFormat.RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM;
case SurfaceFormat.RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT;
case SurfaceFormat.R32F: return DXGI_FORMAT_R32_FLOAT;
case SurfaceFormat.RG32F: return DXGI_FORMAT_R32G32_FLOAT;
case SurfaceFormat.RGB32F: return DXGI_FORMAT_R32G32B32_FLOAT;
case SurfaceFormat.RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT;
// Compressed formats
case SurfaceFormat.DXT1: return DXGI_FORMAT_BC1_UNORM;
case SurfaceFormat.DXT2:
case SurfaceFormat.DXT3:
case SurfaceFormat.DXT4: return DXGI_FORMAT_BC2_UNORM;
case SurfaceFormat.DXT5: return DXGI_FORMAT_BC3_UNORM;
// Depth-Stencil formats
case SurfaceFormat.D16: return DXGI_FORMAT_D16_UNORM;
case SurfaceFormat.D24S8: return DXGI_FORMAT_D24_UNORM_S8_UINT;
case SurfaceFormat.D32F: return DXGI_FORMAT_D32_FLOAT;
}
}
D3D11_TEXTURE_ADDRESS_MODE type_cast(T : D3D11_TEXTURE_ADDRESS_MODE)(TextureAddressMode value)
{
final switch(value)
{
case TextureAddressMode.Clamp: return D3D11_TEXTURE_ADDRESS_CLAMP;
case TextureAddressMode.Wrap: return D3D11_TEXTURE_ADDRESS_WRAP;
case TextureAddressMode.Mirror: return D3D11_TEXTURE_ADDRESS_MIRROR;
case TextureAddressMode.Border: return D3D11_TEXTURE_ADDRESS_BORDER;
}
}
D3D11_FILTER_TYPE type_cast(T : D3D11_FILTER_TYPE)(TextureFilter value)
{
final switch(value)
{
case TextureFilter.Point: return D3D11_FILTER_TYPE_POINT;
case TextureFilter.Linear: return D3D11_FILTER_TYPE_LINEAR;
}
}
DXGI_FORMAT type_cast(T : DXGI_FORMAT)(VertexElementType value)
{
final switch(value)
{
case VertexElementType.Float: return DXGI_FORMAT_R32_FLOAT;
case VertexElementType.Float2: return DXGI_FORMAT_R32G32_FLOAT;
case VertexElementType.Float3: return DXGI_FORMAT_R32G32B32_FLOAT;
case VertexElementType.Float4: return DXGI_FORMAT_R32G32B32A32_FLOAT;
case VertexElementType.Half2: return DXGI_FORMAT_R16G16_FLOAT;
case VertexElementType.Half4: return DXGI_FORMAT_R16G16B16A16_FLOAT;
case VertexElementType.UByte4: return DXGI_FORMAT_R8G8B8A8_UINT;
case VertexElementType.UByte4N: return DXGI_FORMAT_R8G8B8A8_UNORM;
}
}
dword toHz(ref const(DXGI_RATIONAL) refreshRate)
{
return roundTo!uint(refreshRate.Numerator / cast(float)refreshRate.Denominator);
}
DXGI_MODE_DESC[] GetDXGIModeList(IDXGIOutput output, DXGI_FORMAT format)
{
dword numModes;
HRESULT hr = output.GetDisplayModeList(format, 0, numModes, null);
if(FAILED(hr))
throw new RenderException("Failed to retrieve display mode list");
DXGI_MODE_DESC[] modes;
modes.length = numModes;
hr = output.GetDisplayModeList(format, 0, numModes, modes.ptr);
if(FAILED(hr))
throw new RenderException("Failed to retrieve display mode list");
return modes;
}
DXGI_MODE_DESC GetMatchingDXGIMode(ID3D11Device device, IDXGIOutput output, SurfaceFormat format, ref const(DisplayMode) mode, bool fullscreen)
{
DXGI_FORMAT internalFormat = type_cast!DXGI_FORMAT(format);
if(fullscreen)
{
DXGI_MODE_DESC[] modes = GetDXGIModeList(output, internalFormat);
for(dword i = 0; i < modes.length; i++)
{
if(mode.width == modes[i].Width &&
mode.height == modes[i].Height &&
mode.refreshRate == toHz(modes[i].RefreshRate))
{
return modes[i];
}
}
DXGI_MODE_DESC desc =
{
Format : internalFormat,
Width : mode.width,
Height : mode.height,
RefreshRate :
{
Numerator : mode.refreshRate,
Denominator : 1
}
};
HRESULT hr = output.FindClosestMatchingMode(desc, desc, device);
if(FAILED(hr))
throw new RenderException("Cannot find suitable display mode");
Log.Warning("Requested display mode is not available. Falling back to %ux%u %uHz mode", desc.Width, desc.Height, toHz(desc.RefreshRate));
return desc;
}
else
{
DXGI_MODE_DESC desc =
{
Format : internalFormat,
Width : mode.width,
Height : mode.height
};
return desc;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment