English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. C/C++

 
 
Thread Tools
Old 11 August 2024, 03:01   #1
NovaCoder
Registered User
 
NovaCoder's Avatar
 
Join Date: Sep 2007
Location: Melbourne/Australia
Posts: 4,455
Getting millisecond elapsed time using Bebbo's Amiga GCC toolchain

Hi,

Wonder if anyone can help.

I need a fast way to tack how much time has passed in my main loop.

I normally use https://www.tutorialspoint.com/c_sta...tion_clock.htm which has a high frequency on the old GCC 3.4 (milliseconds) but in my Bebbo build it seems to be limited to 50fps.

I've hacked together the following code but I'm not really sure it's the best way to do it.

Code:
	gettimeofday(&lastFrameTime, NULL);

	while (true) {
		gettimeofday(&currentFrameTime, NULL);

	    double elapsedtime = (currentFrameTime.tv_sec - lastFrameTime.tv_sec) * 1000.0;      // sec to ms
	    elapsedtime += (currentFrameTime.tv_usec - lastFrameTime.tv_usec) / 1000.0;          // us to ms


		// Capture user input and update object positions.
		g_world->updateWorld(elapsedtime / 1000);
		g_world->updateFrame();


		memcpy(&lastFrameTime, &currentFrameTime, sizeof(struct timeval));
	}

Thanks
NovaCoder is offline  
Old 11 August 2024, 21:51   #2
Ernst Blofeld
<optimized out>
 
Ernst Blofeld's Avatar
 
Join Date: Sep 2020
Location: <optimized out>
Posts: 323
I don't have a lot of context, but if you're limited to 50 fps, maybe you're using the wrong CIA timer in your gettimeofday?

Anyway, in case it helps, this is how I do it, having taken over the system:

Code:
ULONG GetCurrentTime(void) {
    ULONG hi = ciab->ciatodhi;		// latch activated
    ULONG mid = ciab->ciatodmid;
    ULONG low = ciab->ciatodlow;	// latch deactivated

    return ((hi << 8 | mid) << 8 | low) << 8;
}
Code:
    Display_Show();

    game.isPaused = FALSE;

    ULONG previousTime = GetCurrentTime();
    ULONG lag = 0;

    Game_UpdateDisplay();

    for (;;) {
        ULONG currentTime = GetCurrentTime();
        ULONG elapsedTime = currentTime - previousTime;
        ULONG fps = (256 * CLOCK_CONSTANT) / (227 * elapsedTime);
        previousTime = currentTime;
        lag += elapsedTime;

        ULONG ticks = 0;
        while (lag >= TIMER_INCREMENT_PER_SIMULATION_STEP) {
            Game_ProcessUserInput();
			
            if (game.exitRequested)
                break;

                Game_UpdateState();
                lag -= TIMER_INCREMENT_PER_SIMULATION_STEP;
                ticks++;
            }
		
            if (game.exitRequested)
                break;

            Game_UpdateDisplay();

            TinyText_MoveTo(80 - 8, 0);
            TinyText_PrintFormatted("%4ld fps", fps);

            TinyText_MoveTo(80 - 8, 1);
            TinyText_PrintFormatted("%4ld ipf", ticks);
    }

    game.isPaused = TRUE;

    PrintStatistics();
with this bit when I take over the system:

Code:
    ciab->ciacrb &= ~CIACRBF_ALARM;
Ernst Blofeld is offline  
Old 11 August 2024, 22:22   #3
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,451
Quote:
Originally Posted by NovaCoder View Post
I need a fast way to tack how much time has passed in my main loop.
Is that on Amiga or on *ix? On *ix, you have the clock function (as stated) with various clocks offering different resolutions. You also have gettimeofday() which has milliseconds precision which is in most cases precise enough. On Amiga, you have the ReadEClock() function of the timer.device (see its autodocs), which offers milliseconds resolution, though is not very precise over a very long time.



No, I do not recommend accessing the hardware. Reading the E-clock has certain quirks the Os takes care of.
Thomas Richter is offline  
Old Yesterday, 00:29   #4
NovaCoder
Registered User
 
NovaCoder's Avatar
 
Join Date: Sep 2007
Location: Melbourne/Australia
Posts: 4,455
Quote:
Originally Posted by Thomas Richter View Post
Is that on Amiga or on *ix? On *ix, you have the clock function (as stated) with various clocks offering different resolutions. You also have gettimeofday() which has milliseconds precision which is in most cases precise enough. On Amiga, you have the ReadEClock() function of the timer.device (see its autodocs), which offers milliseconds resolution, though is not very precise over a very long time.

No, I do not recommend accessing the hardware. Reading the E-clock has certain quirks the Os takes care of.

