Skip to content

StrobeAPI Implementation #353

Merged
merged 48 commits into from
Jul 1, 2018
Merged

StrobeAPI Implementation #353

merged 48 commits into from
Jul 1, 2018

Conversation

fuzun
Copy link
Contributor

@fuzun fuzun commented Feb 18, 2018

Pull request for the v1: #343
Discussion: https://forums.blurbusters.com/viewtopic.php?f=7&t=3815

News (v3):

  • Complete refactor due to OOP implementation.
  • Now developers can develop their own Strobe implementation based on Strobe api (by deriving StrobeAPI abstract class).
  • Multiple Strobe implementations can work simultaneously.
  • Bug fixes.
  • ...

News (v2):

  • Rewritten from scratch.
  • Supports phase matching.
  • Burn-in prevention. -> r_strobe_swapinterval .
  • New debug mode! (turn on with r_strobe_debug <0|1>)
  • Brightness reduction statistics in debug stats.
  • PWM (Simulated) statistics in debug stats.
  • Experimental "Badness" function in debug stats.
  • Some bug fixes.
  • Stability analyzing to auto disable strobing (r_strobe_cooldown ) - stats in debug stats.
  • ...

Todo:

  • Making menu entry for enabling strobe mode.
  • Implement high precision timer to keep the internal phase on track with monitor. (In case of freezes etc.) * Thanks @mdrejhon .
  • Make swapping transition seamless by rendering non-opaque frames. * Thanks @mdrejhon .
  • Move "Strobe" to separate files.

Sample screenshot from the debug mode: https://camo.githubusercontent.com/081e3641e118be7554e42f54149dc9ce5c862724/68747470733a2f2f7667792e6d652f54646634675a2e706e67

This pull request will be edited and updated! It stands for review now. (9.03.2018)

@a1batross
Copy link
Member

Great, I will review it ASAP.

@@ -57,13 +57,22 @@ void SCR_DrawFPS( void )
static int minfps = 9999;
static int maxfps = 0;
double newtime;
char fpsstring[64];
char fpsstring[2048]; //char fpsstring[64]; // Heap allocation if 2048 too much ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend you moving this code to separate function, so SCR_DrawFPS stays only for showing FPS information. One function does one thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good to have OOP used here but since it can't be, copy-paste will be required for most of the SCR_DrawFPS code (to the new function). I would not favor copying and pasting because it makes it spaghetti-code but I could not find another good solution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOP is possible for C too. Partially.

I planned to share FPS stats between functions, so net_graph can show precise FPS too, instead of silly 1 / host.frametime.

So we would have a function for FPS calculation(current, best, worst and average), which writes result to public structure, and it can be used by "informative" code, like DrawFPS or Network Graph.


strobeInterval = r_strobe->integer;

