title: Smooth SDL2 sprite movement
body: <p>I make a scene with parallax effect. I can’t make the sprite move smoothly along the X axis. The problem is that regardless of whether I use <code>SDL_RenderCopyF</code> or <code>SDL_RenderCopy</code>, the sprite is drawn according to the pixel grid of the monitor when moving slowly, which is accompanied by a jitter effect,
  when there are many layers and everyone moves at different speeds.</p>
  <p>My problem is like <a href="https://stackoverflow.com/questions/36746246/sdl2-smooth-texturesprite-animation-between-points-in-time-function">SDL2 Smooth texture(sprite) animation between points in time function</a>
  But there was a version of SDL2 that did not support floating-point rendering, and the author had to patch it. Starting from version 2.0.10.0, <code>SDL_RenderCopyF</code>, <code>SDL_FRect</code> and other functions have been added to the SDL, with which you can implement subpixel moving sprites, but I can not get them to work in my scene.</p>
  <p>There are 10 layers in FullHD resolution. Rendering occurs in the FullHD window.</p>
  <p><strong>Window and render initialization code:</strong></p>
  <pre><code>void CreateWidnow()
  {
      var windowFlags = Settings.Fullscreen ?
          SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN : Settings.Resizeble ? 
          SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE : SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN;
          
      renderWindow = SDL.SDL_CreateWindow(
          Settings.DebugMode ? $&quot;{Settings.Title} [DEBUG]&quot; : Settings.Title, SDL.SDL_WINDOWPOS_CENTERED, 
          SDL.SDL_WINDOWPOS_CENTERED,
          Settings.Resolution.Width,
          Settings.Resolution.Height,              
          windowFlags);
  }
  
  void CreateRenderer()
  {
      if(Settings.VSinc) {
          renderer = SDL.SDL_CreateRenderer(
              renderWindow, -1, 
              SDL.SDL_RendererFlags.SDL_RENDERER_PRESENTVSYNC |
              SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED |
              SDL.SDL_RendererFlags.SDL_RENDERER_TARGETTEXTURE
          );
      }
      else
      {
          renderer = SDL.SDL_CreateRenderer(
              renderWindow, -1, 
              SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED |
              SDL.SDL_RendererFlags.SDL_RENDERER_TARGETTEXTURE
          );
      }   
  }
  </code></pre>
  <p><strong>The main cycle with the calculation of <code>deltaTime</code>:</strong></p>
  <pre><code>void RenderLoop()
  {
      bool cap = Settings.FPS &gt; 0;
      var timer_fps = new Timer();
          
      ulong now_counter = SDL.SDL_GetPerformanceCounter();
      ulong last_counter = 0;
      double deltaTime = 0;
          
      while (renderLoop)
      {
          timer_fps.Start();
              
          last_counter = now_counter;
          now_counter = SDL.SDL_GetPerformanceCounter();
              
          deltaTime = (double)(now_counter - last_counter) / SDL.SDL_GetPerformanceFrequency();
              
          OnPreUpdate(deltaTime);
          OnUpdate(deltaTime);
          SDL.SDL_RenderPresent(renderer);
          OnPostUpdate(deltaTime);
              
          if( ( cap ) &amp;&amp; ( timer_fps.GetTicks() &lt; 1000 / Settings.FPS ) )
          {
              SDL.SDL_Delay( ( 1000 / Settings.FPS  ) - timer_fps.GetTicks() );
          }
              
      }
  }
  </code></pre>
  <p><strong>Sprite loading and rendering code:</strong></p>
  <pre><code>public Sprite(string path)
  {
      Path = path;
      Transform = new Transform(new Point(0, 0));
      sprite = Image.IMG_LoadTexture(Game.RenderContext, path);
      SDL.SDL_QueryTexture(sprite, out format, out access, out width, out height); // get the width and height of the texture
  
      draw_rect = new SDL.SDL_FRect();
      draw_rect.w = width;
      draw_rect.h = height;
      scr_rect.x = 0; scr_rect.y = 0; scr_rect.w = width; scr_rect.h = height; 
  }
  
  public Transform Transform
  {
      get; set;
  }
  
  public void Draw()
  {
      draw_rect.x = (float)Transform.Position.X;
      draw_rect.y = (float)Transform.Position.Y;
      SDL.SDL_RenderCopyExF(Game.RenderContext, sprite, ref scr_rect, ref draw_rect, Transform.Degrees, IntPtr.Zero, SDL.SDL_RendererFlip.SDL_FLIP_NONE);
  }
  </code></pre>
  <p><strong>Moving Function (Transform.Translate):</strong></p>
  <pre><code>public void Translate(double x, double y)
  {
      position.X += x;
      position.Y += y;
  }
  </code></pre>
  <p><strong>Parallax implementation function (move the sprites back to the right side when they leave the screen):</strong></p>
  <pre><code>void layerDraw(Sprite l1, Sprite l2, double speed)
  {
      l1.Transform.Translate(speed, 0);
      l1.Draw();
  
  
      l2.Transform.Translate(speed, 0);
      l2.Draw();
  
      if (l1.Transform.Position.X &lt;= -Settings.Resolution.Width)
          l1.Transform.SetPosition(Settings.Resolution.Width + l2.Transform.Position.X, 0);
      if (l2.Transform.Position.X &lt;= -Settings.Resolution.Width)
          l2.Transform.SetPosition(Settings.Resolution.Width + l1.Transform.Position.X, 0);  
      }
  </code></pre>
  <p><strong>Parallax Rendering Function:</strong></p>
  <pre><code>double speed_09 = -2.0, speed_08 = -4.0, speed_07 = -8.0, speed_06 = -16.0;
  double speed_05 = -24.0, speed_04 = -32.0, speed_03 = -64.0, speed_02 = -96.0, speed_01 = -128.0;
  
  protected override void Update(double deltaTime)
  {
      background.Draw();
      layerDraw(forest_091, forest_092, speed_09 * deltaTime);
      layerDraw(forest_081, forest_082, speed_08 * deltaTime);
      layerDraw(forest_071, forest_072, speed_07 * deltaTime);
      layerDraw(forest_061, forest_062, speed_06 * deltaTime);
      layerDraw(particles051, particles052, speed_05 * deltaTime);
      layerDraw(forest_041, forest_042, speed_04 * deltaTime);
      layerDraw(particles_031, particles_032, speed_03 * deltaTime);
      layerDraw(bushes_021, bushes_022, speed_02 * deltaTime);
      layerDraw(mist_011, mist_012, speed_01 * deltaTime);
  }
  </code></pre>
  <p><strong>Outputting <code>forest_091</code> coordinates to the console:</strong></p>
  <pre><code>[11:31:07:32] (x,y): = (1902,7461329999965:0), deltaTime = 0,001391, speed = -0,002782
  [11:31:07:32] (x:y): = (1902,7430913999965:0), deltaTime = 0,0015208, speed = -0,0030416
  [11:31:07:32] (x:y): = (1902,7400399999965:0), deltaTime = 0,0015257, speed = -0,0030514
  [11:31:07:32] (x:y): = (1902,7370409999965:0), deltaTime = 0,0014995, speed = -0,002999
  [11:31:07:32] (x:y): = (1902,7339605999964:0), deltaTime = 0,0015402, speed = -0,0030804
  [11:31:07:33] (x:y): = (1902,7300727999964:0), deltaTime = 0,0019439, speed = -0,0038878
  [11:31:07:33] (x:y): = (1902,7271281999963:0), deltaTime = 0,0014723, speed = -0,0029446
  [11:31:07:33] (x:y): = (1902,7241953999962:0), deltaTime = 0,0014664, speed = -0,0029328
  [11:31:07:33] (x:y): = (1902,7212207999962:0), deltaTime = 0,0014873, speed = -0,0029746
  [11:31:07:33] (x:y): = (1902,7181395999962:0), deltaTime = 0,0015406, speed = -0,0030812
  [11:31:07:33] (x:y): = (1902,715346599996:0), deltaTime = 0,0013965, speed = -0,002793
  [11:31:07:33] (x:y): = (1902,712221399996:0), deltaTime = 0,0015626, speed = -0,0031252
  [11:31:07:34] (x:y): = (1902,709382799996:0), deltaTime = 0,0014193, speed = -0,0028386
  </code></pre>
  <p><a href="https://www.youtube.com/watch?v=kjoDMnizsdM" rel="nofollow noreferrer">Link to youtube with a demonstration of the problemы</a></p>
  <p>I use nectcore3.1, <a href="https://github.com/flibitijibibo/SDL2-CS" rel="nofollow noreferrer">SDL2-CS</a> и SDL2 v2.0.10.0</p>
  <p><strong>UPD: log deltaTime calculations:</strong></p>
  <pre><code>[08:48:34:25] now_counter = 1097310517365, last_counter = 1097310516099, now_counter-last_counter = 1266
  [08:48:34:25] now_counter = 1097310519141, last_counter = 1097310517365, now_counter-last_counter = 1776
  [08:48:34:25] now_counter = 1097310521406, last_counter = 1097310519141, now_counter-last_counter = 2265
  [08:48:34:25] now_counter = 1097310532746, last_counter = 1097310521406, now_counter-last_counter = 11340
  [08:48:34:25] now_counter = 1097310534069, last_counter = 1097310532746, now_counter-last_counter = 1323
  [08:48:34:25] now_counter = 1097310535356, last_counter = 1097310534069, now_counter-last_counter = 1287
  [08:48:34:25] now_counter = 1097310536628, last_counter = 1097310535356, now_counter-last_counter = 1272
  [08:48:34:25] now_counter = 1097310537897, last_counter = 1097310536628, now_counter-last_counter = 1269
  [08:48:34:25] now_counter = 1097310539169, last_counter = 1097310537897, now_counter-last_counter = 1272
  [08:48:34:25] now_counter = 1097310540441, last_counter = 1097310539169, now_counter-last_counter = 1272`
  </code></pre>