logo
Page Index Toggle Pages: 1 Send TopicPrint
Normal Topic [UnExt] Extending interface in UnrealGames. (Read 2829 times)
han
Global Moderator
Unreal Rendering Guru
Developer Team
*****
Offline


Oldunreal member

Posts: 595
Location: Germany
Joined: Dec 10th, 2014
Gender: Male
[UnExt] Extending interface in UnrealGames.
Apr 22nd, 2015 at 9:57pm
Print Post  
Current release:
http://coding.hanfling.de/launch/release/UnExt-20150504.zip

Description:
Unlinke in Unreal 227, the interfaces such as URenderDevice, UAudioSubsystem, UTexture, etc. are rather fixed. So if one would want to extend that functionality one would need to pull in either other packages and thus making these a requirement and largely incompatible, or one adds simply adding console commands. However both solutions are not really satisfying.

I can up with a powerful solution using the FUnknown (COM) interface, which is completely unused, which does not require linking and thus not pulling in any other dependencies.
Code
Select All
//
// COM IUnknown interface.
//
class CORE_API FUnknown
{
public:
	virtual DWORD STDCALL QueryInterface( const FGuid& RefIID, void** InterfacePtr ) {return 0;}
	virtual DWORD STDCALL AddRef() {return 0;}
	virtual DWORD STDCALL Release() {return 0;}
}; 


So basic approach is to have some public available header set, where the interfaces and guid are defined. I started it and simply called the main header UnExt.h.

Currently just one interface/guid is defined named IKnowWonderRenderDevice, which matches the URenderDevice interface found in Disney's Brother Bear and probably their UE1 Harry Potter games.

My first use of it was to add a seperate gamma command to my gameengine class, which would check for that interface, and if not found fall back to using old Flush() style to update gamma.

So lets start with the header files:

UnExt.h
Code
Select All
/*=============================================================================
	UnExt.h: UnrealEngine Extension main header.

	Revision history:
		* Created by Sebastian Kaufel
=============================================================================*/

#ifndef UNEXT_GUID_ONLY
#define UNEXT_AUTOGENERATE_GUID(name,a,b,c,d) extern FGuid name;
#endif

#ifdef UNEXT_INCLUDE_RENDERDEVICES
#include "UnExtRenDev.h"
#endif

#ifndef UNEXT_GUID_ONLY
#undef UNEXT_AUTOGENERATE_GUID
#endif NAMES_ONLY

/*----------------------------------------------------------------------------
	The End.
----------------------------------------------------------------------------*/
 


UnExtRenDev.h
Code
Select All
/*=============================================================================
	UnExtRenDev.h: UnrealEngine Extension render device header.

	Revision history:
		* Created by Sebastian Kaufel
=============================================================================*/

UNEXT_AUTOGENERATE_GUID(IID_IKnowWonderRenderDevice,0xCB1E8908,0xEF4C4A4A,0xA5DD8D16,0x4F335D26)

#ifndef UNEXT_GUID_ONLY

/*-----------------------------------------------------------------------------
	KnowWonder RenderDevice interface.

	Motivation:
		Disney's Brother Bear offers an extended URenderDevice interface,
		which reflects a logical next step to clean up the rendering path
		with it's introduction of an interface to draw bunches of polygons,
		optional font rendering, distance fog and gamma handling.

	Reference implementation:
		* Disney's Brother Bear.

	Notes:
		* Might be (partially) implemented in KnowWonder's Harry Potter games.
-----------------------------------------------------------------------------*/