Q_snprintf(fpsstring,
Copy link
Member

@a1batross a1batross Feb 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to use Con_NXPrintf here to avoid fpsstring overrun and keeping code simple.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look to s_main.c:1607(around s_show->integer check) for example.

@@ -349,6 +349,72 @@ void R_LightForPoint( const vec3_t point, color24 *ambientLight, qboolean invLig
int R_CountSurfaceDlights( msurface_t *surf );
int R_CountDlights( void );


/// <STROBE> // Move to strobe.h ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it would be a good idea.

Because all source code is categorized, you may want to move code to "r_strobe.c" and "r_strobe.h" files.

{
float x, y, w, h;
} _rectf_t;
extern _rectf_t con_rect;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such generic definitions may be moved to "client.h" header, as con_rect can be used by other parts of engine.

Copy link
Contributor Author

@fuzun fuzun Feb 18, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would also require moving the _rectf definition to more generic header (client.h in this case). I did not do that because I intended to use _rectf only for strobe internal purposes.

I am not familiar with Xash3D so,

  1. I am not sure if this definition would be ok to be in a more generic header for public / general uses.
  2. There may also be a float / double rectangle struct definition in the engine that I could not find as it is pretty basic thing. I have found similar wrect but it was int.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just doesn't look right. I don't know can it be used elsewhere than strobing code. If it's intended to be private, you may move extern "_rectf_t" to file with your strobing code and don't show it for everyone(I mean, for code, yes :))

void R_Strobe( void );

typedef enum {
p_positive = 1, // Phase: Positive
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have BIT() macro for eaiser bitmasks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked. Do we have more generic handling for flags or is it only this BIT macro?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's only BIT macro.

Unkle Mike refactored it. He added FBitSet() for bit checking, ClearFlag, SetFlag and ResetFlag macros. But fork still don't moved on his version.

fstate_e frameInfo; // Frame info
} StrobeInfo_t;

extern StrobeInfo_t StrobeInfo;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this defintions are not used by other parts of game engine, it's better to keep them encapsulated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Encapsulating by moving to separate header file dedicated to strobing? If that is the case, I agree.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as you want.

It's not limited to headers. You maybe want to define them in *.c file and declare as "static", so they wouldn't accessible from other compilation units.

#define STROBE_DEVIATION_SIZE 60

// Experimental 'Badness' function
#define STROBE_BADNESS( diffP, diffNs ) \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Macros are one of the greatest thing from C language, but they are also may cause not expected bugs.

In this case, you may pass something like: _calculatePercentage( 2 + 2, 1 ) and this will be expanded to: ( 100 * 2 + 2 / 1 ), leaving you with incorrect result.

I recommend to move them to _inline functions or just put macro arguments to brackets. For example:
#define _calculatePercentage( x, y ) ( 100 * ( y ) / ( x ) )

Another thing is that it will be hard to read such code and debug, so inlined functions is preferred. And you will get a free type checking! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to make them as inline functions at first place, but because of there is no separate c file for strobe I used macros as it would be bad to put these internal functions into gl_rmain.

If you approve separating the "strobe", which in my opinion is the way to go, I will do that but I am not into project files like cmake files so I may not be able to integrate separate files into the project files properly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CMake and Makefiles both do recursive searching of any *.c files.

Except is Android.mk, but it's not too hard. Just add path to file at the end of LOCAL_SRC_FILES and don't forget reverse slash at the end of previous line.

int getInterval = r_strobe->integer; // Check through modified tag first?
static int strobeInterval = 0;
static int swapInterval = 0;
static double recentTime = 0.0, recentTime2 = 0.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static variables is always initialized to zero. It is required by C standard.

swapInterval = r_strobe_swapinterval->integer;

if ( (strobeInterval == 0) ||
((gl_swapInterval->integer == 0) && (strobeInterval != 0)) )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integers are always FALSE when equal to zero and TRUE otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always try to be more explicit, but I noted it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't bad. Just more readable for typical C or C++ programmer, I think.
It will not readable for Java programmers. :D :D

r_strobe_debug = Cvar_Get( "r_strobe_debug", "0", CVAR_ARCHIVE, "show strobe debug information" );
r_strobe_cooldown = Cvar_Get( "r_strobe_cooldown", "3", CVAR_ARCHIVE, "strobe cooldown perios in secs" );

StrobeInfo.pCounter = 0; StrobeInfo.pBCounter = 0; StrobeInfo.pNCounter = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may use Q_memset() here. Also global variables is always zero initialized. It's a standard too.

@fuzun
Copy link
Contributor Author

fuzun commented Feb 22, 2018

In maintenance...
Will be reopened.

@fuzun fuzun closed this Feb 22, 2018
@fuzun fuzun reopened this Mar 3, 2018
@fuzun fuzun changed the title Software Based Strobing Implementation v2 Software Based Strobing Implementation v3 Mar 3, 2018
@a1batross
Copy link
Member

Just comment here, when it will be ready.

I also will merge it for upcoming code base sync with Xash3D 1.0.

@fuzun
Copy link
Contributor Author

fuzun commented Apr 7, 2018

@a1batross Will FPS be global ? You have mentioned about flag operations, does version 1.0 contain new macros for that purpose ? And I have used some reserved keywords like "this" thinking it would not matter since it is a c file and therefore must be treated as c code, should I revert it to _this ?

Also will white color be added to the color table? I think it can be added after the "default" color (+1 increased array size).

@fuzun
Copy link
Contributor Author

fuzun commented May 7, 2018

@fuzun
Copy link
Contributor Author

fuzun commented Jun 30, 2018

sample-strobe-screenshot

How to hook a StrobeAPI derived work (StrobeAPI implementation) into xash3d ?

  • in cl_view.c V_PostRender function, this part can be modified:

    #ifdef STROBE_ENABLED
    		// if ( STROBE_TEMPLATE )
    		//  STROBE_TEMPLATE->STROBE_IMPL_FUNC_DEBUGHANDLER( &STROBE_TEMPLATE );
    		if ( STROBE_CORE )
    			STROBE_CORE->STROBE_IMPL_FUNC_DEBUGHANDLER( &STROBE_CORE );
    #endif 
    

    Debug handling function (printing informative strobe debugging text output from StrobeAPI) of your Strobe implementation can be called here.

  • in gl_rmain.c R_EndFrame function, this part can be modified:

    #ifdef STROBE_ENABLED
    // StrobeAPI.Invoker( STROBE_INVOKE(STROBE_TEMPLATE) );
    StrobeAPI.Invoker( STROBE_INVOKE(STROBE_CORE) );
    #else
    
  • Check r_strobe_template.c.TEMPLATE and r_strobe_template.h.TEMPLATE files. They are templates for creating a new StrobeAPI implementation. They can be used as a base and for actual implementation Strobe-Core (r_strobe_core.h and r_strobe_core.c) can be inspected.

How StrobeAPI and StrobeAPI implementations exposed to xash3d ?

  • A structure which contains console variables and Invoker, Constructor and Destructor functions are exposed through "StrobeAPI" instance of the structure.
  • R_InitStrobeAPI( ) from StrobeAPI for registering console variables is exposed.
  • Constructor, destructor, reinit, main functions from StrobeAPI implementations are exposed while being guarded by StrobeAPI macros.
  • Bunch of macros starting with STROBE or _STROBE from StrobeAPI are exposed.
  • Bunch of structures starting with STROBE or StrobeAPI_ from StrobeAPI are exposed.
  • An implementation structure and its instance from StrobeAPI implementation are exposed while being guarded by StrobeAPI macros.
  • StrobeAPI and StrobeAPI implementation generic functions and private variables etc. are NOT exposed.
  • Contents of r_strobe_api_protected_.h are not exposed.
  • If STROBE_DISABLED is defined, nothing is exposed.

Console variables

  • r_strobe : For setting Strobe method. 1: NORMAL - BLACK , 2 : NORMAL - BLACK - BLACK, -3: BLACK - BLACK - BLACK - NORMAL . Set 0 to disable strobing.
  • r_strobe_swapinterval : For setting phase swap interval of Strobing. (in seconds). May cause image retention if set to 0 in some monitors (0 disables phase swapping).
  • r_strobe_debug : For informing StrobeAPI instance to do something with StrobeAPI informative debug output text. Strobe-Core implementation shows the output text in right-up corner of the screen. (0 or 1)
  • r_strobe_cooldown : When standard deviation of collected FPS values in a certain time period exceeds the determined limit, strobing gets disabled for a time period. This cvar sets the time period of how long strobing is disabled when there is instability. Should be set 0 to disable instability induced strobing cooldown.
  • deviation limit (not implemented): Standard deviation limit of FPS values collected in a time period.
  • count of collected fps values (not implemented): How many FPS values should be collected before calculating standard deviation.

These variables are common in all StrobeAPI implementations because they are defined in StrobeAPI. In future these variables should be defined in implementations instead of StrobeAPI.

@fuzun fuzun changed the title Software Based Strobing Implementation v3 StrobeAPI Implementation Jun 30, 2018
@a1batross
Copy link
Member

Good. I will test it ASAP and merge for old engine. New engine isn't ready for public and things may change, but I hope new engine will support strobing too.

I have a question, though. Will strobing help to defeat motion blur at 60 Hz? I got a new device recently, it's 60 Hz but too blurry.

@fuzun
Copy link
Contributor Author

fuzun commented Jun 30, 2018

I never intended to make this C89 compatible but for some reason compiler that Travis using can not compile if variables are defined in for statements due to "C89 compatibility". It does not need variables to be defined at start of a block.

I have a 60 hz screen and I am not satisfied with the strobing performance. Because even with the least aggressive strobe method (1 or -1), effective fps drops to 60/2 = 30 fps and frequency of signal drops to 30 hz. I find 30 fps/hz to be not enough for games xash3d support. r_strobe -2 method makes effective fps 40 but signal frequency (I call it PWM simulation) drops to 20 hz (60 * 1/3). I think at 20 hz it should not be even called as "strobing".

When I overclock the screen to 75 hz, strobing does not become annoying. So I think 75hz might be bare minimum for strobing.

I have another 60 hz screen that I overclocked it to 108 hz. Strobing helps too much for eliminating motion blur however that screen has only 220 cd/m2 peak brightness and even with least aggressive strobe method, screen becomes too dark. It also had image retention problem but it is fixed with r_strobe_swapinterval setting thanks to @mdrejhon .

And the last monitor I tested strobing is 144 hz. It has built-in hardware strobing so effective fps does not drop. The game is playable even with aggressive strobe methods like r_strobe = 2, 3 or -2 . I think that performance of software strobe method is like 30-35% of the built in hardware strobing.

There is an utility called CRU (Custom resolution utility) for overclocking monitors. I suggest you to try it.

As for this pull request, there are a few things that need to be corrected. They are not critical so you can merge it in my opinion. When I have time I will try to update it.

@a1batross a1batross merged commit 54aeb83 into FWGS:master Jul 1, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants