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