struct IKnowWonderRenderDevice
{
	virtual UBOOL Init( UViewport* InViewport, INT NewX, INT NewY, INT NewColorBytes, UBOOL Fullscreen )=0;
	virtual UBOOL SetRes( INT NewX, INT NewY, INT NewColorBytes, UBOOL Fullscreen )=0;
	virtual void Exit()=0;
	virtual void Flush( UBOOL AllowPrecache )=0;
	virtual UBOOL Exec( const TCHAR* Cmd, FOutputDevice& Ar )=0;
	virtual void Lock( FPlane FlashScale, FPlane FlashFog, FPlane ScreenClear, DWORD RenderLockFlags, BYTE* HitData, INT* HitSize )=0;
	virtual void Unlock( UBOOL Blit )=0;
	virtual void UpdateGamma()=0;
	virtual void RestoreGamma()=0;
	virtual void DrawComplexSurface( FSceneNode* Frame, FSurfaceInfo& Surface, FSurfaceFacet& Facet, DWORD, BYTE )=0;
	virtual INT MaxVertices()=0;
	virtual void DrawTriangles( FSceneNode* Frame, FTextureInfo& Info, FTransTexture** Pts, INT NumPts, _WORD* RemapTable, INT NumIndices, DWORD PolyFlags, class FSpanBuffer* Span )=0;
	virtual void DrawGouraudPolygon( FSceneNode* Frame, FTextureInfo& Info, FTransTexture** Pts, INT NumPts, DWORD PolyFlags, FSpanBuffer* Span )=0;
	virtual void DrawTile( FSceneNode* Frame, FTextureInfo& Info, FLOAT X, FLOAT Y, FLOAT XL, FLOAT YL, FLOAT U, FLOAT V, FLOAT UL, FLOAT VL, class FSpanBuffer* Span, FLOAT Z, FPlane Color, FPlane Fog, DWORD PolyFlags )=0;
	virtual void Draw3DLine( FSceneNode* Frame, FPlane Color, DWORD LineFlags, FVector OrigP, FVector OrigQ )=0;
	virtual void Draw2DClippedLine( FSceneNode* Frame, FPlane Color, DWORD LineFlags, FVector P1, FVector P2 )=0;
	virtual void Draw2DLine( FSceneNode* Frame, FPlane Color, DWORD LineFlags, FVector P1, FVector P2 )=0;
	virtual void Draw2DPoint( FSceneNode* Frame, FPlane Color, DWORD LineFlags, FLOAT X1, FLOAT Y1, FLOAT X2, FLOAT Y2, FLOAT Z )=0;
	virtual void ClearZ( FSceneNode* Frame )=0;
	virtual void PushHit( const BYTE* Data, INT Count )=0;
	virtual void PopHit( INT Count, UBOOL bForce )=0;
	virtual void GetStats( TCHAR* Result )=0;
	virtual void ReadPixels( FColor* Pixels )=0;
	virtual void EndFlash()=0;
	virtual void DrawStats( FSceneNode* Frame )=0;
	virtual void SetSceneNode( FSceneNode* Frame )=0;
	virtual void PrecacheTexture( FTextureInfo& Info, DWORD PolyFlags )=0;
	virtual void SetDistanceFog( UBOOL Enable, FLOAT FogStart, FLOAT FogEnd, FColor Color )=0;
	virtual void SetDistanceFogForceReset( UBOOL Enable )=0;
	virtual UBOOL GetDistanceFogForceReset()=0;
	virtual UBOOL HasNativeDrawString()=0;
	virtual void DrawString( DWORD, UFont* Font, INT& X, INT& Y, const TCHAR* Text, const FPlane& Color )=0;
};

#endif // UNEXT_GUID_ONLY

/*----------------------------------------------------------------------------
	The End.
----------------------------------------------------------------------------*/
 



After including that header (e.g. in your YourRenderDevicePrivate.h) next step would be to add that interface to your RenderDevice and implement the required virtual functions of it.
Code
Select All
class UOpenGLRenderDevice : public URenderDevice, public IKnowWonderRenderDevice { ... }
 



Because you don't want to rely on additional (static) *.lib files, you need to have the Guid's in your Package. You can do this rather similar to Names (without the need for the InitNames() function).
Code
Select All
#define UNEXT_GUID_ONLY
#define UNEXT_AUTOGENERATE_GUID(name,a,b,c,d) FGuid name = FGuid(a,b,c,d);
#include "UnExt.h"
#undef AUTOGENERATE_FUNCTION
#undef UNEXT_GUID_ONLY
 



