PDA

View Full Version : Lazarus port of Tom Nuydens "Mr. Potato Head" GLSL demo using modern GL functionality



Akira13
29-05-2017, 12:24 AM
Full project folder is in the zip file I've attached. In case you're wondering, the "GLMath" unit I used to replace the fixed-function matrix API is a modified version of a unit by Chris Rorden (author of SurfIce, e.t.c). Note that you will obviously need an OpenGL 3.3+ capable GPU to run this, and it's also unfortunately Windows-only. (Not to be harsh but it's kind of Linuxes/OSXes fault for not having anything remotely similar to QueryPerformanceCounter. Seems like something you'd add early on in the development process...) Anways, felt like this might serve as a decent "modern OpenGL" tutorial, so I thought I'd post it here. Enjoy, (or not!)

Thyandyr
29-05-2017, 08:25 AM
Thank you, will look at it tonight when I'm home.

Chebmaster
09-06-2017, 11:48 PM
for not having anything remotely similar to QueryPerformanceCounter.
*sigh*
RDTSC has *much* better precision (about 30 nanoseconds) while performance counter on most systems is bastardized timestamp counter (for compatibility purposes I assume) and is rounded up to granulation of about 400 nanoseconds.
Any CPU today outside of museum range has TSC invariancy meaning yes, you *can* use the counter safely, the frequency is stable and counyer is the same for all cores.

{$ifdef cpuarm}
function UsecDelta (ptsc: pqword): double; cdecl; //allegedly valid ANY Linux and ANY non-x86 CPU
var v: timeval;
begin
v:= ptimeval(ptsc)^;
fpgettimeofday(ptimeval(ptsc), NIL); //one microsecond resolution
{$push}
{$overflowchecks off}
{$rangechecks off}
Result:=
1000000.0 * longint(ptimeval(ptsc)^.tv_sec - v.tv_sec)
+ 1.0 * longint(ptimeval(ptsc)^.tv_usec - v.tv_usec);
{$pop}
if Result < 0.0 then Result:= 0.0;
end;
{$endif}

{$ifdef CPUX86_64}
function UsecDelta (ptsc: pqword): double; cdecl;
var q: qword;
begin
q:= ptsc^;
asm
rdtsc
shl rdx, 32
or rdx, rax
mov rax, qword[ptsc]
mov qword[rax], rdx
end ['rax', 'rdx'];
if q >= ptsc^ then q:=ptsc^ - 1;
Result:= (ptsc^ - q) * Mother^.Timer.RDTSCFactor;
end;
{$endif}

{$ifdef CPU386}
function UsecDelta (ptsc: pqword): double; cdecl;
var q: qword;
begin
q:= ptsc^;
asm
mov ecx, ptsc
rdtsc
mov [ecx], eax
mov [ecx + 4], edx
end ['ecx', 'eax', 'edx'];
if q >= ptsc^ then q:=ptsc^ - 1;
Result:= (ptsc^ - q) * Mother^.Timer.RDTSCFactor;
end;
{$endif}

{$ifndef cpuarm}
var
_PrevTscCheckSec: double = 0;
_PrevTsc: qword;
_tsc: qword;
function RecheckTSCFreq(period: double = TSCMeasuringInterval): longbool;
var
sec: double;
begin
try
sec:= Second();
if (sec - _PrevTscCheckSec) < period then Exit(false);
_tsc:= GetTimeStamp();
Result:= true;
if _tsc <= _prevTsc then Result:= false;
if Result then begin
Mother^.Timer.RDTSCFrequency:= (_tsc - _prevTSC) / (sec - _prevTscCheckSec);
Mother^.Timer.RdtscFactor:= 1000000.0 / Math.max(1.0, Mother^.Timer.RdtscFrequency);
end;
_prevTSC:= _tsc;
_PrevTscCheckSec:= sec;
except end;
end;
{$endif}

function GetTimeStamp: qword;
//is *also* used for hash generation, so requirement obligatory increase is vital
{$ifdef cpuarm}
var v: timeval;
begin
{$push}
{$overflowchecks off}
{$rangechecks off}
fpgettimeofday(@v, NIL); //one microsecond resolution
Result:= qword((int64(1000000) * int64(v.tv_sec)) + int64(v.tv_usec));
{$pop}
{$else cpuarm}
begin
{$ifdef cpu64}
//ONLY the rax register is free to access memory at addresses above 4Gb freely!
asm
rdtsc
shl rdx, 32
or rax, rdx
mov qword[Result], rax
end ['rax', 'rdx'];
{$else}
asm
rdtsc
mov dword[Result], eax
mov dword[Result + 4], edx
end ['eax', 'edx'];
{$endif}
{$endif cpuarm}
if Result <= PrevTimeStamp then begin
Result:=PrevTimeStamp + 1;
inc(PrevTimeStamp);
end
else
PrevTimeStamp:=Result;
end;

Akira13
11-06-2017, 12:17 AM
Honestly my original point was kind of an oversimplification. I basically meant "why isn't QueryPerformanceCounter implemented as an alias for a specific use of GetTimeOfDay that returns identical output to the Windows QueryPerformanceCounter on non-windows FPC platforms?" Also there's more to it than that, I suppose... a larger problem is that there's not really any easy way that I'm aware of to load specific sets of OpenGL extensions for non-windows platforms. (As in, everything up to and including OpenGL 3.3.) Obviously dglOpenGL will load EVERYTHING when you call InitOpenGL and ReadExtensions (on Windows), but for Linux it seems to be tied to the ancient GLX API, and for OSX I'm not sure it has support for modern functionality at all. So I just stuck to FPCs GL and GLEXT units. Again though, without trying to sound like an asshole, it's not my problem that Linux and OSX have significantly less user-friendly/robust OpenGL initialization APIs. Missing functionality is missing functionality. ​It's not something to be catered to.

imcold
06-09-2017, 04:14 PM
Thanks for the sample! One small nitpick: calls to LoadOpenGL('opengl32.dll'); Load_GL_version_1_2; ... Load_GL_version_3_2; - are not needed, at least not if you use units distributed with FPC. LoadOpenGL runs in GL unit initialization and the version loading functions cascade to lower versions.

Akira13
10-09-2017, 01:17 AM
Thanks for the sample! One small nitpick: calls to LoadOpenGL('opengl32.dll'); Load_GL_version_1_2; ... Load_GL_version_3_2; - are not needed, at least not if you use units distributed with FPC. LoadOpenGL runs in GL unit initialization and the version loading functions cascade to lower versions.

Yeah, I actually remembered that a few days after I originally posted this.