diff --git a/Assets/Shaders/BizHawk/BizScanlines.hlsl b/Assets/Shaders/BizHawk/BizScanlines.hlsl
index 96979e7b29..64bf9f9f9c 100644
--- a/Assets/Shaders/BizHawk/BizScanlines.hlsl
+++ b/Assets/Shaders/BizHawk/BizScanlines.hlsl
@@ -1,23 +1,25 @@
void main_vertex
(
- float4 position : POSITION,
- float2 tex : TEXCOORD0,
+ float4 position : POSITION,
+ float2 tex : TEXCOORD0,
- uniform float4x4 modelViewProj,
+ uniform float4x4 modelViewProj,
- out float4 oPosition : POSITION,
- out float2 oTexcoord : TEXCOORD0
+ out float4 oPosition : POSITION,
+ out float2 oTex0 : TEXCOORD0,
+ out float oTex1 : TEXCOORD1
)
{
oPosition = mul(modelViewProj, position);
- oTexcoord = tex;
+ oTex0 = tex;
+ oTex1 = position.y;
}
uniform float uIntensity;
-float4 main_fragment (in float2 texcoord : TEXCOORD0, in float2 wpos : VPOS, uniform sampler2D s_p : TEXUNIT0) : COLOR
+float4 main_fragment (in float4 vpos : POSITION, in float2 tex0 : TEXCOORD0, in float tex1 : TEXCOORD1, uniform sampler2D s_p : TEXUNIT0) : COLOR
{
- float4 temp = tex2D(s_p,texcoord);
- if(floor(wpos.y/2) != floor(wpos.y)/2) temp.rgb *= uIntensity;
+ float4 temp = tex2D(s_p, tex0);
+ if (floor(tex1 / 2) != floor(tex1) / 2) temp.rgb *= uIntensity;
return temp;
}
diff --git a/Assets/Shaders/BizHawk/bicubic-fast.hlsl b/Assets/Shaders/BizHawk/bicubic-fast.hlsl
index db7b9fbbb4..f04219da30 100644
--- a/Assets/Shaders/BizHawk/bicubic-fast.hlsl
+++ b/Assets/Shaders/BizHawk/bicubic-fast.hlsl
@@ -46,13 +46,14 @@ struct input
float2 video_size;
float2 texture_size;
float2 output_size;
- float frame_count;
- float frame_direction;
+ float frame_count;
+ float frame_direction;
float frame_rotation;
};
-struct out_vertex {
+struct out_vertex
+{
float2 texCoord : TEXCOORD0;
float4 t1 : TEXCOORD1;
float4 t2 : TEXCOORD2;
@@ -65,26 +66,29 @@ struct out_vertex {
};
/* VERTEX_SHADER */
-out_vertex main_vertex
+void main_vertex
(
float4 position : POSITION,
- out float4 oPosition : POSITION,
float2 texCoord1 : TEXCOORD0,
- uniform float4x4 modelViewProj,
- uniform input IN
+ uniform float4x4 modelViewProj,
+ uniform input IN,
+
+ out float4 oPosition : POSITION,
+ out out_vertex oVAR
)
{
float2 ps = float2(1.0/IN.texture_size.x, 1.0/IN.texture_size.y);
float dx = ps.x;
float dy = ps.y;
- oPosition = mul(modelViewProj, position);
+ oPosition = mul(modelViewProj, position);
// This line fix a bug in ATI cards.
float2 tex = texCoord1 + float2(0.0000001, 0.0000001);
- out_vertex OUT = {
+ out_vertex OUT =
+ {
tex,
float4(tex,tex) + float4( -dx, -dy, 0.0, -dy),
float4(tex,tex) + float4( dx, -dy, 2.0*dx, -dy),
@@ -96,57 +100,54 @@ out_vertex main_vertex
tex + float2(2.0*dx, 2.0*dy)
};
-
- return OUT;
+ oVAR = OUT;
}
-float4 main_fragment(in out_vertex VAR, uniform sampler2D s_p : TEXUNIT0, uniform input IN) : COLOR
+float4 main_fragment(in float4 vpos : POSITION, in out_vertex VAR, uniform sampler2D s_p : TEXUNIT0, uniform input IN) : COLOR
{
-
- float2 fp = frac(VAR.texCoord*IN.texture_size);
- float3 c00 = tex2D(s_p, VAR.t1.xy).xyz;
- float3 c01 = tex2D(s_p, VAR.t1.zw).xyz;
- float3 c02 = tex2D(s_p, VAR.t2.xy).xyz;
- float3 c03 = tex2D(s_p, VAR.t2.zw).xyz;
- float3 c10 = tex2D(s_p, VAR.t3.xy).xyz;
- float3 c11 = tex2D(s_p, VAR.texCoord).xyz;
- float3 c12 = tex2D(s_p, VAR.t3.zw).xyz;
- float3 c13 = tex2D(s_p, VAR.t4.xy).xyz;
- float3 c20 = tex2D(s_p, VAR.t4.zw).xyz;
- float3 c21 = tex2D(s_p, VAR.t5.xy).xyz;
- float3 c22 = tex2D(s_p, VAR.t5.zw).xyz;
- float3 c23 = tex2D(s_p, VAR.t6.xy).xyz;
- float3 c30 = tex2D(s_p, VAR.t6.zw).xyz;
- float3 c31 = tex2D(s_p, VAR.t7.xy).xyz;
- float3 c32 = tex2D(s_p, VAR.t7.zw).xyz;
- float3 c33 = tex2D(s_p, VAR.t8.xy).xyz;
+ float2 fp = frac(VAR.texCoord*IN.texture_size);
+ float3 c00 = tex2D(s_p, VAR.t1.xy).xyz;
+ float3 c01 = tex2D(s_p, VAR.t1.zw).xyz;
+ float3 c02 = tex2D(s_p, VAR.t2.xy).xyz;
+ float3 c03 = tex2D(s_p, VAR.t2.zw).xyz;
+ float3 c10 = tex2D(s_p, VAR.t3.xy).xyz;
+ float3 c11 = tex2D(s_p, VAR.texCoord).xyz;
+ float3 c12 = tex2D(s_p, VAR.t3.zw).xyz;
+ float3 c13 = tex2D(s_p, VAR.t4.xy).xyz;
+ float3 c20 = tex2D(s_p, VAR.t4.zw).xyz;
+ float3 c21 = tex2D(s_p, VAR.t5.xy).xyz;
+ float3 c22 = tex2D(s_p, VAR.t5.zw).xyz;
+ float3 c23 = tex2D(s_p, VAR.t6.xy).xyz;
+ float3 c30 = tex2D(s_p, VAR.t6.zw).xyz;
+ float3 c31 = tex2D(s_p, VAR.t7.xy).xyz;
+ float3 c32 = tex2D(s_p, VAR.t7.zw).xyz;
+ float3 c33 = tex2D(s_p, VAR.t8.xy).xyz;
- float4x4 red_matrix = float4x4(c00.x, c01.x, c02.x, c03.x,
- c10.x, c11.x, c12.x, c13.x,
- c20.x, c21.x, c22.x, c23.x,
- c30.x, c31.x, c32.x, c33.x);
+ float4x4 red_matrix = float4x4(c00.x, c01.x, c02.x, c03.x,
+ c10.x, c11.x, c12.x, c13.x,
+ c20.x, c21.x, c22.x, c23.x,
+ c30.x, c31.x, c32.x, c33.x);
- float4x4 green_matrix = float4x4(c00.y, c01.y, c02.y, c03.y,
- c10.y, c11.y, c12.y, c13.y,
- c20.y, c21.y, c22.y, c23.y,
- c30.y, c31.y, c32.y, c33.y);
+ float4x4 green_matrix = float4x4(c00.y, c01.y, c02.y, c03.y,
+ c10.y, c11.y, c12.y, c13.y,
+ c20.y, c21.y, c22.y, c23.y,
+ c30.y, c31.y, c32.y, c33.y);
- float4x4 blue_matrix = float4x4(c00.z, c01.z, c02.z, c03.z,
- c10.z, c11.z, c12.z, c13.z,
- c20.z, c21.z, c22.z, c23.z,
- c30.z, c31.z, c32.z, c33.z);
+ float4x4 blue_matrix = float4x4(c00.z, c01.z, c02.z, c03.z,
+ c10.z, c11.z, c12.z, c13.z,
+ c20.z, c21.z, c22.z, c23.z,
+ c30.z, c31.z, c32.z, c33.z);
- float4x1 invX_Px = mul(invX, float4x1(fp.x*fp.x*fp.x, fp.x*fp.x, fp.x, 1.0));
- float1x4 Py_invY = mul(float1x4(fp.y*fp.y*fp.y, fp.y*fp.y, fp.y, 1.0), invY);
+ float4x1 invX_Px = mul(invX, float4x1(fp.x*fp.x*fp.x, fp.x*fp.x, fp.x, 1.0));
+ float1x4 Py_invY = mul(float1x4(fp.y*fp.y*fp.y, fp.y*fp.y, fp.y, 1.0), invY);
- float red = mul(Py_invY, mul( red_matrix, invX_Px));
- float green = mul(Py_invY, mul(green_matrix, invX_Px));
- float blue = mul(Py_invY, mul( blue_matrix, invX_Px));
+ float red = mul(Py_invY, mul( red_matrix, invX_Px));
+ float green = mul(Py_invY, mul(green_matrix, invX_Px));
+ float blue = mul(Py_invY, mul( blue_matrix, invX_Px));
- return float4(red, green, blue, 1.0);
+ return float4(red, green, blue, 1.0);
}
-
diff --git a/Assets/Shaders/BizHawk/bicubic-normal.hlsl b/Assets/Shaders/BizHawk/bicubic-normal.hlsl
index 81aa180deb..34ab1c165e 100644
--- a/Assets/Shaders/BizHawk/bicubic-normal.hlsl
+++ b/Assets/Shaders/BizHawk/bicubic-normal.hlsl
@@ -33,35 +33,32 @@ http://www.gnu.org/copyleft/gpl.html
/* Default Vertex shader */
void main_vertex
(
- float4 position : POSITION,
- //float4 color : COLOR,
- float2 texCoord1 : TEXCOORD0,
+ float4 position : POSITION,
+ float2 texCoord1 : TEXCOORD0,
- uniform float4x4 modelViewProj,
+ uniform float4x4 modelViewProj,
- out float4 oPosition : POSITION,
- //out float4 oColor : COLOR,
- out float2 otexCoord : TEXCOORD
- )
+ out float4 oPosition : POSITION,
+ out float2 otexCoord : TEXCOORD
+)
{
- oPosition = mul(modelViewProj, position);
- //oColor = color;
- otexCoord = texCoord1;
+ oPosition = mul(modelViewProj, position);
+ otexCoord = texCoord1;
}
struct output
{
- float4 color : COLOR;
+ float4 color : COLOR;
};
struct input
{
- float2 video_size;
- float2 texture_size;
- float2 output_size;
- float frame_count;
- float frame_direction;
- float frame_rotation;
+ float2 video_size;
+ float2 texture_size;
+ float2 output_size;
+ float frame_count;
+ float frame_direction;
+ float frame_rotation;
};
float weight(float x)
@@ -123,11 +120,11 @@ float3 line_run(float ypos, float4 xpos, float4 linetaps, uniform sampler2D s_p)
}
-output main_fragment (float2 tex : TEXCOORD0, uniform input IN, uniform sampler2D s_p : TEXUNIT0)
+output main_fragment (in float4 vpos : POSITION, float2 tex : TEXCOORD0, uniform input IN, uniform sampler2D s_p : TEXUNIT0)
{
- float2 stepxy = float2(1.0/IN.texture_size.x, 1.0/IN.texture_size.y);
- float2 pos = tex.xy + stepxy * 0.5;
- float2 f = frac(pos / stepxy);
+ float2 stepxy = float2(1.0/IN.texture_size.x, 1.0/IN.texture_size.y);
+ float2 pos = tex.xy + stepxy * 0.5;
+ float2 f = frac(pos / stepxy);
float4 linetaps = weight4(1.0 - f.x);
float4 columntaps = weight4(1.0 - f.y);
@@ -140,12 +137,11 @@ output main_fragment (float2 tex : TEXCOORD0, uniform input IN, uniform sampler2
float4 xpos = float4(xystart.x, xystart.x + stepxy.x, xystart.x + stepxy.x * 2.0, xystart.x + stepxy.x * 3.0);
-// final sum and weight normalization
- output OUT;
- OUT.color = float4(line_run(xystart.y , xpos, linetaps, s_p) * columntaps.r +
- line_run(xystart.y + stepxy.y , xpos, linetaps, s_p) * columntaps.g +
- line_run(xystart.y + stepxy.y * 2.0, xpos, linetaps, s_p) * columntaps.b +
- line_run(xystart.y + stepxy.y * 3.0, xpos, linetaps, s_p) * columntaps.a,1);
+ // final sum and weight normalization
+ output OUT;
+ OUT.color = float4(line_run(xystart.y , xpos, linetaps, s_p) * columntaps.r +
+ line_run(xystart.y + stepxy.y , xpos, linetaps, s_p) * columntaps.g +
+ line_run(xystart.y + stepxy.y * 2.0, xpos, linetaps, s_p) * columntaps.b +
+ line_run(xystart.y + stepxy.y * 3.0, xpos, linetaps, s_p) * columntaps.a,1);
return OUT;
}
-
diff --git a/Assets/Shaders/BizHawk/bsnes-gamma.hlsl b/Assets/Shaders/BizHawk/bsnes-gamma.hlsl
index 03f2c3bfe6..6abcf6ce2f 100644
--- a/Assets/Shaders/BizHawk/bsnes-gamma.hlsl
+++ b/Assets/Shaders/BizHawk/bsnes-gamma.hlsl
@@ -9,16 +9,17 @@
void main_vertex
(
- float4 position : POSITION,
- out float4 oPosition : POSITION,
- uniform float4x4 modelViewProj,
+ float4 position : POSITION,
+ float2 tex : TEXCOORD,
- float2 tex : TEXCOORD,
- out float2 oTex : TEXCOORD
+ uniform float4x4 modelViewProj,
+
+ out float4 oPosition : POSITION,
+ out float2 oTex : TEXCOORD
)
{
- oPosition = mul(modelViewProj, position);
- oTex = tex;
+ oPosition = mul(modelViewProj, position);
+ oTex = tex;
}
// Tweakables.
@@ -34,7 +35,7 @@ float3 grayscale(float3 col)
return float3(v,v,v);
}
-float4 main_fragment(float2 tex : TEXCOORD, uniform sampler2D s0 : TEXUNIT0) : COLOR
+float4 main_fragment(in float4 vpos : POSITION, in float2 tex : TEXCOORD, uniform sampler2D s0 : TEXUNIT0) : COLOR
{
float3 res = tex2D(s0, tex).xyz;
res = lerp(grayscale(res), res, saturation); // Apply saturation
diff --git a/Assets/Shaders/BizHawk/hq2x.hlsl b/Assets/Shaders/BizHawk/hq2x.hlsl
index 98b7e6f640..c8ce877a96 100644
--- a/Assets/Shaders/BizHawk/hq2x.hlsl
+++ b/Assets/Shaders/BizHawk/hq2x.hlsl
@@ -1,51 +1,51 @@
struct tex_coords
{
- float2 c00 : TEXCOORD0;
- float2 c01 : TEXCOORD1;
- float2 c02 : TEXCOORD2;
- float2 c10 : TEXCOORD3;
- float2 c11 : TEXCOORD4;
- float2 c12 : TEXCOORD5;
- float2 c20 : TEXCOORD6;
- float2 c21 : TEXCOORD7;
- float2 c22 : COLOR0;
+ float2 c00 : TEXCOORD0;
+ float2 c01 : TEXCOORD1;
+ float2 c02 : TEXCOORD2;
+ float2 c10 : TEXCOORD3;
+ float2 c11 : TEXCOORD4;
+ float2 c12 : TEXCOORD5;
+ float2 c20 : TEXCOORD6;
+ float2 c21 : TEXCOORD7;
+ float2 c22 : COLOR0;
};
struct input
{
- float2 video_size;
- float2 texture_size;
- float2 output_size;
+ float2 video_size;
+ float2 texture_size;
+ float2 output_size;
};
void main_vertex
(
- float4 position : POSITION,
- out float4 oPosition : POSITION,
- uniform float4x4 modelViewProj,
+ float4 position : POSITION,
+ float2 tex : TEXCOORD0,
- float2 tex : TEXCOORD0,
+ uniform float4x4 modelViewProj,
+ uniform input IN,
- uniform input IN,
- out tex_coords coords
+ out float4 oPosition : POSITION,
+ out tex_coords coords
)
{
- oPosition = mul(modelViewProj, position);
+ oPosition = mul(modelViewProj, position);
- float2 texsize = IN.texture_size;
- float2 delta = 0.5 / texsize;
- float dx = delta.x;
- float dy = delta.y;
+ float2 texsize = IN.texture_size;
+ float2 delta = 0.5 / texsize;
+ float dx = delta.x;
+ float dy = delta.y;
- coords.c00 = tex + float2(-dx, -dy);
- coords.c01 = tex + float2(-dx, 0.0);
- coords.c02 = tex + float2(-dx, dy);
- coords.c10 = tex + float2(0.0, -dy);
- coords.c11 = tex + float2(0.0, 0.0);
- coords.c12 = tex + float2(0.0, dy);
- coords.c20 = tex + float2(dx, -dy);
- coords.c21 = tex + float2(dx, 0);
- coords.c22 = tex + float2(dx, dy);
+ coords.c00 = tex + float2(-dx, -dy);
+ coords.c01 = tex + float2(-dx, 0.0);
+ coords.c02 = tex + float2(-dx, dy);
+ coords.c10 = tex + float2(0.0, -dy);
+ coords.c11 = tex + float2(0.0, 0.0);
+ coords.c12 = tex + float2(0.0, dy);
+ coords.c20 = tex + float2(dx, -dy);
+ coords.c21 = tex + float2(dx, 0);
+ coords.c22 = tex + float2(dx, dy);
}
static const float mx = 0.325; // start smoothing wt.
@@ -54,40 +54,40 @@ static const float max_w = 0.25; // max filter weigth
static const float min_w = -0.05; // min filter weigth
static const float lum_add = 0.25; // effects smoothing
-float4 main_fragment (in tex_coords co, uniform sampler2D s_p : TEXUNIT0) : COLOR
+float4 main_fragment (in float4 vpos : POSITION, in tex_coords co, uniform sampler2D s_p : TEXUNIT0) : COLOR
{
- float3 c00 = tex2D(s_p, co.c00).xyz;
- float3 c01 = tex2D(s_p, co.c01).xyz;
- float3 c02 = tex2D(s_p, co.c02).xyz;
- float3 c10 = tex2D(s_p, co.c10).xyz;
- float3 c11 = tex2D(s_p, co.c11).xyz;
- float3 c12 = tex2D(s_p, co.c12).xyz;
- float3 c20 = tex2D(s_p, co.c20).xyz;
- float3 c21 = tex2D(s_p, co.c21).xyz;
- float3 c22 = tex2D(s_p, co.c22).xyz;
- float3 dt = float3(1.0,1.0,1.0);
+ float3 c00 = tex2D(s_p, co.c00).xyz;
+ float3 c01 = tex2D(s_p, co.c01).xyz;
+ float3 c02 = tex2D(s_p, co.c02).xyz;
+ float3 c10 = tex2D(s_p, co.c10).xyz;
+ float3 c11 = tex2D(s_p, co.c11).xyz;
+ float3 c12 = tex2D(s_p, co.c12).xyz;
+ float3 c20 = tex2D(s_p, co.c20).xyz;
+ float3 c21 = tex2D(s_p, co.c21).xyz;
+ float3 c22 = tex2D(s_p, co.c22).xyz;
+ float3 dt = float3(1.0,1.0,1.0);
- float md1 = dot(abs(c00 - c22), dt);
- float md2 = dot(abs(c02 - c20), dt);
+ float md1 = dot(abs(c00 - c22), dt);
+ float md2 = dot(abs(c02 - c20), dt);
- float w1 = dot(abs(c22 - c11), dt) * md2;
- float w2 = dot(abs(c02 - c11), dt) * md1;
- float w3 = dot(abs(c00 - c11), dt) * md2;
- float w4 = dot(abs(c20 - c11), dt) * md1;
+ float w1 = dot(abs(c22 - c11), dt) * md2;
+ float w2 = dot(abs(c02 - c11), dt) * md1;
+ float w3 = dot(abs(c00 - c11), dt) * md2;
+ float w4 = dot(abs(c20 - c11), dt) * md1;
- float t1 = w1 + w3;
- float t2 = w2 + w4;
- float ww = max(t1, t2) + 0.0001;
+ float t1 = w1 + w3;
+ float t2 = w2 + w4;
+ float ww = max(t1, t2) + 0.0001;
- c11 = (w1 * c00 + w2 * c20 + w3 * c22 + w4 * c02 + ww * c11) / (t1 + t2 + ww);
+ c11 = (w1 * c00 + w2 * c20 + w3 * c22 + w4 * c02 + ww * c11) / (t1 + t2 + ww);
- float lc1 = k / (0.12 * dot(c10 + c12 + c11, dt) + lum_add);
- float lc2 = k / (0.12 * dot(c01 + c21 + c11, dt) + lum_add);
+ float lc1 = k / (0.12 * dot(c10 + c12 + c11, dt) + lum_add);
+ float lc2 = k / (0.12 * dot(c01 + c21 + c11, dt) + lum_add);
- w1 = clamp(lc1 * dot(abs(c11 - c10), dt) + mx, min_w, max_w);
- w2 = clamp(lc2 * dot(abs(c11 - c21), dt) + mx, min_w, max_w);
- w3 = clamp(lc1 * dot(abs(c11 - c12), dt) + mx, min_w, max_w);
- w4 = clamp(lc2 * dot(abs(c11 - c01), dt) + mx, min_w, max_w);
+ w1 = clamp(lc1 * dot(abs(c11 - c10), dt) + mx, min_w, max_w);
+ w2 = clamp(lc2 * dot(abs(c11 - c21), dt) + mx, min_w, max_w);
+ w3 = clamp(lc1 * dot(abs(c11 - c12), dt) + mx, min_w, max_w);
+ w4 = clamp(lc2 * dot(abs(c11 - c01), dt) + mx, min_w, max_w);
- return float4(w1 * c10 + w2 * c21 + w3 * c12 + w4 * c01 + (1.0 - w1 - w2 - w3 - w4) * c11, 1.0);
+ return float4(w1 * c10 + w2 * c21 + w3 * c12 + w4 * c01 + (1.0 - w1 - w2 - w3 - w4) * c11, 1.0);
}
diff --git a/Assets/Shaders/retroizer/retroizer.hlsl b/Assets/Shaders/retroizer/retroizer.hlsl
index c075770744..9db88a67e6 100644
--- a/Assets/Shaders/retroizer/retroizer.hlsl
+++ b/Assets/Shaders/retroizer/retroizer.hlsl
@@ -1,4 +1,3 @@
-
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
////
@@ -9,26 +8,25 @@
struct input
{
- float2 video_size;
- float2 texture_size;
- float2 output_size;
-
+ float2 video_size;
+ float2 texture_size;
+ float2 output_size;
};
void main_vertex
(
- float4 position : POSITION,
- out float4 oPosition : POSITION,
- uniform float4x4 modelViewProj,
+ float4 position : POSITION,
+ float2 tex : TEXCOORD0,
- float2 tex : TEXCOORD,
+ uniform float4x4 modelViewProj,
+ uniform input IN,
- uniform input IN,
- out float2 oTexcoord : TEXCOORD,
- out float2 oFakeResolution : TEXCOORD1
+ out float4 oPosition : POSITION,
+ out float2 oTexcoord : TEXCOORD0,
+ out float2 oFakeResolution : TEXCOORD1
)
{
- oPosition = mul(modelViewProj, position);
+ oPosition = mul(modelViewProj, position);
oTexcoord = tex;
oFakeResolution = IN.texture_size;
}
@@ -39,105 +37,112 @@ void main_vertex
//// EFFECT CONSTANTS : TWEAK THEM!
////
- // Size of the border effect
- static const float2 OverscanMaskHardness = {12.0f ,12.0f };
- // Attenuation of the border effect
- static const float OverscanMaskPower = 4.0f;
- // Intensity of the border effect
- static const float OverscanIntensity = 0.96f;
-
- // Intensity of the TV Corners (round-ness) deformation
- static const float TVDeformInstensity = 0.02f;
-
-
- // How much R, G and B are offset : default is -0.333 pixels in fake-pixel-space
- static const float ColorFringeIntensity = -0.666;
- // How much luminosity is output by a fake-pixel
- static const float FakePixelMaskGain = 0.75f;
- // How much luminosity is output between fake-pixels (adds to the fake-pixel value)
- static const float FakePixelMaskOffset = 0.55f;
- // How sharp will appear the pixels (Horizontal Sharpness, Vertical Sharpness A.K.A Scanlines)
- static const float2 FakePixelMaskPower = {0.150f ,2.0f };
- // Scanline Off Sync (Slides one line out of two)
- static const float ScanlineOffSync = 0.25;
- // Base Brightness
- static const float BaseBrightness = 0.55f;
-
- // How much the Fake-Pixel effect is Active (0.0 = normal image, 1.0 = full FakePixel Effect)
- static const float FakePixelEffectBlend = 0.95f;
+ // Size of the border effect
+ static const float2 OverscanMaskHardness = {12.0f ,12.0f };
+ // Attenuation of the border effect
+ static const float OverscanMaskPower = 4.0f;
+ // Intensity of the border effect
+ static const float OverscanIntensity = 0.96f;
- // Ghost Sampling : enable define to activate
- #define GHOST_SAMPLING;
+ // Intensity of the TV Corners (round-ness) deformation
+ static const float TVDeformInstensity = 0.02f;
- static const float GhostLatencyIntensity = 0.03f;
- // Number of samples (higer is slower)
- static const int GhostNumSamples = 32;
- // Latency of the RGB Signal (per-signal, in screen width percentage)
- static const float3 SignalLatencyRGB = {0.184f,0.08f,0.0624f};
- // Attenuation of the ghosting latency
- static const float SignalLatencyAttenuation = 1.0f;
- // Bloom : enable define to activate
- #define BLOOM;
- static const float BloomIntensity = 0.75f;
- static const float BloomExponent = 1.00f;
- static const float BloomWeights[5][5] =
- {
- {0.003765, 0.015019, 0.023792, 0.015019, 0.003765},
- {0.015019, 0.059912, 0.094907, 0.059912, 0.015019},
- {0.023792, 0.094907, 0.150342, 0.094907, 0.023792},
- {0.015019, 0.059912, 0.094907, 0.059912, 0.015019},
- {0.003765, 0.015019, 0.023792, 0.015019, 0.003765}
- };
- static const float BloomPositions[5] = { -2, -1, 0 , 1 , 2};
+ // How much R, G and B are offset : default is -0.333 pixels in fake-pixel-space
+ static const float ColorFringeIntensity = -0.666;
+ // How much luminosity is output by a fake-pixel
+ static const float FakePixelMaskGain = 0.75f;
+ // How much luminosity is output between fake-pixels (adds to the fake-pixel value)
+ static const float FakePixelMaskOffset = 0.55f;
+ // How sharp will appear the pixels (Horizontal Sharpness, Vertical Sharpness A.K.A Scanlines)
+ static const float2 FakePixelMaskPower = {0.150f ,2.0f };
+ // Scanline Off Sync (Slides one line out of two)
+ static const float ScanlineOffSync = 0.25;
+ // Base Brightness
+ static const float BaseBrightness = 0.55f;
+
+ // How much the Fake-Pixel effect is Active (0.0 = normal image, 1.0 = full FakePixel Effect)
+ static const float FakePixelEffectBlend = 0.95f;
+ // Ghost Sampling : enable define to activate
+ #define GHOST_SAMPLING;
+
+ static const float GhostLatencyIntensity = 0.03f;
+ // Number of samples (higer is slower)
+ static const int GhostNumSamples = 32;
+ // Latency of the RGB Signal (per-signal, in screen width percentage)
+ static const float3 SignalLatencyRGB = {0.184f,0.08f,0.0624f};
+ // Attenuation of the ghosting latency
+ static const float SignalLatencyAttenuation = 1.0f;
+
+ // Bloom : enable define to activate
+ #define BLOOM;
+
+ static const float BloomIntensity = 0.75f;
+ static const float BloomExponent = 1.00f;
+ static const float BloomWeights[5][5] =
+ {
+ {0.003765, 0.015019, 0.023792, 0.015019, 0.003765},
+ {0.015019, 0.059912, 0.094907, 0.059912, 0.015019},
+ {0.023792, 0.094907, 0.150342, 0.094907, 0.023792},
+ {0.015019, 0.059912, 0.094907, 0.059912, 0.015019},
+ {0.003765, 0.015019, 0.023792, 0.015019, 0.003765}
+ };
+ static const float BloomPositions[5] = { -2, -1, 0 , 1 , 2};
////
////
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
-float expow(float value, float exponent) {
+float expow(float value, float exponent)
+{
return lerp(1.0f,pow(value,max(exponent,1.0f)),saturate(exponent));
}
//the code that calls expow() carefully builds float2 for some reason and calls this only to have it implicitly thrown away (which is a warning)
//so this was added to get rid of the warning
-float expow(float2 value, float2 exponent) {
+float expow(float2 value, float2 exponent)
+{
return lerp(1.0f,pow(value,max(exponent,1.0f)),saturate(exponent)).x;
}
// MultiSampling for ghosting effect
-float3 GhostSample(sampler2D s, float2 t, float latency) {
-
+float3 GhostSample(sampler2D s, float2 t, float latency)
+{
float3 Out = tex2D(s,t);
float Weight = 1.0f;
float2 Direction = float2(-latency,0.0f);
- for(int i=1; i < GhostNumSamples; i++) {
- float curweight = pow(1.0f-((float)i/GhostNumSamples),1.0f/SignalLatencyAttenuation);
+ for(int i=1; i < GhostNumSamples; i++)
+ {
+ float curweight = pow(1.0f-((float)i/GhostNumSamples),1.0f/SignalLatencyAttenuation);
Out += GhostLatencyIntensity * curweight * tex2D(s,saturate(t+(1.0f-curweight)*Direction)).xyz;
Weight += GhostLatencyIntensity * curweight;
}
+
return Out/Weight;
}
// MultiSampling for ghosting effect
-float3 Bloom(sampler2D s, float2 t, float2 r) {
-
+float3 Bloom(sampler2D s, float2 t, float2 r)
+{
float3 Out = float3(0,0,0);
for(int j = 0; j < 5; j++)
+ {
for(int i = 0; i < 5; i++)
{
float2 offset = float2(BloomPositions[i],BloomPositions[j]) / r;
Out += tex2D(s, t + offset).rgb * BloomWeights[i][j];
}
+ }
+
return pow(Out, BloomExponent) * BloomIntensity;
}
// Compositing of the TV Emulation
-float3 TVEffect(float2 in_Position, float2 FakeResolution, sampler2D Texture, float Time) {
-
+float3 TVEffect(float2 in_Position, float2 FakeResolution, sampler2D Texture, float Time)
+{
// TV Deformation
float2 ScreenPos = in_Position + dot(in_Position-0.5f,in_Position-0.5f)*(in_Position-0.5f)* TVDeformInstensity;
@@ -147,9 +152,11 @@ float3 TVEffect(float2 in_Position, float2 FakeResolution, sampler2D Texture, fl
// Sampling 3 Images biased to simulate TV RGB Offset
#ifdef GHOST_SAMPLING
float3 latencyweight = float3(0.0f,0.0f,0.0f);
- for(int i=1; i < GhostNumSamples; i++) {
+ for(int i=1; i < GhostNumSamples; i++)
+ {
latencyweight += tex2D(Texture, ScreenPos + float2(1.0f/FakeResolution.x,0.0f)).xyz;
- }
+ }
+
float3 LatencyRGB = SignalLatencyRGB * (1.0-(latencyweight/GhostNumSamples));
float3 SMP_Red = GhostSample(Texture, (ScreenPos),LatencyRGB.x).xyz;
@@ -183,24 +190,22 @@ float3 TVEffect(float2 in_Position, float2 FakeResolution, sampler2D Texture, fl
float PixelMaskB = expow(saturate(4*frac(ScreenPos.x*FakeResolution.x+float2(ColorFringeIntensity*2.0f,0.0f))*(1.0f-frac(ScreenPos.x*FakeResolution.x+float2(ColorFringeIntensity*2.0f,0.0f)))),FakePixelMaskPower.x);
float PixelMaskScanline = pow(saturate(4*frac(ScreenPos.y*FakeResolution.y)*(1.0f-frac(ScreenPos.y*FakeResolution.y))),FakePixelMaskPower.y);
- float3 PixelRGB = float3 (
- ((PixelMaskR*PixelMaskScanline * FakePixelMaskGain)+FakePixelMaskOffset) * SMP_Red.x ,
- ((PixelMaskG*PixelMaskScanline * FakePixelMaskGain)+FakePixelMaskOffset) * SMP_Green.y ,
- ((PixelMaskB*PixelMaskScanline * FakePixelMaskGain)+FakePixelMaskOffset) * SMP_Blue.z
- );
+ float3 PixelRGB = float3(((PixelMaskR*PixelMaskScanline * FakePixelMaskGain)+FakePixelMaskOffset) * SMP_Red.x,
+ ((PixelMaskG*PixelMaskScanline * FakePixelMaskGain)+FakePixelMaskOffset) * SMP_Green.y,
+ ((PixelMaskB*PixelMaskScanline * FakePixelMaskGain)+FakePixelMaskOffset) * SMP_Blue.z);
// Non-Pixelated Image
float3 ImageRGB = tex2D(Texture, ScreenPos).xyz;
return lerp(ImageRGB, PixelRGB, FakePixelEffectBlend) * mask;
-
+
//return float3(PixelMaskR*PixelMaskScanline,PixelMaskG*PixelMaskScanline,PixelMaskB*PixelMaskScanline);
}
float4 main_fragment
(
- in float2 TexCoord : TEXCOORD,
+ in float4 vpos : POSITION,
+ in float2 TexCoord : TEXCOORD0,
in float2 FakeResolution : TEXCOORD1,
- in float2 wpos : WPOS,
uniform sampler2D s_p : TEXUNIT0,
uniform float Time
) : COLOR
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 8adc668cec..71c2fe93e8 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -18,7 +18,6 @@
-
@@ -34,6 +33,8 @@
+
+
diff --git a/src/BizHawk.Bizware.BizwareGL/EDispMethod.cs b/src/BizHawk.Bizware.BizwareGL/EDispMethod.cs
index 81211d49c8..449b9eb4d8 100644
--- a/src/BizHawk.Bizware.BizwareGL/EDispMethod.cs
+++ b/src/BizHawk.Bizware.BizwareGL/EDispMethod.cs
@@ -4,6 +4,6 @@ namespace BizHawk.Bizware.BizwareGL
{
OpenGL = 0,
GdiPlus = 1,
- D3D9 = 2
+ D3D11 = 2,
}
}
diff --git a/src/BizHawk.Bizware.BizwareGL/IGL.cs b/src/BizHawk.Bizware.BizwareGL/IGL.cs
index cde696cc01..af9c191aa9 100644
--- a/src/BizHawk.Bizware.BizwareGL/IGL.cs
+++ b/src/BizHawk.Bizware.BizwareGL/IGL.cs
@@ -9,7 +9,7 @@ namespace BizHawk.Bizware.BizwareGL
///
/// This is a wrapper over OpenGL and direct3d to give a uniform interface
/// TODO - This really needs to be split up into an internal and a user interface. so many of the functions are made to support the smart wrappers
- /// Maybe make a method that returns an interface used for advanced methods (and IGL_TK could implement that as well and just "return this:")
+ /// Maybe make a method that returns an interface used for advanced methods (and IGL_OpenGL could implement that as well and just "return this:")
///
/// NOTE: THIS SHOULD NOT BE ASSUMED TO BE THREAD SAFE! Make a new IGL if you want to use it in a new thread. I hope that will work...
///
diff --git a/src/BizHawk.Bizware.BizwareGL/RetroShader.cs b/src/BizHawk.Bizware.BizwareGL/RetroShader.cs
index 2eb7f3446f..2f3417b3b8 100644
--- a/src/BizHawk.Bizware.BizwareGL/RetroShader.cs
+++ b/src/BizHawk.Bizware.BizwareGL/RetroShader.cs
@@ -29,6 +29,12 @@ namespace BizHawk.Bizware.BizwareGL
if (!Pipeline.Available)
{
+ // make sure we release the vertex shader if it was compiled ok
+ if (vs.Available)
+ {
+ vs.Release();
+ }
+
Available = false;
return;
}
diff --git a/src/BizHawk.Bizware.Graphics.Controls/Controls/D3D9Control.cs b/src/BizHawk.Bizware.Graphics.Controls/Controls/D3D11Control.cs
similarity index 63%
rename from src/BizHawk.Bizware.Graphics.Controls/Controls/D3D9Control.cs
rename to src/BizHawk.Bizware.Graphics.Controls/Controls/D3D11Control.cs
index 73e66e89b7..6925f3b750 100644
--- a/src/BizHawk.Bizware.Graphics.Controls/Controls/D3D9Control.cs
+++ b/src/BizHawk.Bizware.Graphics.Controls/Controls/D3D11Control.cs
@@ -3,15 +3,15 @@ using System.Windows.Forms;
namespace BizHawk.Bizware.Graphics.Controls
{
- internal sealed class D3D9Control : GraphicsControl
+ internal sealed class D3D11Control : GraphicsControl
{
- private readonly Func _createSwapChain;
- private D3D9SwapChain _swapChain;
- private bool Vsync;
+ private readonly Func _createSwapChain;
+ private D3D11SwapChain _swapChain;
+ private bool Vsync, AllowsTearing;
- private D3D9SwapChain.ControlParameters ControlParameters => new(Handle, Width, Height, Vsync);
+ private D3D11SwapChain.ControlParameters ControlParameters => new(Handle, Width, Height, Vsync, AllowsTearing);
- public D3D9Control(Func createSwapChain)
+ public D3D11Control(Func createSwapChain)
{
_createSwapChain = createSwapChain;
@@ -41,14 +41,11 @@ namespace BizHawk.Bizware.Graphics.Controls
_swapChain.Refresh(ControlParameters);
}
+ public override void AllowTearing(bool state)
+ => AllowsTearing = state;
+
public override void SetVsync(bool state)
- {
- if (Vsync != state)
- {
- Vsync = state;
- _swapChain.Refresh(ControlParameters);
- }
- }
+ => Vsync = state;
public override void Begin()
=> _swapChain.SetBackBuffer();
diff --git a/src/BizHawk.Bizware.Graphics.Controls/Controls/GDIPlusControl.cs b/src/BizHawk.Bizware.Graphics.Controls/Controls/GDIPlusControl.cs
index de5e412427..3367726e45 100644
--- a/src/BizHawk.Bizware.Graphics.Controls/Controls/GDIPlusControl.cs
+++ b/src/BizHawk.Bizware.Graphics.Controls/Controls/GDIPlusControl.cs
@@ -27,6 +27,11 @@ namespace BizHawk.Bizware.Graphics.Controls
///
private GDIPlusRenderTarget RenderTarget { get; }
+ public override void AllowTearing(bool state)
+ {
+ // not controllable
+ }
+
public override void SetVsync(bool state)
{
// not really supported now...
diff --git a/src/BizHawk.Bizware.Graphics.Controls/Controls/OpenGLControl.cs b/src/BizHawk.Bizware.Graphics.Controls/Controls/OpenGLControl.cs
index 3edc498490..13e57c8c4c 100644
--- a/src/BizHawk.Bizware.Graphics.Controls/Controls/OpenGLControl.cs
+++ b/src/BizHawk.Bizware.Graphics.Controls/Controls/OpenGLControl.cs
@@ -68,6 +68,11 @@ namespace BizHawk.Bizware.Graphics.Controls
}
}
+ public override void AllowTearing(bool state)
+ {
+ // not controllable
+ }
+
public override void SetVsync(bool state)
{
MakeContextCurrent();
diff --git a/src/BizHawk.Bizware.Graphics.Controls/GraphicsControl.cs b/src/BizHawk.Bizware.Graphics.Controls/GraphicsControl.cs
index fb32cb464b..87f58f7e79 100644
--- a/src/BizHawk.Bizware.Graphics.Controls/GraphicsControl.cs
+++ b/src/BizHawk.Bizware.Graphics.Controls/GraphicsControl.cs
@@ -4,6 +4,12 @@ namespace BizHawk.Bizware.Graphics.Controls
{
public abstract class GraphicsControl : Control
{
+ ///
+ /// Allows the control to tear when out of vsync
+ /// Only relevant for D3D11Control currently
+ ///
+ public abstract void AllowTearing(bool state);
+
///
/// Sets whether presentation operations on this control will vsync
///
diff --git a/src/BizHawk.Bizware.Graphics.Controls/GraphicsControlFactory.cs b/src/BizHawk.Bizware.Graphics.Controls/GraphicsControlFactory.cs
index 05f11ce9a4..1ad647c38c 100644
--- a/src/BizHawk.Bizware.Graphics.Controls/GraphicsControlFactory.cs
+++ b/src/BizHawk.Bizware.Graphics.Controls/GraphicsControlFactory.cs
@@ -14,7 +14,7 @@ namespace BizHawk.Bizware.Graphics.Controls
GraphicsControl ret = gl switch
{
IGL_OpenGL => new OpenGLControl(),
- IGL_D3D9 d3d9 => new D3D9Control(d3d9.CreateSwapChain),
+ IGL_D3D11 d3d11 => new D3D11Control(d3d11.CreateSwapChain),
IGL_GDIPlus gdiPlus => new GDIPlusControl(gdiPlus.CreateControlRenderTarget),
_ => throw new InvalidOperationException()
};
diff --git a/src/BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj b/src/BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj
index 805c9754c8..34a8259032 100644
--- a/src/BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj
+++ b/src/BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj
@@ -9,8 +9,9 @@
-
+
+
diff --git a/src/BizHawk.Bizware.Graphics/D3D11/D3D11SwapChain.cs b/src/BizHawk.Bizware.Graphics/D3D11/D3D11SwapChain.cs
new file mode 100644
index 0000000000..94dab21bf0
--- /dev/null
+++ b/src/BizHawk.Bizware.Graphics/D3D11/D3D11SwapChain.cs
@@ -0,0 +1,130 @@
+using System;
+
+using Vortice.Direct3D11;
+using Vortice.DXGI;
+
+using DXGIResultCode = Vortice.DXGI.ResultCode;
+
+namespace BizHawk.Bizware.Graphics
+{
+ public sealed class D3D11SwapChain : IDisposable
+ {
+ public readonly struct ControlParameters
+ {
+ public readonly IntPtr Handle;
+ public readonly int Width;
+ public readonly int Height;
+ public readonly bool Vsync;
+ public readonly bool AllowsTearing;
+
+ public ControlParameters(IntPtr handle, int width, int height, bool vsync, bool allowsTearing)
+ {
+ Handle = handle;
+ Width = Math.Max(width, 1);
+ Height = Math.Max(height, 1);
+ Vsync = vsync;
+ AllowsTearing = allowsTearing;
+ }
+ }
+
+ internal class SwapChainResources : IDisposable
+ {
+ public ID3D11Device Device;
+ public ID3D11DeviceContext Context;
+ public ID3D11Texture2D BackBufferTexture;
+ public ID3D11RenderTargetView RTV;
+ public IDXGISwapChain1 SwapChain;
+ public bool AllowsTearing;
+
+ public void Dispose()
+ {
+ // Device/Context not owned by this class
+ Device = null;
+ Context = null;
+ RTV?.Dispose();
+ RTV = null;
+ BackBufferTexture?.Dispose();
+ BackBufferTexture = null;
+ SwapChain?.Dispose();
+ SwapChain = null;
+ }
+ }
+
+ private static readonly SharpGen.Runtime.Result D3DDDIERR_DEVICEREMOVED = new(2289436784UL);
+
+ private readonly SwapChainResources _resources;
+ private readonly Action _resetDeviceCallback;
+
+ private ID3D11Device Device => _resources.Device;
+ private ID3D11DeviceContext Context => _resources.Context;
+ private ID3D11Texture2D BackBufferTexture => _resources.BackBufferTexture;
+ private ID3D11RenderTargetView RTV => _resources.RTV;
+ private IDXGISwapChain1 SwapChain => _resources.SwapChain;
+ private bool AllowsTearing => _resources.AllowsTearing;
+
+ internal D3D11SwapChain(SwapChainResources resources, Action resetDeviceCallback)
+ {
+ _resources = resources;
+ _resetDeviceCallback = resetDeviceCallback;
+ }
+
+ public void Dispose()
+ => _resources.Dispose();
+
+ public void SetBackBuffer()
+ => Context.OMSetRenderTargets(RTV);
+
+ public void PresentBuffer(ControlParameters cp)
+ {
+ SetBackBuffer();
+
+ PresentFlags presentFlags;
+ if (cp.Vsync)
+ {
+ presentFlags = PresentFlags.None;
+ }
+ else
+ {
+ presentFlags = cp.AllowsTearing && AllowsTearing ? PresentFlags.AllowTearing : PresentFlags.DoNotWait;
+ }
+
+ var result = SwapChain.Present(cp.Vsync ? 1 : 0, presentFlags);
+ if (result == DXGIResultCode.DeviceReset
+ || result == DXGIResultCode.DeviceRemoved
+ || result == D3DDDIERR_DEVICEREMOVED)
+ {
+ _resetDeviceCallback(cp);
+ }
+ }
+
+ public void Refresh(ControlParameters cp)
+ {
+ // must be released in order to resize these buffers
+ RTV.Dispose();
+ _resources.RTV = null;
+ BackBufferTexture.Dispose();
+ _resources.BackBufferTexture = null;
+
+ var result = SwapChain.ResizeBuffers(
+ bufferCount: 2,
+ cp.Width,
+ cp.Height,
+ Format.B8G8R8A8_UNorm,
+ AllowsTearing ? SwapChainFlags.AllowTearing : SwapChainFlags.None);
+
+ if (result == DXGIResultCode.DeviceReset
+ || result == DXGIResultCode.DeviceRemoved
+ || result == D3DDDIERR_DEVICEREMOVED)
+ {
+ _resetDeviceCallback(cp);
+ }
+ else
+ {
+ result.CheckError();
+ _resources.BackBufferTexture = SwapChain.GetBuffer(0);
+ var rtvd = new RenderTargetViewDescription(RenderTargetViewDimension.Texture2D, Format.B8G8R8A8_UNorm);
+ _resources.RTV = Device.CreateRenderTargetView(BackBufferTexture, rtvd);
+ }
+ }
+ }
+}
diff --git a/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs b/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs
new file mode 100644
index 0000000000..869218720a
--- /dev/null
+++ b/src/BizHawk.Bizware.Graphics/D3D11/IGL_D3D11.cs
@@ -0,0 +1,1288 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Numerics;
+
+using BizHawk.Bizware.BizwareGL;
+using BizHawk.Common;
+using BizHawk.Common.StringExtensions;
+
+using Vortice.D3DCompiler;
+using Vortice.Direct3D;
+using Vortice.Direct3D11;
+using Vortice.Direct3D11.Shader;
+using Vortice.DXGI;
+
+// todo - do a better job selecting shader model? base on caps somehow? try several and catch compilation exceptions (yuck, exceptions)
+namespace BizHawk.Bizware.Graphics
+{
+ ///
+ /// Direct3D11 implementation of the BizwareGL.IGL interface
+ ///
+ public sealed class IGL_D3D11 : IGL
+ {
+ public EDispMethod DispMethodEnum => EDispMethod.D3D11;
+
+ private struct D3D11Resources : IDisposable
+ {
+ public IDXGIFactory2 Factory;
+ public ID3D11Device Device;
+ public ID3D11DeviceContext Context;
+ public ID3D11BlendState BlendEnableState;
+ public ID3D11BlendState BlendDisableState;
+ public ID3D11SamplerState PointSamplerState;
+ public ID3D11SamplerState LinearSamplerState;
+ public ID3D11RasterizerState RasterizerState;
+
+ public FeatureLevel DeviceFeatureLevel;
+
+ public void CreateResources()
+ {
+ try
+ {
+ // we need IDXGIFactory2 for CreateSwapChainForHwnd
+ Factory = DXGI.CreateDXGIFactory1();
+#if false
+ // use this to debug D3D11 calls
+ // note debug layer requires extra steps to use: https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-devices-layers#debug-layer
+ // also debug output will only be present with a "native debugger" attached (pure managed debugger can't see this output)
+ const DeviceCreationFlags creationFlags = DeviceCreationFlags.Singlethreaded | DeviceCreationFlags.BgraSupport | DeviceCreationFlags.Debug;
+#else
+ // IGL is not thread safe, so let's not bother making this implementation thread safe
+ const DeviceCreationFlags creationFlags = DeviceCreationFlags.Singlethreaded | DeviceCreationFlags.BgraSupport;
+#endif
+ D3D11.D3D11CreateDevice(
+ adapter: null,
+ DriverType.Hardware,
+ creationFlags,
+ null!, // this is safe to be null
+ out Device,
+ out Context).CheckError();
+
+ using var dxgiDevice = Device.QueryInterface();
+ dxgiDevice.MaximumFrameLatency = 1;
+
+ var bd = default(BlendDescription);
+ bd.AlphaToCoverageEnable = false;
+ bd.IndependentBlendEnable = false;
+ bd.RenderTarget[0].BlendEnable = true;
+ bd.RenderTarget[0].SourceBlend = Blend.SourceAlpha;
+ bd.RenderTarget[0].DestinationBlend = Blend.InverseSourceAlpha;
+ bd.RenderTarget[0].BlendOperation = BlendOperation.Add;
+ bd.RenderTarget[0].SourceBlendAlpha = Blend.One;
+ bd.RenderTarget[0].DestinationBlendAlpha = Blend.Zero;
+ bd.RenderTarget[0].BlendOperationAlpha = BlendOperation.Add;
+ bd.RenderTarget[0].RenderTargetWriteMask = ColorWriteEnable.All;
+ BlendEnableState = Device.CreateBlendState(bd);
+
+ bd.RenderTarget[0].BlendEnable = false;
+ bd.RenderTarget[0].SourceBlend = Blend.One;
+ bd.RenderTarget[0].DestinationBlend = Blend.Zero;
+ BlendDisableState = Device.CreateBlendState(bd);
+
+ PointSamplerState = Device.CreateSamplerState(SamplerDescription.PointClamp);
+ LinearSamplerState = Device.CreateSamplerState(SamplerDescription.LinearClamp);
+
+ DeviceFeatureLevel = Device.FeatureLevel;
+
+ var rd = new RasterizerDescription
+ {
+ CullMode = CullMode.None,
+ FillMode = FillMode.Solid,
+ ScissorEnable = true,
+ DepthClipEnable = DeviceFeatureLevel is FeatureLevel.Level_9_1 or FeatureLevel.Level_9_2 or FeatureLevel.Level_9_3,
+ };
+
+ RasterizerState = Device.CreateRasterizerState(rd);
+
+ Context.IASetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
+ }
+ catch
+ {
+ Dispose();
+ throw;
+ }
+ }
+
+ public void Dispose()
+ {
+ LinearSamplerState?.Dispose();
+ LinearSamplerState = null;
+ PointSamplerState?.Dispose();
+ PointSamplerState = null;
+
+ RasterizerState?.Dispose();
+ RasterizerState = null;
+
+ BlendEnableState?.Dispose();
+ BlendEnableState = null;
+ BlendDisableState?.Dispose();
+ BlendDisableState = null;
+
+ Context?.Dispose();
+ Context = null;
+ Device?.Dispose();
+ Device = null;
+
+ Factory?.Dispose();
+ Factory = null;
+ }
+ }
+
+ // D3D11 resources
+ // these might need to be thrown out and recreated if the device is lost
+ private D3D11Resources _resources;
+
+ private IDXGIFactory2 Factory => _resources.Factory;
+ private ID3D11Device Device => _resources.Device;
+ private ID3D11DeviceContext Context => _resources.Context;
+ private ID3D11BlendState BlendEnableState => _resources.BlendEnableState;
+ private ID3D11BlendState BlendDisableState => _resources.BlendDisableState;
+ private ID3D11SamplerState PointSamplerState => _resources.PointSamplerState;
+ private ID3D11SamplerState LinearSamplerState => _resources.LinearSamplerState;
+ private ID3D11RasterizerState RasterizerState => _resources.RasterizerState;
+
+ private FeatureLevel DeviceFeatureLevel => _resources.DeviceFeatureLevel;
+
+ // rendering state
+ private Pipeline _curPipeline;
+ private RenderTarget _curRenderTarget;
+ private D3D11SwapChain.SwapChainResources _controlSwapChain;
+
+ // misc state
+ private readonly HashSet _renderTargets = new();
+ private readonly HashSet _shaderTextures = new();
+ private readonly HashSet _vertexShaders = new();
+ private readonly HashSet _pixelShaders = new();
+ private readonly HashSet _pipelines = new();
+
+ public string API => "D3D11";
+
+ public IGL_D3D11()
+ {
+ if (OSTailoredCode.IsUnixHost)
+ {
+ throw new NotSupportedException("D3D11 is Windows only");
+ }
+
+ _resources.CreateResources();
+ }
+
+ private IDXGISwapChain1 CreateDXGISwapChain(D3D11SwapChain.ControlParameters cp)
+ {
+ // this is the optimal swapchain model
+ // note however it requires windows 10+
+ // a less optimal model will end up being used in case this fails
+ var sd = new SwapChainDescription1(
+ width: cp.Width,
+ height: cp.Height,
+ format: Format.B8G8R8A8_UNorm,
+ stereo: false,
+ swapEffect: SwapEffect.FlipDiscard,
+ bufferUsage: Usage.RenderTargetOutput,
+ bufferCount: 2,
+ scaling: Scaling.Stretch,
+ alphaMode: AlphaMode.Ignore,
+ flags: SwapChainFlags.AllowTearing);
+
+ IDXGISwapChain1 ret;
+ try
+ {
+ ret = Factory.CreateSwapChainForHwnd(Device, cp.Handle, sd);
+ }
+ catch
+ {
+ sd.SwapEffect = SwapEffect.Discard;
+ sd.Flags = SwapChainFlags.None;
+ ret = Factory.CreateSwapChainForHwnd(Device, cp.Handle, sd);
+ }
+
+ // don't allow DXGI to snoop alt+enter and such
+ using var parentFactory = ret.GetParent();
+ parentFactory.MakeWindowAssociation(cp.Handle, WindowAssociationFlags.IgnoreAll);
+ return ret;
+ }
+
+ private void ResetDevice(D3D11SwapChain.ControlParameters cp)
+ {
+ _controlSwapChain.Dispose();
+ Context.Flush(); // important to properly dispose of the swapchain
+
+ foreach (var pipeline in _pipelines)
+ {
+ var pw = (PipelineWrapper)pipeline.Opaque;
+
+ for (var i = 0; i < ID3D11DeviceContext.CommonShaderConstantBufferSlotCount; i++)
+ {
+ pw.VSConstantBuffers[i]?.Dispose();
+ pw.VSConstantBuffers[i] = null;
+ pw.PSConstantBuffers[i]?.Dispose();
+ pw.PSConstantBuffers[i] = null;
+ }
+
+ var vlw = (VertexLayoutWrapper)pipeline.VertexLayout.Opaque;
+ vlw.VertexInputLayout.Dispose();
+ vlw.VertexInputLayout = null;
+ vlw.VertexBuffer.Dispose();
+ vlw.VertexBuffer = null;
+ vlw.VertexBufferCount = 0;
+ }
+
+ foreach (var sw in _vertexShaders.Select(vertexShader => (ShaderWrapper)vertexShader.Opaque))
+ {
+ sw.VS.Dispose();
+ sw.VS = null;
+ }
+
+ foreach (var sw in _pixelShaders.Select(pixelShader => (ShaderWrapper)pixelShader.Opaque))
+ {
+ sw.PS.Dispose();
+ sw.PS = null;
+ }
+
+ foreach (var rt in _renderTargets)
+ {
+ var rw = (RenderTargetWrapper)rt.Opaque;
+ rw.RTV.Dispose();
+ rw.RTV = null;
+
+ var tw = (TextureWrapper)rt.Texture2d.Opaque;
+ tw.SRV.Dispose();
+ tw.SRV = null;
+ tw.Texture.Dispose();
+ tw.Texture = null;
+ }
+
+ foreach (var tw in _shaderTextures.Select(tex2d => (TextureWrapper)tex2d.Opaque))
+ {
+ tw.SRV.Dispose();
+ tw.SRV = null;
+ tw.Texture.Dispose();
+ tw.Texture = null;
+ }
+
+ _resources.Dispose();
+ _resources.CreateResources();
+
+ foreach (var tex2d in _shaderTextures)
+ {
+ var tw = (TextureWrapper)tex2d.Opaque;
+ tw.Texture = CreateTextureForShader(tex2d.IntWidth, tex2d.IntHeight);
+
+ var srvd = new ShaderResourceViewDescription(ShaderResourceViewDimension.Texture2D, Format.B8G8R8A8_UNorm, mostDetailedMip: 0, mipLevels: 1);
+ tw.SRV = Device.CreateShaderResourceView(tw.Texture, srvd);
+ }
+
+ foreach (var rt in _renderTargets)
+ {
+ var tw = (TextureWrapper)rt.Texture2d.Opaque;
+ tw.Texture = CreateTextureForRenderTarget(rt.Texture2d.IntWidth, rt.Texture2d.IntHeight);
+
+ var srvd = new ShaderResourceViewDescription(ShaderResourceViewDimension.Texture2D, Format.B8G8R8A8_UNorm, mostDetailedMip: 0, mipLevels: 1);
+ tw.SRV = Device.CreateShaderResourceView(tw.Texture, srvd);
+
+ var rw = (RenderTargetWrapper)rt.Opaque;
+ var rtvd = new RenderTargetViewDescription(RenderTargetViewDimension.Texture2D, Format.B8G8R8A8_UNorm);
+ rw.RTV = Device.CreateRenderTargetView(tw.Texture, rtvd);
+ }
+
+ foreach (var sw in _vertexShaders.Select(vertexShader => (ShaderWrapper)vertexShader.Opaque))
+ {
+ sw.VS = Device.CreateVertexShader(sw.Bytecode.Span);
+ }
+
+ foreach (var sw in _pixelShaders.Select(pixelShader => (ShaderWrapper)pixelShader.Opaque))
+ {
+ sw.PS = Device.CreatePixelShader(sw.Bytecode.Span);
+ }
+
+ foreach (var pipeline in _pipelines)
+ {
+ var pw = (PipelineWrapper)pipeline.Opaque;
+ for (var i = 0; i < ID3D11DeviceContext.CommonShaderConstantBufferSlotCount; i++)
+ {
+ var cbw = pw.PendingBuffers[i];
+ if (cbw == null)
+ {
+ break;
+ }
+
+ if (cbw.VSBufferSize > 0)
+ {
+ var bd = new BufferDescription(cbw.VSBufferSize, BindFlags.ConstantBuffer, ResourceUsage.Dynamic, CpuAccessFlags.Write);
+ pw.VSConstantBuffers[i] = Device.CreateBuffer(bd);
+ cbw.VSBufferDirty = true;
+ }
+
+ if (cbw.PSBufferSize > 0)
+ {
+ var bd = new BufferDescription(cbw.PSBufferSize, BindFlags.ConstantBuffer, ResourceUsage.Dynamic, CpuAccessFlags.Write);
+ pw.PSConstantBuffers[i] = Device.CreateBuffer(bd);
+ cbw.PSBufferDirty = true;
+ }
+ }
+
+ CreateInputLayout(pipeline.VertexLayout, pw.VertexShader);
+ }
+
+ var swapChain = CreateDXGISwapChain(cp);
+ var bbTex = swapChain.GetBuffer(0);
+ var bbRtvd = new RenderTargetViewDescription(RenderTargetViewDimension.Texture2D, Format.B8G8R8A8_UNorm);
+ var rtv = Device.CreateRenderTargetView(bbTex, bbRtvd);
+
+ _controlSwapChain.Device = Device;
+ _controlSwapChain.Context = Context;
+ _controlSwapChain.BackBufferTexture = bbTex;
+ _controlSwapChain.RTV = rtv;
+ _controlSwapChain.SwapChain = swapChain;
+ _controlSwapChain.AllowsTearing = (swapChain.Description1.Flags & SwapChainFlags.AllowTearing) != 0;
+ }
+
+ public D3D11SwapChain CreateSwapChain(D3D11SwapChain.ControlParameters cp)
+ {
+ if (_controlSwapChain != null)
+ {
+ throw new InvalidOperationException($"{nameof(IGL_D3D11)} can only have 1 control swap chain");
+ }
+
+ var swapChain = CreateDXGISwapChain(cp);
+ var bbTex = swapChain.GetBuffer(0);
+ var rtvd = new RenderTargetViewDescription(RenderTargetViewDimension.Texture2D, Format.B8G8R8A8_UNorm);
+ var rtv = Device.CreateRenderTargetView(bbTex, rtvd);
+
+ _controlSwapChain = new D3D11SwapChain.SwapChainResources
+ {
+ Device = Device,
+ Context = Context,
+ BackBufferTexture = bbTex,
+ RTV = rtv,
+ SwapChain = swapChain,
+ AllowsTearing = (swapChain.Description1.Flags & SwapChainFlags.AllowTearing) != 0,
+ };
+
+ return new(_controlSwapChain, ResetDevice);
+ }
+
+ public void Dispose()
+ {
+ _controlSwapChain.Dispose();
+ Context.Flush();
+ _resources.Dispose();
+ }
+
+ public void ClearColor(Color color)
+ {
+ if (_curRenderTarget == null)
+ {
+ Context.ClearRenderTargetView(_controlSwapChain.RTV, new(color.R, color.B, color.G, color.A));
+ }
+ else
+ {
+ var rw = (RenderTargetWrapper)_curRenderTarget.Opaque;
+ Context.ClearRenderTargetView(rw.RTV, new(color.R, color.B, color.G, color.A));
+ }
+ }
+
+ public void FreeTexture(Texture2d tex)
+ {
+ var tw = (TextureWrapper)tex.Opaque;
+ tw.SRV.Dispose();
+ tw.Texture.Dispose();
+ _shaderTextures.Remove(tex);
+ }
+
+ private class ShaderWrapper // Disposable fields cleaned up by Internal_FreeShader
+ {
+ public ReadOnlyMemory Bytecode;
+ public ID3D11ShaderReflection Reflection;
+ public ID3D11VertexShader VS;
+ public ID3D11PixelShader PS;
+ public Shader IGLShader;
+ }
+
+ /// is and compilation error occurred
+ public Shader CreateVertexShader(string source, string entry, bool required)
+ {
+ try
+ {
+ var sw = new ShaderWrapper();
+
+ var profile = DeviceFeatureLevel switch
+ {
+ FeatureLevel.Level_9_1 or FeatureLevel.Level_9_2 => "vs_4_0_level_9_1",
+ FeatureLevel.Level_9_3 => "vs_4_0_level_9_3",
+ _ => "vs_4_0",
+ };
+
+ // note: we use D3D9-like shaders for legacy reasons, so we need the backwards compat flag
+ // TODO: remove D3D9 syntax from shaders
+ var result = Compiler.Compile(
+ shaderSource: source,
+ entryPoint: entry,
+ sourceName: null!, // this is safe to be null
+ profile: profile,
+ shaderFlags: ShaderFlags.OptimizationLevel3 | ShaderFlags.EnableBackwardsCompatibility);
+
+ sw.VS = Device.CreateVertexShader(result.Span);
+ sw.Reflection = Compiler.Reflect(result.Span);
+ sw.Bytecode = result;
+
+ var s = new Shader(this, sw, true);
+ sw.IGLShader = s;
+ _vertexShaders.Add(s);
+
+ return s;
+ }
+ catch (Exception ex)
+ {
+ if (required)
+ {
+ throw;
+ }
+
+ return new(this, null, false) { Errors = ex.ToString() };
+ }
+ }
+
+ /// is and compilation error occurred
+ public Shader CreateFragmentShader(string source, string entry, bool required)
+ {
+ try
+ {
+ var sw = new ShaderWrapper();
+
+ var profile = DeviceFeatureLevel switch
+ {
+ FeatureLevel.Level_9_1 or FeatureLevel.Level_9_2 => "ps_4_0_level_9_1",
+ FeatureLevel.Level_9_3 => "ps_4_0_level_9_3",
+ _ => "ps_4_0",
+ };
+
+ // note: we use D3D9-like shaders for legacy reasons, so we need the backwards compat flag
+ // TODO: remove D3D9 syntax from shaders
+ var result = Compiler.Compile(
+ shaderSource: source,
+ entryPoint: entry,
+ sourceName: null!, // this is safe to be null
+ profile: profile,
+ shaderFlags: ShaderFlags.OptimizationLevel3 | ShaderFlags.EnableBackwardsCompatibility);
+
+ sw.PS = Device.CreatePixelShader(result.Span);
+ sw.Reflection = Compiler.Reflect(result.Span);
+ sw.Bytecode = result;
+
+ var s = new Shader(this, sw, true);
+ sw.IGLShader = s;
+ _pixelShaders.Add(s);
+
+ return s;
+ }
+ catch (Exception ex)
+ {
+ if (required)
+ {
+ throw;
+ }
+
+ return new(this, null, false) { Errors = ex.ToString() };
+ }
+ }
+
+ public void EnableBlending()
+ => Context.OMSetBlendState(BlendEnableState);
+
+ public void DisableBlending()
+ => Context.OMSetBlendState(BlendDisableState);
+
+ private void CreateInputLayout(VertexLayout vertexLayout, ShaderWrapper vertexShader)
+ {
+ var ves = new InputElementDescription[vertexLayout.Items.Count];
+ var stride = 0;
+ foreach (var (i, item) in vertexLayout.Items)
+ {
+ if (item.AttribType != VertexAttribPointerType.Float)
+ {
+ throw new NotSupportedException();
+ }
+
+ var semanticName = item.Usage switch
+ {
+ AttribUsage.Position => "POSITION",
+ AttribUsage.Color0 => "COLOR",
+ AttribUsage.Texcoord0 or AttribUsage.Texcoord1 => "TEXCOORD",
+ _ => throw new InvalidOperationException()
+ };
+
+ var format = item.Components switch
+ {
+ 1 => Format.R32_Float,
+ 2 => Format.R32G32_Float,
+ 3 => Format.R32G32B32_Float,
+ 4 => Format.R32G32B32A32_Float,
+ _ => throw new InvalidOperationException()
+ };
+
+ ves[i] = new(semanticName, item.Usage == AttribUsage.Texcoord1 ? 1 : 0, format, item.Offset, 0);
+ stride += 4 * item.Components;
+ }
+
+ var vlw = (VertexLayoutWrapper)vertexLayout.Opaque;
+ var bc = vertexShader.Bytecode.Span;
+ vlw.VertexInputLayout = Device.CreateInputLayout(ves, bc);
+ vlw.VertexStride = stride;
+ }
+
+ ///
+ /// is and either or is unavailable (their property is ), or
+ /// one of 's items has an unsupported value in , , or
+ ///
+ public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required, string memo)
+ {
+ if (!vertexShader.Available || !fragmentShader.Available)
+ {
+ var 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 D3D11 pipeline:\r\n{errors}");
+ }
+
+ return new(this, null, false, null, null, null) { Errors = errors };
+ }
+
+ var pw = new PipelineWrapper
+ {
+ VertexShader = (ShaderWrapper)vertexShader.Opaque,
+ FragmentShader = (ShaderWrapper)fragmentShader.Opaque,
+ };
+
+ CreateInputLayout(vertexLayout, pw.VertexShader);
+
+ // scan uniforms from reflection
+ var uniforms = new List();
+ var vsrefl = pw.VertexShader.Reflection;
+ var psrefl = pw.FragmentShader.Reflection;
+ foreach (var refl in new[] { vsrefl, psrefl })
+ {
+ var isVs = refl == vsrefl;
+ var reflCbs = refl.ConstantBuffers;
+ var todo = new Queue<(string, PendingBufferWrapper, string, int, ID3D11ShaderReflectionType)>();
+ for (var i = 0; i < reflCbs.Length; i++)
+ {
+ var cbDesc = reflCbs[i].Description;
+ var bd = new BufferDescription((cbDesc.Size + 15) & ~15, BindFlags.ConstantBuffer, ResourceUsage.Dynamic, CpuAccessFlags.Write);
+ var constantBuffer = Device.CreateBuffer(bd);
+ var pendingBuffer = Marshal.AllocCoTaskMem(cbDesc.Size);
+ pw.PendingBuffers[i] ??= new();
+ if (isVs)
+ {
+ pw.VSConstantBuffers[i] = constantBuffer;
+ pw.PendingBuffers[i].VSPendingBuffer = pendingBuffer;
+ pw.PendingBuffers[i].VSBufferSize = cbDesc.Size;
+ }
+ else
+ {
+ pw.PSConstantBuffers[i] = constantBuffer;
+ pw.PendingBuffers[i].PSPendingBuffer = pendingBuffer;
+ pw.PendingBuffers[i].PSBufferSize = cbDesc.Size;
+ }
+
+ var prefix = cbDesc.Name.RemovePrefix('$');
+ prefix = prefix is "Params" or "Globals" ? "" : $"{prefix}.";
+ foreach (var reflVari in reflCbs[i].Variables)
+ {
+ var reflVariDesc = reflVari.Description;
+ todo.Enqueue((prefix, pw.PendingBuffers[i], reflVariDesc.Name, reflVariDesc.StartOffset, reflVari.VariableType));
+ }
+ }
+
+ while (todo.Count != 0)
+ {
+ var (prefix, pbw, reflVariName, reflVariOffset, reflVariType) = todo.Dequeue();
+ var reflVariTypeDesc = reflVariType.Description;
+ if (reflVariTypeDesc.MemberCount > 0)
+ {
+ prefix = $"{prefix}{reflVariName}.";
+ for (var i = 0; i < reflVariTypeDesc.MemberCount; i++)
+ {
+ var memberName = reflVariType.GetMemberTypeName(i);
+ var memberType = reflVariType.GetMemberTypeByIndex(i);
+ var memberOffset = memberType.Description.Offset;
+ todo.Enqueue((prefix, pbw, memberName, reflVariOffset + memberOffset, memberType));
+ }
+
+ continue;
+ }
+
+ if (reflVariTypeDesc.Type is not (ShaderVariableType.Bool or ShaderVariableType.Float))
+ {
+ // unsupported type
+ continue;
+ }
+
+ var reflVariSize = 4 * reflVariTypeDesc.ColumnCount * reflVariTypeDesc.RowCount;
+ uniforms.Add(new()
+ {
+ Name = $"{prefix}{reflVariName}",
+ Opaque = new UniformWrapper
+ {
+ PBW = pbw,
+ VariableStartOffset = reflVariOffset,
+ VariableSize = reflVariSize,
+ VS = isVs
+ }
+ });
+ }
+
+ uniforms.AddRange(refl.BoundResources
+ .Where(resource => resource.Type == ShaderInputType.Sampler)
+ .Select(resource => new UniformInfo
+ {
+ IsSampler = true,
+ Name = resource.Name.RemovePrefix('$'),
+ Opaque = new UniformWrapper { VS = isVs },
+ SamplerIndex = resource.BindPoint
+ }));
+ }
+
+ var ret = new Pipeline(this, pw, true, vertexLayout, uniforms, memo);
+ _pipelines.Add(ret);
+ return ret;
+ }
+
+ public void FreePipeline(Pipeline pipeline)
+ {
+ // unavailable pipelines will have no opaque
+ if (pipeline.Opaque is not PipelineWrapper pw)
+ {
+ return;
+ }
+
+ for (var i = 0; i < ID3D11DeviceContext.CommonShaderConstantBufferSlotCount; i++)
+ {
+ if (pw.PendingBuffers[i] != null)
+ {
+ Marshal.FreeCoTaskMem(pw.PendingBuffers[i].VSPendingBuffer);
+ Marshal.FreeCoTaskMem(pw.PendingBuffers[i].PSPendingBuffer);
+ pw.PendingBuffers[i] = null;
+ }
+
+ pw.VSConstantBuffers[i]?.Dispose();
+ pw.VSConstantBuffers[i] = null;
+ pw.PSConstantBuffers[i]?.Dispose();
+ pw.PSConstantBuffers[i] = null;
+ }
+
+ pw.VertexShader.IGLShader.Release();
+ pw.FragmentShader.IGLShader.Release();
+
+ _pipelines.Remove(pipeline);
+ }
+
+ public void Internal_FreeShader(Shader shader)
+ {
+ var sw = (ShaderWrapper)shader.Opaque;
+ sw.Reflection?.Dispose();
+ sw.Reflection = null;
+
+ if (sw.VS != null)
+ {
+ sw.VS.Dispose();
+ sw.VS = null;
+ _vertexShaders.Remove(shader);
+ }
+
+ if (sw.PS != null)
+ {
+ sw.PS.Dispose();
+ sw.PS = null;
+ _pixelShaders.Remove(shader);
+ }
+ }
+
+ private class UniformWrapper
+ {
+ public PendingBufferWrapper PBW;
+ public int VariableStartOffset;
+ public int VariableSize;
+ public bool VS;
+ }
+
+ private class PendingBufferWrapper
+ {
+ public IntPtr VSPendingBuffer, PSPendingBuffer;
+ public int VSBufferSize, PSBufferSize;
+ public bool VSBufferDirty, PSBufferDirty;
+ }
+
+ private class PipelineWrapper // Disposable fields cleaned up by FreePipeline
+ {
+ public readonly PendingBufferWrapper[] PendingBuffers = new PendingBufferWrapper[ID3D11DeviceContext.CommonShaderConstantBufferSlotCount];
+ public readonly ID3D11Buffer[] VSConstantBuffers = new ID3D11Buffer[ID3D11DeviceContext.CommonShaderConstantBufferSlotCount];
+ public readonly ID3D11Buffer[] PSConstantBuffers = new ID3D11Buffer[ID3D11DeviceContext.CommonShaderConstantBufferSlotCount];
+ public ShaderWrapper VertexShader, FragmentShader;
+ }
+
+ private class TextureWrapper
+ {
+ public ID3D11Texture2D Texture;
+ public ID3D11ShaderResourceView SRV;
+ public bool LinearFiltering;
+ }
+
+ private class RenderTargetWrapper
+ {
+ public ID3D11RenderTargetView RTV;
+ }
+
+ private class VertexLayoutWrapper
+ {
+ public ID3D11InputLayout VertexInputLayout;
+ public int VertexStride;
+
+ public ID3D11Buffer VertexBuffer;
+ public int VertexBufferCount;
+ }
+
+ public VertexLayout CreateVertexLayout()
+ => new(this, new VertexLayoutWrapper());
+
+ public void Internal_FreeVertexLayout(VertexLayout layout)
+ {
+ var vlw = (VertexLayoutWrapper)layout.Opaque;
+ vlw.VertexInputLayout?.Dispose();
+ vlw.VertexInputLayout = null;
+ vlw.VertexBuffer?.Dispose();
+ vlw.VertexBuffer = null;
+ }
+
+ public void BindPipeline(Pipeline pipeline)
+ {
+ _curPipeline = pipeline;
+
+ if (pipeline == null)
+ {
+ // unbind? i don't know
+ return;
+ }
+
+ var pw = (PipelineWrapper)pipeline.Opaque;
+ Context.VSSetShader(pw.VertexShader.VS);
+ Context.PSSetShader(pw.FragmentShader.PS);
+
+ Context.VSSetConstantBuffers(0, pw.VSConstantBuffers);
+ Context.PSSetConstantBuffers(0, pw.PSConstantBuffers);
+
+ var vlw = (VertexLayoutWrapper)pipeline.VertexLayout.Opaque;
+ Context.IASetInputLayout(vlw.VertexInputLayout);
+ Context.IASetVertexBuffer(0, vlw.VertexBuffer, vlw.VertexStride);
+
+ // not sure if this applies to the current pipeline or all pipelines
+ // just set it every time to be safe
+ Context.RSSetState(RasterizerState);
+ }
+
+ public void SetPipelineUniform(PipelineUniform uniform, bool value)
+ {
+ foreach (var ui in uniform.UniformInfos)
+ {
+ var uw = (UniformWrapper)ui.Opaque;
+ var pendingBuffer = uw.VS ? uw.PBW.VSPendingBuffer : uw.PBW.PSPendingBuffer;
+ unsafe
+ {
+ // note: HLSL bool is 4 bytes large
+ var b = value ? 1 : 0;
+ var variablePtr = (void*)(pendingBuffer + uw.VariableStartOffset);
+ Buffer.MemoryCopy(&b, variablePtr, uw.VariableSize, sizeof(int));
+ }
+
+ if (uw.VS)
+ {
+ uw.PBW.VSBufferDirty = true;
+ }
+ else
+ {
+ uw.PBW.PSBufferDirty = true;
+ }
+ }
+ }
+
+ public void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4x4 mat, bool transpose)
+ => SetPipelineUniformMatrix(uniform, ref mat, transpose);
+
+ public void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4x4 mat, bool transpose)
+ {
+ foreach (var ui in uniform.UniformInfos)
+ {
+ var uw = (UniformWrapper)ui.Opaque;
+ var pendingBuffer = uw.VS ? uw.PBW.VSPendingBuffer : uw.PBW.PSPendingBuffer;
+ unsafe
+ {
+ // transpose logic is inversed
+ var m = transpose ? Matrix4x4.Transpose(mat) : mat;
+ var variablePtr = (void*)(pendingBuffer + uw.VariableStartOffset);
+ Buffer.MemoryCopy(&m, variablePtr, uw.VariableSize, sizeof(Matrix4x4));
+ }
+
+ if (uw.VS)
+ {
+ uw.PBW.VSBufferDirty = true;
+ }
+ else
+ {
+ uw.PBW.PSBufferDirty = true;
+ }
+ }
+ }
+
+ public void SetPipelineUniform(PipelineUniform uniform, Vector4 value)
+ {
+ foreach (var ui in uniform.UniformInfos)
+ {
+ var uw = (UniformWrapper)ui.Opaque;
+ var pendingBuffer = uw.VS ? uw.PBW.VSPendingBuffer : uw.PBW.PSPendingBuffer;
+ unsafe
+ {
+ var variablePtr = (void*)(pendingBuffer + uw.VariableStartOffset);
+ Buffer.MemoryCopy(&value, variablePtr, uw.VariableSize, sizeof(Vector4));
+ }
+
+ if (uw.VS)
+ {
+ uw.PBW.VSBufferDirty = true;
+ }
+ else
+ {
+ uw.PBW.PSBufferDirty = true;
+ }
+ }
+ }
+
+ public void SetPipelineUniform(PipelineUniform uniform, Vector2 value)
+ {
+ foreach (var ui in uniform.UniformInfos)
+ {
+ var uw = (UniformWrapper)ui.Opaque;
+ var pendingBuffer = uw.VS ? uw.PBW.VSPendingBuffer : uw.PBW.PSPendingBuffer;
+ unsafe
+ {
+ var variablePtr = (void*)(pendingBuffer + uw.VariableStartOffset);
+ Buffer.MemoryCopy(&value, variablePtr, uw.VariableSize, sizeof(Vector2));
+ }
+
+ if (uw.VS)
+ {
+ uw.PBW.VSBufferDirty = true;
+ }
+ else
+ {
+ uw.PBW.PSBufferDirty = true;
+ }
+ }
+ }
+
+ public void SetPipelineUniform(PipelineUniform uniform, float value)
+ {
+ foreach (var ui in uniform.UniformInfos)
+ {
+ var uw = (UniformWrapper)ui.Opaque;
+ var pendingBuffer = uw.VS ? uw.PBW.VSPendingBuffer : uw.PBW.PSPendingBuffer;
+ unsafe
+ {
+ var variablePtr = (void*)(pendingBuffer + uw.VariableStartOffset);
+ Buffer.MemoryCopy(&value, variablePtr, uw.VariableSize, sizeof(float));
+ }
+
+ if (uw.VS)
+ {
+ uw.PBW.VSBufferDirty = true;
+ }
+ else
+ {
+ uw.PBW.PSBufferDirty = true;
+ }
+ }
+ }
+
+ public void SetPipelineUniform(PipelineUniform uniform, Vector4[] values)
+ {
+ foreach (var ui in uniform.UniformInfos)
+ {
+ var uw = (UniformWrapper)ui.Opaque;
+ var pendingBuffer = uw.VS ? uw.PBW.VSPendingBuffer : uw.PBW.PSPendingBuffer;
+ unsafe
+ {
+ fixed (Vector4* v = values)
+ {
+ var variablePtr = (void*)(pendingBuffer + uw.VariableStartOffset);
+ Buffer.MemoryCopy(v, variablePtr, uw.VariableSize, values.Length * sizeof(Vector4));
+ }
+ }
+
+ if (uw.VS)
+ {
+ uw.PBW.VSBufferDirty = true;
+ }
+ else
+ {
+ uw.PBW.PSBufferDirty = true;
+ }
+ }
+ }
+
+ public void SetPipelineUniformSampler(PipelineUniform uniform, Texture2d tex)
+ {
+ if (uniform.Owner == null)
+ {
+ return; // uniform was optimized out
+ }
+
+ var tw = (TextureWrapper)tex.Opaque;
+ var sampler = tw.LinearFiltering ? LinearSamplerState : PointSamplerState;
+
+ foreach (var ui in uniform.UniformInfos)
+ {
+ if (!ui.IsSampler)
+ {
+ throw new InvalidOperationException("Uniform was not a texture/sampler");
+ }
+
+ var uw = (UniformWrapper)ui.Opaque;
+ if (uw.VS)
+ {
+ if (DeviceFeatureLevel == FeatureLevel.Level_9_1)
+ {
+ throw new InvalidOperationException("Feature level 9.1 does not support setting a shader resource in a vertex shader");
+ }
+
+ Context.VSSetShaderResource(ui.SamplerIndex, tw.SRV);
+ Context.VSSetSampler(ui.SamplerIndex, sampler);
+ }
+ else
+ {
+ Context.PSSetShaderResource(ui.SamplerIndex, tw.SRV);
+ Context.PSSetSampler(ui.SamplerIndex, sampler);
+ }
+ }
+ }
+
+ // TODO: Remove this, we only use clamp ever anyways
+ public void SetTextureWrapMode(Texture2d tex, bool clamp)
+ {
+ }
+
+ // TODO: Merge these to one call (for Linear / Nearest)
+ public void SetMinFilter(Texture2d texture, TextureMinFilter minFilter)
+ {
+ var tw = (TextureWrapper)texture.Opaque;
+ tw.LinearFiltering = minFilter == TextureMinFilter.Linear;
+ }
+
+ public void SetMagFilter(Texture2d texture, TextureMagFilter magFilter)
+ {
+ var tw = (TextureWrapper)texture.Opaque;
+ tw.LinearFiltering = magFilter == TextureMagFilter.Linear;
+ }
+
+ public Texture2d LoadTexture(Bitmap bitmap)
+ {
+ using var bmp = new BitmapBuffer(bitmap, new());
+ return LoadTexture(bmp);
+ }
+
+ public Texture2d LoadTexture(Stream stream)
+ {
+ using var bmp = new BitmapBuffer(stream, new());
+ return LoadTexture(bmp);
+ }
+
+ private ID3D11Texture2D CreateTextureForShader(int width, int height)
+ {
+ return Device.CreateTexture2D(
+ Format.B8G8R8A8_UNorm,
+ width,
+ height,
+ mipLevels: 1,
+ bindFlags: BindFlags.ShaderResource,
+ usage: ResourceUsage.Dynamic,
+ cpuAccessFlags: CpuAccessFlags.Write);
+ }
+
+ public Texture2d CreateTexture(int width, int height)
+ {
+ var tex = CreateTextureForShader(width, height);
+ var srvd = new ShaderResourceViewDescription(ShaderResourceViewDimension.Texture2D, Format.B8G8R8A8_UNorm, mostDetailedMip: 0, mipLevels: 1);
+ var srv = Device.CreateShaderResourceView(tex, srvd);
+ var tw = new TextureWrapper { Texture = tex, SRV = srv };
+ var ret = new Texture2d(this, tw, width, height);
+ _shaderTextures.Add(ret);
+ return ret;
+ }
+
+ public Texture2d WrapGLTexture2d(IntPtr glTexId, int width, int height)
+ {
+ // not used for non-GL backends
+ return null;
+ }
+
+ /// GDI+ call returned unexpected data
+ public unsafe void LoadTextureData(Texture2d tex, BitmapBuffer bmp)
+ {
+ if (bmp.Width != tex.IntWidth || bmp.Height != tex.IntHeight)
+ {
+ throw new InvalidOperationException();
+ }
+
+ var tw = (TextureWrapper)tex.Opaque;
+ var bmpData = bmp.LockBits();
+
+ try
+ {
+ var srcSpan = new ReadOnlySpan(bmpData.Scan0.ToPointer(), bmpData.Stride * bmp.Height);
+ var mappedTex = Context.Map(tw.Texture, 0, 0, MapMode.WriteDiscard);
+
+ if (srcSpan.Length == mappedTex.Length)
+ {
+ srcSpan.CopyTo(mappedTex);
+ }
+ else
+ {
+ // D3D11 sometimes has weird pitches (seen with 3DS)
+ int srcStart = 0, dstStart = 0;
+ int srcStride = bmpData.Stride, dstStride = mappedTex.Length / bmp.Height;
+ var height = bmp.Height;
+ for (var i = 0; i < height; i++)
+ {
+ srcSpan.Slice(srcStart, srcStride)
+ .CopyTo(mappedTex.Slice(dstStart, dstStride));
+ srcStart += srcStride;
+ dstStart += dstStride;
+ }
+ }
+ }
+ finally
+ {
+ Context.Unmap(tw.Texture, 0);
+ bmp.UnlockBits(bmpData);
+ }
+ }
+
+ public Texture2d LoadTexture(BitmapBuffer bmp)
+ {
+ var ret = CreateTexture(bmp.Width, bmp.Height);
+ LoadTextureData(ret, bmp);
+ return ret;
+ }
+
+ /// Vortice call returned unexpected data
+ public BitmapBuffer ResolveTexture2d(Texture2d tex)
+ {
+ using var target = Device.CreateTexture2D(
+ Format.B8G8R8A8_UNorm,
+ tex.IntWidth,
+ tex.IntHeight,
+ mipLevels: 1,
+ bindFlags: BindFlags.None,
+ usage: ResourceUsage.Staging,
+ cpuAccessFlags: CpuAccessFlags.Read);
+
+ var tw = (TextureWrapper)tex.Opaque;
+ Context.CopyResource(target, tw.Texture);
+
+ try
+ {
+ var srcSpan = Context.MapReadOnly(target);
+ var pixels = new int[tex.IntWidth * tex.IntHeight];
+ var dstSpan = MemoryMarshal.AsBytes(pixels.AsSpan());
+
+ if (srcSpan.Length == dstSpan.Length)
+ {
+ srcSpan.CopyTo(dstSpan);
+ }
+ else
+ {
+ int srcStart = 0, dstStart = 0;
+ int srcStride = srcSpan.Length / tex.IntHeight, dstStride = tex.IntWidth * sizeof(int);
+ var height = tex.IntHeight;
+ for (var i = 0; i < height; i++)
+ {
+ srcSpan.Slice(srcStart, dstStride)
+ .CopyTo(dstSpan.Slice(dstStart, dstStride));
+ srcStart += srcStride;
+ dstStart += dstStride;
+ }
+ }
+
+ return new(tex.IntWidth, tex.IntHeight, pixels);
+ }
+ finally
+ {
+ Context.Unmap(target, 0);
+ }
+ }
+
+ public Texture2d LoadTexture(string path)
+ {
+ using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
+ return LoadTexture(fs);
+ }
+
+ public Matrix4x4 CreateGuiProjectionMatrix(int w, int h)
+ => CreateGuiProjectionMatrix(new(w, h));
+
+ public Matrix4x4 CreateGuiViewMatrix(int w, int h, bool autoFlip)
+ => CreateGuiViewMatrix(new(w, h), autoFlip);
+
+ public Matrix4x4 CreateGuiProjectionMatrix(Size dims)
+ {
+ var ret = Matrix4x4.Identity;
+ ret.M11 = 2.0f / dims.Width;
+ ret.M22 = 2.0f / dims.Height;
+ return ret;
+ }
+
+ public Matrix4x4 CreateGuiViewMatrix(Size dims, bool autoFlip)
+ {
+ var ret = Matrix4x4.Identity;
+ ret.M22 = -1.0f;
+ ret.M41 = -dims.Width * 0.5f;
+ ret.M42 = dims.Height * 0.5f;
+
+ // auto-flipping isn't needed on D3D
+ return ret;
+ }
+
+ public void SetViewport(int x, int y, int width, int height)
+ {
+ Context.RSSetViewport(x, y, width, height);
+ Context.RSSetScissorRect(x, y, width, height);
+ }
+
+ public void SetViewport(int width, int height)
+ => SetViewport(0, 0, width, height);
+
+ public void SetViewport(Size size)
+ => SetViewport(size.Width, size.Height);
+
+ public void FreeRenderTarget(RenderTarget rt)
+ {
+ var rw = (RenderTargetWrapper)rt.Opaque;
+ rw.RTV.Dispose();
+ _renderTargets.Remove(rt);
+ var tw = (TextureWrapper)rt.Texture2d.Opaque;
+ tw.SRV.Dispose();
+ tw.Texture.Dispose();
+ }
+
+ private ID3D11Texture2D CreateTextureForRenderTarget(int width, int height)
+ {
+ return Device.CreateTexture2D(
+ Format.B8G8R8A8_UNorm,
+ width,
+ height,
+ mipLevels: 1,
+ bindFlags: BindFlags.RenderTarget | BindFlags.ShaderResource, // ack! need to also let this be bound to shaders
+ usage: ResourceUsage.Default,
+ cpuAccessFlags: CpuAccessFlags.None);
+ }
+
+ public RenderTarget CreateRenderTarget(int w, int h)
+ {
+ var tex = CreateTextureForRenderTarget(w, h);
+ var srvd = new ShaderResourceViewDescription(ShaderResourceViewDimension.Texture2D, Format.B8G8R8A8_UNorm, mostDetailedMip: 0, mipLevels: 1);
+ var srv = Device.CreateShaderResourceView(tex, srvd);
+ var tw = new TextureWrapper { Texture = tex, SRV = srv };
+ var tex2d = new Texture2d(this, tw, w, h);
+
+ var rtvd = new RenderTargetViewDescription(RenderTargetViewDimension.Texture2D, Format.B8G8R8A8_UNorm);
+ var rw = new RenderTargetWrapper { RTV = Device.CreateRenderTargetView(tw.Texture, rtvd) };
+ var rt = new RenderTarget(this, rw, tex2d);
+ _renderTargets.Add(rt);
+ return rt;
+ }
+
+ public void BindRenderTarget(RenderTarget rt)
+ {
+ _curRenderTarget = rt;
+
+ if (rt == null)
+ {
+ Context.OMSetRenderTargets(_controlSwapChain.RTV);
+ return;
+ }
+
+ var rw = (RenderTargetWrapper)rt.Opaque;
+ Context.OMSetRenderTargets(rw.RTV);
+ }
+
+ public void Draw(IntPtr data, int count)
+ {
+ var vlw = (VertexLayoutWrapper)_curPipeline.VertexLayout.Opaque;
+ var stride = vlw.VertexStride;
+
+ if (vlw.VertexBufferCount < count)
+ {
+ vlw.VertexBuffer?.Dispose();
+ var bd = new BufferDescription(stride * count, BindFlags.VertexBuffer, ResourceUsage.Dynamic, CpuAccessFlags.Write);
+ vlw.VertexBuffer = Device.CreateBuffer(in bd, data);
+ vlw.VertexBufferCount = count;
+ }
+ else
+ {
+ var mappedVb = Context.Map(vlw.VertexBuffer, MapMode.WriteDiscard);
+ try
+ {
+ unsafe
+ {
+ Buffer.MemoryCopy((void*)data, (void*)mappedVb.DataPointer, stride * vlw.VertexBufferCount, stride * count);
+ }
+ }
+ finally
+ {
+ Context.Unmap(vlw.VertexBuffer);
+ }
+ }
+
+ unsafe
+ {
+ var pw = (PipelineWrapper)_curPipeline.Opaque;
+ for (var i = 0; i < ID3D11DeviceContext.CommonShaderConstantBufferSlotCount; i++)
+ {
+ var pbw = pw.PendingBuffers[i];
+ if (pbw == null)
+ {
+ break;
+ }
+
+ if (pbw.VSBufferDirty)
+ {
+ var vsCb = Context.Map(pw.VSConstantBuffers[i], MapMode.WriteDiscard);
+ Buffer.MemoryCopy((void*)pbw.VSPendingBuffer, (void*)vsCb.DataPointer, pbw.VSBufferSize, pbw.VSBufferSize);
+ Context.Unmap(pw.VSConstantBuffers[i]);
+ pbw.VSBufferDirty = false;
+ }
+
+ if (pbw.PSBufferDirty)
+ {
+ var psCb = Context.Map(pw.PSConstantBuffers[i], MapMode.WriteDiscard);
+ Buffer.MemoryCopy((void*)pbw.PSPendingBuffer, (void*)psCb.DataPointer, pbw.PSBufferSize, pbw.PSBufferSize);
+ Context.Unmap(pw.PSConstantBuffers[i]);
+ pbw.PSBufferDirty = false;
+ }
+ }
+ }
+
+ Context.Draw(count, 0);
+ }
+
+ public void BeginScene()
+ {
+ }
+
+ public void EndScene()
+ {
+ }
+ }
+}
diff --git a/src/BizHawk.Bizware.Graphics/D3D9/D3D9SwapChain.cs b/src/BizHawk.Bizware.Graphics/D3D9/D3D9SwapChain.cs
deleted file mode 100644
index 77e494317f..0000000000
--- a/src/BizHawk.Bizware.Graphics/D3D9/D3D9SwapChain.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System;
-
-using SharpDX;
-using SharpDX.Direct3D9;
-
-namespace BizHawk.Bizware.Graphics
-{
- public sealed class D3D9SwapChain : IDisposable
- {
- public readonly struct ControlParameters
- {
- public readonly IntPtr Handle;
- public readonly int Width;
- public readonly int Height;
- public readonly bool Vsync;
-
- public ControlParameters(IntPtr handle, int width, int height, bool vsync)
- {
- Handle = handle;
- Width = Math.Max(width, 1);
- Height = Math.Max(height, 1);
- Vsync = vsync;
- }
- }
-
- private const int D3DERR_DEVICELOST = unchecked((int)0x88760868);
-
- private readonly Device _device;
- private readonly Func _resetDeviceCallback;
- private readonly Func _resetSwapChainCallback;
-
- private SwapChain _swapChain;
-
- internal D3D9SwapChain(Device device, SwapChain swapChain,
- Func resetDeviceCallback, Func resetSwapChainCallback)
- {
- _device = device;
- _swapChain = swapChain;
- _resetDeviceCallback = resetDeviceCallback;
- _resetSwapChainCallback = resetSwapChainCallback;
- }
-
- public void Dispose()
- {
- _swapChain?.Dispose();
- _swapChain = null;
- }
-
- public void SetBackBuffer()
- {
- using var surface = _swapChain.GetBackBuffer(0);
- _device.SetRenderTarget(0, surface);
- _device.DepthStencilSurface = null;
- }
-
- public void PresentBuffer(ControlParameters cp)
- {
- SetBackBuffer();
-
- try
- {
- _swapChain.Present(Present.None);
- }
- catch (SharpDXException ex)
- {
- if (ex.ResultCode.Code == D3DERR_DEVICELOST)
- {
- _swapChain = _resetDeviceCallback(cp);
- }
- }
- }
-
- public void Refresh(ControlParameters cp)
- => _swapChain = _resetSwapChainCallback(cp);
- }
-}
diff --git a/src/BizHawk.Bizware.Graphics/D3D9/IGL_D3D9.cs b/src/BizHawk.Bizware.Graphics/D3D9/IGL_D3D9.cs
deleted file mode 100644
index c4c239fa28..0000000000
--- a/src/BizHawk.Bizware.Graphics/D3D9/IGL_D3D9.cs
+++ /dev/null
@@ -1,839 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.IO;
-using System.Linq;
-using System.Numerics;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-using BizHawk.Bizware.BizwareGL;
-using BizHawk.Common;
-using BizHawk.Common.StringExtensions;
-
-using SharpDX.Direct3D9;
-
-using static SDL2.SDL;
-
-// todo - do a better job selecting shader model? base on caps somehow? try several and catch compilation exceptions (yuck, exceptions)
-namespace BizHawk.Bizware.Graphics
-{
- ///
- /// Direct3D9 implementation of the BizwareGL.IGL interface
- ///
- public sealed class IGL_D3D9 : IGL
- {
- public EDispMethod DispMethodEnum => EDispMethod.D3D9;
-
- private const int D3DERR_DEVICENOTRESET = unchecked((int)0x88760869);
-
- private Device _device;
- private SwapChain _controlSwapchain;
-
- private IntPtr _offscreenSdl2Window;
- private IntPtr OffscreenNativeWindow;
-
- // rendering state
- private Pipeline _currPipeline;
-
- // misc state
- private readonly HashSet _renderTargets = new();
-
- public string API => "D3D9";
-
- static IGL_D3D9()
- {
- if (SDL_Init(SDL_INIT_VIDEO) != 0)
- {
- throw new($"Failed to init SDL video, SDL error: {SDL_GetError()}");
- }
- }
-
- public IGL_D3D9()
- {
- if (OSTailoredCode.IsUnixHost)
- {
- throw new NotSupportedException("D3D9 is Windows only");
- }
-
- // make an 'offscreen context' so we can at least do things without having to create a window
- _offscreenSdl2Window = SDL_CreateWindow(null, 0, 0, 1, 1, SDL_WindowFlags.SDL_WINDOW_HIDDEN);
- if (_offscreenSdl2Window == IntPtr.Zero)
- {
- throw new($"Failed to create offscreen SDL window, SDL error: {SDL_GetError()}");
- }
-
- // get the native window handle
- var wminfo = default(SDL_SysWMinfo);
- SDL_GetVersion(out wminfo.version);
- SDL_GetWindowWMInfo(_offscreenSdl2Window, ref wminfo);
- if (wminfo.subsystem != SDL_SYSWM_TYPE.SDL_SYSWM_WINDOWS)
- {
- throw new($"SDL_SysWMinfo did not report SDL_SYSWM_WINDOWS? Something went wrong... SDL error: {SDL_GetError()}");
- }
-
- OffscreenNativeWindow = wminfo.info.win.window;
-
- CreateDevice();
- }
-
- public void AlternateVsyncPass(int pass)
- {
- while (true)
- {
- var status = _device.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 CreateDevice()
- {
- // this object is only used for creating a device, it's not needed afterwards
- using var d3d9 = new Direct3D();
-
- var pp = MakePresentParameters();
-
- var flags = (d3d9.GetDeviceCaps(0, DeviceType.Hardware).DeviceCaps & DeviceCaps.HWTransformAndLight) != 0
- ? CreateFlags.HardwareVertexProcessing
- : CreateFlags.SoftwareVertexProcessing;
-
- flags |= CreateFlags.FpuPreserve;
- _device = new(d3d9, 0, DeviceType.Hardware, pp.DeviceWindowHandle, flags, pp);
- }
-
- private PresentParameters MakePresentParameters()
- {
- return new()
- {
- BackBufferCount = 1,
- SwapEffect = SwapEffect.Discard,
- DeviceWindowHandle = OffscreenNativeWindow,
- Windowed = true,
- PresentationInterval = PresentInterval.Immediate,
- };
- }
-
- private static PresentParameters MakePresentParameters(D3D9SwapChain.ControlParameters cp)
- {
- return new()
- {
- BackBufferWidth = cp.Width,
- BackBufferHeight = cp.Height,
- BackBufferFormat = Format.X8R8G8B8,
- BackBufferCount = 2,
- SwapEffect = SwapEffect.Discard,
- DeviceWindowHandle = cp.Handle,
- Windowed = true,
- PresentationInterval = cp.Vsync ? PresentInterval.One : PresentInterval.Immediate
- };
- }
-
- private SwapChain ResetDevice(D3D9SwapChain.ControlParameters cp)
- {
- SuspendRenderTargets();
- _controlSwapchain.Dispose();
-
- var pp = MakePresentParameters();
-
- while (true)
- {
- var result = _device.TestCooperativeLevel();
- if (result.Success)
- {
- break;
- }
-
- if (result.Code == D3DERR_DEVICENOTRESET)
- {
- try
- {
- _device.Reset(pp);
- break;
- }
- catch
- {
- // ignored
- }
- }
-
- Thread.Sleep(100);
- }
-
- ResumeRenderTargets();
-
- pp = MakePresentParameters(cp);
- _controlSwapchain = new(_device, pp);
- return _controlSwapchain;
- }
-
- private SwapChain ResetSwapChain(D3D9SwapChain.ControlParameters cp)
- {
- _controlSwapchain.Dispose();
-
- var pp = MakePresentParameters(cp);
- _controlSwapchain = new(_device, pp);
- return _controlSwapchain;
- }
-
- public D3D9SwapChain CreateSwapChain(D3D9SwapChain.ControlParameters cp)
- {
- if (_controlSwapchain != null)
- {
- throw new InvalidOperationException($"{nameof(IGL_D3D9)} can only have 1 control swap chain");
- }
-
- var pp = MakePresentParameters(cp);
- _controlSwapchain = new(_device, pp);
- return new(_device, _controlSwapchain, ResetDevice, ResetSwapChain);
- }
-
- public void Dispose()
- {
- if (_device != null)
- {
- _device.Dispose();
- _device = null;
- }
-
- if (_offscreenSdl2Window != IntPtr.Zero)
- {
- SDL_DestroyWindow(_offscreenSdl2Window);
- _offscreenSdl2Window = OffscreenNativeWindow = IntPtr.Zero;
- }
-
- _controlSwapchain = null;
- }
-
- public void ClearColor(Color color)
- => _device.Clear(ClearFlags.Target, color.ToSharpDXColor(), 0.0f, 0);
-
- public void FreeTexture(Texture2d tex)
- {
- var tw = (TextureWrapper)tex.Opaque;
- tw.Texture.Dispose();
- }
-
- private class ShaderWrapper // Disposable fields cleaned up by Internal_FreeShader
- {
- public ShaderBytecode Bytecode;
- public VertexShader VS;
- public PixelShader PS;
- public Shader IGLShader;
- }
-
- /// is and compilation error occurred
- public Shader CreateFragmentShader(string source, string entry, bool required)
- {
- try
- {
- var sw = new ShaderWrapper();
-
- // ShaderFlags.EnableBackwardsCompatibility - used this once upon a time (please leave a note about why)
- var result = ShaderBytecode.Compile(
- shaderSource: source,
- entryPoint: entry,
- profile: "ps_3_0",
- shaderFlags: ShaderFlags.UseLegacyD3DX9_31Dll);
-
- sw.PS = new(_device, result);
- sw.Bytecode = result;
-
- var s = new Shader(this, sw, true);
- sw.IGLShader = s;
-
- return s;
- }
- catch (Exception ex)
- {
- if (required)
- {
- throw;
- }
-
- return new(this, null, false) { Errors = ex.ToString() };
- }
- }
-
- /// is and compilation error occurred
- public Shader CreateVertexShader(string source, string entry, bool required)
- {
- try
- {
- var sw = new ShaderWrapper();
-
- var result = ShaderBytecode.Compile(
- shaderSource: source,
- entryPoint: entry,
- profile: "vs_3_0",
- shaderFlags: ShaderFlags.UseLegacyD3DX9_31Dll);
-
- sw.VS = new(_device, result);
- sw.Bytecode = result;
-
- var s = new Shader(this, sw, true);
- sw.IGLShader = s;
-
- return s;
- }
- catch (Exception ex)
- {
- if (required)
- {
- throw;
- }
-
- return new(this, null, false) { Errors = ex.ToString() };
- }
- }
-
- public void EnableBlending()
- {
- _device.SetRenderState(RenderState.AlphaBlendEnable, true);
- _device.SetRenderState(RenderState.SeparateAlphaBlendEnable, true);
-
- _device.SetRenderState(RenderState.BlendOperation, BlendOperation.Add);
- _device.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
- _device.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);
-
- _device.SetRenderState(RenderState.BlendOperationAlpha, BlendOperation.Add);
- _device.SetRenderState(RenderState.SourceBlendAlpha, Blend.One);
- _device.SetRenderState(RenderState.DestinationBlendAlpha, Blend.Zero);
- }
-
- public void DisableBlending()
- => _device.SetRenderState(RenderState.AlphaBlendEnable, false);
-
- ///
- /// is and either or is unavailable (their property is ), or
- /// one of 's items has an unsupported value in , , or
- ///
- public Pipeline CreatePipeline(VertexLayout vertexLayout, Shader vertexShader, Shader fragmentShader, bool required, string memo)
- {
- if (!vertexShader.Available || !fragmentShader.Available)
- {
- var 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}");
- }
-
- return new(this, null, false, null, null, null) { Errors = errors };
- }
-
- var ves = new VertexElement[vertexLayout.Items.Count + 1];
- var stride = 0;
- foreach (var (i, item) in vertexLayout.Items)
- {
- DeclarationType declType;
- // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
- switch (item.AttribType)
- {
- case VertexAttribPointerType.Float:
- declType = item.Components switch
- {
- 1 => DeclarationType.Float1,
- 2 => DeclarationType.Float2,
- 3 => DeclarationType.Float3,
- 4 => DeclarationType.Float4,
- _ => throw new InvalidOperationException()
- };
- stride += 4 * item.Components;
- break;
- default:
- throw new NotSupportedException();
- }
-
- DeclarationUsage usage;
- byte usageIndex = 0;
- // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
- switch (item.Usage)
- {
- case AttribUsage.Position:
- usage = DeclarationUsage.Position;
- break;
- case AttribUsage.Texcoord0:
- usage = DeclarationUsage.TextureCoordinate;
- break;
- case AttribUsage.Texcoord1:
- usage = DeclarationUsage.TextureCoordinate;
- usageIndex = 1;
- break;
- case AttribUsage.Color0:
- usage = DeclarationUsage.Color;
- break;
- default:
- throw new NotSupportedException();
- }
-
- ves[i] = new(0, (short)item.Offset, declType, DeclarationMethod.Default, usage, usageIndex);
- }
-
- // must be placed at the end
- ves[vertexLayout.Items.Count] = VertexElement.VertexDeclarationEnd;
-
- var pw = new PipelineWrapper
- {
- VertexDeclaration = new(_device, ves),
- VertexShader = (ShaderWrapper)vertexShader.Opaque,
- FragmentShader = (ShaderWrapper)fragmentShader.Opaque,
- VertexStride = stride
- };
-
- // scan uniforms from reflection
- var uniforms = new List();
- var vsct = pw.VertexShader.Bytecode.ConstantTable;
- var psct = pw.FragmentShader.Bytecode.ConstantTable;
- foreach (var ct in new[] { vsct, psct })
- {
- var todo = new Queue<(string, EffectHandle)>();
- var n = ct.Description.Constants;
- for (var i = 0; i < n; i++)
- {
- var handle = ct.GetConstant(null, i);
- todo.Enqueue((string.Empty, handle));
- }
-
- while (todo.Count != 0)
- {
- var (prefix, handle) = todo.Dequeue();
- var descr = ct.GetConstantDescription(handle);
-
- // Console.WriteLine($"D3D UNIFORM: {descr.Name}");
-
- if (descr.StructMembers != 0)
- {
- var newPrefix = $"{prefix}{descr.Name}.";
- for (var j = 0; j < descr.StructMembers; j++)
- {
- var subHandle = ct.GetConstant(handle, j);
- todo.Enqueue((newPrefix, subHandle));
- }
-
- continue;
- }
-
- var ui = new UniformInfo();
- var uw = new UniformWrapper();
-
- ui.Opaque = uw;
- var name = prefix + descr.Name;
-
- // uniforms done through the entry point signature have $ in their names which isn't helpful, so get rid of that
- name = name.RemovePrefix('$');
-
- ui.Name = name;
- uw.EffectHandle = handle;
- uw.CT = ct;
-
- if (descr.Type == ParameterType.Sampler2D)
- {
- ui.IsSampler = true;
- ui.SamplerIndex = descr.RegisterIndex;
- }
-
- uniforms.Add(ui);
- }
- }
-
- return new(this, pw, true, vertexLayout, uniforms, memo);
- }
-
- public void FreePipeline(Pipeline pipeline)
- {
- // unavailable pipelines will have no opaque
- if (pipeline.Opaque is not PipelineWrapper pw)
- {
- return;
- }
-
- pw.VertexDeclaration.Dispose();
- pw.FragmentShader.IGLShader.Release();
- pw.VertexShader.IGLShader.Release();
- }
-
- public void Internal_FreeShader(Shader shader)
- {
- var sw = (ShaderWrapper)shader.Opaque;
- sw.Bytecode.Dispose();
- sw.PS?.Dispose();
- sw.VS?.Dispose();
- }
-
- private class UniformWrapper
- {
- public EffectHandle EffectHandle;
- public ConstantTable CT;
- }
-
- private class PipelineWrapper // Disposable fields cleaned up by FreePipeline
- {
- public VertexDeclaration VertexDeclaration;
- public ShaderWrapper VertexShader, FragmentShader;
- public int VertexStride;
- }
-
- private class TextureWrapper
- {
- public Texture Texture;
- public TextureAddress WrapClamp = TextureAddress.Clamp;
- public TextureFilter MinFilter = TextureFilter.Point, MagFilter = TextureFilter.Point;
- }
-
- public VertexLayout CreateVertexLayout()
- => new(this, null);
-
- public void Internal_FreeVertexLayout(VertexLayout layout)
- {
- }
-
- public void BindPipeline(Pipeline pipeline)
- {
- _currPipeline = pipeline;
-
- if (pipeline == null)
- {
- // unbind? i don't know
- return;
- }
-
- var pw = (PipelineWrapper)pipeline.Opaque;
- _device.PixelShader = pw.FragmentShader.PS;
- _device.VertexShader = pw.VertexShader.VS;
- _device.VertexDeclaration = pw.VertexDeclaration;
- }
-
- public void SetPipelineUniform(PipelineUniform uniform, bool value)
- {
- foreach (var ui in uniform.UniformInfos)
- {
- var uw = (UniformWrapper)ui.Opaque;
- uw.CT.SetValue(_device, uw.EffectHandle, value);
- }
- }
-
- public void SetPipelineUniformMatrix(PipelineUniform uniform, Matrix4x4 mat, bool transpose)
- => SetPipelineUniformMatrix(uniform, ref mat, transpose);
-
- public void SetPipelineUniformMatrix(PipelineUniform uniform, ref Matrix4x4 mat, bool transpose)
- {
- foreach (var ui in uniform.UniformInfos)
- {
- var uw = (UniformWrapper)ui.Opaque;
- uw.CT.SetValue(_device, uw.EffectHandle, mat.ToSharpDXMatrix(!transpose));
- }
- }
-
- public void SetPipelineUniform(PipelineUniform uniform, Vector4 value)
- {
- foreach (var ui in uniform.UniformInfos)
- {
- var uw = (UniformWrapper)ui.Opaque;
- uw.CT.SetValue(_device, uw.EffectHandle, value.ToSharpDXVector4());
- }
- }
-
- public void SetPipelineUniform(PipelineUniform uniform, Vector2 value)
- {
- foreach (var ui in uniform.UniformInfos)
- {
- var uw = (UniformWrapper)ui.Opaque;
- uw.CT.SetValue(_device, uw.EffectHandle, value.ToSharpDXVector2());
- }
- }
-
- public void SetPipelineUniform(PipelineUniform uniform, float value)
- {
- foreach (var ui in uniform.UniformInfos)
- {
- var uw = (UniformWrapper)ui.Opaque;
- uw.CT.SetValue(_device, uw.EffectHandle, value);
- }
- }
-
- public void SetPipelineUniform(PipelineUniform uniform, Vector4[] values)
- {
- var v = Array.ConvertAll(values, v => v.ToSharpDXVector4());
- foreach (var ui in uniform.UniformInfos)
- {
- var uw = (UniformWrapper)ui.Opaque;
- uw.CT.SetValue(_device, uw.EffectHandle, v);
- }
- }
-
- public void SetPipelineUniformSampler(PipelineUniform uniform, Texture2d tex)
- {
- if (uniform.Owner == null)
- {
- return; // uniform was optimized out
- }
-
- var tw = (TextureWrapper)tex.Opaque;
- foreach (var ui in uniform.UniformInfos)
- {
- if (!ui.IsSampler)
- {
- throw new InvalidOperationException("Uniform was not a texture/sampler");
- }
-
- _device.SetTexture(ui.SamplerIndex, tw.Texture);
-
- _device.SetSamplerState(ui.SamplerIndex, SamplerState.AddressU, (int)tw.WrapClamp);
- _device.SetSamplerState(ui.SamplerIndex, SamplerState.AddressV, (int)tw.WrapClamp);
- _device.SetSamplerState(ui.SamplerIndex, SamplerState.MinFilter, (int)tw.MinFilter);
- _device.SetSamplerState(ui.SamplerIndex, SamplerState.MagFilter, (int)tw.MagFilter);
- }
- }
-
- public void SetTextureWrapMode(Texture2d tex, bool clamp)
- {
- var tw = (TextureWrapper)tex.Opaque;
- tw.WrapClamp = clamp ? TextureAddress.Clamp : TextureAddress.Wrap;
- }
-
- public void SetMinFilter(Texture2d texture, TextureMinFilter minFilter)
- => ((TextureWrapper)texture.Opaque).MinFilter = minFilter == TextureMinFilter.Linear
- ? TextureFilter.Linear
- : TextureFilter.Point;
-
- public void SetMagFilter(Texture2d texture, TextureMagFilter magFilter)
- => ((TextureWrapper)texture.Opaque).MagFilter = magFilter == TextureMagFilter.Linear
- ? TextureFilter.Linear
- : TextureFilter.Point;
-
- public Texture2d LoadTexture(Bitmap bitmap)
- {
- using var bmp = new BitmapBuffer(bitmap, new());
- return LoadTexture(bmp);
- }
-
- public Texture2d LoadTexture(Stream stream)
- {
- using var bmp = new BitmapBuffer(stream, new());
- return LoadTexture(bmp);
- }
-
- public Texture2d CreateTexture(int width, int height)
- {
- var tex = new Texture(_device, width, height, 1, Usage.None, Format.A8R8G8B8, Pool.Managed);
- var tw = new TextureWrapper { Texture = tex };
- var ret = new Texture2d(this, tw, width, height);
- return ret;
- }
-
- public Texture2d WrapGLTexture2d(IntPtr glTexId, int width, int height)
- {
- // only used for OpenGL
- return null;
- }
-
- /// GDI+ call returned unexpected data
- public unsafe void LoadTextureData(Texture2d tex, BitmapBuffer bmp)
- {
- var tw = (TextureWrapper)tex.Opaque;
- var bmpData = bmp.LockBits();
-
- try
- {
- var dr = tw.Texture.LockRectangle(0, LockFlags.None);
-
- // TODO - do we need to handle odd sizes, weird pitches here?
- if (bmp.Width * 4 != bmpData.Stride || bmpData.Stride != dr.Pitch)
- {
- throw new InvalidOperationException();
- }
-
- var srcSpan = new ReadOnlySpan(bmpData.Scan0.ToPointer(), bmpData.Stride * bmp.Height);
- var dstSpan = new Span(dr.DataPointer.ToPointer(), dr.Pitch * bmp.Height);
- srcSpan.CopyTo(dstSpan);
- }
- finally
- {
- tw.Texture.UnlockRectangle(0);
- bmp.UnlockBits(bmpData);
- }
- }
-
- public Texture2d LoadTexture(BitmapBuffer bmp)
- {
- var ret = CreateTexture(bmp.Width, bmp.Height);
- LoadTextureData(ret, bmp);
- return ret;
- }
-
- /// Vortice call returned unexpected data
- public BitmapBuffer ResolveTexture2d(Texture2d tex)
- {
- // TODO - lazy create and cache resolving target in RT
- using var target = new Texture(_device, tex.IntWidth, tex.IntHeight, 1, Usage.None, Format.A8R8G8B8, Pool.SystemMemory);
- var tw = (TextureWrapper)tex.Opaque;
-
- using var rtSurf = tw.Texture.GetSurfaceLevel(0);
- using var dstSurf = target.GetSurfaceLevel(0);
- _device.GetRenderTargetData(rtSurf, dstSurf);
-
- try
- {
- var dr = target.LockRectangle(0, LockFlags.ReadOnly);
-
- if (dr.Pitch != tex.IntWidth * 4)
- {
- throw new InvalidOperationException();
- }
-
- var pixels = new int[tex.IntWidth * tex.IntHeight];
- Marshal.Copy(dr.DataPointer, pixels, 0, tex.IntWidth * tex.IntHeight);
- return new(tex.IntWidth, tex.IntHeight, pixels);
- }
- finally
- {
- target.UnlockRectangle(0);
- }
- }
-
- public Texture2d LoadTexture(string path)
- {
- using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
- return LoadTexture(fs);
- }
-
- public Matrix4x4 CreateGuiProjectionMatrix(int w, int h)
- {
- return CreateGuiProjectionMatrix(new(w, h));
- }
-
- public Matrix4x4 CreateGuiViewMatrix(int w, int h, bool autoFlip)
- {
- return CreateGuiViewMatrix(new(w, h), autoFlip);
- }
-
- public Matrix4x4 CreateGuiProjectionMatrix(Size dims)
- {
- var ret = Matrix4x4.Identity;
- ret.M11 = 2.0f / dims.Width;
- ret.M22 = 2.0f / dims.Height;
- return ret;
- }
-
- public Matrix4x4 CreateGuiViewMatrix(Size dims, bool autoFlip)
- {
- var ret = Matrix4x4.Identity;
- ret.M22 = -1.0f;
- ret.M41 = -dims.Width * 0.5f - 0.5f;
- ret.M42 = dims.Height * 0.5f + 0.5f;
-
- // auto-flipping isn't needed on D3D
- return ret;
- }
-
- public void SetViewport(int x, int y, int width, int height)
- {
- _device.Viewport = new() { X = x, Y = y, Width = width, Height = height, MinDepth = 0, MaxDepth = 1 };
- _device.ScissorRect = new(x, y, x + width, y + height);
- }
-
- public void SetViewport(int width, int height)
- {
- SetViewport(0, 0, width, height);
- }
-
- public void SetViewport(Size size)
- {
- SetViewport(size.Width, size.Height);
- }
-
- public void FreeRenderTarget(RenderTarget rt)
- {
- var tw = (TextureWrapper)rt.Texture2d.Opaque;
- 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;
- }
-
- private Texture CreateRenderTargetTexture(int w, int h)
- => new(_device, w, h, 1, Usage.RenderTarget, Format.A8R8G8B8, Pool.Default);
-
- private void SuspendRenderTargets()
- {
- foreach (var tw in _renderTargets.Select(tex => (TextureWrapper)tex.Opaque))
- {
- tw.Texture.Dispose();
- tw.Texture = null;
- }
- }
-
- private void ResumeRenderTargets()
- {
- foreach (var rt in _renderTargets)
- {
- var tw = (TextureWrapper)rt.Opaque;
- tw.Texture = CreateRenderTargetTexture(rt.Texture2d.IntWidth, rt.Texture2d.IntHeight);
- }
- }
-
- public void BindRenderTarget(RenderTarget rt)
- {
- if (rt == null)
- {
- using var bb = _controlSwapchain.GetBackBuffer(0);
- _device.SetRenderTarget(0, bb);
- _device.DepthStencilSurface = null;
- return;
- }
-
- // dispose doesn't seem necessary for reset here...
- var tw = (TextureWrapper)rt.Opaque;
- using var texSurface = tw.Texture.GetSurfaceLevel(0);
- _device.SetRenderTarget(0, texSurface);
- _device.DepthStencilSurface = null;
- }
-
- private delegate void DrawPrimitiveUPDelegate(Device device, PrimitiveType primitiveType, int primitiveCount, IntPtr vertexStreamZeroDataRef, int vertexStreamZeroStride);
-
- private static readonly Lazy _drawPrimitiveUP = new(() =>
- {
- var mi = typeof(Device).GetMethod("DrawPrimitiveUP", BindingFlags.Instance | BindingFlags.NonPublic);
- return (DrawPrimitiveUPDelegate)Delegate.CreateDelegate(typeof(DrawPrimitiveUPDelegate), mi!);
- });
-
- private void DrawPrimitiveUP(PrimitiveType primitiveType, int primitiveCount, IntPtr vertexStreamZeroDataRef, int vertexStreamZeroStride)
- => _drawPrimitiveUP.Value(_device, primitiveType, primitiveCount, vertexStreamZeroDataRef, vertexStreamZeroStride);
-
- public void Draw(IntPtr data, int count)
- {
- var pw = (PipelineWrapper)_currPipeline.Opaque;
-
- // this is stupid, sharpdx only public exposes DrawUserPrimatives
- // why is this bad? it takes in an array of T
- // and uses the size of T to determine stride
- // since stride for us is just completely variable, this is no good
- // DrawPrimitiveUP is internal so we have to use this hack to use it directly
-
- DrawPrimitiveUP(PrimitiveType.TriangleStrip, count - 2, data, pw.VertexStride);
- }
-
- public void BeginScene()
- {
- _device.BeginScene();
- _device.SetRenderState(RenderState.CullMode, Cull.None);
- _device.SetRenderState(RenderState.ZEnable, false);
- _device.SetRenderState(RenderState.ZWriteEnable, false);
- _device.SetRenderState(RenderState.Lighting, false);
- }
-
- public void EndScene()
- => _device.EndScene();
- }
-}
diff --git a/src/BizHawk.Bizware.Graphics/D3D9/SharpDXExtensions.cs b/src/BizHawk.Bizware.Graphics/D3D9/SharpDXExtensions.cs
deleted file mode 100644
index ee650588a6..0000000000
--- a/src/BizHawk.Bizware.Graphics/D3D9/SharpDXExtensions.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System.Drawing;
-using System.Numerics;
-
-using SharpDX.Mathematics.Interop;
-
-namespace BizHawk.Bizware.Graphics
-{
- internal static class SharpDXExtensions
- {
- // SharpDX RawMatrix and Numerics Matrix4x4 are identical in structure
- public static RawMatrix ToSharpDXMatrix(this Matrix4x4 m, bool transpose)
- {
- // Transpose call could be inlined to reduce 2 sets of copies to 1
- if (transpose)
- {
- m = Matrix4x4.Transpose(m);
- }
-
- return new()
- {
- M11 = m.M11, M12 = m.M12, M13 = m.M13, M14 = m.M14,
- M21 = m.M21, M22 = m.M22, M23 = m.M23, M24 = m.M24,
- M31 = m.M31, M32 = m.M32, M33 = m.M33, M34 = m.M34,
- M41 = m.M41, M42 = m.M42, M43 = m.M43, M44 = m.M44
- };
- }
-
- public static RawVector2 ToSharpDXVector2(this Vector2 v)
- => new(v.X, v.Y);
-
- public static RawVector4 ToSharpDXVector4(this Vector4 v)
- => new(v.X, v.Y, v.Z, v.W);
-
- public static RawColorBGRA ToSharpDXColor(this Color c)
- => new(c.B, c.G, c.R, c.A);
- }
-}
diff --git a/src/BizHawk.Bizware.Graphics/Renderers/GuiRenderer.cs b/src/BizHawk.Bizware.Graphics/Renderers/GuiRenderer.cs
index 0d5df3a2bf..5061db80c0 100644
--- a/src/BizHawk.Bizware.Graphics/Renderers/GuiRenderer.cs
+++ b/src/BizHawk.Bizware.Graphics/Renderers/GuiRenderer.cs
@@ -31,15 +31,18 @@ namespace BizHawk.Bizware.Graphics
string psProgram, vsProgram;
- if (owner.API == "D3D9")
+ switch (owner.API)
{
- vsProgram = DefaultShader_d3d9;
- psProgram = DefaultShader_d3d9;
- }
- else
- {
- vsProgram = DefaultVertexShader_gl;
- psProgram = DefaultPixelShader_gl;
+ case "D3D11":
+ vsProgram = DefaultShader_d3d9;
+ psProgram = DefaultShader_d3d9;
+ break;
+ case "OPENGL":
+ vsProgram = DefaultVertexShader_gl;
+ psProgram = DefaultPixelShader_gl;
+ break;
+ default:
+ throw new InvalidOperationException();
}
var vs = Owner.CreateVertexShader(vsProgram, "vsmain", true);
@@ -177,7 +180,6 @@ namespace BizHawk.Bizware.Graphics
SetModulateColorWhite();
}
-
public void Flush()
{
// no batching, nothing to do here yet
@@ -381,6 +383,56 @@ namespace BizHawk.Bizware.Graphics
// shaders are hand-coded for each platform to make sure they stay as fast as possible
+#if false // this doesn't work for reasons unknown (TODO make this work)
+ public const string DefaultShader_d3d11 = @"
+//vertex shader uniforms
+float4x4 um44Modelview, um44Projection;
+float4 uModulateColor;
+
+//pixel shader uniforms
+bool uSamplerEnable;
+Texture2D texture0, texture1;
+SamplerState uSampler0 = sampler_state { Texture = (texture0); };
+
+struct VS_INPUT
+{
+ float2 aPosition : POSITION;
+ float2 aTexcoord : TEXCOORD0;
+ float4 aColor : TEXCOORD1;
+};
+
+struct VS_OUTPUT
+{
+ float4 vPosition : SV_POSITION;
+ float2 vTexcoord0 : TEXCOORD0;
+ float4 vCornerColor : COLOR0;
+};
+
+struct PS_INPUT
+{
+ float4 vPosition : SV_POSITION;
+ float2 vTexcoord0 : TEXCOORD0;
+ float4 vCornerColor : COLOR0;
+};
+
+VS_OUTPUT vsmain(VS_INPUT src)
+{
+ VS_OUTPUT dst;
+ dst.vPosition = float4(src.aPosition,0,1);
+ dst.vTexcoord0 = src.aTexcoord;
+ dst.vCornerColor = src.aColor;
+ return dst;
+}
+
+float4 psmain(PS_INPUT src) : SV_TARGET
+{
+ float4 temp = src.vCornerColor;
+ temp *= texture0.Sample(uSampler0,src.vTexcoord0);
+ return temp;
+}
+";
+#endif
+
public const string DefaultShader_d3d9 = @"
//vertex shader uniforms
float4x4 um44Modelview, um44Projection;
@@ -407,6 +459,7 @@ struct VS_OUTPUT
struct PS_INPUT
{
+ float4 vPosition : POSITION;
float2 vTexcoord0 : TEXCOORD0;
float4 vCornerColor : COLOR0;
};
diff --git a/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs b/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs
index 706d613035..c19aeb1839 100644
--- a/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs
+++ b/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs
@@ -88,7 +88,7 @@ namespace BizHawk.Client.Common
LoadCustomFont(fceux);
}
- if (dispMethod is EDispMethod.OpenGL or EDispMethod.D3D9)
+ if (dispMethod is EDispMethod.OpenGL or EDispMethod.D3D11)
{
var fiHq2x = new FileInfo(Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk/hq2x.cgp"));
if (fiHq2x.Exists)
@@ -102,7 +102,7 @@ namespace BizHawk.Client.Common
using var stream = fiScanlines.OpenRead();
_shaderChainScanlines = new(_gl, new(stream), Path.Combine(PathUtils.ExeDirectoryPath, "Shaders/BizHawk"));
}
- var bicubicPath = dispMethod == EDispMethod.D3D9 ? "Shaders/BizHawk/bicubic-normal.cgp" : "Shaders/BizHawk/bicubic-fast.cgp";
+ var bicubicPath = dispMethod is EDispMethod.D3D11 ? "Shaders/BizHawk/bicubic-normal.cgp" : "Shaders/BizHawk/bicubic-fast.cgp";
var fiBicubic = new FileInfo(Path.Combine(PathUtils.ExeDirectoryPath, bicubicPath));
if (fiBicubic.Exists)
{
diff --git a/src/BizHawk.Client.Common/DisplayManager/Filters/Retro.cs b/src/BizHawk.Client.Common/DisplayManager/Filters/Retro.cs
index 3ea9f51b30..b36a8e2d2f 100644
--- a/src/BizHawk.Client.Common/DisplayManager/Filters/Retro.cs
+++ b/src/BizHawk.Client.Common/DisplayManager/Filters/Retro.cs
@@ -45,7 +45,7 @@ namespace BizHawk.Client.Common.Filters
Passes = preset.Passes.ToArray();
Errors = string.Empty;
- if (owner.API is not ("OPENGL" or "D3D9"))
+ if (owner.API is not ("OPENGL" or "D3D11"))
{
Errors = $"Unsupported API {owner.API}";
return;
@@ -70,7 +70,7 @@ namespace BizHawk.Client.Common.Filters
path = owner.API switch
{
"OPENGL" => Path.ChangeExtension(path, ".glsl"),
- "D3D9" => Path.ChangeExtension(path, ".hlsl"),
+ "D3D11" => Path.ChangeExtension(path, ".hlsl"),
_ => throw new InvalidOperationException(),
};
}
diff --git a/src/BizHawk.Client.Common/HostCapabilityDetector.cs b/src/BizHawk.Client.Common/HostCapabilityDetector.cs
index feb4914c49..943eacb86c 100644
--- a/src/BizHawk.Client.Common/HostCapabilityDetector.cs
+++ b/src/BizHawk.Client.Common/HostCapabilityDetector.cs
@@ -6,10 +6,10 @@ namespace BizHawk.Client.Common
{
public static class HostCapabilityDetector
{
- private static readonly Lazy _hasD3D9 = new(() =>
+ private static readonly Lazy _hasD3D11 = new(() =>
{
if (OSTailoredCode.IsUnixHost) return false;
- var p = OSTailoredCode.LinkedLibManager.LoadOrZero("d3dx9_43.dll");
+ var p = OSTailoredCode.LinkedLibManager.LoadOrZero("d3d11.dll");
if (p == IntPtr.Zero) return false;
OSTailoredCode.LinkedLibManager.FreeByPtr(p);
return true;
@@ -37,7 +37,7 @@ namespace BizHawk.Client.Common
return false;
});
- public static bool HasD3D9 => _hasD3D9.Value;
+ public static bool HasD3D11 => _hasD3D11.Value;
public static bool HasXAudio2 => _hasXAudio2.Value;
}
}
diff --git a/src/BizHawk.Client.Common/config/Config.cs b/src/BizHawk.Client.Common/config/Config.cs
index 5df041be6f..5c6ecf00a4 100644
--- a/src/BizHawk.Client.Common/config/Config.cs
+++ b/src/BizHawk.Client.Common/config/Config.cs
@@ -186,9 +186,9 @@ namespace BizHawk.Client.Common
public bool VSync { get; set; }
///
- /// Tries to use an alternate vsync mechanism, for video cards that just can't do it right
+ /// Allows non-vsync'd video to tear, this is needed for VFR monitors reportedly
///
- public bool DispAlternateVsync { get; set; }
+ public bool DispAllowTearing { get; set; }
// Display options
public bool DisplayFps { get; set; }
@@ -220,7 +220,7 @@ namespace BizHawk.Client.Common
public int DispPrescale { get; set; } = 1;
- public EDispMethod DispMethod { get; set; } = HostCapabilityDetector.HasD3D9 && !OSTailoredCode.IsWine ? EDispMethod.D3D9 : EDispMethod.OpenGL;
+ public EDispMethod DispMethod { get; set; } = HostCapabilityDetector.HasD3D11 && !OSTailoredCode.IsWine ? EDispMethod.D3D11 : EDispMethod.OpenGL;
public int DispChromeFrameWindowed { get; set; } = 2;
public bool DispChromeStatusBarWindowed { get; set; } = true;
diff --git a/src/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/src/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj
index 60ddd391ff..01d82d398f 100755
--- a/src/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj
+++ b/src/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj
@@ -263,7 +263,7 @@
-
+
diff --git a/src/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs b/src/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs
index 802bc31b0d..92d084336b 100644
--- a/src/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs
+++ b/src/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs
@@ -57,8 +57,6 @@ namespace BizHawk.Client.EmuHawk
protected override void UpdateSourceDrawingWork(JobInfo job)
{
- bool alternateVsync = false;
-
if (!job.Offscreen)
{
//apply the vsync setting (should probably try to avoid repeating this)
@@ -73,12 +71,9 @@ namespace BizHawk.Client.EmuHawk
vsync = false;
//for now, it's assumed that the presentation panel is the main window, but that may not always be true
- if (vsync && GlobalConfig.DispAlternateVsync && GlobalConfig.VSyncThrottle && _gl.DispMethodEnum is EDispMethod.D3D9)
- {
- alternateVsync = true;
- //unset normal vsync if we've chosen the alternate vsync
- vsync = false;
- }
+
+ // no cost currently to just always call this...
+ _graphicsControl.AllowTearing(GlobalConfig.DispAllowTearing);
//TODO - whats so hard about triple buffering anyway? just enable it always, and change api to SetVsync(enable,throttle)
//maybe even SetVsync(enable,throttlemethod) or just SetVsync(enable,throttle,advanced)
@@ -99,7 +94,6 @@ namespace BizHawk.Client.EmuHawk
// begin rendering on this context
// should this have been done earlier?
// do i need to check this on an intel video card to see if running excessively is a problem? (it used to be in the FinalTarget command below, shouldn't be a problem)
- //GraphicsControl.Begin(); // CRITICAL POINT for yabause+GL
//TODO - auto-create and age these (and dispose when old)
int rtCounter = 0;
@@ -119,17 +113,11 @@ namespace BizHawk.Client.EmuHawk
Debug.Assert(inFinalTarget);
- // wait for vsync to begin
- if (alternateVsync) ((dynamic) _gl).AlternateVsyncPass(0);
-
// present and conclude drawing
_graphicsControl.SwapBuffers();
- // wait for vsync to end
- if (alternateVsync) ((dynamic) _gl).AlternateVsyncPass(1);
-
// nope. don't do this. workaround for slow context switching on intel GPUs. just switch to another context when necessary before doing anything
- // presentationPanel.GraphicsControl.End();
+ // _graphicsControl.End();
}
}
}
diff --git a/src/BizHawk.Client.EmuHawk/GraphicsImplementations/RetainedGraphicsControl.cs b/src/BizHawk.Client.EmuHawk/GraphicsImplementations/RetainedGraphicsControl.cs
index 660850859c..4751d0f441 100644
--- a/src/BizHawk.Client.EmuHawk/GraphicsImplementations/RetainedGraphicsControl.cs
+++ b/src/BizHawk.Client.EmuHawk/GraphicsImplementations/RetainedGraphicsControl.cs
@@ -53,6 +53,9 @@ namespace BizHawk.Client.EmuHawk
_graphicsControl.End();
}
+ public override void AllowTearing(bool state)
+ => _graphicsControl.AllowTearing(state);
+
public override void SetVsync(bool state)
=> _graphicsControl.SetVsync(state);
diff --git a/src/BizHawk.Client.EmuHawk/Program.cs b/src/BizHawk.Client.EmuHawk/Program.cs
index a19063d780..aacd20c9ff 100644
--- a/src/BizHawk.Client.EmuHawk/Program.cs
+++ b/src/BizHawk.Client.EmuHawk/Program.cs
@@ -207,9 +207,9 @@ namespace BizHawk.Client.EmuHawk
// try to fallback on the faster option on Windows
// if we're on a Unix platform, there's only 1 fallback here...
1 when OSTailoredCode.IsUnixHost => (EDispMethod.GdiPlus, "GDI+"),
- 1 or 2 when !OSTailoredCode.IsUnixHost => dispMethod == EDispMethod.D3D9
+ 1 or 2 when !OSTailoredCode.IsUnixHost => dispMethod == EDispMethod.D3D11
? (EDispMethod.OpenGL, "OpenGL")
- : (EDispMethod.D3D9, "Direct3D9"),
+ : (EDispMethod.D3D11, "Direct3D11"),
_ => (EDispMethod.GdiPlus, "GDI+")
};
@@ -229,7 +229,7 @@ namespace BizHawk.Client.EmuHawk
switch (dispMethod)
{
- case EDispMethod.D3D9:
+ case EDispMethod.D3D11:
if (OSTailoredCode.IsUnixHost || OSTailoredCode.IsWine)
{
// possibly sharing config w/ Windows, assume the user wants the not-slow method (but don't change the config)
@@ -237,12 +237,12 @@ namespace BizHawk.Client.EmuHawk
}
try
{
- return CheckRenderer(new IGL_D3D9());
+ return CheckRenderer(new IGL_D3D11());
}
catch (Exception ex)
{
var (method, name) = ChooseFallback();
- new ExceptionBox(new Exception($"Initialization of Direct3D9 Display Method failed; falling back to {name}", ex)).ShowDialog();
+ new ExceptionBox(new Exception($"Initialization of Direct3D11 Display Method failed; falling back to {name}", ex)).ShowDialog();
return TryInitIGL(initialConfig.DispMethod = method);
}
case EDispMethod.OpenGL:
diff --git a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.Designer.cs b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.Designer.cs
index cddc5b0da7..5661250feb 100644
--- a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.Designer.cs
+++ b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.Designer.cs
@@ -81,9 +81,9 @@
this.label6 = new BizHawk.WinForms.Controls.LocLabelEx();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.label13 = new BizHawk.WinForms.Controls.LocLabelEx();
- this.cbAlternateVsync = new System.Windows.Forms.CheckBox();
+ this.cbAllowTearing = new System.Windows.Forms.CheckBox();
this.label8 = new BizHawk.WinForms.Controls.LocLabelEx();
- this.rbD3D9 = new System.Windows.Forms.RadioButton();
+ this.rbD3D11 = new System.Windows.Forms.RadioButton();
this.label7 = new BizHawk.WinForms.Controls.LocLabelEx();
this.rbGDIPlus = new System.Windows.Forms.RadioButton();
this.tpMisc = new System.Windows.Forms.TabPage();
@@ -446,7 +446,7 @@
//
this.label5.Location = new System.Drawing.Point(21, 123);
this.label5.Name = "label5";
- this.label5.Text = " • May malfunction on some systems.\r\n • May have increased performance for OpenGL" +
+ this.label5.Text = " • May malfunction on some systems.\r\n • Will have increased performance for OpenGL" +
"-based emulation cores.\r\n • May have reduced performance on some systems.\r\n";
//
// tabControl1
@@ -633,9 +633,9 @@
// groupBox3
//
this.groupBox3.Controls.Add(this.label13);
- this.groupBox3.Controls.Add(this.cbAlternateVsync);
+ this.groupBox3.Controls.Add(this.cbAllowTearing);
this.groupBox3.Controls.Add(this.label8);
- this.groupBox3.Controls.Add(this.rbD3D9);
+ this.groupBox3.Controls.Add(this.rbD3D11);
this.groupBox3.Controls.Add(this.label7);
this.groupBox3.Controls.Add(this.rbGDIPlus);
this.groupBox3.Controls.Add(this.label5);
@@ -652,34 +652,34 @@
this.label13.Name = "label13";
this.label13.Text = resources.GetString("label13.Text");
this.label13.Click += new System.EventHandler(this.Label13_Click);
- this.label13.DoubleClick += new System.EventHandler(this.Label13_Click);
- //
- // cbAlternateVsync
- //
- this.cbAlternateVsync.AutoSize = true;
- this.cbAlternateVsync.Location = new System.Drawing.Point(28, 60);
- this.cbAlternateVsync.Name = "cbAlternateVsync";
- this.cbAlternateVsync.Size = new System.Drawing.Size(15, 14);
- this.cbAlternateVsync.TabIndex = 21;
- this.cbAlternateVsync.UseVisualStyleBackColor = true;
+ this.label13.DoubleClick += new System.EventHandler(this.Label13_Click);
+ //
+ // cbAllowTearing
+ //
+ this.cbAllowTearing.AutoSize = true;
+ this.cbAllowTearing.Location = new System.Drawing.Point(28, 60);
+ this.cbAllowTearing.Name = "cbAllowTearing";
+ this.cbAllowTearing.Size = new System.Drawing.Size(15, 14);
+ this.cbAllowTearing.TabIndex = 21;
+ this.cbAllowTearing.UseVisualStyleBackColor = true;
//
// label8
//
this.label8.Location = new System.Drawing.Point(21, 30);
this.label8.Name = "label8";
- this.label8.Text = " • Best compatibility\r\n • May have trouble with OpenGL-based cores (N64)\r\n";
- //
- // rbD3D9
- //
- this.rbD3D9.AutoSize = true;
- this.rbD3D9.Checked = true;
- this.rbD3D9.Location = new System.Drawing.Point(6, 10);
- this.rbD3D9.Name = "rbD3D9";
- this.rbD3D9.Size = new System.Drawing.Size(73, 17);
- this.rbD3D9.TabIndex = 19;
- this.rbD3D9.TabStop = true;
- this.rbD3D9.Text = "Direct3D9";
- this.rbD3D9.UseVisualStyleBackColor = true;
+ this.label8.Text = " • Best compatibility\r\n • Decreased performance for OpenGL-based cores (NDS, 3DS)\r\n";
+ //
+ // rbD3D11
+ //
+ this.rbD3D11.AutoSize = true;
+ this.rbD3D11.Checked = true;
+ this.rbD3D11.Location = new System.Drawing.Point(6, 10);
+ this.rbD3D11.Name = "rbD3D11";
+ this.rbD3D11.Size = new System.Drawing.Size(73, 17);
+ this.rbD3D11.TabIndex = 19;
+ this.rbD3D11.TabStop = true;
+ this.rbD3D11.Text = "Direct3D11";
+ this.rbD3D11.UseVisualStyleBackColor = true;
//
// label7
//
@@ -1050,7 +1050,7 @@
private System.Windows.Forms.RadioButton rbGDIPlus;
private System.Windows.Forms.TabPage tpMisc;
private BizHawk.WinForms.Controls.LocLabelEx label8;
- private System.Windows.Forms.RadioButton rbD3D9;
+ private System.Windows.Forms.RadioButton rbD3D11;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.CheckBox cbStatusBarWindowed;
private BizHawk.WinForms.Controls.LocLabelEx label9;
@@ -1078,7 +1078,7 @@
private System.Windows.Forms.TextBox txtCustomARX;
private System.Windows.Forms.CheckBox cbAutoPrescale;
private BizHawk.WinForms.Controls.LocLabelEx label13;
- private System.Windows.Forms.CheckBox cbAlternateVsync;
+ private System.Windows.Forms.CheckBox cbAllowTearing;
private BizHawk.WinForms.Controls.LocLabelEx label1;
private System.Windows.Forms.CheckBox cbFullscreenHacks;
private System.Windows.Forms.Button btnDefaults;
diff --git a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs
index cf25fcd905..51d368a557 100755
--- a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs
+++ b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.cs
@@ -53,7 +53,7 @@ namespace BizHawk.Client.EmuHawk
cbFullscreenHacks.Checked = _config.DispFullscreenHacks;
cbAutoPrescale.Checked = _config.DispAutoPrescale;
- cbAlternateVsync.Checked = _config.DispAlternateVsync;
+ cbAllowTearing.Checked = _config.DispAllowTearing;
if (_config.DispSpeedupFeatures == 2) rbDisplayFull.Checked = true;
if (_config.DispSpeedupFeatures == 1) rbDisplayMinimal.Checked = true;
@@ -63,7 +63,7 @@ namespace BizHawk.Client.EmuHawk
rbOpenGL.Checked = _config.DispMethod == EDispMethod.OpenGL;
rbGDIPlus.Checked = _config.DispMethod == EDispMethod.GdiPlus;
- rbD3D9.Checked = _config.DispMethod == EDispMethod.D3D9;
+ rbD3D11.Checked = _config.DispMethod == EDispMethod.D3D11;
cbStatusBarWindowed.Checked = _config.DispChromeStatusBarWindowed;
cbCaptionWindowed.Checked = _config.DispChromeCaptionWindowed;
@@ -110,11 +110,11 @@ namespace BizHawk.Client.EmuHawk
RefreshAspectRatioOptions();
- if (!HostCapabilityDetector.HasD3D9)
+ if (!HostCapabilityDetector.HasD3D11)
{
- rbD3D9.Enabled = false;
- rbD3D9.AutoCheck = false;
- cbAlternateVsync.Enabled = false;
+ rbD3D11.Enabled = false;
+ rbD3D11.AutoCheck = false;
+ cbAllowTearing.Enabled = false;
label13.Enabled = false;
label8.Enabled = false;
}
@@ -146,7 +146,7 @@ namespace BizHawk.Client.EmuHawk
_config.DispFullscreenHacks = cbFullscreenHacks.Checked;
_config.DispAutoPrescale = cbAutoPrescale.Checked;
- _config.DispAlternateVsync = cbAlternateVsync.Checked;
+ _config.DispAllowTearing = cbAllowTearing.Checked;
_config.DispChromeStatusBarWindowed = cbStatusBarWindowed.Checked;
_config.DispChromeCaptionWindowed = cbCaptionWindowed.Checked;
@@ -228,8 +228,8 @@ namespace BizHawk.Client.EmuHawk
_config.DispMethod = EDispMethod.OpenGL;
if(rbGDIPlus.Checked)
_config.DispMethod = EDispMethod.GdiPlus;
- if(rbD3D9.Checked)
- _config.DispMethod = EDispMethod.D3D9;
+ if(rbD3D11.Checked)
+ _config.DispMethod = EDispMethod.D3D11;
if (int.TryParse(txtCropLeft.Text, out int dispCropLeft))
{
@@ -368,7 +368,7 @@ namespace BizHawk.Client.EmuHawk
private void Label13_Click(object sender, EventArgs e)
{
- cbAlternateVsync.Checked ^= true;
+ cbAllowTearing.Checked ^= true;
}
private void BtnDefaults_Click(object sender, EventArgs e)
diff --git a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.resx b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.resx
index 86fa78854a..ae153eb520 100644
--- a/src/BizHawk.Client.EmuHawk/config/DisplayConfig.resx
+++ b/src/BizHawk.Client.EmuHawk/config/DisplayConfig.resx
@@ -121,9 +121,9 @@
17, 17
- Alternate VSync Method: Some GPUs have problems VSyncing. It seems
-to manifest on full-screen windows. There will be hiccups when throttling
-to VSync. Use this to try and solve it (busy loop; burns major CPU)
+ Allow Tearing: Allows fullscreen to tear when VSync is disabled.
+This is required for variable frame rate (VFR) monitors to properly work.
+Fast forward performance might decrease with tearing allowed.
For Windows operating systems >= Vista, with