Fix BizHawk.Bizware.Test and related

This commit is contained in:
CasualPokePlayer 2024-05-29 16:02:49 -07:00
parent 73395c9e49
commit e46267494d
11 changed files with 156 additions and 140 deletions

View File

@ -1,15 +1,15 @@
#ifdef VERTEX
uniform mat4 modelViewProj;
uniform mat4 MVPMatrix;
in vec4 position;
in vec2 tex;
in vec4 VertexCoord;
in vec2 TexCoord;
out vec2 vTex;
void main()
{
gl_Position = modelViewProj * position;
vTex = tex;
gl_Position = MVPMatrix * VertexCoord;
vTex = TexCoord;
}
#endif
@ -21,14 +21,14 @@ uniform sampler2D s_p;
in vec2 vTex;
out vec4 oColor;
out vec4 FragColor;
void main()
{
vec4 temp = texture2D(s_p,vTex);
vec4 temp = texture(s_p,vTex);
vec2 wpos = gl_FragCoord.xy;
if(floor(wpos.y/2.0) != floor(wpos.y)/2.0) temp.rgb *= uIntensity;
oColor = temp;
FragColor = temp;
}
#endif

View File

@ -8,17 +8,17 @@
// Shader that replicates gamma-ramp of bSNES/Higan.
#ifdef VERTEX
uniform mat4 modelViewProj;
uniform mat4 MVPMatrix;
in vec4 position;
in vec2 tex;
in vec4 VertexCoord;
in vec2 TexCoord;
out vec2 vTex;
void main()
{
gl_Position = modelViewProj * position;
vTex = tex;
gl_Position = MVPMatrix * VertexCoord;
vTex = TexCoord;
}
#endif
@ -29,7 +29,7 @@ uniform sampler2D s_p;
in vec2 vTex;
out vec4 oColor;
out vec4 FragColor;
// Tweakables.
#define saturation 1.0
@ -47,10 +47,10 @@ vec3 grayscale(vec3 col)
void main()
{
vec3 res = texture2D(s_p,vTex).xyz;
vec3 res = texture(s_p,vTex).xyz;
res = mix(grayscale(res), res, saturation); // Apply saturation
res = pow(res, vec3(gamma,gamma,gamma)); // Apply gamma
oColor = vec4(clamp(res * luminance,0.0,1.0), 1.0);
FragColor = vec4(clamp(res * luminance,0.0,1.0), 1.0);
}
#endif

View File

@ -6,31 +6,31 @@ uniform struct
} IN;
#ifdef VERTEX
uniform mat4 modelViewProj;
uniform mat4 MVPMatrix;
in vec4 position;
in vec2 tex;
in vec4 VertexCoord;
in vec2 TexCoord;
out vec2 coords[9];
void main()
{
gl_Position = modelViewProj * position;
gl_Position = MVPMatrix * VertexCoord;
vec2 texsize = IN.texture_size;
vec2 delta = 0.5 / texsize;
float dx = delta.x;
float dy = delta.y;
coords[0] = tex + vec2(-dx, -dy);
coords[1] = tex + vec2(-dx, 0.0);
coords[2] = tex + vec2(-dx, dy);
coords[3] = tex + vec2(0.0, -dy);
coords[4] = tex + vec2(0.0, 0.0);
coords[5] = tex + vec2(0.0, dy);
coords[6] = tex + vec2(dx, -dy);
coords[7] = tex + vec2(dx, 0);
coords[8] = tex + vec2(dx, dy);
coords[0] = TexCoord + vec2(-dx, -dy);
coords[1] = TexCoord + vec2(-dx, 0.0);
coords[2] = TexCoord + vec2(-dx, dy);
coords[3] = TexCoord + vec2(0.0, -dy);
coords[4] = TexCoord + vec2(0.0, 0.0);
coords[5] = TexCoord + vec2(0.0, dy);
coords[6] = TexCoord + vec2(dx, -dy);
coords[7] = TexCoord + vec2(dx, 0);
coords[8] = TexCoord + vec2(dx, dy);
}
#endif
@ -41,7 +41,7 @@ uniform sampler2D s_p;
in vec2 coords[9];
out vec4 oColor;
out vec4 FragColor;
const float mx = 0.325; // start smoothing wt.
const float k = -0.250; // wt. decrease factor
@ -51,15 +51,15 @@ const float lum_add = 0.25; // effects smoothing
void main()
{
vec3 c00 = texture2D(s_p, coords[0]).xyz;
vec3 c01 = texture2D(s_p, coords[1]).xyz;
vec3 c02 = texture2D(s_p, coords[2]).xyz;
vec3 c10 = texture2D(s_p, coords[3]).xyz;
vec3 c11 = texture2D(s_p, coords[4]).xyz;
vec3 c12 = texture2D(s_p, coords[5]).xyz;
vec3 c20 = texture2D(s_p, coords[6]).xyz;
vec3 c21 = texture2D(s_p, coords[7]).xyz;
vec3 c22 = texture2D(s_p, coords[8]).xyz;
vec3 c00 = texture(s_p, coords[0]).xyz;
vec3 c01 = texture(s_p, coords[1]).xyz;
vec3 c02 = texture(s_p, coords[2]).xyz;
vec3 c10 = texture(s_p, coords[3]).xyz;
vec3 c11 = texture(s_p, coords[4]).xyz;
vec3 c12 = texture(s_p, coords[5]).xyz;
vec3 c20 = texture(s_p, coords[6]).xyz;
vec3 c21 = texture(s_p, coords[7]).xyz;
vec3 c22 = texture(s_p, coords[8]).xyz;
vec3 dt = vec3(1.0,1.0,1.0);
float md1 = dot(abs(c00 - c22), dt);
@ -84,7 +84,7 @@ void main()
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);
oColor = vec4(w1 * c10 + w2 * c21 + w3 * c12 + w4 * c01 + (1.0 - w1 - w2 - w3 - w4) * c11, 1.0);
FragColor = vec4(w1 * c10 + w2 * c21 + w3 * c12 + w4 * c01 + (1.0 - w1 - w2 - w3 - w4) * c11, 1.0);
}
#endif