Yep, I'm currently using gettimeofday (see my code snippet above).

I would prefer to just use clock() but the resolution is too low with GCC 6.5 (at least in this build, not sure if it can be changed?).

My code works fine, just looks messy and maybe slow.

I don't want to take over the system, I normally try and write OS friendly code if possible.

I did have a quick look at eclock, I'll try and get it working.

I'm only interested in elapsed time per frame so it doesn't matter if it looses a few seconds over time.


Do you have to do any setup code to use ReadEClock()?


LoL, turns out that I was asking about this 13 years ago

https://eab.abime.net/showthread.php?t=59705

This is also helpful..

https://eab.abime.net/showthread.php?t=59869

Last edited by NovaCoder; Yesterday at 01:55.
NovaCoder is offline  
Old Yesterday, 08:49   #5
Thorham
Computer Nerd
 
Thorham's Avatar
 
Join Date: Sep 2007
Location: Rotterdam/Netherlands
Age: 48
Posts: 3,945
Quote:
Originally Posted by Thomas Richter View Post
On Amiga, you have the ReadEClock() function of the timer.device (see its autodocs), which offers milliseconds resolution, though is not very precise over a very long time.
Why not use a Time Request and get microseconds?
Thorham is online now  
Old Yesterday, 10:34   #6
bebbo
bye
 
Join Date: Jun 2016
Location: Some / Where
Posts: 690
Quote:
Originally Posted by NovaCoder View Post
Hi,

Wonder if anyone can help.

I need a fast way to tack how much time has passed in my main loop.

I normally use https://www.tutorialspoint.com/c_sta...tion_clock.htm which has a high frequency on the old GCC 3.4 (milliseconds) but in my Bebbo build it seems to be limited to 50fps.

I've hacked together the following code but I'm not really sure it's the best way to do it.

Code:
    gettimeofday(&lastFrameTime, NULL);

    while (true) {
        gettimeofday(&currentFrameTime, NULL);

        double elapsedtime = (currentFrameTime.tv_sec - lastFrameTime.tv_sec) * 1000.0;      // sec to ms
        elapsedtime += (currentFrameTime.tv_usec - lastFrameTime.tv_usec) / 1000.0;          // us to ms


        // Capture user input and update object positions.
        g_world->updateWorld(elapsedtime / 1000);
        g_world->updateFrame();


        memcpy(&lastFrameTime, &currentFrameTime, sizeof(struct timeval));
    }
Thanks



luckily I stumbled across this thread... Better raise an issue at https://github.com/bebbo/libnix/issues - this warrants that I read it, but not that I fix/change it^^



I changed clock() to use ReadEClock() instead of DateStamp(). Should be live soon.
bebbo is offline  
Old Yesterday, 10:37   #7
bebbo
bye
 
Join Date: Jun 2016
Location: Some / Where
Posts: 690
Quote:
Originally Posted by NovaCoder View Post
Do you have to do any setup code to use ReadEClock()?

You don't need any setup code to call timer functions. Just steal the pointer from DOSBase:


Code:
    struct Device * TimerBase = DOSBase->dl_TimeReq->tr_node.io_Device;
    ReadEClock(&eval0);
bebbo is offline  
Old Yesterday, 10:49   #8
hooverphonique
ex. demoscener "Bigmama"
 
Join Date: Jun 2012
Location: Fyn / Denmark
Posts: 1,659
Isn't clock() supposed to give you the cpu time used by the current process (I.e. not absolute time) ?
hooverphonique is offline  
Old Yesterday, 10:58   #9
NovaCoder
Registered User
 
NovaCoder's Avatar
 
Join Date: Sep 2007
Location: Melbourne/Australia
Posts: 4,455
Quote:
Originally Posted by bebbo View Post
luckily I stumbled across this thread... Better raise an issue at https://github.com/bebbo/libnix/issues - this warrants that I read it, but not that I fix/change it^^



I changed clock() to use ReadEClock() instead of DateStamp(). Should be live soon.
Great, thanks for that mate
NovaCoder is offline  
Old Yesterday, 11:14   #10
bebbo
bye
 
Join Date: Jun 2016
Location: Some / Where
Posts: 690
Quote:
Originally Posted by hooverphonique View Post
Isn't clock() supposed to give you the cpu time used by the current process (I.e. not absolute time) ?

"The clock() function shall return the implementation's best approximation to the processor time used by the process since the beginning of an implementation-defined era related only to the process invocation."


...
"In order to measure the time spent in a program, clock() should be called at the start of the program and its return value subtracted from the value returned by subsequent calls."


