2014-04-15 21:46:18 +00:00
//TODO
//we could flag textures as 'actually' render targets (keep a reference to the render target?) which could allow us to convert between them more quickly in some cases
using System ;
2014-02-07 02:36:27 +00:00
using System.IO ;
2012-04-16 08:18:41 +00:00
using System.Collections.Generic ;
2014-04-15 21:46:18 +00:00
using System.Diagnostics ;
2012-04-16 08:18:41 +00:00
using System.Drawing ;
2013-11-04 01:39:19 +00:00
using BizHawk.Emulation.Common ;
2013-10-25 00:57:23 +00:00
using BizHawk.Client.Common ;
2014-04-15 21:46:18 +00:00
using BizHawk.Client.EmuHawk.FilterManager ;
2014-01-27 00:02:21 +00:00
using BizHawk.Bizware.BizwareGL ;
2014-02-07 02:36:27 +00:00
using OpenTK ;
2013-11-03 03:54:37 +00:00
namespace BizHawk.Client.EmuHawk
2012-04-16 08:18:41 +00:00
{
2014-01-28 04:39:27 +00:00
/// <summary>
/// A DisplayManager is destined forevermore to drive the PresentationPanel it gets initialized with.
/// Its job is to receive OSD and emulator outputs, and produce one single buffer (BitampBuffer? Texture2d?) for display by the PresentationPanel.
/// Details TBD
/// </summary>
2012-04-16 08:18:41 +00:00
public class DisplayManager : IDisposable
{
2014-06-29 02:28:48 +00:00
class DisplayManagerRenderTargetProvider : IRenderTargetProvider
2014-04-15 21:46:18 +00:00
{
DisplayManagerRenderTargetProvider ( Func < Size , RenderTarget > callback ) { Callback = callback ; }
Func < Size , RenderTarget > Callback ;
2014-06-29 02:28:48 +00:00
RenderTarget IRenderTargetProvider . Get ( Size size )
2014-04-15 21:46:18 +00:00
{
return Callback ( size ) ;
}
}
2014-01-28 04:39:27 +00:00
public DisplayManager ( PresentationPanel presentationPanel )
2012-04-16 08:18:41 +00:00
{
2014-01-28 04:39:27 +00:00
GL = GlobalWin . GL ;
this . presentationPanel = presentationPanel ;
GraphicsControl = this . presentationPanel . GraphicsControl ;
2014-06-08 23:30:34 +00:00
CR_GraphicsControl = GlobalWin . GLManager . GetContextForGraphicsControl ( GraphicsControl ) ;
2012-04-16 08:18:41 +00:00
2014-01-28 04:39:27 +00:00
//it's sort of important for these to be initialized to something nonzero
currEmuWidth = currEmuHeight = 1 ;
2012-09-02 14:54:30 +00:00
2014-01-28 04:39:27 +00:00
Renderer = new GuiRenderer ( GL ) ;
2014-02-14 00:55:18 +00:00
VideoTextureFrugalizer = new TextureFrugalizer ( GL ) ;
2012-09-02 14:54:30 +00:00
2014-02-07 02:36:27 +00:00
ShaderChainFrugalizers = new RenderTargetFrugalizer [ 16 ] ; //hacky hardcoded limit.. need some other way to manage these
for ( int i = 0 ; i < 16 ; i + + )
2014-02-03 05:27:59 +00:00
{
2014-02-07 02:36:27 +00:00
ShaderChainFrugalizers [ i ] = new RenderTargetFrugalizer ( GL ) ;
2014-02-03 05:27:59 +00:00
}
2014-02-07 02:36:27 +00:00
using ( var xml = typeof ( Program ) . Assembly . GetManifestResourceStream ( "BizHawk.Client.EmuHawk.Resources.courier16px.fnt" ) )
using ( var tex = typeof ( Program ) . Assembly . GetManifestResourceStream ( "BizHawk.Client.EmuHawk.Resources.courier16px_0.png" ) )
TheOneFont = new StringRenderer ( GL , xml , tex ) ;
2014-02-03 07:01:31 +00:00
2014-06-29 02:28:48 +00:00
var fiHq2x = new FileInfo ( Path . Combine ( PathManager . GetExeDirectoryAbsolute ( ) , "Shaders/BizHawk/hq2x.cgp" ) ) ;
2014-02-07 02:36:27 +00:00
if ( fiHq2x . Exists )
using ( var stream = fiHq2x . OpenRead ( ) )
2014-06-29 02:28:48 +00:00
ShaderChain_hq2x = new Filters . RetroShaderChain ( GL , new Filters . RetroShaderPreset ( stream ) , Path . Combine ( PathManager . GetExeDirectoryAbsolute ( ) , "Shaders/BizHawk" ) ) ;
var fiScanlines = new FileInfo ( Path . Combine ( PathManager . GetExeDirectoryAbsolute ( ) , "Shaders/BizHawk/BizScanlines.cgp" ) ) ;
2014-02-07 02:36:27 +00:00
if ( fiScanlines . Exists )
using ( var stream = fiScanlines . OpenRead ( ) )
2014-06-29 02:28:48 +00:00
ShaderChain_scanlines = new Filters . RetroShaderChain ( GL , new Filters . RetroShaderPreset ( stream ) , Path . Combine ( PathManager . GetExeDirectoryAbsolute ( ) , "Shaders/BizHawk" ) ) ;
var fiBicubic = new FileInfo ( Path . Combine ( PathManager . GetExeDirectoryAbsolute ( ) , "Shaders/BizHawk/bicubic-fast.cgp" ) ) ;
2014-04-15 21:46:18 +00:00
if ( fiBicubic . Exists )
using ( var stream = fiBicubic . OpenRead ( ) )
2014-06-29 02:28:48 +00:00
ShaderChain_bicubic = new Filters . RetroShaderChain ( GL , new Filters . RetroShaderPreset ( stream ) , Path . Combine ( PathManager . GetExeDirectoryAbsolute ( ) , "Shaders/BizHawk" ) ) ;
2014-02-14 00:55:18 +00:00
LuaSurfaceSets [ "emu" ] = new SwappableDisplaySurfaceSet ( ) ;
LuaSurfaceSets [ "native" ] = new SwappableDisplaySurfaceSet ( ) ;
LuaSurfaceFrugalizers [ "emu" ] = new TextureFrugalizer ( GL ) ;
LuaSurfaceFrugalizers [ "native" ] = new TextureFrugalizer ( GL ) ;
2014-04-15 21:46:18 +00:00
RefreshUserShader ( ) ;
2012-09-02 14:54:30 +00:00
}
2014-01-28 04:39:27 +00:00
public bool Disposed { get ; private set ; }
2012-09-21 18:01:24 +00:00
2014-01-28 04:39:27 +00:00
public void Dispose ( )
2012-04-16 08:18:41 +00:00
{
2014-01-28 04:39:27 +00:00
if ( Disposed ) return ;
Disposed = true ;
VideoTextureFrugalizer . Dispose ( ) ;
2014-02-14 00:55:18 +00:00
foreach ( var f in LuaSurfaceFrugalizers . Values )
f . Dispose ( ) ;
2014-02-07 02:36:27 +00:00
foreach ( var f in ShaderChainFrugalizers )
if ( f ! = null )
f . Dispose ( ) ;
2014-01-28 04:39:27 +00:00
}
2012-06-10 23:27:30 +00:00
2014-02-14 00:55:18 +00:00
//dont know what to do about this yet
public bool NeedsToPaint { get ; set ; }
2014-01-28 04:39:27 +00:00
//rendering resources:
2014-06-08 23:30:34 +00:00
public IGL GL ;
2014-01-28 04:39:27 +00:00
StringRenderer TheOneFont ;
GuiRenderer Renderer ;
2012-07-15 07:38:36 +00:00
2014-01-28 04:39:27 +00:00
//layer resources
PresentationPanel presentationPanel ; //well, its the final layer's target, at least
GraphicsControl GraphicsControl ; //well, its the final layer's target, at least
2014-06-08 23:30:34 +00:00
GLManager . ContextRef CR_GraphicsControl ;
2014-06-29 02:28:48 +00:00
FilterProgram CurrentFilterProgram ;
2012-06-10 23:27:30 +00:00
2014-01-28 04:39:27 +00:00
/// <summary>
/// these variables will track the dimensions of the last frame's (or the next frame? this is confusing) emulator native output size
/// </summary>
int currEmuWidth , currEmuHeight ;
2012-07-15 09:13:46 +00:00
2014-02-14 00:55:18 +00:00
TextureFrugalizer VideoTextureFrugalizer ;
Dictionary < string , TextureFrugalizer > LuaSurfaceFrugalizers = new Dictionary < string , TextureFrugalizer > ( ) ;
2014-02-07 02:36:27 +00:00
RenderTargetFrugalizer [ ] ShaderChainFrugalizers ;
2014-04-15 21:46:18 +00:00
Filters . RetroShaderChain ShaderChain_hq2x , ShaderChain_scanlines , ShaderChain_bicubic ;
Filters . RetroShaderChain ShaderChain_user ;
2012-06-10 23:27:30 +00:00
2014-04-15 21:46:18 +00:00
public void RefreshUserShader ( )
2014-01-28 04:39:27 +00:00
{
2014-04-15 21:46:18 +00:00
if ( ShaderChain_user ! = null )
ShaderChain_user . Dispose ( ) ;
2014-04-15 23:42:05 +00:00
if ( File . Exists ( Global . Config . DispUserFilterPath ) )
{
var fi = new FileInfo ( Global . Config . DispUserFilterPath ) ;
2014-04-15 21:46:18 +00:00
using ( var stream = fi . OpenRead ( ) )
ShaderChain_user = new Filters . RetroShaderChain ( GL , new Filters . RetroShaderPreset ( stream ) , Path . GetDirectoryName ( Global . Config . DispUserFilterPath ) ) ;
2014-04-15 23:42:05 +00:00
}
2014-04-15 21:46:18 +00:00
}
2012-04-16 08:18:41 +00:00
2014-07-22 00:04:28 +00:00
FilterProgram BuildDefaultChain ( Size chain_insize , Size chain_outsize , bool includeOSD )
2014-04-15 21:46:18 +00:00
{
//select user special FX shader chain
Dictionary < string , object > selectedChainProperties = new Dictionary < string , object > ( ) ;
Filters . RetroShaderChain selectedChain = null ;
2014-02-07 02:36:27 +00:00
if ( Global . Config . TargetDisplayFilter = = 1 & & ShaderChain_hq2x ! = null & & ShaderChain_hq2x . Available )
selectedChain = ShaderChain_hq2x ;
if ( Global . Config . TargetDisplayFilter = = 2 & & ShaderChain_scanlines ! = null & & ShaderChain_scanlines . Available )
2014-04-15 21:46:18 +00:00
{
//shader.Pipeline["uIntensity"].Set(1.0f - Global.Config.TargetScanlineFilterIntensity / 256.0f);
2014-02-07 02:36:27 +00:00
selectedChain = ShaderChain_scanlines ;
2014-04-15 21:46:18 +00:00
selectedChainProperties [ "uIntensity" ] = 1.0f - Global . Config . TargetScanlineFilterIntensity / 256.0f ;
}
if ( Global . Config . TargetDisplayFilter = = 3 & & ShaderChain_user ! = null & & ShaderChain_user . Available )
selectedChain = ShaderChain_user ;
2014-02-07 02:36:27 +00:00
2014-04-15 21:46:18 +00:00
Filters . FinalPresentation fPresent = new Filters . FinalPresentation ( chain_outsize ) ;
Filters . SourceImage fInput = new Filters . SourceImage ( chain_insize ) ;
Filters . OSD fOSD = new Filters . OSD ( ) ;
fOSD . RenderCallback = ( ) = >
{
2014-07-22 00:04:28 +00:00
if ( ! includeOSD )
return ;
2014-04-15 21:46:18 +00:00
var size = fOSD . FindInput ( ) . SurfaceFormat . Size ;
Renderer . Begin ( size . Width , size . Height ) ;
MyBlitter myBlitter = new MyBlitter ( this ) ;
myBlitter . ClipBounds = new Rectangle ( 0 , 0 , size . Width , size . Height ) ;
Renderer . SetBlendState ( GL . BlendNormal ) ;
GlobalWin . OSD . Begin ( myBlitter ) ;
GlobalWin . OSD . DrawScreenInfo ( myBlitter ) ;
GlobalWin . OSD . DrawMessages ( myBlitter ) ;
Renderer . End ( ) ;
} ;
2014-06-29 02:28:48 +00:00
var chain = new FilterProgram ( ) ;
2014-04-15 21:46:18 +00:00
//add the first filter, encompassing output from the emulator core
chain . AddFilter ( fInput , "input" ) ;
//add lua layer 'emu'
AppendLuaLayer ( chain , "emu" ) ;
//add user-selected retro shader
2014-02-07 02:36:27 +00:00
if ( selectedChain ! = null )
2014-04-15 21:46:18 +00:00
AppendRetroShaderChain ( chain , "retroShader" , selectedChain , selectedChainProperties ) ;
//choose final filter
Filters . FinalPresentation . eFilterOption finalFilter = Filters . FinalPresentation . eFilterOption . None ;
if ( Global . Config . DispFinalFilter = = 1 ) finalFilter = Filters . FinalPresentation . eFilterOption . Bilinear ;
if ( Global . Config . DispFinalFilter = = 2 ) finalFilter = Filters . FinalPresentation . eFilterOption . Bicubic ;
//if bicubic is selected and unavailable, dont use it
if ( ! ShaderChain_bicubic . Available & & fPresent . FilterOption = = Filters . FinalPresentation . eFilterOption . Bicubic )
2014-02-03 07:01:31 +00:00
{
2014-04-15 21:46:18 +00:00
finalFilter = Filters . FinalPresentation . eFilterOption . None ;
2014-02-03 07:01:31 +00:00
}
2014-04-15 21:46:18 +00:00
fPresent . FilterOption = finalFilter ;
2014-02-03 05:27:59 +00:00
2014-04-15 21:46:18 +00:00
//now if bicubic is chosen, insert it
if ( finalFilter = = Filters . FinalPresentation . eFilterOption . Bicubic )
AppendRetroShaderChain ( chain , "bicubic" , ShaderChain_bicubic , null ) ;
2012-04-16 08:18:41 +00:00
2014-04-15 21:46:18 +00:00
//add final presentation
chain . AddFilter ( fPresent , "presentation" ) ;
2014-02-03 05:27:59 +00:00
2014-04-15 21:46:18 +00:00
//add lua layer 'native'
AppendLuaLayer ( chain , "native" ) ;
2014-01-28 04:39:27 +00:00
2014-04-15 21:46:18 +00:00
//and OSD goes on top of that
2014-07-22 00:04:28 +00:00
//TODO - things break if this isnt present (the final presentation filter gets messed up)
//so, always include it (we'll handle this flag in the callback to do no rendering)
//if (includeOSD)
chain . AddFilter ( fOSD , "osd" ) ;
2014-02-03 05:27:59 +00:00
2014-04-15 21:46:18 +00:00
return chain ;
}
2014-01-28 04:39:27 +00:00
2014-06-29 02:28:48 +00:00
void AppendRetroShaderChain ( FilterProgram program , string name , Filters . RetroShaderChain retroChain , Dictionary < string , object > properties )
2014-04-15 21:46:18 +00:00
{
for ( int i = 0 ; i < retroChain . Passes . Length ; i + + )
{
var pass = retroChain . Passes [ i ] ;
var rsp = new Filters . RetroShaderPass ( retroChain , i ) ;
string fname = string . Format ( "{0}[{1}]" , name , i ) ;
program . AddFilter ( rsp , fname ) ;
rsp . Parameters = properties ;
}
}
2014-06-29 02:28:48 +00:00
Filters . LuaLayer AppendLuaLayer ( FilterProgram chain , string name )
2014-04-15 21:46:18 +00:00
{
Texture2d luaNativeTexture = null ;
var luaNativeSurface = LuaSurfaceSets [ name ] . GetCurrent ( ) ;
if ( luaNativeSurface = = null )
2014-05-07 01:27:41 +00:00
return null ;
2014-04-15 21:46:18 +00:00
luaNativeTexture = LuaSurfaceFrugalizers [ name ] . Get ( luaNativeSurface ) ;
var fLuaLayer = new Filters . LuaLayer ( ) ;
fLuaLayer . SetTexture ( luaNativeTexture ) ;
chain . AddFilter ( fLuaLayer , name ) ;
2014-05-07 01:27:41 +00:00
return fLuaLayer ;
2014-04-15 21:46:18 +00:00
}
/// <summary>
/// Using the current filter program, turn a mouse coordinate from window space to the original emulator screen space.
/// </summary>
public Point UntransformPoint ( Point p )
{
//first, turn it into a window coordinate
p = presentationPanel . Control . PointToClient ( p ) ;
//now, if theres no filter program active, just give up
if ( CurrentFilterProgram = = null ) return p ;
//otherwise, have the filter program untransform it
Vector2 v = new Vector2 ( p . X , p . Y ) ;
v = CurrentFilterProgram . UntransformPoint ( "default" , v ) ;
return new Point ( ( int ) v . X , ( int ) v . Y ) ;
2014-10-10 00:15:15 +00:00
}
/// <summary>
/// Using the current filter program, turn a emulator screen space coordinat to a window coordinate (suitable for lua layer drawing)
/// </summary>
public Point TransformPoint ( Point p )
{
//now, if theres no filter program active, just give up
if ( CurrentFilterProgram = = null ) return p ;
//otherwise, have the filter program untransform it
Vector2 v = new Vector2 ( p . X , p . Y ) ;
v = CurrentFilterProgram . TransformPoint ( "default" , v ) ;
return new Point ( ( int ) v . X , ( int ) v . Y ) ;
2014-04-15 21:46:18 +00:00
}
2014-05-17 03:50:19 +00:00
2014-04-15 21:46:18 +00:00
/// <summary>
/// This will receive an emulated output frame from an IVideoProvider and run it through the complete frame processing pipeline
/// Then it will stuff it into the bound PresentationPanel.
/// ---
/// If the int[] is size=1, then it contains an openGL texture ID (and the size should be as specified from videoProvider)
/// Don't worry about the case where the frontend isnt using opengl; it isnt supported yet, and it will be my responsibility to deal with anyway
/// </summary>
public void UpdateSource ( IVideoProvider videoProvider )
2014-05-17 03:50:19 +00:00
{
2014-06-02 20:16:59 +00:00
var job = new JobInfo
{
videoProvider = videoProvider ,
simulate = false ,
2014-07-22 00:04:28 +00:00
chain_outsize = GraphicsControl . Size ,
includeOSD = true
2014-06-02 20:16:59 +00:00
} ;
UpdateSourceInternal ( job ) ;
2014-05-17 03:50:19 +00:00
}
2014-07-22 00:04:28 +00:00
public BitmapBuffer RenderOffscreen ( IVideoProvider videoProvider , bool includeOSD )
2014-06-02 20:16:59 +00:00
{
var job = new JobInfo
{
videoProvider = videoProvider ,
simulate = false ,
chain_outsize = GraphicsControl . Size ,
2014-07-22 00:04:28 +00:00
offscreen = true ,
includeOSD = includeOSD
2014-06-02 20:16:59 +00:00
} ;
UpdateSourceInternal ( job ) ;
return job . offscreenBB ;
}
2014-05-17 03:50:19 +00:00
class FakeVideoProvider : IVideoProvider
{
public int [ ] GetVideoBuffer ( ) { return new int [ ] { } ; }
public int VirtualWidth { get ; set ; }
public int VirtualHeight { get ; set ; }
public int BufferWidth { get ; set ; }
public int BufferHeight { get ; set ; }
public int BackgroundColor { get ; set ; }
}
/// <summary>
/// Attempts to calculate a good client size with the given zoom factor, considering the user's DisplayManager preferences
/// </summary>
public Size CalculateClientSize ( IVideoProvider videoProvider , int zoom )
{
2014-09-14 00:51:30 +00:00
bool ar_active = Global . Config . DispFixAspectRatio ;
bool ar_system = Global . Config . DispManagerAR = = Config . EDispManagerAR . System ;
bool ar_custom = Global . Config . DispManagerAR = = Config . EDispManagerAR . Custom ;
bool ar_correct = ar_system | | ar_custom ;
bool ar_unity = ! ar_correct ;
bool ar_integer = Global . Config . DispFixScaleInteger ;
2014-07-20 06:51:31 +00:00
int bufferWidth = videoProvider . BufferWidth ;
int bufferHeight = videoProvider . BufferHeight ;
int virtualWidth = videoProvider . VirtualWidth ;
int virtualHeight = videoProvider . VirtualHeight ;
2014-09-14 00:51:30 +00:00
if ( ar_custom )
{
virtualWidth = Global . Config . DispCustomUserARWidth ;
virtualHeight = Global . Config . DispCustomUserARHeight ;
}
//Console.WriteLine("DISPZOOM " + zoom); //test
2014-07-20 06:51:31 +00:00
//old stuff
var fvp = new FakeVideoProvider ( ) ;
fvp . BufferWidth = bufferWidth ;
fvp . BufferHeight = bufferHeight ;
fvp . VirtualWidth = virtualWidth ;
fvp . VirtualHeight = virtualHeight ;
2014-05-17 03:50:19 +00:00
Size chain_outsize = new Size ( fvp . BufferWidth * zoom , fvp . BufferHeight * zoom ) ;
2014-05-28 02:19:51 +00:00
2014-07-20 06:51:31 +00:00
if ( ar_active )
{
2014-09-14 00:51:30 +00:00
if ( ar_correct )
2014-07-20 06:51:31 +00:00
{
if ( ar_integer )
{
Vector2 VS = new Vector2 ( virtualWidth , virtualHeight ) ;
Vector2 BS = new Vector2 ( bufferWidth , bufferHeight ) ;
Vector2 AR = Vector2 . Divide ( VS , BS ) ;
float target_par = ( AR . X / AR . Y ) ;
Vector2 PS = new Vector2 ( 1 , 1 ) ; //this would malfunction for AR <= 0.5 or AR >= 2.0
//here's how we define zooming, in this case:
//make sure each step is an increment of zoom for at least one of the dimensions (or maybe both of them)
//look for the increment which helps the AR the best
//TODO - this cant possibly support scale factors like 1.5x
//TODO - also, this might be messing up zooms and stuff, we might need to run this on the output size of the filter chain
for ( int i = 1 ; i < zoom ; i + + )
{
//would not be good to run this per frame, but it seems to only run when the resolution changes, etc.
Vector2 [ ] trials = new [ ] {
PS + new Vector2 ( 1 , 0 ) ,
PS + new Vector2 ( 0 , 1 ) ,
PS + new Vector2 ( 1 , 1 )
} ;
int bestIndex = - 1 ;
float bestValue = 1000.0f ;
for ( int t = 0 ; t < trials . Length ; t + + )
{
//I.
float test_ar = trials [ t ] . X / trials [ t ] . Y ;
//II.
//Vector2 calc = Vector2.Multiply(trials[t], VS);
//float test_ar = calc.X / calc.Y;
//not clear which approach is superior
float deviation_linear = Math . Abs ( test_ar - target_par ) ;
float deviation_geom = test_ar / target_par ;
if ( deviation_geom < 1 ) deviation_geom = 1.0f / deviation_geom ;
float value = deviation_linear ;
if ( value < bestValue )
{
bestIndex = t ;
bestValue = value ;
}
}
//is it possible to get here without selecting one? doubtful.
PS = trials [ bestIndex ] ;
}
chain_outsize = new Size ( ( int ) ( bufferWidth * PS . X ) , ( int ) ( bufferHeight * PS . Y ) ) ;
}
else
{
//obey the AR, but allow free scaling: just zoom the virtual size
chain_outsize = new Size ( virtualWidth * zoom , virtualHeight * zoom ) ;
}
}
else
{
//ar_unity:
//just choose to zoom the buffer (make no effort to incorporate AR)
chain_outsize = new Size ( bufferWidth * zoom , bufferHeight * zoom ) ;
}
}
else
{
//!ar_active:
//just choose to zoom the buffer (make no effort to incorporate AR)
chain_outsize = new Size ( bufferWidth * zoom , bufferHeight * zoom ) ;
}
2014-05-17 03:50:19 +00:00
2014-06-02 20:16:59 +00:00
var job = new JobInfo
{
videoProvider = fvp ,
simulate = true ,
2014-07-22 00:04:28 +00:00
chain_outsize = chain_outsize ,
2014-06-02 20:16:59 +00:00
} ;
var filterProgram = UpdateSourceInternal ( job ) ;
2014-05-17 03:50:19 +00:00
var size = filterProgram . Filters [ filterProgram . Filters . Count - 1 ] . FindOutput ( ) . SurfaceFormat . Size ;
2014-07-20 06:51:31 +00:00
Console . WriteLine ( "Selecting size " + size . ToString ( ) ) ;
2014-05-17 03:50:19 +00:00
return size ;
}
2014-06-02 20:16:59 +00:00
class JobInfo
{
public IVideoProvider videoProvider ;
public bool simulate ;
public Size chain_outsize ;
public bool offscreen ;
public BitmapBuffer offscreenBB ;
2014-07-22 00:04:28 +00:00
public bool includeOSD ;
2014-06-02 20:16:59 +00:00
}
2014-06-29 02:28:48 +00:00
FilterProgram UpdateSourceInternal ( JobInfo job )
2014-04-15 21:46:18 +00:00
{
2014-06-08 23:30:34 +00:00
GlobalWin . GLManager . Activate ( CR_GraphicsControl ) ;
2014-06-02 20:16:59 +00:00
IVideoProvider videoProvider = job . videoProvider ;
bool simulate = job . simulate ;
Size chain_outsize = job . chain_outsize ;
2014-05-02 04:27:08 +00:00
int vw = videoProvider . BufferWidth ;
int vh = videoProvider . BufferHeight ;
2014-05-03 02:52:35 +00:00
2014-09-14 00:51:30 +00:00
if ( Global . Config . DispFixAspectRatio )
2014-05-02 04:27:08 +00:00
{
2014-09-14 00:51:30 +00:00
if ( Global . Config . DispManagerAR = = Config . EDispManagerAR . System )
{
vw = videoProvider . VirtualWidth ;
vh = videoProvider . VirtualHeight ;
}
if ( Global . Config . DispManagerAR = = Config . EDispManagerAR . Custom )
{
vw = Global . Config . DispCustomUserARWidth ;
vh = Global . Config . DispCustomUserARHeight ;
}
2014-05-02 04:27:08 +00:00
}
2014-04-15 21:46:18 +00:00
int [ ] videoBuffer = videoProvider . GetVideoBuffer ( ) ;
TESTEROO :
int bufferWidth = videoProvider . BufferWidth ;
int bufferHeight = videoProvider . BufferHeight ;
bool isGlTextureId = videoBuffer . Length = = 1 ;
BitmapBuffer bb = null ;
2014-05-17 03:50:19 +00:00
Texture2d videoTexture = null ;
if ( ! simulate )
2014-04-15 21:46:18 +00:00
{
2014-05-17 03:50:19 +00:00
if ( isGlTextureId )
{
videoTexture = GL . WrapGLTexture2d ( new IntPtr ( videoBuffer [ 0 ] ) , bufferWidth , bufferHeight ) ;
}
else
{
//wrap the videoprovider data in a BitmapBuffer (no point to refactoring that many IVideoProviders)
bb = new BitmapBuffer ( bufferWidth , bufferHeight , videoBuffer ) ;
2014-04-15 21:46:18 +00:00
2014-05-17 03:50:19 +00:00
//now, acquire the data sent from the videoProvider into a texture
videoTexture = VideoTextureFrugalizer . Get ( bb ) ;
GL . SetTextureWrapMode ( videoTexture , true ) ;
}
2014-04-15 21:46:18 +00:00
2014-05-17 03:50:19 +00:00
//TEST (to be removed once we have an actual example of bring in a texture ID from opengl emu core):
if ( ! isGlTextureId )
{
videoBuffer = new int [ 1 ] { videoTexture . Id . ToInt32 ( ) } ;
goto TESTEROO ;
}
2014-04-15 21:46:18 +00:00
}
//record the size of what we received, since lua and stuff is gonna want to draw onto it
currEmuWidth = bufferWidth ;
currEmuHeight = bufferHeight ;
//build the default filter chain and set it up with services filters will need
2014-05-07 01:27:41 +00:00
Size chain_insize = new Size ( bufferWidth , bufferHeight ) ;
2014-05-17 03:50:19 +00:00
2014-07-22 00:04:28 +00:00
var filterProgram = BuildDefaultChain ( chain_insize , chain_outsize , job . includeOSD ) ;
2014-05-17 03:50:19 +00:00
filterProgram . GuiRenderer = Renderer ;
filterProgram . GL = GL ;
2014-04-15 21:46:18 +00:00
//setup the source image filter
2014-05-17 03:50:19 +00:00
Filters . SourceImage fInput = filterProgram [ "input" ] as Filters . SourceImage ;
2014-04-15 21:46:18 +00:00
fInput . Texture = videoTexture ;
//setup the final presentation filter
2014-05-17 03:50:19 +00:00
Filters . FinalPresentation fPresent = filterProgram [ "presentation" ] as Filters . FinalPresentation ;
2014-05-07 01:27:41 +00:00
fPresent . VirtualTextureSize = new Size ( vw , vh ) ;
2014-05-03 02:52:35 +00:00
fPresent . TextureSize = new Size ( bufferWidth , bufferHeight ) ;
2014-04-15 21:46:18 +00:00
fPresent . BackgroundColor = videoProvider . BackgroundColor ;
fPresent . GuiRenderer = Renderer ;
fPresent . GL = GL ;
2014-06-02 20:16:59 +00:00
filterProgram . Compile ( "default" , chain_insize , chain_outsize , ! job . offscreen ) ;
2014-05-01 07:10:07 +00:00
2014-05-17 03:50:19 +00:00
if ( simulate )
{
}
else
{
CurrentFilterProgram = filterProgram ;
2014-06-02 20:16:59 +00:00
UpdateSourceDrawingWork ( job ) ;
2014-05-17 03:50:19 +00:00
}
//cleanup:
if ( bb ! = null ) bb . Dispose ( ) ;
return filterProgram ;
}
2014-06-02 20:16:59 +00:00
void UpdateSourceDrawingWork ( JobInfo job )
2014-05-17 03:50:19 +00:00
{
2014-05-01 07:10:07 +00:00
//begin rendering on this context
//should this have been done earlier?
//do i need to check this on an intel video card to see if running excessively is a problem? (it used to be in the FinalTarget command below, shouldnt be a problem)
2014-06-08 23:30:34 +00:00
//GraphicsControl.Begin();
2014-04-15 21:46:18 +00:00
//run filter chain
Texture2d texCurr = null ;
RenderTarget rtCurr = null ;
int rtCounter = 0 ;
bool inFinalTarget = false ;
foreach ( var step in CurrentFilterProgram . Program )
{
switch ( step . Type )
{
2014-06-29 02:28:48 +00:00
case FilterProgram . ProgramStepType . Run :
2014-04-15 21:46:18 +00:00
{
2014-05-17 03:50:19 +00:00
int fi = ( int ) step . Args ;
var f = CurrentFilterProgram . Filters [ fi ] ;
f . SetInput ( texCurr ) ;
f . Run ( ) ;
var orec = f . FindOutput ( ) ;
if ( orec ! = null )
2014-04-15 21:46:18 +00:00
{
2014-06-29 02:28:48 +00:00
if ( orec . SurfaceDisposition = = SurfaceDisposition . Texture )
2014-05-17 03:50:19 +00:00
{
texCurr = f . GetOutput ( ) ;
rtCurr = null ;
}
2014-04-15 21:46:18 +00:00
}
2014-05-17 03:50:19 +00:00
break ;
2014-04-15 21:46:18 +00:00
}
2014-06-29 02:28:48 +00:00
case FilterProgram . ProgramStepType . NewTarget :
2014-05-17 03:50:19 +00:00
{
var size = ( Size ) step . Args ;
rtCurr = ShaderChainFrugalizers [ rtCounter + + ] . Get ( size ) ;
rtCurr . Bind ( ) ;
CurrentFilterProgram . CurrRenderTarget = rtCurr ;
break ;
}
2014-06-29 02:28:48 +00:00
case FilterProgram . ProgramStepType . FinalTarget :
2014-06-02 20:16:59 +00:00
{
var size = ( Size ) step . Args ;
inFinalTarget = true ;
rtCurr = null ;
CurrentFilterProgram . CurrRenderTarget = null ;
GL . BindRenderTarget ( null ) ;
break ;
}
2014-04-15 21:46:18 +00:00
}
}
2014-06-02 20:16:59 +00:00
if ( job . offscreen )
2014-05-01 06:37:51 +00:00
{
2014-06-02 20:16:59 +00:00
job . offscreenBB = rtCurr . Texture2d . Resolve ( ) ;
2014-05-01 06:37:51 +00:00
}
2014-06-02 20:16:59 +00:00
else
{
Debug . Assert ( inFinalTarget ) ;
//apply the vsync setting (should probably try to avoid repeating this)
bool vsync = Global . Config . VSyncThrottle | | Global . Config . VSync ;
if ( LastVsyncSetting ! = vsync | | LastVsyncSettingGraphicsControl ! = presentationPanel . GraphicsControl )
{
presentationPanel . GraphicsControl . SetVsync ( vsync ) ;
LastVsyncSettingGraphicsControl = presentationPanel . GraphicsControl ;
LastVsyncSetting = vsync ;
}
2014-01-28 04:39:27 +00:00
2014-06-02 20:16:59 +00:00
//present and conclude drawing
presentationPanel . GraphicsControl . SwapBuffers ( ) ;
2014-04-26 21:55:04 +00:00
2014-06-02 20:16:59 +00:00
//nope. dont do this. workaround for slow context switching on intel GPUs. just switch to another context when necessary before doing anything
//presentationPanel.GraphicsControl.End();
2014-01-28 04:39:27 +00:00
2014-06-02 20:16:59 +00:00
NeedsToPaint = false ; //??
}
2012-04-16 08:18:41 +00:00
}
2014-05-01 06:37:51 +00:00
bool? LastVsyncSetting ;
GraphicsControl LastVsyncSettingGraphicsControl ;
2014-02-14 00:55:18 +00:00
Dictionary < string , DisplaySurface > MapNameToLuaSurface = new Dictionary < string , DisplaySurface > ( ) ;
Dictionary < DisplaySurface , string > MapLuaSurfaceToName = new Dictionary < DisplaySurface , string > ( ) ;
Dictionary < string , SwappableDisplaySurfaceSet > LuaSurfaceSets = new Dictionary < string , SwappableDisplaySurfaceSet > ( ) ;
2012-04-16 08:18:41 +00:00
SwappableDisplaySurfaceSet luaNativeSurfaceSet = new SwappableDisplaySurfaceSet ( ) ;
public void SetLuaSurfaceNativePreOSD ( DisplaySurface surface ) { luaNativeSurfaceSet . SetPending ( surface ) ; }
2014-02-14 00:55:18 +00:00
2014-06-03 03:09:43 +00:00
/// <summary>
/// Peeks a locked lua surface, or returns null if it isnt locked
/// </summary>
public DisplaySurface PeekLockedLuaSurface ( string name )
{
if ( MapNameToLuaSurface . ContainsKey ( name ) )
return MapNameToLuaSurface [ name ] ;
return null ;
}
2014-02-14 00:55:18 +00:00
/// <summary>
/// Locks the requested lua surface name
/// </summary>
public DisplaySurface LockLuaSurface ( string name )
2012-04-16 08:18:41 +00:00
{
2014-02-14 00:55:18 +00:00
if ( MapNameToLuaSurface . ContainsKey ( name ) )
throw new InvalidOperationException ( "Lua surface is already locked: " + name ) ;
SwappableDisplaySurfaceSet sdss ;
if ( ! LuaSurfaceSets . TryGetValue ( name , out sdss ) )
{
sdss = new SwappableDisplaySurfaceSet ( ) ;
LuaSurfaceSets . Add ( name , sdss ) ;
}
//placeholder logic for more abstracted surface definitions from filter chain
2014-01-28 04:39:27 +00:00
int currNativeWidth = presentationPanel . NativeSize . Width ;
int currNativeHeight = presentationPanel . NativeSize . Height ;
2014-02-14 00:55:18 +00:00
int width , height ;
if ( name = = "emu" ) { width = currEmuWidth ; height = currEmuHeight ; }
else if ( name = = "native" ) { width = currNativeWidth ; height = currNativeHeight ; }
else throw new InvalidOperationException ( "Unknown lua surface name: " + name ) ;
DisplaySurface ret = sdss . AllocateSurface ( width , height ) ;
MapNameToLuaSurface [ name ] = ret ;
MapLuaSurfaceToName [ ret ] = name ;
return ret ;
2012-04-16 08:18:41 +00:00
}
2014-04-15 21:46:18 +00:00
public void ClearLuaSurfaces ( )
{
foreach ( var kvp in LuaSurfaceSets )
{
2014-06-03 03:09:43 +00:00
var surf = PeekLockedLuaSurface ( kvp . Key ) ;
DisplaySurface surfLocked = null ;
if ( surf = = null )
surf = surfLocked = LockLuaSurface ( kvp . Key ) ;
2014-04-15 21:46:18 +00:00
surf . Clear ( ) ;
2014-06-03 03:09:43 +00:00
if ( surfLocked ! = null )
UnlockLuaSurface ( surfLocked ) ;
2014-05-01 07:10:07 +00:00
LuaSurfaceSets [ kvp . Key ] . SetPending ( null ) ;
2014-04-15 21:46:18 +00:00
}
}
2014-02-14 00:55:18 +00:00
/// <summary>
/// Unlocks this DisplaySurface which had better have been locked as a lua surface
/// </summary>
public void UnlockLuaSurface ( DisplaySurface surface )
2012-05-06 07:09:04 +00:00
{
2014-02-14 00:55:18 +00:00
if ( ! MapLuaSurfaceToName . ContainsKey ( surface ) )
throw new InvalidOperationException ( "Surface was not locked as a lua surface" ) ;
string name = MapLuaSurfaceToName [ surface ] ;
MapLuaSurfaceToName . Remove ( surface ) ;
MapNameToLuaSurface . Remove ( name ) ;
LuaSurfaceSets [ name ] . SetPending ( surface ) ;
2012-05-06 07:09:04 +00:00
}
2014-01-28 04:39:27 +00:00
//helper classes:
2012-04-16 08:18:41 +00:00
2014-01-28 04:39:27 +00:00
class MyBlitter : IBlitter
2012-04-16 08:18:41 +00:00
{
2014-01-28 04:39:27 +00:00
DisplayManager Owner ;
public MyBlitter ( DisplayManager dispManager )
{
Owner = dispManager ;
}
2012-04-16 08:18:41 +00:00
2014-01-28 04:39:27 +00:00
class FontWrapper : IBlitterFont
{
public FontWrapper ( StringRenderer font )
{
this . font = font ;
}
2012-04-16 08:18:41 +00:00
2014-01-28 04:39:27 +00:00
public readonly StringRenderer font ;
}
IBlitterFont IBlitter . GetFontType ( string fontType ) { return new FontWrapper ( Owner . TheOneFont ) ; }
void IBlitter . DrawString ( string s , IBlitterFont font , Color color , float x , float y )
{
var stringRenderer = ( ( FontWrapper ) font ) . font ;
Owner . Renderer . SetModulateColor ( color ) ;
stringRenderer . RenderString ( Owner . Renderer , x , y , s ) ;
Owner . Renderer . SetModulateColorWhite ( ) ;
}
SizeF IBlitter . MeasureString ( string s , IBlitterFont font )
{
var stringRenderer = ( ( FontWrapper ) font ) . font ;
return stringRenderer . Measure ( s ) ;
}
public Rectangle ClipBounds { get ; set ; }
2012-06-10 23:27:30 +00:00
}
2012-06-10 20:48:04 +00:00
2014-04-15 21:46:18 +00:00
2012-04-16 08:18:41 +00:00
}
2014-01-28 04:39:27 +00:00
2012-04-16 08:18:41 +00:00
}