View File

@ -8,17 +8,17 @@
#ifdef VERTEX
uniform mat4 modelViewProj;
uniform mat4 MVPMatrix;
in vec4 position;
in vec2 tex;
in vec4 VertexCoord;
in vec2 TexCoord;
out vec2 vTexcoord;
void main()
{
gl_Position = modelViewProj * position;
vTexcoord = tex;
gl_Position = MVPMatrix * VertexCoord;
vTexcoord = TexCoord;
}
#endif //VERTEX
@ -38,7 +38,7 @@ uniform sampler2D s_p;
in vec2 vTexcoord;
out vec4 oColor;
out vec4 FragColor;
float saturate(float x)
{
@ -128,13 +128,13 @@ float expow(vec2 value, float exponent) {
// MultiSampling for ghosting effect
vec3 GhostSample(vec2 t, float latency) {
vec3 Out = texture2D(s_p,t).rgb;
vec3 Out = texture(s_p,t).rgb;
float Weight = 1.0f;
vec2 Direction = vec2(-latency,0.0f);
for(int i=1; i < GhostNumSamples; i++) {
float curweight = pow(1.0f-(float(i)/GhostNumSamples),1.0f/SignalLatencyAttenuation);
Out += GhostLatencyIntensity * curweight * texture2D(s_p,saturate(t+(1.0f-curweight)*Direction)).xyz;
Out += GhostLatencyIntensity * curweight * texture(s_p,saturate(t+(1.0f-curweight)*Direction)).xyz;
Weight += GhostLatencyIntensity * curweight;
}
return Out/Weight;
@ -148,7 +148,7 @@ vec3 Bloom(vec2 t, vec2 r) {
for(int i = 0; i < 5; i++)
{
vec2 offset = vec2(BloomPositions[i],BloomPositions[j]) / r;
Out += texture2D(s_p, t + offset).rgb * BloomWeights[i*5+j];
Out += texture(s_p, t + offset).rgb * BloomWeights[i*5+j];
}
return pow(Out, vec3(BloomExponent,BloomExponent,BloomExponent)) * BloomIntensity;
}
@ -166,7 +166,7 @@ vec3 TVEffect(vec2 in_Position, vec2 FakeResolution, float Time) {
#ifdef GHOST_SAMPLING
vec3 latencyweight = vec3(0.0f,0.0f,0.0f);
for(int i=1; i < GhostNumSamples; i++) {
latencyweight += texture2D(s_p, ScreenPos + vec2(1.0f/FakeResolution.x,0.0f)).xyz;
latencyweight += texture(s_p, ScreenPos + vec2(1.0f/FakeResolution.x,0.0f)).xyz;
}
vec3 LatencyRGB = SignalLatencyRGB * (1.0-(latencyweight/GhostNumSamples));
@ -174,9 +174,9 @@ vec3 TVEffect(vec2 in_Position, vec2 FakeResolution, float Time) {
vec3 SMP_Green = GhostSample((ScreenPos) + ((vec2(ColorFringeIntensity,0.0f))/FakeResolution),LatencyRGB.y).xyz;
vec3 SMP_Blue = GhostSample((ScreenPos) + ((vec2(ColorFringeIntensity*2.0f,0.0f))/FakeResolution),LatencyRGB.z).xyz;
#else
vec3 SMP_Red = texture2D(s_p, (ScreenPos)).xyz;
vec3 SMP_Green = texture2D(s_p, (ScreenPos) + ((vec2(ColorFringeIntensity,0.0f))/FakeResolution)).xyz;
vec3 SMP_Blue = texture2D(s_p, (ScreenPos) + ((vec2(ColorFringeIntensity*2.0f,0.0f))/FakeResolution)).xyz;
vec3 SMP_Red = texture(s_p, (ScreenPos)).xyz;
vec3 SMP_Green = texture(s_p, (ScreenPos) + ((vec2(ColorFringeIntensity,0.0f))/FakeResolution)).xyz;
vec3 SMP_Blue = texture(s_p, (ScreenPos) + ((vec2(ColorFringeIntensity*2.0f,0.0f))/FakeResolution)).xyz;
#endif
#ifdef BLOOM
@ -209,7 +209,7 @@ vec3 TVEffect(vec2 in_Position, vec2 FakeResolution, float Time) {
);
// Non-Pixelated Image
vec3 ImageRGB = texture2D(s_p, ScreenPos).xyz;
vec3 ImageRGB = texture(s_p, ScreenPos).xyz;
return mix(ImageRGB, PixelRGB, FakePixelEffectBlend) * mask;
}
@ -218,7 +218,7 @@ void main()
{
vec4 color = vec4(1.0f,1.0f,1.0f,1.0f);
color.xyz = TVEffect(vTexcoord, IN.texture_size, Time);
oColor = color;
FragColor = color;
}
#endif

View File

@ -1,9 +1,6 @@
using System.Windows.Forms;
using BizHawk.Bizware.Graphics;
using BizHawk.Bizware.Graphics.Controls;
namespace BizHawk.Client.EmuHawk
namespace BizHawk.Bizware.Graphics.Controls
{
/// <summary>
/// Adapts a GraphicsControl to gain the power of remembering what was drawn to it, and keeping it presented in response to Paint events
@ -15,6 +12,20 @@ namespace BizHawk.Client.EmuHawk
_gl = gl;
_graphicsControl = GraphicsControlFactory.CreateGraphicsControl(gl);
_guiRenderer = new(gl);
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.UserMouse, true);
_graphicsControl.Dock = DockStyle.Fill;
_graphicsControl.MouseDoubleClick += (_, e) => OnMouseDoubleClick(e);
_graphicsControl.MouseClick += (_, e) => OnMouseClick(e);
_graphicsControl.MouseEnter += (_, e) => OnMouseEnter(e);
_graphicsControl.MouseLeave += (_, e) => OnMouseLeave(e);
_graphicsControl.MouseMove += (_, e) => OnMouseMove(e);
_graphicsControl.Paint += (_, e) => OnPaint(e);
Controls.Add(_graphicsControl);
}
/// <summary>

View File

@ -48,7 +48,7 @@ namespace BizHawk.Bizware.Graphics
vertexLayoutItems,
vertexShaderArgs: new(vsProgram, "vsmain"),
fragmentShaderArgs: new(psProgram, "psmain"),
fragmentOutputName: "oColor");
fragmentOutputName: "FragColor");
Pipeline = igl.CreatePipeline(compileArgs);
igl.BindPipeline(Pipeline);
@ -143,8 +143,8 @@ float4 psmain(PS_INPUT src) : SV_Target
";
public const string ImGuiVertexShader_gl = @"
//opengl 3.0
#version 130
//opengl 3.2
#version 150
uniform mat4 um44Projection;
in vec2 aPosition;
@ -163,21 +163,21 @@ void main()
}";
public const string ImGuiPixelShader_gl = @"
//opengl 3.0
#version 130
//opengl 3.2
#version 150
uniform bool uSamplerEnable;
uniform sampler2D uSampler0;
in vec2 vTexcoord0;
in vec4 vColor0;
out vec4 oColor;
out vec4 FragColor;
void main()
{
vec4 temp = vColor0;
if(uSamplerEnable) temp *= texture2D(uSampler0, vTexcoord0);
oColor = temp;
if(uSamplerEnable) temp *= texture(uSampler0, vTexcoord0);
FragColor = temp;
}";
}
}