So next step is to implement the QueryInterface() method in your RenderDevice:
Code
Select All
	//
	// FUnknown.
	//
	DWORD STDCALL QueryInterface( const FGuid& RefIID, void** InterfacePtr )
	{
		if ( RefIID==IID_IKnowWonderRenderDevice )
		{
			*(IKnowWonderRenderDevice**)InterfacePtr = this;
			return 1;
		}
		return 0;
	}
 


Thats all you need to implement in your RenderDevice to make use of it.

So for using it on the other side you include the UnExt.h header to, and add the code for UNEXT_AUTOGENERATE_GUID in the same way. Next step is to query this interface, e.g. as part of my URevisionGameEngine::Exec() function:
Code
Select All
	// Gamma update command using IKnowWonderRenderDevice
	else if( ParseCommand(&Str,TEXT("UPDATEGAMMA")) )
	{
		if ( Client && Client->Viewports.Num() && Client->Viewports(0) && Client->Viewports(0)->RenDev )
		{
			IKnowWonderRenderDevice* I = NULL;
			if ( Client->Viewports(0)->RenDev->QueryInterface(IID_IKnowWonderRenderDevice,(void**)&I) && I )
			{
				I->UpdateGamma();
				Out.Logf( TEXT("Updated gamma using IKnowWonderRenderDevice.") );
				return 1;
			}

			// Fallback.
			Client->Viewports(0)->RenDev->Flush( 0 );
			Out.Logf( TEXT("Updated gamma using fallback.") );
			return 1;
		}

		// Fallback for Fallback.
		Flush( 0 );
		Out.Logf( TEXT("Updated gamma using fallbacks fallback.") );
		return 1;
	}
 



So thats just a start, one can go ahead and make up an extended canvas class and hook in the HasNativeDrawString() / DrawString(), etc.

To sum up:
You get a clean way to extend individual components, have a clean way to check for these capabilities and don't need to depend on any specific class or implementation and get a great deal of compatiblity this way.
« Last Edit: Oct 24th, 2015 at 1:47pm by han »  

HX on Mod DB. Revision on Steam. Löffels on Patreon.
Back to top
 
IP Logged
 
han
Global Moderator
Unreal Rendering Guru
Developer Team
*****
Offline


Oldunreal member

Posts: 595
Location: Germany
Joined: Dec 10th, 2014
Gender: Male
Re: Extending interface in UnrealGames.
Reply #1 - Oct 24th, 2015 at 1:14pm
Print Post  
Proposal: Standarization of timing source usage
I have just another idea what should be standarized. Despite usage of RDTSC is still a problem for some old System running old Software, it would be nice to have a defined way to control which timing source is using.

The idea is to bend GTimestamp, a globally accessible variable. In stock UnrealEngine 1 it's value 0 indicates that appSecondsSlow() is used as a timing source, and the value 1 indicates that the RDTSC counter is used. It might be worth to mention that setting GTimestamp to 0 cause problems at least if the system uptime is a few weeks.

So my suggestion is to extend the meaning of GTimestamp in the following way:
Code
Select All
GTimestamp = 0: appSecondsSlow()
GTimestamp = 1; RDTSC based timer as implemented in headers.
GTimestamp = 2; (WIN32) QueryPerformanceCounter API.
GTimestamp = 3; (WIN32) timeGetTime.
GTimestamp = 4; (WIN32) GetSystemTimePreciseAsFileTime.
 


Notes:
QPC API is just included because Kenties Deus Ex Launcher uses it. I personally use a check for RDTSC validity (OS >= Windows Vista, TscInvariant flag set) and fallback to timeGetTime() which Unreal 227 uses as a timing source.

Probably the best approach would be to develop the reference implementation as part of UnExt as appSecondsUnExt(). That way a component can just use appSecondsUnExt() instead of appSeconds() to support the feature.

It is the responsibility of the game Launcher to set the it's appropreciated GTimestamp value, though I might add a sample implementation for the selection code to UnExt.

Additions to UnExt:
IHumanHeadAudioSubsystem offering StopSoundId() / StopAllSound() functionality and additionaly adds PostRender() for older engine versions (Nerf, etc.).

