I'm setting up a little project part of which emulates older hardware.
As a 41 year old, I remember the 'good ole' days of computers that didn't have any concept of a GPU or 'modern' graphics architecture, typically addressing memory directly for the display i.e. you draw the pixels directly, and no not in a shader :)
In my little project I have a tiny 128x128 texure and an array. I set pixel data in the array and then use the SetData method in Texture2D to get that over to the GPU. I know this process is inherently slow but its a small texture and should be fine for real time use. I then upscale that using the GPU to draw my 'display'.
However, on DesktopGL project the performance of SetData is erratic. For example my timings for the same code using MG DirectX show SetData at a consistent ~0.2-0.3ms per execution across many reruns and for a decent period of time (same ~0.2-0.3ms when using FNA as an aside).
Back on MG DesktopGL I'm seeing anywhere from ~0.5-16ms. Sometimes its fine, sometimes it climbs in duration after 5-10 seconds, bizarrely if I move the MG game window around with my mouse it seems to drop back down and then climb again, can't figure it out! Same behaviour on my Mac.
Seems like a bug if MG DirectX is consistently ~0.3ms? Any ideas?
MonoGame - 3.7.0.1371
Win 10 v1709
VS Community 2017 15.5.2
Code showing what I'm doing (totally unoptimised :)), hit the space bar to get your timings.
using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace _8bitDNA
{
public class Game1 : Game
{
private GraphicsDeviceManager graphics;
private SpriteBatch spriteBatch;
Texture2D display;
byte[] rawData = new byte[128 * 128 * 4];
Stopwatch watch = new Stopwatch();
KeyboardState previousState;
private int frames = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 512;
graphics.PreferredBackBufferHeight = 512;
}
protected override void Initialize()
{
display = new Texture2D(
GraphicsDevice,
128, 128,
false,
GraphicsDevice.PresentationParameters.BackBufferFormat);
watch = new Stopwatch();
previousState = Keyboard.GetState();
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
KeyboardState state = Keyboard.GetState();
if (state.IsKeyDown(Keys.Escape))
Exit();
if (state.IsKeyDown(Keys.Space) & !previousState.IsKeyDown(Keys.Space))
{
Console.WriteLine(watch.Elapsed.TotalMilliseconds / frames);
this.frames = 0;
this.watch.Restart();
}
previousState = state;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
setDisplayMemory();
watch.Start();
display.SetData(rawData);
watch.Stop();
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp,DepthStencilState.None,RasterizerState.CullNone);
spriteBatch.Draw(display, new Rectangle(0, 0, 512, 512), Color.White);
spriteBatch.End();
frames++;
base.Draw(gameTime);
}
private void setDisplayMemory()
{
for (var x = 0; x < 128; x++)
{
for (var y = 0; y < 128; y++)
{
rawData[((y * 128) + x) * 4] = 255;
}
}
}
}
}
Maybe the texture is locked while it's being used by the GPU. The 16ms you mentioned is very close to one frame. Maybe if you disable VSync and fixedTimeStep and measure it again you get a better idea as to what is going on.
Can you try double buffering, using two textures and swap them on each frame?
@nkast - That is a good idea.
Also i wonder if our check there for blocking if the GL calls are not in the main thread are causing these stalls. Specifically i mean our implementation of Threading.BlockOnUIThread.
The only other thing could be that the way we do SetData isn't efficient. I know on DirectX can use an intermediate surface to receive the the data then copies to the final surface. I wonder if that is something we should be doing in GL.
Anyway... not a GL expert here.
Thanks for the info and advice @nkast and @tomspilman
Ok, a little more on this......
If I switch off VSync the SetData call is taking ~0.8ms per frame. If I re-enable VSync again and switch off fixed time step the SetData call is taking ~16.5ms per frame (i.e. the whole frame).
Switching VSync off AND switching off a fixed time step the SetData call is taking ~0.1ms per frame.
I don't quite understand those timings though. I can't quite understand why the just the SetData calls alone would be quicker? In my code you can see I'm isolating just the SetData call as I noticed whilst profiling it was the bottleneck. I understand the frequency they get called would change but why will it perform better? Sorry if I'm being a little thick!
Going to setup a little double buffer arrangement and report back, thanks for that tip @nkast
Ok, tried a quick double buffering approach and same timings as originally seen. Seeing similar timings to my last post regarding VSync and fixed step disabling as well. Not really sure where to go with it now as unfortunately I don't have the tech chops :(
Heres my quick and dirty double buffering, 'doublebuffer' is a bool set at class level and 'display2' is simply another 128x128 texture with the same spec as 'display'.....
protected override void Draw(GameTime gameTime)
{
setDisplayMemory();
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone);
watch.Start();
if(doublebuffer)
{
doublebuffer = false;
display.SetData(rawData);
spriteBatch.Draw(display2, new Rectangle(0, 0, 512, 512), Color.White);
}
else
{
doublebuffer = true;
display2.SetData(rawData);
spriteBatch.Draw(display, new Rectangle(0, 0, 512, 512), Color.White);
}
watch.Stop();
spriteBatch.End();
frames++;
base.Draw(gameTime);
}
@8bitdna
I think you should set and draw the same texture. Now you are Setting the texture that was scheduled for drawing in the previous frame and might still be locked by the GPU.
@tomspilman
This reminds me of DynamicVertexBuffer and SetDataOptions.Discard. MG doesn't have something analogous for textures (a DynamicTexture2D()?).
@8bitdna
The delay you see is the GPU blocking the thread until it's done rendering the texture from the previous frame, which is as much as one VSync behind. This article describes a similar situation on VertexBuffers.
https://blogs.msdn.microsoft.com/shawnhar/2010/07/07/setdataoptions-nooverwrite-versus-discard/
This reminds me of DynamicVertexBuffer and SetDataOptions.Discard.
MG doesn't have something analogous for textures
Well you could say RenderTarget2D is the dynamic version of a texture in XNA.
Still there is no reason Texture2D.SetData should perform worse on OpenGL.
Hi @nkast I did have it set to that same texture to begin with and got the same problem timings unfortunately. I then tried the flip which you see in the code to see if the 'delay' of actually using the texture would help, sadly neither did.
@tomspilman I have also tried with a RenderTarget2D instead of Texture2D in-case there was any change just for the hell of it, still the same result :(
Thanks again both both of your time, wish I knew more about OpenGL and MG in a depper sense. I have the develop branch source code setup so I can compile MG to try anything out if anybody has any ideas.
p.s. I'm too old for modern rendering, can you tell :)
@8bitdna - We'll figure this out.
@dellis1972 - Any ideas?
In DX the present() blocks until all the commands are finished and it's safe to set them right away in the next draw(). I don't know much about the GL platform but the findings point me to the direction that's a different in how the GPU is synced with the gameloop.
@8bitdna
Add a second stopwatch to measure the intervals between frames right after SetData().
If you see a steady 16.6ms without fluctuations then the delay you see in SetData in normal and you don't have to worry about it.
@nkast Thanks again, I tried this by adding StopWatch watch2.....
watch2.Stop();
watch.Start();
if(doublebuffer)
{
//doublebuffer = false;
display.SetData(rawData);
spriteBatch.Draw(display, new Rectangle(0, 0, 512, 512), Color.White);
}
else
{
//doublebuffer = true;
display2.SetData(rawData);
spriteBatch.Draw(display2, new Rectangle(0, 0, 512, 512), Color.White);
}
watch.Stop();
watch2.Start();
...and am getting the following back per frame averaged out (in ms) since the last time I requested it by pressing space bar, I'm waiting about 1 second in-between each space bar press. The two totalled together make the 16.6ms. Also note how StopWatch watch creeps up to the 16.6ms, thats the odd thing. VSync on and fixed time step on.
0.387366666666667 - Stopwatch 'watch'
16.2759572916667 - Stopwatch 'watch2'
0.982524066390042 - Stopwatch 'watch'
15.7529643153527 - Stopwatch 'watch2'
0.979578362573099 - Stopwatch 'watch'
15.6891643274854 - Stopwatch 'watch2'
2.57076892655367 - Stopwatch 'watch'
14.0934073446328 - Stopwatch 'watch2'
4.22953672316384 - Stopwatch 'watch'
12.5311005649718 - Stopwatch 'watch2'
5.45515652173913 - Stopwatch 'watch'
11.2109951086957 - Stopwatch 'watch2'
6.88870529411765 - Stopwatch 'watch'
9.77903941176471 - Stopwatch 'watch2'
8.30429763313609 - Stopwatch 'watch'
8.36082899408284 - Stopwatch 'watch2'
9.60005476190476 - Stopwatch 'watch'
7.06658988095238 - Stopwatch 'watch2'
10.870924822695 - Stopwatch 'watch'
5.79471985815603 - Stopwatch 'watch2'
11.8688324675325 - Stopwatch 'watch'
4.79757792207792 - Stopwatch 'watch2'
13.182925974026 - Stopwatch 'watch'
3.48352987012987 - Stopwatch 'watch2'
14.3651448275862 - Stopwatch 'watch'
2.30871655172414 - Stopwatch 'watch2'
15.5888647798742 - Stopwatch 'watch'
1.07201698113208 - Stopwatch 'watch2'
16.4487431506849 - Stopwatch 'watch'
0.231343835616438 - Stopwatch 'watch2'
16.5115448275862 - Stopwatch 'watch'
0.150429655172414 - Stopwatch 'watch2'
16.5305985915493 - Stopwatch 'watch'
0.151644366197183 - Stopwatch 'watch2'
16.5194822695035 - Stopwatch 'watch'
0.15404609929078 - Stopwatch 'watch2'
I disabled the double buffering for the moment as well to keep the test as simple as possible as well just in-case you didn't spot it in the code snippet :)
I don't think you can use stop watch like that in this particular case.
Its short circuiting the render update loop i think.
This is what i got from a altered 1 second elapsed time test run on GL.
The longer the timing the more accurate the average time is but mehh.
elapsed time: 1.001076 frames: 1425 updates: 1426 fps: 1423.46834805749
elapsed time: 1.0003664 frames: 1748 updates: 1748 fps: 1747.35976738123
elapsed time: 1.0004643 frames: 1753 updates: 1753 fps: 1752.1864598267
elapsed time: 1.0006279 frames: 1779 updates: 1779 fps: 1777.88366684559
elapsed time: 1.0001524 frames: 1771 updates: 1771 fps: 1770.73014072655
elapsed time: 1.0004066 frames: 1775 updates: 1775 fps: 1774.27857833005
elapsed time: 1.0001327 frames: 928 updates: 928 fps: 927.876870739253 // moved window
elapsed time: 1.0001287 frames: 1727 updates: 1727 fps: 1726.77776370181
elapsed time: 1.0000698 frames: 1721 updates: 1721 fps: 1720.8798825842
elapsed time: 1.0004206 frames: 1723 updates: 1723 fps: 1722.27561087806
elapsed time: 1.0000604 frames: 1725 updates: 1725 fps: 1724.89581629269
elapsed time: 1.0006554 frames: 1731 updates: 1731 fps: 1729.86624566259
elapsed time: 1.000095 frames: 1724 updates: 1724 fps: 1723.83623555762
elapsed time: 1.0000237 frames: 1723 updates: 1723 fps: 1722.95916586777
elapsed time: 1.0000247 frames: 1744 updates: 1744 fps: 1743.95692426397
elapsed time: 1.0004007 frames: 1702 updates: 1702 fps: 1701.3182817645
elapsed time: 1.0001026 frames: 1709 updates: 1709 fps: 1708.82467458839
elapsed time: 1.0002233 frames: 1707 updates: 1707 fps: 1706.61891199695
elapsed time: 1.0000082 frames: 1680 updates: 1680 fps: 1679.98622411296
Try your test like this on both gl and dx and see what you get.
using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Game_SetDataExample
{
public class Game1 : Game
{
private GraphicsDeviceManager graphics;
private SpriteBatch spriteBatch;
Texture2D display;
byte[] rawData = new byte[128 * 128 * 4];
KeyboardState previousState;
private double frames = 0;
private double updates = 0;
private double elapsed = 0;
private double last = 0;
private double now = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
graphics.PreferredBackBufferWidth = 512;
graphics.PreferredBackBufferHeight = 512;
this.TargetElapsedTime = TimeSpan.FromSeconds(1d / 60d);
this.IsFixedTimeStep = false;
graphics.SynchronizeWithVerticalRetrace = false;
graphics.ApplyChanges();
display = new Texture2D(
GraphicsDevice,
128, 128,
false,
GraphicsDevice.PresentationParameters.BackBufferFormat
);
previousState = Keyboard.GetState();
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void UnloadContent() { }
protected override void Update(GameTime gameTime)
{
KeyboardState state = Keyboard.GetState();
if (state.IsKeyDown(Keys.Escape))
Exit();
now = gameTime.TotalGameTime.TotalSeconds;
elapsed = (double)(now - last);
if(elapsed > 2.0d)
{
Console.WriteLine("elapsed time: " + elapsed.ToString() + " frames: "+ frames + " updates: "+updates+" fps: "+ (frames / elapsed).ToString() );
elapsed = 0;
frames = 0;
updates = 0;
last = now;
}
previousState = state;
updates++;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
setDisplayMemory();
display.SetData(rawData);
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone);
spriteBatch.Draw(display, new Rectangle(0, 0, 512, 512), Color.White);
spriteBatch.End();
frames++;
base.Draw(gameTime);
}
Random rnd = new Random();
private void setDisplayMemory()
{
for (var x = 0; x < 128; x++)
{
for (var y = 0; y < 128; y++)
{
int r = rnd.Next(0, 255);
rawData[((y * 128) + x) * 4] = (byte)r;
}
}
}
}
}
Hi @willmotil Thanks for looking at this as well. I tried your test and I get 60 FPS etc. However, am I right in saying MG have VSync and fixed time step enabled by default will always hit that cap?
I disabled vsync and fixed time step by adding this in the Game1 constructor. If I understand things correct the code will run full pelt that way...
graphics.SynchronizeWithVerticalRetrace = false;
this.IsFixedTimeStep = false;
With that done heres the timings for GL...
elapsed time: 6 frames: 15982 updates: 15983 fps: 2663
elapsed time: 6 frames: 34476 updates: 34477 fps: 5746
elapsed time: 6 frames: 52610 updates: 52611 fps: 8768
elapsed time: 6 frames: 70943 updates: 70944 fps: 11823
elapsed time: 6 frames: 89445 updates: 89446 fps: 14907
elapsed time: 6 frames: 107693 updates: 107694 fps: 17948
elapsed time: 6 frames: 126044 updates: 126045 fps: 21007
elapsed time: 6 frames: 144342 updates: 144343 fps: 24057
elapsed time: 6 frames: 162581 updates: 162582 fps: 27096
...and DX...
elapsed time: 6 frames: 28333 updates: 28334 fps: 4722
elapsed time: 6 frames: 60183 updates: 60184 fps: 10030
elapsed time: 6 frames: 94248 updates: 94249 fps: 15708
elapsed time: 6 frames: 127906 updates: 127907 fps: 21317
elapsed time: 6 frames: 161904 updates: 161905 fps: 26984
elapsed time: 6 frames: 195637 updates: 195638 fps: 32606
elapsed time: 6 frames: 229517 updates: 229518 fps: 38252
elapsed time: 6 frames: 262759 updates: 262760 fps: 43793
elapsed time: 6 frames: 296228 updates: 296229 fps: 49371
I think there's an issues with the timing code as it stops running so I'm assuming its overflowing due its much more rapid use or something. The timings themselves don't look right with the increasing FPS? Even so theres a big difference between GL and DX?
Oh sorry.
I had forgot to clear the update and draw counters at first.
You must have copied it before i edited it.
I just re: edited it again, just copy paste it directly.
_Ya on win 10 i remember reading something about windows wants to have its way with vsync. but i don't remember the details._
Details.
The stopwatch class is good for timing individual blocks of code when they are unaffected by the timing of the entire app. However since what is in the block of code is affected by gameTime. Brute system interrupts to get the clock ticks stop watch uses, i suspect are short circuiting the app and probably causing IsRunningSlowly to be set and probably worse to update, just messing it up.
Though i didn't verify this is the actual case it was my suspicion at first glance. A very long time ago i ran into a similar problem.
Thank you again @willmotil
Ok, heres the new timings.... For GL....
elapsed time: 2.0001189 frames: 4675 updates: 4676 fps: 2337.36104388594
elapsed time: 2.0000824 frames: 5744 updates: 5744 fps: 2871.88167847485
elapsed time: 2.0075146 frames: 5364 updates: 5364 fps: 2671.96064227877
elapsed time: 2.0003095 frames: 4966 updates: 4966 fps: 2482.6158152026
elapsed time: 2.0000234 frames: 5059 updates: 5059 fps: 2529.47040519626
elapsed time: 2.0001315 frames: 5105 updates: 5105 fps: 2552.33218415889
elapsed time: 2.0001806 frames: 5218 updates: 5218 fps: 2608.7644285721
elapsed time: 2.0001788 frames: 5423 updates: 5423 fps: 2711.25761356935
elapsed time: 2.0003212 frames: 5400 updates: 5400 fps: 2699.56644962819
elapsed time: 2.0001479 frames: 5344 updates: 5344 fps: 2671.80242021102
elapsed time: 2.0000099 frames: 5452 updates: 5452 fps: 2725.9865063668
...and for DX...
elapsed time: 2.0002247 frames: 3755 updates: 3756 fps: 1877.28908657112
elapsed time: 2.0003103 frames: 4253 updates: 4253 fps: 2126.17012470515
elapsed time: 2.0001593 frames: 3819 updates: 3819 fps: 1909.34792043814
elapsed time: 2.0004948 frames: 3485 updates: 3485 fps: 1742.0690121264
elapsed time: 2.0003603 frames: 3598 updates: 3598 fps: 1798.67596852427
elapsed time: 2.00027 frames: 3713 updates: 3713 fps: 1856.24940633015
elapsed time: 2.0000214 frames: 3741 updates: 3741 fps: 1870.47998586415
elapsed time: 2.0003807 frames: 3623 updates: 3623 fps: 1811.15524659881
elapsed time: 2.0002075 frames: 3644 updates: 3644 fps: 1821.81098711009
elapsed time: 2.0004165 frames: 3647 updates: 3647 fps: 1823.1203351902
Working that FPS back into ms for the frame I make the GL draw 0.35ms and the DX one 0.56ms (roughly). Definitely in the ballpark of what I would have liked to see. I'll run some more tests here but looks like you solved the case Sherlock!
Is it worth mentioning I saw the funny timings in the VS profiler as well? Is gametime definitely a perfect reference for this kind of timing?
MG have VSync and fixed time step enabled by default will always hit that cap?
Doesn't XNA as well?
Does MonoGame's DirectX and DesktopGL have different defaults? I don't think it should.
Hi @tomspilman
Yes I think so, I never did any XNA dev but my learnings tell me they are enabled by default. I just needed them switched off so we can time things another way from what @willmotil has brought to light.
@willmotil I dropped in a StopWatch and replaced the timing line of code like this...
//now = gameTime.TotalGameTime.TotalSeconds;
now = myWatch.Elapsed.TotalMilliseconds / 1000;
I'm getting the same timings as I posted about an hour ago so things are looking good. Difficult one to understand but I do seem to be able to get a consistent result now.
Ya it's the start() method primarily. Stop watch needs it for high precision timings on small code blocks. Its some deep greasy hack. It's really the only thing that makes it both special and screwy. Without it you get the same accuracy as gametime about 20 ms of inaccuracy so you just measure over a longer time like shown above.
Im surprised to see it go this fast honestly, on xna and it was tricky and way slow.
good luck.
@willmotil yes I understand. Reading up StopWatch uses actual hardware in the PC for it timings which makes sense given its degree of potential accuracy. The thing that started me down this path of using StopWatch etc was the fact the .NET profiler in VS was showing me some big differences in timings, it still does. My problem is now I don't know what to believe it correct or how to troubleshoot hotspots, I assumed the profiler would be the defacto measure of time?
Well im not sure if there is a reliable way if you don't have a high performance monitor on your comp. You can get 0 second return intervals on small blocks. You can maybe try with Enviroment.TickCount but this isn't guaranteed either.
So basically the thing is you can measure small blocks with stopwatch or the whole app with regular old gameTime. but... mixing it up can give false results.
Anyways i just keep a framerate counter up from the start if i see a big dip then ill start to second guess my functions. https://github.com/willmotil/MonoGameUtilityClasses
Cheers, As I understand every 'modern' PC (like not over 10 years old) has the required performance hardware. The PC I'm using is a 2 year old gaming rig so I'd be shocked to find it didn't have this. The thing is I did use StopWatch to time one call to Texture2D.setData orginally as in my code right up above. I'd prefer not to use just FPS alone. How will I know how to profile things and isolate a hotspot method etc? I would have expected the VS profiler to be my standard way to analyse code or am I understanding things wrong? Remember I only see the issue on GL for Desktop with, DX is is consistently (and significantly) faster. I also tried using FNA which reported on app start in the console it was using GL and theres no issue there, similar timings to MG DX. Try the test again with MG GL for Desktop and the odd timings start. Confused....
Hey Guys, Congrats on the 3.7 release :) Been some time since I tried this and will try this out again and report back if I see any differences.
No dice I'm afraid, I'm seeing the same as before sadly. However, I had wondered if I recreated the instance of Texture2D every frame instead of reusing the same one might help, as it does. So I did this...
using System;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Pixel
{
public class Game1 : Game
{
private GraphicsDeviceManager graphics;
private SpriteBatch spriteBatch;
byte[] rawData = new byte[128 * 128 * 4];
Stopwatch watch = new Stopwatch();
KeyboardState previousState;
private int frames = 0;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 512;
graphics.PreferredBackBufferHeight = 512;
}
protected override void Initialize()
{
watch = new Stopwatch();
previousState = Keyboard.GetState();
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
KeyboardState state = Keyboard.GetState();
if (state.IsKeyDown(Keys.Escape))
Exit();
if (state.IsKeyDown(Keys.Space) & !previousState.IsKeyDown(Keys.Space))
{
Console.WriteLine(watch.Elapsed.TotalMilliseconds / frames);
this.frames = 0;
this.watch.Restart();
}
previousState = state;
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
setDisplayMemory();
Texture2D display = new Texture2D(
GraphicsDevice,
128, 128,
false,
GraphicsDevice.PresentationParameters.BackBufferFormat);
watch.Start();
display.SetData(rawData);
watch.Stop();
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp,DepthStencilState.None,RasterizerState.CullNone);
spriteBatch.Draw(display, new Rectangle(0, 0, 512, 512), Color.White);
spriteBatch.End();
display.Dispose();
frames++;
base.Draw(gameTime);
}
private void setDisplayMemory()
{
for (var x = 0; x < 128; x++)
{
for (var y = 0; y < 128; y++)
{
rawData[((y * 128) + x) * 4] = 255;
}
}
}
}
}
...Now I get a timing of ~0.6ms per frame which is much more like what I was expecting. Obviously there's a little time spent throwing away and recreating the texture but it might help someone with more brain power get to bottom of whats going on. Thanks again, great project.