View File

@ -26,7 +26,7 @@ namespace BizHawk.Bizware.Graphics
{
if (!Available)
{
throw new InvalidOperationException("OpenGL 3.0 is required and unavailable");
throw new InvalidOperationException("OpenGL 3.2 is required and unavailable");
}
GL = GL.GetApi(SDL2OpenGLContext.GetGLProcAddress);

View File

@ -47,7 +47,7 @@ namespace BizHawk.Bizware.Graphics
vertexLayoutItems,
vertexShaderArgs: new(vsProgram, "vsmain"),
fragmentShaderArgs: new(psProgram, "psmain"),
fragmentOutputName: "oColor");
fragmentOutputName: "FragColor");
CurrPipeline = DefaultPipeline = Owner.CreatePipeline(compileArgs);
}
@ -325,8 +325,8 @@ float4 psmain(PS_INPUT src) : SV_Target
";
public const string DefaultVertexShader_gl = @"
//opengl 3.0
#version 130
//opengl 3.2
#version 150
uniform mat4 um44Modelview, um44Projection;
uniform vec4 uModulateColor;
@ -346,21 +346,21 @@ void main()
}";
public const string DefaultPixelShader_gl = @"
//opengl 3.0
#version 130
//opengl 3.2
#version 150
uniform bool uSamplerEnable;
uniform sampler2D uSampler0;
in vec2 vTexcoord0;
in vec4 vCornerColor;
out vec4 oColor;
out vec4 FragColor;
void main()
{
vec4 temp = vCornerColor;
if(uSamplerEnable) temp *= texture2D(uSampler0,vTexcoord0);
oColor = temp;
if(uSamplerEnable) temp *= texture(uSampler0,vTexcoord0);
FragColor = temp;
}";
}