Adaption of UnExt:
Recent ALAudio builds support the IHumanHeadAudioSubsystem API.
Deus Ex: Revision optionally utilizes IKnowWonderRenderDevice::UpdateGamma() for it's gamma adjustment screen.
« Last Edit: Oct 28th, 2015 at 12:48pm by han »  

HX on Mod DB. Revision on Steam. Löffels on Patreon.
Back to top
 
IP Logged
 
Smirftsch
Forum Administrator
*****
Offline



Posts: 8100
Location: at home
Joined: Apr 30th, 1998
Gender: Male
Re: [UnExt] Extending interface in UnrealGames.
Reply #2 - Oct 25th, 2015 at 11:53am
Print Post  
to bad GetSystemTimePreciseAsFileTime won't work on pre Win8 systems. That's something I really miss.
Although, I stumbled across NtQuerySystemTime - maybe that's worth a look yet. On the other hand MS pretty much declared this deprecated already. For some reason I always come back to timeGetTime.
  

Sometimes you have to lose a fight to win the war.
Back to top
WWWICQ  
IP Logged
 
han
Global Moderator
Unreal Rendering Guru
Developer Team
*****
Offline


Oldunreal member

Posts: 595
Location: Germany
Joined: Dec 10th, 2014
Gender: Male
Re: [UnExt] Extending interface in UnrealGames.
Reply #3 - Oct 25th, 2015 at 4:48pm
Print Post  
Selecting a timing source is really not a simple task and has lots of caveeats (and we both discussed the topic quite a lot in the past).

However, the timing thing is sth. which solves itsself more an more. One of the reasons beeing that new CPU models over the last couple of years reverted back to having a constant frequency TSC as well as System > WinXp do handle synchronisation (if needed) across cores gracefully. So the amount of Systems running WinXP or lower and/or using a CPU without a constant frequency TSC dimishs in the future. Also one can just go ahead and use GetSystemTimePreciseAsFileTime() on Win8+ as the amount of Systems >= Win8 get more an more. So all in all the problems solves itsself, though choosing the right timer for the problematic systems remains a hard descission. But making this descission is not a part of UnExt. UnExt just deferres the descission to the Launcher and make it easy for others components to comply with this descission as having different timing sources across the same application can cause issues.
  

HX on Mod DB. Revision on Steam. Löffels on Patreon.
Back to top
 
IP Logged
 
han
Global Moderator
Unreal Rendering Guru
Developer Team
*****
Offline


Oldunreal member

Posts: 595
Location: Germany
Joined: Dec 10th, 2014
Gender: Male
Re: [UnExt] Extending interface in UnrealGames.
Reply #4 - Oct 28th, 2015 at 1:08pm
Print Post  
Proposal: Standard interface for querying capabilities
A general useable interface query certain capabilities would be useful. A usage example would be an URenderDevice implementation which queries the URenderBase implementation used about a UNEXT_RENDEV_CAP_LOCKS_WITH_BUMPMAP capability to figure out if whether the URenderBase implementation passes an extended FSurfaceInfo to URenderDevice::DrawComplexSurface() which adds FTextureInfo* BumpMap. This way the Render can lock the (unused) UTexture::BumpMap and passes it to the RenderDevice which is in turn is aware whether the shorter FSurfaceInfo or the extended one is used and can thus safely access the additional BumpMap. This way one can continue to use the UTexture class and use it's BumpMap property for rendering. The format of the BumpMap could be RGBA8, with the first 3 channels beeing normals, and the alpha channel beeing a displacement offset to be used for geometry generation in RenderDevice scope.

While my first idea is to simpy use some boolean style QueryCapability() function, it might be a better idea to add a more extensible interface, allowing to query other standard UnrealEngine datatypes (UBOOL, INT, DWORD, BYTE, SWORD, TCHAR*, FString, void*, etc.). An idea for such an interface would be a similar approach to glGet*() https://www.opengl.org/sdk/docs/man/html/glGet.xhtml
  

HX on Mod DB. Revision on Steam. Löffels on Patreon.
Back to top
 
IP Logged
 
Page Index Toggle Pages: 1
Send TopicPrint
Bookmarks: del.icio.us Digg Facebook Google Google+ Linked in reddit StumbleUpon Twitter Yahoo