994 lines
30 KiB
C#
994 lines
30 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Drawing.Imaging;
|
|
using System.Windows.Forms;
|
|
|
|
using swf = System.Windows.Forms;
|
|
using sd = System.Drawing;
|
|
using sdi = System.Drawing.Imaging;
|
|
|
|
using BizHawk.Bizware.BizwareGL;
|
|
|
|
using SlimDX.Direct3D9;
|
|
using d3d9=SlimDX.Direct3D9;
|
|
using OpenTK;
|
|
using OpenTK.Graphics;
|
|
using gl=OpenTK.Graphics.OpenGL;
|
|
|
|
//todo - do a better job selecting shader model? base on caps somehow? try several and catch compilation exceptions (yuck, exceptions)
|
|
|
|
namespace BizHawk.Bizware.BizwareGL.Drivers.SlimDX
|
|
{
|
|
|
|
public class IGL_SlimDX9 : IGL
|
|
{
|
|
const int D3DERR_DEVICELOST = -2005530520;
|
|
const int D3DERR_DEVICENOTRESET = -2005530519;
|
|
|
|
static Direct3D d3d;
|
|
internal Device dev;
|
|
INativeWindow OffscreenNativeWindow;
|
|
|
|
//rendering state
|
|
IntPtr _pVertexData;
|
|
Pipeline _CurrPipeline;
|
|
GLControlWrapper_SlimDX9 _CurrentControl;
|
|
|
|
public string API { get { return "D3D9"; } }
|
|
|
|
public IGL_SlimDX9()
|
|
{
|
|
if (d3d == null)
|
|
{
|
|
d3d = new Direct3D();
|
|
}
|
|
|
|
//make an 'offscreen context' so we can at least do things without having to create a window
|
|
OffscreenNativeWindow = new OpenTK.NativeWindow();
|
|
OffscreenNativeWindow.ClientSize = new sd.Size(8, 8);
|
|
|
|
CreateDevice();
|
|
CreateRenderStates();
|
|
}
|
|
|
|
public void AlternateVsyncPass(int pass)
|
|
{
|
|
for (; ; )
|
|
{
|
|
var status = dev.GetRasterStatus(0);
|
|
if (status.InVBlank && pass == 0) return; //wait for vblank to begin
|
|
if (!status.InVBlank && pass == 1) return; //wait for vblank to end
|
|
//STOP! think you can use System.Threading.SpinWait? No, it's way too slow.
|
|
//(on my system, the vblank is something like 24 of 1074 scanlines @ 60hz ~= 0.35ms which is an awfully small window to nail)
|
|
}
|
|
}
|
|
|
|
private void DestroyDevice()
|
|
{
|
|
if (dev != null)
|
|
{
|
|
dev.Dispose();
|
|
dev = null;
|
|
}
|
|
}
|
|
|
|
PresentParameters MakePresentParameters()
|
|
{
|
|
return new PresentParameters
|
|
{
|
|
BackBufferWidth = 8,
|
|
BackBufferHeight = 8,
|
|
BackBufferCount = 2,
|
|
DeviceWindowHandle = OffscreenNativeWindow.WindowInfo.Handle,
|
|
PresentationInterval = PresentInterval.Immediate,
|
|
EnableAutoDepthStencil = false
|
|
};
|
|
}
|
|
|
|
void ResetDevice(GLControlWrapper_SlimDX9 control)
|
|
{
|
|
SuspendRenderTargets();
|
|
FreeControlSwapChain(control);
|
|
for (; ; )
|
|
{
|
|
var result = dev.TestCooperativeLevel();
|
|
if (result.IsSuccess)
|
|
break;
|
|
if (result.Code == D3DERR_DEVICENOTRESET)
|
|
{
|
|
try
|
|
{
|
|
var pp = MakePresentParameters();
|
|
dev.Reset(pp);
|
|
break;
|
|
}
|
|
catch { }
|
|
}
|
|
System.Threading.Thread.Sleep(100);
|
|
}
|
|
RefreshControlSwapChain(control);
|
|
ResumeRenderTargets();
|
|
}
|
|
|
|
public void CreateDevice()
|
|
{
|
|
DestroyDevice();
|
|
|
|
var pp = MakePresentParameters();
|
|
|
|
var flags = CreateFlags.SoftwareVertexProcessing;
|
|
if ((d3d.GetDeviceCaps(0, DeviceType.Hardware).DeviceCaps & DeviceCaps.HWTransformAndLight) != 0)
|
|
{
|
|
flags = CreateFlags.HardwareVertexProcessing;
|
|
}
|
|
|
|
flags |= CreateFlags.FpuPreserve;
|
|
dev = new Device(d3d, 0, DeviceType.Hardware, pp.DeviceWindowHandle, flags, pp);
|
|
}
|
|
|
|
void IDisposable.Dispose()
|
|
{
|
|
DestroyDevice();
|
|
d3d.Dispose();
|
|
}
|
|
|
|
public void Clear(OpenTK.Graphics.OpenGL.ClearBufferMask mask)
|
|
{
|
|
ClearFlags flags = ClearFlags.None;
|
|
if ((mask & gl.ClearBufferMask.ColorBufferBit) != 0) flags |= ClearFlags.Target;
|
|
if ((mask & gl.ClearBufferMask.DepthBufferBit) != 0) flags |= ClearFlags.ZBuffer;
|
|
if ((mask & gl.ClearBufferMask.StencilBufferBit) != 0) flags |= ClearFlags.Stencil;
|
|
dev.Clear(flags, _clearColor, 0.0f, 0);
|
|
}
|
|
|
|
int _clearColor;
|
|
public void SetClearColor(sd.Color color)
|
|
{
|
|
_clearColor = color.ToArgb();
|
|
}
|
|
|
|
public IBlendState CreateBlendState(gl.BlendingFactorSrc colorSource, gl.BlendEquationMode colorEquation, gl.BlendingFactorDest colorDest,
|
|
gl.BlendingFactorSrc alphaSource, gl.BlendEquationMode alphaEquation, gl.BlendingFactorDest alphaDest)
|
|
{
|
|
return new CacheBlendState(true, colorSource, colorEquation, colorDest, alphaSource, alphaEquation, alphaDest);
|
|
}
|
|
|
|
|
|
public void FreeTexture(Texture2d tex) {
|
|
var tw = tex.Opaque as TextureWrapper;
|
|
tw.Texture.Dispose();
|
|
}
|
|
|
|
class ShaderWrapper // Disposable fields cleaned up by Internal_FreeShader
|
|
{
|
|
public d3d9.ShaderBytecode bytecode;
|
|
public d3d9.VertexShader vs;
|
|
public d3d9.PixelShader ps;
|
|
public Shader IGLShader;
|
|
public Dictionary<string, string> MapCodeToNative;
|
|
public Dictionary<string, string> MapNativeToCode;
|
|
}
|
|
|
|
public Shader CreateFragmentShader(bool cg, string source, string entry, bool required)
|
|
{
|
|
try
|
|
{
|
|
ShaderWrapper sw = new ShaderWrapper();
|
|
if (cg)
|
|
{
|
|
var cgc = new CGC();
|
|
var results = cgc.Run(source, entry, "hlslf", true);
|
|
source = results.Code;
|
|
entry = "main";
|
|
if (!results.Succeeded)
|
|
{
|
|
if (required) throw new InvalidOperationException(results.Errors);
|
|
else return new Shader(this, null, false);
|
|
}
|
|
|
|
sw.MapCodeToNative = results.MapCodeToNative;
|
|
sw.MapNativeToCode = results.MapNativeToCode;
|
|
}
|
|
|
|
string errors = null;
|
|
d3d9.ShaderBytecode bytecode = null;
|
|
|
|
try
|
|
{
|
|
//cgc can create shaders that will need backwards compatibility...
|
|
string profile = "ps_1_0";
|
|
if (cg)
|
|
profile = "ps_3_0"; //todo - smarter logic somehow
|
|
|
|
//ShaderFlags.EnableBackwardsCompatibility - used this once upon a time (please leave a note about why)
|
|
//
|
|
bytecode = d3d9.ShaderBytecode.Compile(source, null, null, entry, profile, ShaderFlags.UseLegacyD3DX9_31Dll, out errors);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new InvalidOperationException($"Error compiling shader: {errors}", ex);
|
|
}
|
|
|
|
sw.ps = new PixelShader(dev, bytecode);
|
|
sw.bytecode = bytecode;
|
|
|
|
Shader s = new Shader(this, sw, true);
|
|
sw.IGLShader = s;
|
|
|
|
return s;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (required)
|
|
throw;
|
|
var s = new Shader(this, null, false);
|
|
s.Errors = ex.ToString();
|
|
return s;
|
|
}
|
|
}
|
|
|
|
public Shader CreateVertexShader(bool cg, string source, string entry, bool required)
|
|
{
|
|
try
|
|
{
|
|
ShaderWrapper sw = new ShaderWrapper();
|
|
if (cg)
|
|
{
|
|
var cgc = new CGC();
|
|
var results = cgc.Run(source, entry, "hlslv", true);
|
|
source = results.Code;
|
|
entry = "main";
|
|
if (!results.Succeeded)
|
|
{
|
|
if (required) throw new InvalidOperationException(results.Errors);
|
|
else return new Shader(this, null, false);
|
|
}
|
|
|
|
sw.MapCodeToNative = results.MapCodeToNative;
|
|
sw.MapNativeToCode = results.MapNativeToCode;
|
|
}
|
|
|
|
string errors = null;
|
|
d3d9.ShaderBytecode bytecode = null;
|
|
|
|
try
|
|
{
|
|
//cgc can create shaders that will need backwards compatibility...
|
|
string profile = "vs_1_1";
|
|
if (cg)
|
|
profile = "vs_3_0"; //todo - smarter logic somehow
|
|
|
|
bytecode = d3d9.ShaderBytecode.Compile(source, null, null, entry, profile, ShaderFlags.EnableBackwardsCompatibility, out errors);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new InvalidOperationException($"Error compiling shader: {errors}", ex);
|
|
}
|
|
|
|
sw.vs = new VertexShader(dev, bytecode);
|
|
sw.bytecode = bytecode;
|
|
|
|
Shader s = new Shader(this, sw, true);
|
|
sw.IGLShader = s;
|
|
return s;
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
if (required)
|
|
throw;
|
|
var s = new Shader(this, null, false);
|
|
s.Errors = ex.ToString();
|
|
return s;
|
|
}
|
|
}
|
|
|
|
BlendOperation ConvertBlendOp(gl.BlendEquationMode glmode)
|
|
{
|
|
if (glmode == gl.BlendEquationMode.FuncAdd) return BlendOperation.Add;
|
|
if (glmode == gl.BlendEquationMode.FuncSubtract) return BlendOperation.Subtract;
|
|
if (glmode == gl.BlendEquationMode.Max) return BlendOperation.Maximum;
|
|
if (glmode == gl.BlendEquationMode.Min) return BlendOperation.Minimum;
|
|
if (glmode == gl.BlendEquationMode.FuncReverseSubtract) return BlendOperation.ReverseSubtract;
|
|
throw new ArgumentOutOfRangeException();
|
|
}
|
|
|
|
Blend ConvertBlendArg(gl.BlendingFactorDest glmode) { return ConvertBlendArg((gl.BlendingFactorSrc)glmode); }
|
|
|
|
Blend ConvertBlendArg(gl.BlendingFactorSrc glmode) => glmode switch
|
|
{
|
|
gl.BlendingFactorSrc.Zero => Blend.Zero,
|
|
gl.BlendingFactorSrc.One => Blend.One,
|
|
gl.BlendingFactorSrc.SrcColor => Blend.SourceColor,
|
|
gl.BlendingFactorSrc.OneMinusSrcColor => Blend.InverseSourceColor,
|
|
gl.BlendingFactorSrc.SrcAlpha => Blend.SourceAlpha,
|
|
gl.BlendingFactorSrc.OneMinusSrcAlpha => Blend.InverseSourceAlpha,
|
|
gl.BlendingFactorSrc.DstAlpha => Blend.DestinationAlpha,
|
|
gl.BlendingFactorSrc.OneMinusDstAlpha => Blend.InverseDestinationAlpha,
|
|
gl.BlendingFactorSrc.DstColor => Blend.DestinationColor,
|
|
gl.BlendingFactorSrc.OneMinusDstColor => Blend.InverseDestinationColor,
|
|
gl.BlendingFactorSrc.SrcAlphaSaturate => Blend.SourceAlphaSaturated,
|
|
gl.BlendingFactorSrc.ConstantColor => Blend.BlendFactor,
|
|
gl.BlendingFactorSrc.OneMinusConstantColor => Blend.InverseBlendFactor,
|
|
gl.BlendingFactorSrc.ConstantAlpha => throw new NotSupportedException(),
|
|
gl.BlendingFactorSrc.OneMinusConstantAlpha => throw new NotSupportedException(),
|
|
gl.BlendingFactorSrc.Src1Alpha => throw new NotSupportedException(),
|
|
gl.BlendingFactorSrc.Src1Color => throw new NotSupportedException(),
|
|
gl.BlendingFactorSrc.OneMinusSrc1Color => throw new NotSupportedException(),
|
|
gl.BlendingFactorSrc.OneMinusSrc1Alpha => throw new NotSupportedException(),
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
};
|
|
|
|
public void SetBlendState(IBlendState rsBlend)
|
|
{
|
|
|
|
var mybs = rsBlend as CacheBlendState;
|
|
if (mybs.Enabled)
|
|
{
|
|
dev.SetRenderState(RenderState.AlphaBlendEnable, true);
|
|
dev.SetRenderState(RenderState.SeparateAlphaBlendEnable, true);
|
|
|
|
dev.SetRenderState(RenderState.BlendOperation, ConvertBlendOp(mybs.colorEquation));
|
|
dev.SetRenderState(RenderState.SourceBlend, ConvertBlendArg(mybs.colorSource));
|
|
dev.SetRenderState(RenderState.DestinationBlend, ConvertBlendArg(mybs.colorDest));
|
|
|
|
dev.SetRenderState(RenderState.BlendOperationAlpha, ConvertBlendOp(mybs.alphaEquation));
|
|
dev.SetRenderState(RenderState.SourceBlendAlpha, ConvertBlendArg(mybs.alphaSource));
|
|
dev.SetRenderState(RenderState.DestinationBlendAlpha, ConvertBlendArg(mybs.alphaDest));
|
|
}
|
|
else dev.SetRenderState(RenderState.AlphaBlendEnable, false);
|
|
if (rsBlend == _rsBlendNoneOpaque)
|
|
{
|
|
//make sure constant color is set correctly
|
|
dev.SetRenderState(RenderState.BlendFactor, -1); //white
|
|
}
|
|
}
|
|
|
|
void CreateRenderStates()
|
|
{
|
|
_rsBlendNoneVerbatim = new CacheBlendState(
|
|
false,
|
|
gl.BlendingFactorSrc.One, gl.BlendEquationMode.FuncAdd, gl.BlendingFactorDest.Zero,
|
|
gl.BlendingFactorSrc.One, gl.BlendEquationMode.FuncAdd, gl.BlendingFactorDest.Zero);
|
|
|
|
_rsBlendNoneOpaque = new CacheBlendState(
|
|
false,
|
|
gl.BlendingFactorSrc.One, gl.BlendEquationMode.FuncAdd, gl.BlendingFactorDest.Zero,
|
|
gl.BlendingFactorSrc.ConstantAlpha, gl.BlendEquationMode.FuncAdd, gl.BlendingFactorDest.Zero);
|
|
|
|
_rsBlendNormal = new CacheBlendState(
|
|
true,
|
|
gl.BlendingFactorSrc.SrcAlpha, gl.BlendEquationMode.FuncAdd, gl.BlendingFactorDest.OneMinusSrcAlpha,
|
|
gl.BlendingFactorSrc.One, gl.BlendEquationMode.FuncAdd, gl.BlendingFactorDest.Zero);
|
|
}
|
|
|
|
CacheBlendState _rsBlendNoneVerbatim, _rsBlendNoneOpaque, _rsBlendNormal;
|
|
|
|
public IBlendState BlendNoneCopy { get { return _rsBlendNoneVerbatim; } }
|
|
public IBlendState BlendNoneOpaque { get { return _rsBlendNoneOpaque; } }
|
|
public IBlendState BlendNormal { get { return _rsBlendNormal; } }
|
|
|
|
public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required, string memo)
|
|
{
|
|
if (!vertexShader.Available || !fragmentShader.Available)
|
|
{
|
|
string errors = $"Vertex Shader:\r\n {vertexShader.Errors} \r\n-------\r\nFragment Shader:\r\n{fragmentShader.Errors}";
|
|
if (required)
|
|
throw new InvalidOperationException($"Couldn't build required GL pipeline:\r\n{errors}");
|
|
var pipeline = new Pipeline(this, null, false, null, null, null);
|
|
pipeline.Errors = errors;
|
|
return pipeline;
|
|
}
|
|
|
|
VertexElement[] ves = new VertexElement[vertexLayout.Items.Count];
|
|
int stride = 0;
|
|
foreach (var kvp in vertexLayout.Items)
|
|
{
|
|
var item = kvp.Value;
|
|
d3d9.DeclarationType decltype = DeclarationType.Float1;
|
|
switch (item.AttribType)
|
|
{
|
|
case gl.VertexAttribPointerType.Float:
|
|
if (item.Components == 1) decltype = DeclarationType.Float1;
|
|
else if (item.Components == 2) decltype = DeclarationType.Float2;
|
|
else if (item.Components == 3) decltype = DeclarationType.Float3;
|
|
else if (item.Components == 4) decltype = DeclarationType.Float4;
|
|
else throw new NotSupportedException();
|
|
stride += 4 * item.Components;
|
|
break;
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
d3d9.DeclarationUsage usage = DeclarationUsage.Position;
|
|
byte usageIndex = 0;
|
|
switch(item.Usage)
|
|
{
|
|
case AttributeUsage.Position:
|
|
usage = DeclarationUsage.Position;
|
|
break;
|
|
case AttributeUsage.Texcoord0:
|
|
usage = DeclarationUsage.TextureCoordinate;
|
|
break;
|
|
case AttributeUsage.Texcoord1:
|
|
usage = DeclarationUsage.TextureCoordinate;
|
|
usageIndex = 1;
|
|
break;
|
|
case AttributeUsage.Color0:
|
|
usage = DeclarationUsage.Color;
|
|
break;
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
ves[kvp.Key] = new VertexElement(0, (short)item.Offset, decltype, DeclarationMethod.Default, usage, usageIndex);
|
|
}
|
|
|
|
|
|
var pw = new PipelineWrapper()
|
|
{
|
|
VertexDeclaration = new VertexDeclaration(dev, ves),
|
|
VertexShader = vertexShader.Opaque as ShaderWrapper,
|
|
FragmentShader = fragmentShader.Opaque as ShaderWrapper,
|
|
VertexStride = stride,
|
|
};
|
|
|
|
//scan uniforms from constant tables
|
|
//handles must be disposed later (with the pipeline probably)
|
|
var uniforms = new List<UniformInfo>();
|
|
var fs = pw.FragmentShader;
|
|
var vs = pw.VertexShader;
|
|
var fsct = fs.bytecode.ConstantTable;
|
|
var vsct = vs.bytecode.ConstantTable;
|
|
foreach(var ct in new[]{fsct,vsct})
|
|
{
|
|
Queue<Tuple<string,EffectHandle>> todo = new Queue<Tuple<string,EffectHandle>>();
|
|
int n = ct.Description.Constants;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
var handle = ct.GetConstant(null, i);
|
|
todo.Enqueue(Tuple.Create("", handle));
|
|
}
|
|
|
|
while(todo.Count != 0)
|
|
{
|
|
var tuple = todo.Dequeue();
|
|
var prefix = tuple.Item1;
|
|
var handle = tuple.Item2;
|
|
var descr = ct.GetConstantDescription(handle);
|
|
|
|
//Console.WriteLine($"D3D UNIFORM: {descr.Name}");
|
|
|
|
if (descr.StructMembers != 0)
|
|
{
|
|
string newprefix = $"{prefix}{descr.Name}.";
|
|
for (int j = 0; j < descr.StructMembers; j++)
|
|
{
|
|
var subhandle = ct.GetConstant(handle, j);
|
|
todo.Enqueue(Tuple.Create(newprefix, subhandle));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
UniformInfo ui = new UniformInfo();
|
|
UniformWrapper uw = new UniformWrapper();
|
|
|
|
ui.Opaque = uw;
|
|
string name = prefix + descr.Name;
|
|
|
|
//mehhh not happy about this stuff
|
|
if (fs.MapCodeToNative != null || vs.MapCodeToNative != null)
|
|
{
|
|
string key = name.TrimStart('$');
|
|
if (descr.Rows != 1)
|
|
key += "[0]";
|
|
if (fs.MapCodeToNative != null && ct == fsct) if (fs.MapCodeToNative.ContainsKey(key)) name = fs.MapCodeToNative[key];
|
|
if (vs.MapCodeToNative != null && ct == vsct) if (vs.MapCodeToNative.ContainsKey(key)) name = vs.MapCodeToNative[key];
|
|
}
|
|
|
|
ui.Name = name;
|
|
uw.Description = descr;
|
|
uw.EffectHandle = handle;
|
|
uw.FS = (ct == fsct);
|
|
uw.CT = ct;
|
|
if (descr.Type == ParameterType.Sampler2D)
|
|
{
|
|
ui.IsSampler = true;
|
|
ui.SamplerIndex = descr.RegisterIndex;
|
|
uw.SamplerIndex = descr.RegisterIndex;
|
|
}
|
|
uniforms.Add(ui);
|
|
}
|
|
}
|
|
|
|
pw.fsct = fsct;
|
|
pw.vsct = vsct;
|
|
|
|
return new Pipeline(this, pw, true, vertexLayout, uniforms,memo);
|
|
}
|
|
|
|
public void FreePipeline(Pipeline pipeline)
|
|
{
|
|
var pw = pipeline.Opaque as PipelineWrapper;
|
|
|
|
//unavailable pipelines will have no opaque
|
|
if (pw == null)
|
|
return;
|
|
|
|
pw.VertexDeclaration.Dispose();
|
|
pw.FragmentShader.IGLShader.Release();
|
|
pw.VertexShader.IGLShader.Release();
|
|
}
|
|
|
|
public void Internal_FreeShader(Shader shader)
|
|
{
|
|
var sw = shader.Opaque as ShaderWrapper;
|
|
sw.bytecode.Dispose();
|
|
if (sw.ps != null) sw.ps.Dispose();
|
|
if (sw.vs != null) sw.vs.Dispose();
|
|
}
|
|
|
|
class UniformWrapper
|
|
{
|
|
public d3d9.EffectHandle EffectHandle;
|
|
public d3d9.ConstantDescription Description;
|
|
public bool FS;
|
|
public d3d9.ConstantTable CT;
|
|
public int SamplerIndex;
|
|
}
|
|
|
|
class PipelineWrapper // Disposable fields cleaned up by FreePipeline
|
|
{
|
|
public d3d9.VertexDeclaration VertexDeclaration;
|
|
public ShaderWrapper VertexShader, FragmentShader;
|
|
public int VertexStride;
|
|
public d3d9.ConstantTable fsct, vsct;
|
|
}
|
|
|
|
class TextureWrapper
|
|
{
|
|
public d3d9.Texture Texture;
|
|
public TextureAddress WrapClamp = TextureAddress.Clamp;
|
|
public TextureFilter MinFilter = TextureFilter.Point, MagFilter = TextureFilter.Point;
|
|
}
|
|
|
|
public VertexLayout CreateVertexLayout() { return new VertexLayout(this, new IntPtr(0)); }
|
|
|
|
public void BindPipeline(Pipeline pipeline)
|
|
{
|
|
_CurrPipeline = pipeline;
|
|
|
|
if (pipeline == null)
|
|
{
|
|
//unbind? i dont know
|
|
return;
|
|
}
|
|
|
|
var pw = pipeline.Opaque as PipelineWrapper;
|
|
dev.PixelShader = pw.FragmentShader.ps;
|
|
dev.VertexShader = pw.VertexShader.vs;
|
|
dev.VertexDeclaration = pw.VertexDeclaration;
|
|
|
|
//not helpful...
|
|
//pw.vsct.SetDefaults(dev);
|
|
//pw.fsct.SetDefaults(dev);
|
|
}
|
|
|
|
public void SetPipelineUniform(PipelineUniform uniform, bool value)
|
|
{
|
|
if (uniform.Owner == null) return; //uniform was optimized out
|
|
|
|
foreach (var ui in uniform.UniformInfos)
|
|
{
|
|
var uw = ui.Opaque as UniformWrapper;
|
|
uw.CT.SetValue(dev, uw.EffectHandle, value);
|
|
}
|
|
}
|
|
|
|
public unsafe void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4 mat, bool transpose)
|
|
{
|
|
if (uniform.Owner == null) return; //uniform was optimized out
|
|
|
|
foreach (var ui in uniform.UniformInfos)
|
|
{
|
|
var uw = ui.Opaque as UniformWrapper;
|
|
uw.CT.SetValue(dev, uw.EffectHandle, mat.ToSlimDXMatrix(!transpose));
|
|
}
|
|
}
|
|
|
|
public unsafe void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4 mat, bool transpose)
|
|
{
|
|
if (uniform.Owner == null) return; //uniform was optimized out
|
|
|
|
foreach (var ui in uniform.UniformInfos)
|
|
{
|
|
var uw = ui.Opaque as UniformWrapper;
|
|
uw.CT.SetValue(dev, uw.EffectHandle, mat.ToSlimDXMatrix(!transpose));
|
|
}
|
|
}
|
|
|
|
public void SetPipelineUniform(PipelineUniform uniform, Vector4 value)
|
|
{
|
|
if (uniform.Owner == null) return; //uniform was optimized out
|
|
|
|
foreach (var ui in uniform.UniformInfos)
|
|
{
|
|
var uw = ui.Opaque as UniformWrapper;
|
|
uw.CT.SetValue(dev, uw.EffectHandle, value.ToSlimDXVector4());
|
|
}
|
|
}
|
|
|
|
public void SetPipelineUniform(PipelineUniform uniform, Vector2 value)
|
|
{
|
|
if (uniform.Owner == null) return; //uniform was optimized out
|
|
|
|
foreach (var ui in uniform.UniformInfos)
|
|
{
|
|
var uw = ui.Opaque as UniformWrapper;
|
|
uw.CT.SetValue(dev, uw.EffectHandle, value.ToSlimDXVector2());
|
|
}
|
|
}
|
|
|
|
public void SetPipelineUniform(PipelineUniform uniform, float value)
|
|
{
|
|
if (uniform.Owner == null) return; //uniform was optimized out
|
|
foreach (var ui in uniform.UniformInfos)
|
|
{
|
|
var uw = ui.Opaque as UniformWrapper;
|
|
uw.CT.SetValue(dev, uw.EffectHandle, value);
|
|
}
|
|
}
|
|
|
|
public unsafe void SetPipelineUniform(PipelineUniform uniform, Vector4[] values)
|
|
{
|
|
if (uniform.Owner == null) return; //uniform was optimized out
|
|
var v = new global::SlimDX.Vector4[values.Length];
|
|
for (int i = 0; i < values.Length; i++)
|
|
v[i] = values[i].ToSlimDXVector4();
|
|
foreach (var ui in uniform.UniformInfos)
|
|
{
|
|
var uw = ui.Opaque as UniformWrapper;
|
|
uw.CT.SetValue(dev, uw.EffectHandle, v);
|
|
}
|
|
}
|
|
|
|
public void SetPipelineUniformSampler(PipelineUniform uniform, Texture2d tex)
|
|
{
|
|
if (uniform.Owner == null) return; //uniform was optimized out
|
|
var tw = tex.Opaque as TextureWrapper;
|
|
|
|
foreach (var ui in uniform.UniformInfos)
|
|
{
|
|
var uw = ui.Opaque as UniformWrapper;
|
|
dev.SetTexture(uw.SamplerIndex, tw.Texture);
|
|
|
|
dev.SetSamplerState(uw.SamplerIndex, SamplerState.AddressU, tw.WrapClamp);
|
|
dev.SetSamplerState(uw.SamplerIndex, SamplerState.AddressV, tw.WrapClamp);
|
|
dev.SetSamplerState(uw.SamplerIndex, SamplerState.MinFilter, tw.MinFilter);
|
|
dev.SetSamplerState(uw.SamplerIndex, SamplerState.MagFilter, tw.MagFilter);
|
|
}
|
|
}
|
|
|
|
public void SetTextureWrapMode(Texture2d tex, bool clamp)
|
|
{
|
|
var tw = tex.Opaque as TextureWrapper;
|
|
tw.WrapClamp = clamp ? TextureAddress.Clamp : TextureAddress.Wrap;
|
|
}
|
|
|
|
public void TexParameter2d(Texture2d tex, gl.TextureParameterName pname, int param)
|
|
{
|
|
var tw = tex.Opaque as TextureWrapper;
|
|
|
|
if(pname == gl.TextureParameterName.TextureMinFilter)
|
|
tw.MinFilter = (param == (int)gl.TextureMinFilter.Linear) ? TextureFilter.Linear : TextureFilter.Point;
|
|
if (pname == gl.TextureParameterName.TextureMagFilter)
|
|
tw.MagFilter = (param == (int)gl.TextureMagFilter.Linear) ? TextureFilter.Linear : TextureFilter.Point;
|
|
}
|
|
|
|
public Texture2d LoadTexture(sd.Bitmap bitmap)
|
|
{
|
|
using (var bmp = new BitmapBuffer(bitmap, new BitmapLoadOptions()))
|
|
return (this as IGL).LoadTexture(bmp);
|
|
}
|
|
|
|
public Texture2d LoadTexture(Stream stream)
|
|
{
|
|
using (var bmp = new BitmapBuffer(stream, new BitmapLoadOptions()))
|
|
return (this as IGL).LoadTexture(bmp);
|
|
}
|
|
|
|
public Texture2d CreateTexture(int width, int height)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public Texture2d WrapGLTexture2d(IntPtr glTexId, int width, int height)
|
|
{
|
|
//not needed 1st pass (except for GL cores)
|
|
//TODO - need to rip the texturedata. we had code for that somewhere...
|
|
return null;
|
|
}
|
|
|
|
public unsafe void LoadTextureData(Texture2d tex, BitmapBuffer bmp)
|
|
{
|
|
sdi.BitmapData bmp_data = bmp.LockBits();
|
|
var tw = tex.Opaque as TextureWrapper;
|
|
var dr = tw.Texture.LockRectangle(0, LockFlags.None);
|
|
|
|
//TODO - do we need to handle odd sizes, weird pitches here?
|
|
if (bmp.Width * 4 != bmp_data.Stride)
|
|
throw new InvalidOperationException();
|
|
|
|
dr.Data.WriteRange(bmp_data.Scan0, bmp.Width * bmp.Height * 4);
|
|
dr.Data.Close();
|
|
|
|
tw.Texture.UnlockRectangle(0);
|
|
bmp.UnlockBits(bmp_data);
|
|
}
|
|
|
|
|
|
public Texture2d LoadTexture(BitmapBuffer bmp)
|
|
{
|
|
var tex = new d3d9.Texture(dev, bmp.Width, bmp.Height, 1, d3d9.Usage.None, d3d9.Format.A8R8G8B8, d3d9.Pool.Managed);
|
|
var tw = new TextureWrapper() { Texture = tex };
|
|
var ret = new Texture2d(this, tw, bmp.Width, bmp.Height);
|
|
LoadTextureData(ret, bmp);
|
|
return ret;
|
|
}
|
|
|
|
public unsafe BitmapBuffer ResolveTexture2d(Texture2d tex)
|
|
{
|
|
//TODO - lazy create and cache resolving target in RT
|
|
var target = new d3d9.Texture(dev, tex.IntWidth, tex.IntHeight, 1, d3d9.Usage.None, d3d9.Format.A8R8G8B8, d3d9.Pool.SystemMemory);
|
|
var tw = tex.Opaque as TextureWrapper;
|
|
dev.GetRenderTargetData(tw.Texture.GetSurfaceLevel(0), target.GetSurfaceLevel(0));
|
|
var dr = target.LockRectangle(0, LockFlags.ReadOnly);
|
|
if (dr.Pitch != tex.IntWidth * 4) throw new InvalidOperationException();
|
|
int[] pixels = new int[tex.IntWidth * tex.IntHeight];
|
|
dr.Data.ReadRange(pixels, 0, tex.IntWidth * tex.IntHeight);
|
|
var bb = new BitmapBuffer(tex.IntWidth, tex.IntHeight, pixels);
|
|
target.UnlockRectangle(0);
|
|
target.Dispose(); //buffer churn warning
|
|
return bb;
|
|
}
|
|
|
|
public Texture2d LoadTexture(string path)
|
|
{
|
|
//not needed 1st pass ??
|
|
//todo
|
|
//using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
|
// return (this as IGL).LoadTexture(fs);
|
|
return null;
|
|
}
|
|
|
|
public Matrix4 CreateGuiProjectionMatrix(int w, int h)
|
|
{
|
|
return CreateGuiProjectionMatrix(new sd.Size(w, h));
|
|
}
|
|
|
|
public Matrix4 CreateGuiViewMatrix(int w, int h, bool autoflip)
|
|
{
|
|
return CreateGuiViewMatrix(new sd.Size(w, h), autoflip);
|
|
}
|
|
|
|
public Matrix4 CreateGuiProjectionMatrix(sd.Size dims)
|
|
{
|
|
Matrix4 ret = Matrix4.Identity;
|
|
ret.M11 = 2.0f / (float)dims.Width;
|
|
ret.M22 = 2.0f / (float)dims.Height;
|
|
return ret;
|
|
}
|
|
|
|
public Matrix4 CreateGuiViewMatrix(sd.Size dims, bool autoflip)
|
|
{
|
|
Matrix4 ret = Matrix4.Identity;
|
|
ret.M22 = -1.0f;
|
|
ret.M41 = -(float)dims.Width * 0.5f - 0.5f;
|
|
ret.M42 = (float)dims.Height * 0.5f + 0.5f;
|
|
//autoflipping isnt needed on d3d
|
|
return ret;
|
|
}
|
|
|
|
public void SetViewport(int x, int y, int width, int height)
|
|
{
|
|
dev.Viewport = new Viewport(x, y, width, height);
|
|
dev.ScissorRect = new Rectangle(x, y, width, height);
|
|
}
|
|
|
|
public void SetViewport(int width, int height)
|
|
{
|
|
SetViewport(0, 0, width, height);
|
|
}
|
|
|
|
public void SetViewport(sd.Size size)
|
|
{
|
|
SetViewport(size.Width, size.Height);
|
|
}
|
|
|
|
public void SetViewport(swf.Control control)
|
|
{
|
|
var r = control.ClientRectangle;
|
|
SetViewport(r.Left, r.Top, r.Width, r.Height);
|
|
}
|
|
|
|
public void BeginControl(GLControlWrapper_SlimDX9 control)
|
|
{
|
|
_CurrentControl = control;
|
|
|
|
//this dispose isnt strictly needed but it seems benign
|
|
var surface = _CurrentControl.SwapChain.GetBackBuffer(0);
|
|
dev.SetRenderTarget(0, surface);
|
|
surface.Dispose();
|
|
}
|
|
|
|
public void EndControl(GLControlWrapper_SlimDX9 control)
|
|
{
|
|
if (control != _CurrentControl)
|
|
throw new InvalidOperationException();
|
|
|
|
var surface = _CurrentControl.SwapChain.GetBackBuffer(0);
|
|
dev.SetRenderTarget(0, surface);
|
|
surface.Dispose();
|
|
|
|
_CurrentControl = null;
|
|
}
|
|
|
|
public void SwapControl(GLControlWrapper_SlimDX9 control)
|
|
{
|
|
EndControl(control);
|
|
|
|
try
|
|
{
|
|
var result = control.SwapChain.Present(Present.None);
|
|
//var rs = dev.GetRasterStatus(0);
|
|
}
|
|
catch(d3d9.Direct3D9Exception ex)
|
|
{
|
|
if (ex.ResultCode.Code == D3DERR_DEVICELOST)
|
|
ResetDevice(control);
|
|
}
|
|
}
|
|
|
|
HashSet<RenderTarget> _renderTargets = new HashSet<RenderTarget>();
|
|
|
|
public void FreeRenderTarget(RenderTarget rt)
|
|
{
|
|
var tw = rt.Texture2d.Opaque as TextureWrapper;
|
|
tw.Texture.Dispose();
|
|
tw.Texture = null;
|
|
_renderTargets.Remove(rt);
|
|
}
|
|
|
|
public RenderTarget CreateRenderTarget(int w, int h)
|
|
{
|
|
var tw = new TextureWrapper() { Texture = CreateRenderTargetTexture(w, h) };
|
|
var tex = new Texture2d(this, tw, w, h);
|
|
var rt = new RenderTarget(this, tw, tex);
|
|
_renderTargets.Add(rt);
|
|
return rt;
|
|
}
|
|
|
|
d3d9.Texture CreateRenderTargetTexture(int w, int h)
|
|
{
|
|
return new d3d9.Texture(dev, w, h, 1, d3d9.Usage.RenderTarget, d3d9.Format.A8R8G8B8, d3d9.Pool.Default);
|
|
}
|
|
|
|
void SuspendRenderTargets()
|
|
{
|
|
foreach (var rt in _renderTargets)
|
|
{
|
|
var tw = rt.Opaque as TextureWrapper;
|
|
tw.Texture.Dispose();
|
|
tw.Texture = null;
|
|
}
|
|
}
|
|
|
|
void ResumeRenderTargets()
|
|
{
|
|
foreach (var rt in _renderTargets)
|
|
{
|
|
var tw = rt.Opaque as TextureWrapper;
|
|
tw.Texture = CreateRenderTargetTexture(rt.Texture2d.IntWidth, rt.Texture2d.IntHeight);
|
|
}
|
|
}
|
|
|
|
public void BindRenderTarget(RenderTarget rt)
|
|
{
|
|
if (rt == null)
|
|
{
|
|
//this dispose is needed for correct device resets, I have no idea why
|
|
//don't try caching it either
|
|
var surface = _CurrentControl.SwapChain.GetBackBuffer(0);
|
|
dev.SetRenderTarget(0, surface);
|
|
surface.Dispose();
|
|
|
|
dev.DepthStencilSurface = null;
|
|
return;
|
|
}
|
|
|
|
//dispose doesn't seem necessary for reset here...
|
|
var tw = rt.Opaque as TextureWrapper;
|
|
dev.SetRenderTarget(0, tw.Texture.GetSurfaceLevel(0));
|
|
dev.DepthStencilSurface = null;
|
|
}
|
|
|
|
public void FreeControlSwapChain(GLControlWrapper_SlimDX9 control)
|
|
{
|
|
if (control.SwapChain != null)
|
|
{
|
|
control.SwapChain.Dispose();
|
|
control.SwapChain = null;
|
|
}
|
|
}
|
|
|
|
public void RefreshControlSwapChain(GLControlWrapper_SlimDX9 control)
|
|
{
|
|
FreeControlSwapChain(control);
|
|
|
|
var pp = new PresentParameters
|
|
{
|
|
BackBufferWidth = Math.Max(8,control.ClientSize.Width),
|
|
BackBufferHeight = Math.Max(8, control.ClientSize.Height),
|
|
BackBufferCount = 1,
|
|
BackBufferFormat = Format.X8R8G8B8,
|
|
DeviceWindowHandle = control.Handle,
|
|
Windowed = true,
|
|
PresentationInterval = control.Vsync ? PresentInterval.One : PresentInterval.Immediate
|
|
};
|
|
|
|
control.SwapChain = new SwapChain(dev, pp);
|
|
}
|
|
|
|
public IGraphicsControl Internal_CreateGraphicsControl()
|
|
{
|
|
var ret = new GLControlWrapper_SlimDX9(this);
|
|
RefreshControlSwapChain(ret);
|
|
return ret;
|
|
}
|
|
|
|
public unsafe void DrawArrays(gl.PrimitiveType mode, int first, int count)
|
|
{
|
|
PrimitiveType pt = PrimitiveType.TriangleStrip;
|
|
|
|
if(mode != gl.PrimitiveType.TriangleStrip)
|
|
throw new NotSupportedException();
|
|
|
|
//for tristrip
|
|
int primCount = (count - 2);
|
|
|
|
var pw = _CurrPipeline.Opaque as PipelineWrapper;
|
|
int stride = pw.VertexStride;
|
|
byte* ptr = (byte*)_pVertexData.ToPointer() + first * stride;
|
|
|
|
dev.DrawUserPrimitives(pt, primCount, (void*)ptr, (uint)stride);
|
|
}
|
|
|
|
|
|
public unsafe void BindArrayData(void* pData)
|
|
{
|
|
_pVertexData = new IntPtr(pData);
|
|
}
|
|
|
|
public void BeginScene()
|
|
{
|
|
dev.BeginScene();
|
|
dev.SetRenderState(RenderState.CullMode, Cull.None);
|
|
dev.SetRenderState(RenderState.ZEnable, false);
|
|
dev.SetRenderState(RenderState.ZWriteEnable, false);
|
|
dev.SetRenderState(RenderState.Lighting, false);
|
|
}
|
|
|
|
public void EndScene()
|
|
{
|
|
dev.EndScene();
|
|
}
|
|
|
|
|
|
|
|
} //class IGL_SlimDX
|
|
|
|
}
|