View File

@ -16,15 +16,18 @@ namespace BizHawk.Bizware.Graphics
Owner = owner;
var vertexLayoutItems = new PipelineCompileArgs.VertexLayoutItem[3];
vertexLayoutItems[0] = new("position", 4, 0, AttribUsage.Position);
vertexLayoutItems[1] = new("color", 4, 16, AttribUsage.Color0); // just dead weight, i have no idea why this is here. but some old HLSL compilers (used in bizhawk for various reasons) will want it to exist here since it exists in the vertex shader
vertexLayoutItems[2] = new("tex", 2, 32, AttribUsage.Texcoord0);
// note: vertex input names don't matter for HLSL, only for GLSL do they matter
// inversely, semantic usage doesn't matter for GLSL, only for HLSL do they matter
vertexLayoutItems[0] = new("VertexCoord", 4, 0, AttribUsage.Position);
vertexLayoutItems[1] = new("COLOR", 4, 16, AttribUsage.Color0); // just dead weight, i have no idea why this is here. but some old HLSL compilers (used in bizhawk for various reasons) will want it to exist here since it exists in the vertex shader
vertexLayoutItems[2] = new("TexCoord", 2, 32, AttribUsage.Texcoord0);
string vsSource, psSource;
if (owner.DispMethodEnum == EDispMethod.OpenGL)
{
vsSource = "#version 130\r\n";
psSource = "#version 130\r\n";
// versions must be specified first, even before defines
vsSource = "#version 150\r\n";
psSource = "#version 150\r\n";
}
else
{
@ -39,7 +42,7 @@ namespace BizHawk.Bizware.Graphics
vertexLayoutItems,
vertexShaderArgs: new(vsSource, "main_vertex"),
fragmentShaderArgs: new(psSource, "main_fragment"),
fragmentOutputName: "oColor");
fragmentOutputName: "FragColor");
try
{
@ -86,16 +89,31 @@ namespace BizHawk.Bizware.Graphics
// ack! make sure to set the pipeline before setting uniforms
Bind();
Pipeline.SetUniform("IN.video_size", new Vector2(InputSize.Width, InputSize.Height));
Pipeline.SetUniform("IN.texture_size", new Vector2(tex.Width, tex.Height));
Pipeline.SetUniform("IN.output_size", new Vector2(OutputSize.Width, OutputSize.Height));
var videoSize = new Vector2(InputSize.Width, InputSize.Height);
var texSize = new Vector2(tex.Width, tex.Height);
var outputSize = new Vector2(OutputSize.Width, OutputSize.Height);
// cg struct based IN
Pipeline.SetUniform("IN.video_size", videoSize);
Pipeline.SetUniform("IN.texture_size", texSize);
Pipeline.SetUniform("IN.output_size", outputSize);
Pipeline.SetUniform("IN.frame_count", 1); //todo
Pipeline.SetUniform("IN.frame_direction", 1); //todo
var Projection = Owner.CreateGuiProjectionMatrix(OutputSize);
var Modelview = Owner.CreateGuiViewMatrix(OutputSize);
var mat = Modelview * Projection;
Pipeline.SetUniformMatrix("modelViewProj", mat);
// cg2glsl based IN
Pipeline.SetUniform("VideoSize", videoSize);
Pipeline.SetUniform("TextureSize", texSize);
Pipeline.SetUniform("OutputSize", outputSize);
Pipeline.SetUniform("FrameCount", 1); //todo
Pipeline.SetUniform("FrameDirection", 1); //todo
var projection = Owner.CreateGuiProjectionMatrix(OutputSize);
var modelView = Owner.CreateGuiViewMatrix(OutputSize);
var mat = modelView * projection;
// cg based projection matrix
Pipeline.SetUniformMatrix("modelViewProj", ref mat);
// cg2glsl based projection matrix
Pipeline.SetUniformMatrix("MVPMatrix", ref mat);
Pipeline.SetUniformSampler(sampler0, tex);
Owner.SetViewport(OutputSize);

View File

@ -2,14 +2,21 @@
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<Import Project="../MainSlnExecutable.props" />
<Import Project="../MainSlnCommon.props" />
<PropertyGroup>
<DefineConstants>$(DefineConstants);EXE_PROJECT</DefineConstants>
<OutputPath>$(ProjectDir)bin<!--/$(Configuration)/$(TargetFramework)--></OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputType>WinExe</OutputType>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms" />
<ProjectReference Include="$(ProjectDir)../BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj" />
<ProjectReference Include="$(ProjectDir)../BizHawk.Bizware.Graphics/BizHawk.Bizware.Graphics.csproj" />
<ProjectReference Include="$(ProjectDir)../BizHawk.Bizware.Graphics.Controls/BizHawk.Bizware.Graphics.Controls.csproj" />
<EmbeddedResource Include="TestImages/**/*" />
<None Include="$(ProjectDir)../../Assets/dll/*SDL2*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@ -2,32 +2,16 @@
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using BizHawk.Bizware.BizwareGL;
using BizHawk.Bizware.Graphics;
using BizHawk.Client.EmuHawk;
using BizHawk.Bizware.Graphics.Controls;
namespace BizHawk.Bizware.Test
{
public static class Program
{
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += (_, args) =>
{
lock (AppDomain.CurrentDomain)
{
var firstAsm = Array.Find(AppDomain.CurrentDomain.GetAssemblies(), asm => asm.FullName == args.Name);
if (firstAsm is not null) return firstAsm;
var guessFilename = Path.Combine(AppContext.BaseDirectory, "dll", $"{new AssemblyName(args.Name).Name}.dll");
return File.Exists(guessFilename) ? Assembly.LoadFile(guessFilename) : null;
}
};
}
public static void Main() => RunTest();
private sealed class TestForm : Form
@ -46,13 +30,14 @@ namespace BizHawk.Bizware.Test
private static void RunTest()
{
IGL igl = new IGL_OpenGL(2, 0, false);
ArtManager am = new(igl);
var testArts = typeof(Program).Assembly.GetManifestResourceNames().Where(s => s.Contains("flame"))
.Select(s => am.LoadArt(ReflectionCache.EmbeddedResourceStream(s.Substring(21)))) // ReflectionCache adds back the prefix
IGL igl = new IGL_OpenGL();
// graphics control must be made right away to create the OpenGL context
RetainedGraphicsControl? c = new(igl) { Dock = DockStyle.Fill, BackColor = Color.Black };
var testTexs = typeof(Program).Assembly.GetManifestResourceNames().Where(s => s.Contains("flame"))
.Select(s => igl.LoadTexture(ReflectionCache.EmbeddedResourceStream(s[21..]))) // ReflectionCache adds back the prefix
.ToList();
var smile = am.LoadArt(ReflectionCache.EmbeddedResourceStream("TestImages.smile.png"));
am.Close();
var smile = igl.LoadTexture(ReflectionCache.EmbeddedResourceStream("TestImages.smile.png"))!;
StringRenderer sr;
using (var xml = ReflectionCache.EmbeddedResourceStream("TestImages.courier16px.fnt"))
using (var tex = ReflectionCache.EmbeddedResourceStream("TestImages.courier16px_0.png"))
@ -61,8 +46,6 @@ namespace BizHawk.Bizware.Test
}
GuiRenderer gr = new(igl);
RetainedGraphicsControl? c = new(igl) { Dock = DockStyle.Fill };
TestForm tf = new() { Controls = { c } };
tf.FormClosing += (_, _) =>
{
@ -81,26 +64,24 @@ namespace BizHawk.Bizware.Test
// create a render target
var rt = igl.CreateRenderTarget(60, 60);
rt.Bind();
igl.SetClearColor(Color.Blue);
igl.Clear(ClearBufferMask.ColorBufferBit);
igl.ClearColor(Color.Blue);
gr.Begin(60, 60);
gr.Draw(smile);
gr.End();
rt.Unbind();
igl.BindDefaultRenderTarget();
var rttex2d = igl.LoadTexture(rt.Texture2d.Resolve());
var rttex2d = igl.LoadTexture(rt.Resolve())!;
// test retroarch shader
var rt2 = igl.CreateRenderTarget(240, 240);
rt2.Bind();
igl.SetClearColor(Color.CornflowerBlue);
igl.Clear(ClearBufferMask.ColorBufferBit);
igl.ClearColor(Color.CornflowerBlue);
RetroShader shader;
using (var stream = ReflectionCache.EmbeddedResourceStream("TestImages.4xSoft.glsl"))
{
shader = new(igl, new StreamReader(stream).ReadToEnd());
}
igl.SetBlendState(igl.BlendNoneCopy);
igl.DisableBlending();
shader.Run(rttex2d, new Size(60, 60), new Size(240, 240), true);
var running = true;
@ -118,38 +99,37 @@ namespace BizHawk.Bizware.Test
{
c.Begin();
igl.SetClearColor(Color.Red);
igl.Clear(ClearBufferMask.ColorBufferBit);
igl.ClearColor(Color.Red);
var frame = (int) (DateTime.Now - start).TotalSeconds % testArts.Count;
var frame = (int) (DateTime.Now - start).TotalSeconds % testTexs.Count;
gr.Begin(c.ClientSize.Width, c.ClientSize.Height);
gr.SetBlendState(igl.BlendNormal);
gr.EnableBlending();
gr.SetModulateColor(Color.Green);
gr.RectFill(250, 0, 16, 16);
gr.DrawSubrect(null, 250, 0, 16, 16, 0, 0, 1, 1);
gr.SetBlendState(igl.BlendNoneCopy);
gr.DisableBlending();
gr.Draw(rttex2d, 0, 20);
gr.SetBlendState(igl.BlendNormal);
gr.EnableBlending();
sr.RenderString(gr, 0, 0, "?? fps");
gr.SetModulateColor(Color.FromArgb(255, 255, 255, 255));
gr.SetCornerColor(0, new(1.0f, 0.0f, 0.0f, 1.0f));
gr.Draw(rt2.Texture2d, 0, 0);
gr.Draw(rt2, 0, 0);
gr.SetCornerColor(0, new(1.0f, 1.0f, 1.0f, 1.0f));
gr.SetModulateColorWhite();
gr.Modelview.Translate((float) Math.Sin(wobble / 360.0f) * 50, 0);
gr.Modelview.Translate(100, 100);
gr.Modelview.Push();
gr.Modelview.Translate(testArts[frame].Width, 0);
gr.Modelview.Scale(-1, 1);
gr.ModelView.Translate((float) Math.Sin(wobble / 360.0f) * 50, 0);
gr.ModelView.Translate(100, 100);
gr.ModelView.Push();
gr.ModelView.Translate(testTexs[frame].Width, 0);
gr.ModelView.Scale(-1, 1);
wobble++;
gr.SetModulateColor(Color.Yellow);
gr.DrawFlipped(testArts[frame], true, false);
gr.DrawSubrect(testTexs[frame], 0, 0, testTexs[frame].Width, testTexs[frame].Height, 1, 0, 0, 1);
gr.SetModulateColorWhite();
gr.Modelview.Pop();
gr.SetBlendState(igl.BlendNormal);
gr.ModelView.Pop();
gr.EnableBlending();
gr.Draw(smile);
gr.End();