Jump to content
  • Advertisement

ROGRat

Member
  • Content Count

    21
  • Joined

  • Last visited

Community Reputation

7 Neutral

About ROGRat

  • Rank
    Member

Personal Information

  • Interests
    Programming

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Thanks very much, @MJP for that link, where I found this, which exports the font as a plain old Bitmap.
  2. Could someone please tell me what format the output from the DXTK Sprite Font tool is? I generated RGBA32 output, but it won't load with WIC and isn't recognized by TheGimp. Cheers --ROGRat
  3. ROGRat

    Fixed timestep glitch.

    Hey @lawnjelly, thanks very much for your comprehensive reply. With your input, and @L Spiro's, I've been able to eliminate all frame skip (at this point) in addition to having learned a number of things along the way. The source of the issue I was having with an apparent -Update/+Draw is situated between the chair I'm sitting on and the keyboard in front of me. I was expecting 2 draws for e very 1 update, completely forgetting the fact that the display-- just like every other -- isn't refreshing at exactly the value given in the DXGI. ModeDescription. I really wanted that 2:1 ratio though, so I wrote a routine that detects the exact refresh rate (144.028 in this case), and then adjusts the Update ticks accordingly (72.014). Pretty pointless and a bit OCD, but no magic number hack 🙂 I did this by passing in the calculated monitor refresh time instead of QPC elapsed ticks and added the "dozen or so ticks" I mentioned in my OP, arriving at the refresh value reported by DirectX. That's when I realized it was just drift I was experiencing. Aside from being really smooth, it reinforced what L. Spiro said about not passing 1 to the Interpolator: FrameT: 69431 Accumulate: 0 Alpha: 0 Draws: 2 Updates: 2 FrameT: 69431 Accumulate: 69444 Alpha: 0.5 Draws: 3 Updates: 2 FrameT: 69431 Accumulate: 0 Alpha: 0 Draws: 4 Updates: 3 FrameT: 69431 Accumulate: 69444 Alpha: 0.5 Draws: 5 Updates: 3 FrameT: 69431 Accumulate: 0 Alpha: 0 Draws: 6 Updates: 4 FrameT: 69431 Accumulate: 69444 Alpha: 0.5 Draws: 7 Updates: 4 Yes, yes and yes. My first attempt at logging destroyed my framerate straight up, so I switched to StreamWriter and it was fine. I've been running in a fullscreen window for the sake of being able to access breakpoints in the debugger. I tried connecting my desktop display to my laptop so I could see the debugger in fullscreen, but the DVI port only does 50Hz and my monitor doesn't. On the nVidia side, it's a lot smoother and sort of hides the problem at times.. Fullscreen mode is definitely smoother, but the two main causes behind large frame times were garbage collection and "cold start" costs imposed by JIT'ism. Because of the immutable nature of many DirectX objects, I'm having to create, use and dispose of these objects at runtime. Until I replace them with mutable/poolable alternatives I'm managing garbage collection manually to prevent the runtime from performing a full blocking/compacting collection. Thanks for the links, I will check them out later. I was calling GetRawInputBuffer() from Update() and the cost was showing up in the log. It's only 40 bytes/packet but it shows, and also has cold-start penalty. I tried pre-allocating a buffer and pinning it with a GCHandle but it didn't work, so I moved it onto another thread until I sort that out. My most allocated and longest lived type was System.String, from all the debug text I was displaying onscreen. The main problem is the DirectX objects being New'd and Disppsed at frame rate. Don't do that 😉
  4. ROGRat

    Fixed timestep glitch.

    Hey guys, thanks very much for taking the time to have a look at this, I appreciate it. @L Spiro -- I'm glad you mentioned the >= case because I put it on the backburner a while ago, thinking it was contributing to the frame skip but at the same time, saw that a potential Update() was being missed. I wasn't aware that passing in 1 was a problem and have the necessary adjustments, thanks for that. @lawnjelly I did as you suggested and added logging, something I really should have done earlier. Aside from revealing why I'm losing an Update every so often (in addition to L. Spiro's point above), it highlighted the timing issue you touched on. Up until now I had been displaying the Frametime onscreen at runtime, but it didn't show the large variations in that time which are directly responsible for frame loss. I kept tabs on the highest, lowest and average frame time over the course of a 10 minute run. For the sake of getting this timestep issue resolved, I've just got a couple of sprites moving back and forth, some debug text and have cut interpolation back to just a translate matrix. Highest was 510561, Lowest 8430 and Average 69426. I don't know whether the out-of-band values are the result of QPC misbehaving (it appears that the more frequently you call QPC, the larger the margin of error), or whether it's due to my thread being hoisted by the scheduler but I'm not familiar with people capping or shaping timer values beyond ensuring they're not zero or negative, as QPC does on some systems. Is there an established method for dealing with those values? The few remedies I did try (like substituting high and low values with the average frame time) just didn't feel right. Here's an excerpt from my log which shows why I'm missing updates when the interpolation factor is near 0. FrameT: 69191 Accumulate: 66888 Alpha: 0.4815967 Draws: 7329 Updates: 3663 FrameT: 68145 Accumulate: 135033 Alpha: 0.9722438 Draws: 7330 Updates: 3663 FrameT: 75063 Accumulate: 71208 Alpha: 0.5127009 Draws: 7331 Updates: 3664 FrameT: 68592 Accumulate: 912 Alpha: 0.006566442 Draws: 7332 Updates: 3665 FrameT: 65353 Accumulate: 66265 Alpha: 0.477111 Draws: 7333 Updates: 3665 FrameT: 72306 Accumulate: 138571 Alpha: 0.9977176 Draws: 7334 Updates: 3665 FrameT: 71837 Accumulate: 71520 Alpha: 0.5149473 Draws: 7335 Updates: 3666 FrameT: 66574 Accumulate: 138094 Alpha: 0.9942831 Draws: 7336 Updates: 3666 FrameT: 72569 Accumulate: 71775 Alpha: 0.5167833 Draws: 7337 Updates: 3667 FrameT: 63456 Accumulate: 135231 Alpha: 0.9736694 Draws: 7338 Updates: 3667 FrameT: 69301 Accumulate: 65644 Alpha: 0.4726398 Draws: 7339 Updates: 3668 FrameT: 74165 Accumulate: 921 Alpha: 0.006631243 Draws: 7340 Updates: 3669 FrameT: 66417 Accumulate: 67338 Alpha: 0.4848367 Draws: 7341 Updates: 3669 FrameT: 72052 Accumulate: 502 Alpha: 0.003614423 Draws: 7342 Updates: 3670 FrameT: 64242 Accumulate: 64744 Alpha: 0.4661598 Draws: 7343 Updates: 3670 FrameT: 69928 Accumulate: 134672 Alpha: 0.9696446 Draws: 7344 Updates: 3670 FrameT: 69455 Accumulate: 65239 Alpha: 0.4697238 Draws: 7345 Updates: 3671 FrameT: 74134 Accumulate: 485 Alpha: 0.003492022 Draws: 7346 Updates: 3672 FrameT: 67106 Accumulate: 67591 Alpha: 0.4866583 Draws: 7347 Updates: 3672 FrameT: 69110 Accumulate: 136701 Alpha: 0.9842535 Draws: 7348 Updates: 3672 FrameT: 73498 Accumulate: 71311 Alpha: 0.5134425 Draws: 7349 Updates: 3673 FrameT: 68647 Accumulate: 1070 Alpha: 0.007704049 Draws: 7350 Updates: 3674 FrameT: 67602 Accumulate: 68672 Alpha: 0.4944416 Draws: 7351 Updates: 3674 FrameT: 68210 Accumulate: 136882 Alpha: 0.9855567 Draws: 7352 Updates: 3674 FrameT: 73221 Accumulate: 71215 Alpha: 0.5127513 Draws: 7353 Updates: 3675 FrameT: 65159 Accumulate: 136374 Alpha: 0.9818991 Draws: 7354 Updates: 3675 FrameT: 74278 Accumulate: 71764 Alpha: 0.5167041 Draws: 7355 Updates: 3676 FrameT: 64930 Accumulate: 136694 Alpha: 0.9842031 Draws: 7356 Updates: 3676 FrameT: 67312 Accumulate: 65118 Alpha: 0.4688526 Draws: 7357 Updates: 3677 FrameT: 74949 Accumulate: 1179 Alpha: 0.008488854 Draws: 7358 Updates: 3678 FrameT: 67914 Accumulate: 69093 Alpha: 0.4974728 Draws: 7359 Updates: 3678 FrameT: 67283 Accumulate: 136376 Alpha: 0.9819135 Draws: 7360 Updates: 3678 FrameT: 75550 Accumulate: 73038 Alpha: 0.5258769 Draws: 7361 Updates: 3679 FrameT: 62028 Accumulate: 135066 Alpha: 0.9724814 Draws: 7362 Updates: 3679 FrameT: 72305 Accumulate: 68483 Alpha: 0.4930808 Draws: 7363 Updates: 3680 FrameT: 64991 Accumulate: 133474 Alpha: 0.961019 Draws: 7364 Updates: 3680 FrameT: 75387 Accumulate: 69973 Alpha: 0.5038088 Draws: 7365 Updates: 3681 FrameT: 69627 Accumulate: 712 Alpha: 0.005126433 Draws: 7366 Updates: 3682 FrameT: 64625 Accumulate: 65337 Alpha: 0.4704294 Draws: 7367 Updates: 3682 FrameT: 70444 Accumulate: 135781 Alpha: 0.9776295 Draws: 7368 Updates: 3682 FrameT: 74890 Accumulate: 71783 Alpha: 0.5168409 Draws: 7369 Updates: 3683 FrameT: 62398 Accumulate: 134181 Alpha: 0.9661094 Draws: 7370 Updates: 3683 FrameT: 73042 Accumulate: 68335 Alpha: 0.4920152 Draws: 7371 Updates: 3684 FrameT: 65401 Accumulate: 133736 Alpha: 0.9629053 Draws: 7372 Updates: 3684 FrameT: 72797 Accumulate: 67645 Alpha: 0.4870471 Draws: 7373 Updates: 3685 FrameT: 69771 Accumulate: 137416 Alpha: 0.9894015 Draws: 7374 Updates: 3685 FrameT: 65935 Accumulate: 64463 Alpha: 0.4641366 Draws: 7375 Updates: 3686 FrameT: 72778 Accumulate: 137241 Alpha: 0.9881415 Draws: 7376 Updates: 3686 FrameT: 72291 Accumulate: 70644 Alpha: 0.5086401 Draws: 7377 Updates: 3687 FrameT: 64307 Accumulate: 134951 Alpha: 0.9716534 Draws: 7378 Updates: 3687 FrameT: 74957 Accumulate: 71020 Alpha: 0.5113473 Draws: 7379 Updates: 3688 FrameT: 68318 Accumulate: 450 Alpha: 0.003240021 Draws: 7380 Updates: 3689 FrameT: 60889 Accumulate: 61339 Alpha: 0.4416436 Draws: 7381 Updates: 3689 FrameT: 79307 Accumulate: 1758 Alpha: 0.01265768 Draws: 7382 Updates: 3690 FrameT: 68708 Accumulate: 70466 Alpha: 0.5073584 Draws: 7383 Updates: 3690 FrameT: 62191 Accumulate: 132657 Alpha: 0.9551365 Draws: 7384 Updates: 3690 FrameT: 76123 Accumulate: 69892 Alpha: 0.5032256 Draws: 7385 Updates: 3691 FrameT: 69184 Accumulate: 188 Alpha: 0.001353609 Draws: 7386 Updates: 3692 FrameT: 66033 Accumulate: 66221 Alpha: 0.4767942 Draws: 7387 Updates: 3692 FrameT: 66812 Accumulate: 133033 Alpha: 0.9578437 Draws: 7388 Updates: 3692 ...back to business as usual. FrameT: 70594 Accumulate: 64739 Alpha: 0.4661238 Draws: 7389 Updates: 3693 FrameT: 68693 Accumulate: 133432 Alpha: 0.9607165 Draws: 7390 Updates: 3693 FrameT: 70182 Accumulate: 64726 Alpha: 0.4660302 Draws: 7391 Updates: 3694 FrameT: 74161 Accumulate: 138887 Alpha: 0.9999928 Draws: 7392 Updates: 3694 FrameT: 67348 Accumulate: 67347 Alpha: 0.4849015 Draws: 7393 Updates: 3695 FrameT: 70203 Accumulate: 137550 Alpha: 0.9903663 Draws: 7394 Updates: 3695 FrameT: 71742 Accumulate: 70404 Alpha: 0.5069121 Draws: 7395 Updates: 3696 FrameT: 64437 Accumulate: 134841 Alpha: 0.9708614 Draws: 7396 Updates: 3696 FrameT: 74511 Accumulate: 70464 Alpha: 0.5073441 Draws: 7397 Updates: 3697 FrameT: 62325 Accumulate: 132789 Alpha: 0.9560869 Draws: 7398 Updates: 3697 FrameT: 77072 Accumulate: 70973 Alpha: 0.5110089 Draws: 7399 Updates: 3698 FrameT: 65231 Accumulate: 136204 Alpha: 0.9806751 Draws: 7400 Updates: 3698 FrameT: 73219 Accumulate: 70535 Alpha: 0.5078552 Draws: 7401 Updates: 3699 FrameT: 68335 Accumulate: 138870 Alpha: 0.9998704 Draws: 7402 Updates: 3699 FrameT: 66736 Accumulate: 66718 Alpha: 0.4803727 Draws: 7403 Updates: 3700 FrameT: 65417 Accumulate: 132135 Alpha: 0.9513781 Draws: 7404 Updates: 3700 FrameT: 76962 Accumulate: 70209 Alpha: 0.505508 Draws: 7405 Updates: 3701 FrameT: 68379 Accumulate: 138588 Alpha: 0.99784 Draws: 7406 Updates: 3701 FrameT: 63899 Accumulate: 63599 Alpha: 0.4579157 Draws: 7407 Updates: 3702 FrameT: 74913 Accumulate: 138512 Alpha: 0.9972928 Draws: 7408 Updates: 3702 FrameT: 67676 Accumulate: 67300 Alpha: 0.4845631 Draws: 7409 Updates: 3703 FrameT: 64634 Accumulate: 131934 Alpha: 0.9499309 Draws: 7410 Updates: 3703 The accumulation variable eventually arrives at a point where it's so low that it takes 3 iterations to bring it back up to the >= required for an update. If the timer is especially erratic at this time, interpolation jumps back and forth from close to 0 to close to 1 and movement jitters significantly. By trimming slightly less than 138888 from the accumulator after update, it can be made to occur less often but it's just another magic number hack that is only delaying the inevitable. Interestingly, I've been using an Intel adaptor in this system because I wanted the lower end as a yardstick. Switching briefly to the nVidia adaptor, with G-Sync enabled, the above issue doesn't occur at all. Now that I've got logging, I'm keen to work out how it does it. Any further thoughts on this would be gratefully received. 🙂 Thanks again for your input. --ROGRat
  5. After reading Mr. @L Spiro's excellent article on fixed timestep, I was convinced that my own implementation was incorrect, suffering from very occasional frame skip. After looking closely at the numbers, I found one source of frame skip. Every 40 seconds or so, I get an extra Draw(), at the cost of an Update(). It occurs when the interpolation factor approaches 1. It's not always visually discernible. I'm drawing at 144Hz (not ideal, but the only refresh rate supported by this display) and my update rate is set to half that. Adding a dozen or so ticks to the accumulated time each loop iteration fixes the issue, but that's not a fix.. it's a hack. FrameTime = Timer.ElapsedQPCTicks; Accumulation += FrameTime; // // Update // while (Accumulation > UpdateTicks) // @72hz UpdateTicks = 138888 { Update(UpdateTicks); Accumulation -= UpdateTicks; } // // Draw // Draw(Accumulation / (double)UpdateTicks); Swapchain.TryPresent(DXGI.PresentFlags.None, 0, null); Values shown are Int64/Long Any guidance would be gratefully received. Thanks in advance. 🙂 -- ROGRat
  6. @datboi By overlay you mean something like Overwolf?
  7. Timing on Windows is something that I’ve invested a bit of (no pun intended) time in, and I can offer the following advice based on my own research and experiments: Use QueryPerformanceCounter. TimeGetTime/GetTimeOfDay etc will not provide the same resolution when called from a loop. All the major Windows game titles on the market use QPC and have for a long time. As mentioned above, WM_TIMER is not suitable either. There are processors out in the field right now, manufactured as recently as 2014, that have problems with QPC if Windows scheduler moves the calling thread to another processor core as its being called. You can work around this, as many major titles do, by calling QPC from a separate thread which has affinity to one processor core. Calling QPC from multiple threads simultaneously on these processors can also lead to problems. QPC is a cheap call, and calling it 60,120,240 times a second shouldn’t negatively impact your applications performance. How many times per second is your inner loop running? The question you need to ask is, “What’s the slowest rate I can run that loop before it negatively effects my game?”
  8. I’m just wondering why you’re using DirectX 9? I don’t mean that as a criticism, because it’s your business what API you use. If you can run your code on DirectX 10 or higher, you have the option of using DirectWrite. It’s fast, easy to get a handle on and frees you up from having to worry about styles, formatting and so on. In the event that it’s not fast enough for your needs, you can use it to render your glyphs to an offscreen surface and you can then copy characters from that surface at your leisure, but with DirectWrite providing hints about the individual character bounds.
  9. ROGRat

    10hz Smoothness

    Thanks for your comments, guys. Much appreciated! 🙂 I just realized that the video I uploaded was of the debug build, so it's choppier than it could have been. Never mind, so long as people get the idea. This is something I should have done years ago. I'm really impressed with the playoff for the small amount of time invested. It's not that I ever doubted it or thought that somehow my own hacks would be better, not at all. I just get distracted with a squillion other things. It's no surprise that the Gaffer implementation is nearly the de facto standard for game loops and if there was an established repertoire of programming patterns for game authorship, I'd reckon it'd be right up there. It's hard to fault and the frame rate gain is nothing to shrug at. After going through the paces of adding a Gafferesque core loop and states, I can see why people either have difficulty putting it in their code or come away unsure as to whether they've done it correctly. Wrapping the whole shebang in a state structure makes for easier repeat visits. I cheated a bit and used the structures and methods in the System.Numerics namespace which are conveniently SIMD/SSE enabled. I've no doubt there are others, but I'm thinking of writing a top-down guide with all the nitty gritty details. Speaking of which -- what's going on with the variable t I have seen in some bare-bone use-cases? :-)
  10. ROGRat

    10hz Smoothness

    The old fixed timestep paradigm. Juicy stuff, and arguably the backbone of many a great gaming title Until recently, I'd not bothered to implement it, instead preferring my own questionable potpourri of odds and ends, with maybe a little vertical blanking here and there. I checked out DeWitter's game loop many moons ago but, in spite of it's virtues, it wasn't what I needed at the time. After being asked by a work-colleage-turned-indy-gamedev guy for a rundown on how it worked, what the point of it is and 'what the hell is this interpolation business?', I cobbled together a little demo. In this video, everything is updated at a fixed timestep (60hz) however, only the colored bar is rendered with interpolated state. At about the halfway mark, after throwing some white boxes onto the screen, I switch down to a 10hz update rate (after a brief pause). Thanks to interpolation,, the colored bar retains most of it's smooth movement, even at `10hz. The white boxes, with all their physics goodness, don't fare well at 10hz without interpolation. I haven't implemented it there because I'm honestly not sure how.. https://www.youtube.com/watch?v=6SptavKHwew&feature=youtu.be
  11. ROGRat

    Diablo:Immortal

    Blizzard lost their way a few years ago. World of Warcraft has lost most of its appeal and although Diablo 3 was fun for a short while, it’s a huge departure from the near perfection that was Diablo 2. If Blizzard want to retain their existing customer base and see continued growth, all the need to do is go back to their roots. What they’re doing in this day and age just doesn’t work.
  12. It’s not pointless at all. Assigning your main thread to a single core, just to sidestep threading, is not just a code smell, it’s a train wreck. If another process comes along and does the same thing, and uses more CPU time than your application can tolerate, it will either perform poorly or stop responding entirely. Until that other process starts behaving. If ever. If the same thing happened with my implementation, even if two processes came along and specifically targeted the core my main thread was running on and the core my timer was running on, not only would my application continue to run (since it’s free to be scheduled on another, less utilised, core) but my timer almost certainly would too. :+D
  13. I wish it was a problem that was confined to a handful of old processors that you rarely see in the wild anymore. On some systems the problem is in the BIOS and keep in mind that the vast majority of users never update their BIOS. The problem is made worse by the fact that most mainboard and system vendors do not provide an automated update path for it. Implementing a timer using the method I suggested (on Windows) not only ensures an accurate timer across a broad range of systems, it also minimises scalability problems down the track. If your application/game has multiple threads on multiple cores and needs to call QueryPerformance from those threads, the resulting performance hit from doing so compared to calling it from a single dedicated thread is too high to ignore. The so-called cost and complexity of threads, aside from being negligible in this context, is something that needs to be confronted and accepted in the era of many-core processors. In this case, the small investment of time and effort goes a long way. 🙂 Edit/Note: For C#/.Net Framework, call Stopwatch.GetTimestamp. It calls QueryPerformanceCounter internally. Do NOT use DateTime.Now or Environment.TickCount.
  14. On Windows, you should create a timer using QueryPerformanceCounter on a separate thread (not a worker thread) and lock that threads affinity to one processor core (NumberOfCores- 1 is a good choice, but using the same core as the games main thread is fine also). If the timer thread is allowed to transition between cores, QueryPerformanceCounter will return imprecise values on some systems.
  15. ROGRat

    What are the principles of game design?

    Goals are usually accompanied by Reward, something that provides incentive for working toward said goal. That little dopamine hit that we’ve all gone scrambling for!
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!