The time spent is not the used cpu time.
bebbo is offline  
Old Yesterday, 11:24   #11
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,451
Quote:
Originally Posted by bebbo View Post
You don't need any setup code to call timer functions. Just steal the pointer from DOSBase:


Code:
    struct Device * TimerBase = DOSBase->dl_TimeReq->tr_node.io_Device;
    ReadEClock(&eval0);
Argh! This is a dos private! It is not guaranteed to stay there. OpenDevice() is not asking for too much.
Thomas Richter is offline  
Old Yesterday, 11:28   #12
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,451
Quote:
Originally Posted by bebbo View Post
I changed clock() to use ReadEClock() instead of DateStamp(). Should be live soon.
Note that ReadEClock() is not long-time stable, but DateStamp() is, so it depends on what you want to do. If clock resolution matters, there is also clock_gettime(), which offers multiple clocks. One could be mapped to the E-clock, others to the system (VBI-based) clock.
Thomas Richter is offline  
Old Yesterday, 12:08   #13
bebbo
bye
 
Join Date: Jun 2016
Location: Some / Where
Posts: 690
Quote:
Originally Posted by Thomas Richter View Post
Argh! This is a dos private! It is not guaranteed to stay there. OpenDevice() is not asking for too much.

I followed these docs
bebbo is offline  
Old Yesterday, 13:28   #14
hooverphonique
ex. demoscener "Bigmama"
 
Join Date: Jun 2012
Location: Fyn / Denmark
Posts: 1,659
Quote:
Originally Posted by bebbo View Post
"The clock() function shall return the implementation's best approximation to the processor time used by the process since the beginning of an implementation-defined era related only to the process invocation."

...
"In order to measure the time spent in a program, clock() should be called at the start of the program and its return value subtracted from the value returned by subsequent calls."

The time spent is not the used cpu time.
You said it yourself: "processor time used by the process"

Also: https://www.gnu.org/software/libc/ma.../CPU-Time.html


I was just asking in order to make people aware of the distinction between cpu time spent by the process and wall time regarding the clock() function. What you do with your c library is up to you, of course.
hooverphonique is offline  
Old Yesterday, 14:01   #15
Thomas Richter
Registered User
 
Join Date: Jan 2019
Location: Germany
Posts: 3,451
Unfortunately AmigaOs has nothing to offer in terms of processor time. Since the OP used gettimeofday() which supplies wall-clock time, ReadEClock() is probably the best viable option for microsecond resolution.
Thomas Richter is offline  
Old Yesterday, 14:29   #16
bebbo
bye
 
Join Date: Jun 2016
Location: Some / Where
Posts: 690
Quote:
Originally Posted by hooverphonique View Post
You said it yourself: "processor time used by the process"

Also: https://www.gnu.org/software/libc/ma.../CPU-Time.html


I was just asking in order to make people aware of the distinction between cpu time spent by the process and wall time regarding the clock() function. What you do with your c library is up to you, of course.

I cited.


And it also reads: "the implementation's best approximation".


Right now this is the best approximation. Feel free to provide a better one.
bebbo is offline  
Old Yesterday, 17:22   #17
phx
Natteravn
 
phx's Avatar
 
Join Date: Nov 2009
Location: Herford / Germany
Posts: 2,570
Quote:
Originally Posted by Thomas Richter View Post
Since the OP used gettimeofday() which supplies wall-clock time, ReadEClock() is probably the best viable option for microsecond resolution.
True. I also doubt that process-time (clock()) makes sense in the OP's game context.

But gettimeofday() is no portable ISO-C. Before calling emulated POSIX or BSD functions you can also call Amiga-functions with much less overhead.

Quote:
Originally Posted by bebbo View Post
Right now this is the best approximation.
It unfortunately is. And such a function should be in the OS. Maybe the OS3.3-team reads this...
I remember I implemented a real clock() in vclib for MorphOS, because the OS provides it, which is quite nice:
Code:
clock_t clock(void)
{
  UQUAD ticks,cputime;

  NewGetSystemAttrs(&ticks,sizeof(ticks),SYSTEMINFOTYPE_LASTSECTICKS,TAG_DONE);
  NewGetTaskAttrs(FindTask(NULL),&cputime,sizeof(cputime),
                  TASKINFOTYPE_CPUTIME,TAG_DONE);
  return (clock_t)((CLOCKS_PER_SEC * cputime) / ticks);
}
phx is offline  
Old Yesterday, 18:49   #18
paraj
Registered User
 
paraj's Avatar
 
Join Date: Feb 2017
Location: Denmark
Posts: 1,289
FWIW even latest MSVC on Windows (probably due to backwards compatibility) also returns wall clock time since the process started (not the time used by the process) for clock(), and in DOS days it used the 18.2Hz timer so precision was not great.

Like phx mentions it very likely wouldn't make sense to use process time for OPs case. Don't think I've ever head of a game (intentionally) timed to the process-time..

Doesn't seem like there's any reason to overthink it. Just use EClock. I'm very sure the marginal drift is negligible. If not, then tie you timing to something that does matter (like audio playback).

Use is pretty, simple, basic template:
Code:
struct Device* TimerBase;
static struct timerequest TimerRequest;
static uint32_t TimerRate;

uint64_t TimerRead(void)
{
    struct EClockVal val;
    ReadEClock(&val);
    return (uint64_t)val.ev_hi << 32 | val.ev_lo;
}

..

    /* Init */
    if (OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)&TimerRequest, 0L))   
        /* Should not happen... */
    TimerBase = TimerRequest.tr_node.io_Device;
    struct EClockVal val;
    TimerRate = ReadEClock(&val);

    /* Use TimerRead here..*/

    /* Cleanup */
    CloseDevice((struct IORequest*)&TimerRequest);
paraj is offline  
Old Yesterday, 20:39   #19
Olaf Barthel
Registered User
 
Join Date: Aug 2010
Location: Germany
Posts: 540
Here is some code (proably not too complete) which I'm using to converting the EClock timer value into a reference time which increases monotonously. You need gcc or any other compiler which supports 64 bit integers to make it work.

Code:
#include <proto/timer.h>
#include <proto/exec.h>

#include <string.h>

#define OK 0

ULONG EClockFrequency;
unsigned long long EClockReference;
ULONG EClockMicrosecondFactor;

struct timerequest TimerRequest;
struct Library * TimerBase;

BOOL
init_time_elapsed(void)
{
	struct EClockVal eclock_reference;
	BOOL successful = FALSE;

	memset(&TimerRequest, 0, sizeof(TimerRequest));

	if (OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)&TimerRequest, 0) == OK)
	{
		TimerBase = TimerRequest.tr_node.io_Device;

		EClockFrequency = ReadEClock(&eclock_reference);

		EClockReference = (((unsigned long long)eclock_reference.ev_hi) << 32) | eclock_reference.ev_lo;

		EClockMicrosecondFactor = 65536ULL * 1000000ULL / EClockFrequency;
		
		successful = TRUE;
	}

	return successful;
}

void
get_time_elapsed(struct timeval * time_elapsed_ptr)
{
	struct EClockVal now;
	unsigned long long time_elapsed;
	unsigned long long eclock_elapsed;
	ULONG seconds_remainder;
	unsigned long long microseconds;

	ReadEClock(&now);
	eclock_now = (((unsigned long long)now.ev_hi) << 32) | now.ev_lo;

	/* How many EClock ticks occurred since the reference point? */
	eclock_elapsed = eclock_now - EClockReference;

	/* Turn the number of EClock ticks into the number of seconds
	 * and a remainder value which still needs to be normalized
	 * in order to yield the proper number of microseconds.
	 */
	time_elapsed_ptr->tv_secs = eclock_elapsed / EClockFrequency;
	seconds_remainder = eclock_elapsed % EClockFrequency;

	/* The remainder of the EClock frequency division needs to be
	 * normalized so that it comes out as the number of microseconds.
	 * The conversion is performed by multiplying with a precalculated
	 * factor which has been premultiplied by 65536. That scaling
	 * needs to be taken out of the end result and does not require a division.
	 */
	microseconds = seconds_remainder * EClockMicrosecondFactor;

	time_elapsed_ptr->tv_micro = microseconds >> 16;

	/* Paranoia: make sure that the output is really "normal". */
	while (time_elapsed_ptr->tv_micro > 1000000)
	{
		time_elapsed_ptr->tv_secs++;

		time_elapsed_ptr->tv_micro -= 1000000;
	}
}

Last edited by Olaf Barthel; Today at 09:46. Reason: Inevitable code corrections...
Olaf Barthel is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
Setting up a debugging environment for Bebbo's Amiga GCC toolchain Nightfox Coders. C/C++ 3 12 July 2024 19:48
GCC 6.2 toolchain for AmigaOS 3 Samurai_Crow Coders. C/C++ 1555 01 May 2024 19:56
Can't make bebbo's amiga-gcc work Bren McGuire Coders. General 18 12 June 2023 01:09
bebbo gcc+eclipse problem Raislin77it Coders. C/C++ 1 15 February 2022 08:37
gcc-bebbo: argc is always 0 sparhawk Coders. C/C++ 8 31 January 2021 18:42

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 17:22.

Top

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Page generated in 0.10107 seconds with 15 queries