Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Hardware & Software > Software players
Register FAQ Calendar Today's Posts Search

Reply
 
Thread Tools Search this Thread Display Modes
Old 23rd October 2014, 11:54   #241  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
RenderScript interfaces and classes

MPDN v2.8.4 (To be updated to v2.9.0 soon!!!)

IRenderer.cs

PHP Code:
using System.Drawing;
using Mpdn.RenderScript.Scaler;
using SharpDX;
using SharpDX.Direct3D9;
using Point System.Drawing.Point;

namespace 
Mpdn.RenderScript
{
    public interface 
IRenderer
    
{
        
// Returns original video size
        
Size VideoSize get; }

        
// Returns original luma size
        
Size LumaSize get; }

        
// Returns original chroma size
        
Size ChromaSize get; }

        
// Returns the aspect ratio (nominator=X and denominator=Y)
        
Point AspectRatio get; }

        
// Returns the target window size (after applying letterboxing)
        // Can change mid-playback (OnTargetSizeChanged will be called)
        
Size TargetSize get; }

        
// Returns the YUV colorimetrics type (as seen on Ctrl-J stats screen)
        // Can change mid-playback (OnTargetSizeChanged will be called)
        
YuvColorimetric Colorimetric get; }

        
// Returns the input format (as seen on Ctrl-J stats screen)
        
FrameBufferInputFormat InputFormat get; }

        
// Returns the current output bit depth
        // Can change mid-playback (OnTargetSizeChanged will be called)
        
BitDepth OutputBitDepth get; }

        
// Original Y plane texture
        
ITexture TextureY get; }

        
// Original U plane texture
        
ITexture TextureU get; }

        
// Original V plane texture
        
ITexture TextureV get; }

        
// The active luma upscaler (as set in MPDN options menu)
        
IScaler LumaUpscaler get; }

        
// The active luma downscaler (as set in MPDN options menu)
        
IScaler LumaDownscaler get; }

        
// The active chroma upscaler (as set in MPDN options menu)
        
IScaler ChromaUpscaler get; }

        
// The active chroma downscaler (as set in MPDN options menu)
        
IScaler ChromaDownscaler get; }
        
        
// The input render target that holds the MPDN prescaled texture
        
ITexture InputRenderTarget get; }

        
// The output render target that MPDN will display on screen
        // InputRenderTarget can the same as OutputRenderTarget if both can share the same texture
        
ITexture OutputRenderTarget get; }

        
// When render scripts are chained, the next render script in the chain could request for its input in YUV
        
bool OutputYuv get; }

        
// Create a render target of 'size' to use in the script
        
ITexture CreateRenderTarget(Size size);

        
// Create a texture of 'size' to use in the script
        
ITexture CreateTexture(Size size);

        
// Update the texture created via 'CreateTexture'
        // 'data' is of type Half (16-bit float) and the length of the array must be equals to 
        // texture width * 4 (RGBA) * height
        
void UpdateTexture(ITexture textureHalf[] data);
        
        
// Compile shader program (hlsl) and return a shader object
        
IShader CompileShader(string filename);

        
// Load precompiled shader program (compiled shader object) and return a shader object
        
IShader LoadShader(string filename);

        
// Render to renderTarget using the specified shader
        
void Render(ITexture renderTargetIShader shader);

        
// Render to renderTarget using the specified texture (no pixel shader)
        // enableHwFiltering = true sets to linear filter, false sets it to point filtering
        
void Render(ITexture renderTargetITexture texturebool enableHwFiltering);
        
        
// Same as above except it uses MPDN's internal scaler
        // 'Scale' automatically determines which scaler (upscaler or downscaler, or none) to use depending
        // on whether the render to renderTarget is an upscale, downscale or straight blit
        
void Scale(ITexture renderTargetITexture textureIScaler upscalerIScaler downscaler);
    }

IScriptRenderer.cs

PHP Code:
namespace Mpdn.RenderScript
{
    public interface 
IScriptRenderer
    
{
        
ScriptDescriptor Descriptor get; }

        
ScriptInputDescriptor InputDescriptor get; }
        
        
// Called immediately when this class is instantiated
        // 'instanceId' is zero-based index of the instance of this class
        // in a render script chain
        
void Initialize(int instanceId);

        
// Will not be called if InputDescriptor.HasConfigDialog returns false
        
void ShowConfigDialog();

        
// Called when this object is assigned to MPDN's renderer
        // This will happen before OnTargetSizeChanged() and Render() ever gets called
        
void Setup(IRenderer renderer);

        
// Called when target size has changed (e.g. window resize)
        
void OnTargetSizeChanged();

        
// Render your frame to IRenderer.OutputRenderTarget here
        
void Render();
    }

ITexture.cs

PHP Code:
using SharpDX.Direct3D9;

namespace 
Mpdn.RenderScript
{
    public interface 
ITexture
    
{
        
Texture Texture get; }
        
Surface Surface get; }

        
int Width get; }
        
int Height get; }
    }

IShader.cs

PHP Code:
using System;
using System.IO;
using SharpDX.Direct3D9;

namespace 
Mpdn.RenderScript
{
    public interface 
IShader IDisposable
    
{
        
void SetConstant<T>(string identifierT valuebool throwOnErrorwhere T struct;

        
void SetTextureConstant(string identifierTexture valuebool enableHwFilteringbool throwOnError);

        
void SetTextureConstant(string identifierBaseTexture valuebool enableHwFilteringbool throwOnError);
    }

ScriptDescriptor.cs

PHP Code:
using System;

namespace 
Mpdn.RenderScript
{
    public class 
ScriptDescriptor
    
{
        
// Each script needs a unique GUID
        
public Guid Guid Guid.Empty;
        public 
string Name;
        public 
string Description;
        public 
string Copyright;
        public 
bool HasConfigDialog;
    }

ScriptInputDescriptor.cs

PHP Code:
using System.Drawing;

namespace 
Mpdn.RenderScript
{
    public class 
ScriptInputDescriptor
    
{
        
// IRenderer object's InputRenderTarget will be provided in this size
        
public Size Size;
        
// If true is returned, dithering is disabled and the prescaled texture will 
        // be provided in YUV format
        
public bool WantYuv;
        
// Set Prescale to false if don't need MPDN to prescale Y, U and V textures
        // In which case, IRenderer object's InputRenderTarget will be blank
        // Typically, you'd only set this to false if you are doing your own scaling
        // and you are using TextureY, TextureU and TextureV directly to render your
        // target (which means you don't need MPDN's prescaled texture)
        // Default is true
        
public bool Prescale true;
    }

Scalers.cs

PHP Code:
using SharpDX.Direct3D9;

namespace 
Mpdn.RenderScript.Scaler
{
    public interface 
IScaler
    
{
        
ImageScaler ScalerType get; }
    }
}

// Scaler types and their constructors:
//     HwPoint();
//     HwBilinear();
//     Bilinear();
//     Softcubic(float softness); // softness 0.0f - 1.0f
//     Bicubic(float sharpness, bool antiRingingEnabled); // sharpness 0.0f - 1.0f
//     Lanczos(Lanczos.Taps tapCount, bool antiRingingEnabled) // tapCount Lanczos.Taps.Four, Six, Eight, Twelve, Sixteen
//     Spline(Spline.Taps tapCount, bool antiRingingEnabled) // tapCount Spline.Taps.Four, Six, Eight
//     Jinc(Jinc.Taps tapCount, bool antiRingingEnabled) // tapCount Jinc.Taps.Four, Six, Eight, Twelve, Sixteen

// Usage example:
// IScaler upscaler = new HwPoint();
// IScaler upscaler = new Jinc(Jinc.Taps.Sixteen, true); 
Declarations.cs

PHP Code:
using System.ComponentModel;

namespace 
Mpdn.RenderScript
{
    public 
enum FrameBufferInputFormat
    
{
        [
Description("NV12 8-bit 4:2:0")]
        
Nv12,
        [
Description("YV12 8-bit 4:2:0")]
        
Yv12,
        [
Description("P010 10-bit 4:2:0")]
        
P010,
        [
Description("P016 16-bit 4:2:0")]
        
P016
    
}

    public 
enum YuvColorimetric
    
{
        [
Description("Full Range")]
        
FullRange,
        [
Description("ITU-R Rec BT.601")]
        
ItuBt601,
        [
Description("ITU-R Rec BT.709")]
        
ItuBt709
    
}

    public 
enum BitDepth
    
{
        [
Description("8 bits")]
        
EightBits,
        [
Description("10 bits")]
        
TenBits,
        [
Description("16 bits")]
        
SixteenBits
    
}
    
    public 
enum ImageScaler
    
{
        
Hardware,
        
Bilinear,
        
Bicubic,
        
Softcubic,
        
Lanczos,
        
Spline,
        
Jinc
    
}


Last edited by Zachs; 8th November 2014 at 14:06.
Zachs is offline   Reply With Quote
Old 23rd October 2014, 14:10   #242  |  Link
Anime Viewer
Troubleshooter
 
Anime Viewer's Avatar
 
Join Date: Feb 2014
Posts: 339
Quote:
Originally Posted by Zachs View Post
Hi everyone,



Starting v2.8.0, MPDN has a new feature called "Render Script". This takes advantage of C#-script to allow user to fully customize / rewrite the entire rendering pipeline of MPDN. Essentially, all of MPDN's current scaling algorithms could be fully implemented via its Render Script system and run as fast and efficient as it is implemented in MPDN. In other words, there is no advantage for your custom algo to be coded in MPDN. In fact, in moving forward, any new scaling algorithms I implement for MPDN will be via its Render Script system. In the RenderScript folder, you will find some sample scripts which by no means showcase anywhere near its full capabilities. Detailed documentation and more advanced samples to follow (this will take some time - some help appreciated).

[/CODE]
I'm guessing I'm not understanding how the Renderscripts and example SweetFX shaders enable. Selecting the monochrome option does turn the video black and white, but the other SweetFX doesn't appear to change the picture in any way from when the none setting is selected instead, and in all cases the configuration button is greyed out when the SweetFX options are selected.

The SweetFX is supposed to have an effect like shown in the comparison pictures at the link below, correct?
http://forums.elderscrollsonline.com...likely-missing
__________________
System specs: Sager NP9150 SE with i7-3630QM 2.40GHz, 16 GB RAM, 64-bit Windows 10 Pro, NVidia GTX 680M/Intel 4000 HD optimus dual GPU system. Video viewed on LG notebook screen and LG 3D passive TV.
Anime Viewer is offline   Reply With Quote
Old 23rd October 2014, 14:27   #243  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
SweetFX is a suite of Pixel Shader files for post processing. The two render scripts are really just examples of what could be done. The luma shapen one, take a look at the corresponding files in the SweetFX folder (LumaSharpen.hlsl) for the different settings you could choose.

MPDN render scripts are like winamp dsp plugins except there is no need to compile them into DLLs. If you have used post processing shaders before then render script is similar but is much more flexible.

As to why configure button is disabled, that's because the example render scripts say there's nothing to configure.

Ultimately this opens up MPDN for full extensibility by the community. For example, Shiandow's amazing NEDI scaling algo could be implemented and it would be just a click away to activate it.

Last edited by Zachs; 23rd October 2014 at 14:33.
Zachs is offline   Reply With Quote
Old 24th October 2014, 06:06   #244  |  Link
patul
Registered User
 
Join Date: Sep 2005
Posts: 130
Thanks for the new version. A bit unusual approach to implement custom shader support, on contrary to other players. I was bit skeptical to this approach, but then I realized the full potential of this approach, it will be easier to port custom scalers into MPDN, I would love to see Shiandow's NEDI scaler ported to MPDN.

However, I still think you probably have to consider non-developer users too, if you want this player to be widely used. Convenience of usage is needed, for example stacking, ordering of shaders.
patul is offline   Reply With Quote
Old 24th October 2014, 06:21   #245  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
Quote:
Originally Posted by patul View Post
Thanks for the new version. A bit unusual approach to implement custom shader support, on contrary to other players. I was bit skeptical to this approach, but then I realized the full potential of this approach, it will be easier to port custom scalers into MPDN, I would love to see Shiandow's NEDI scaler ported to MPDN.

However, I still think you probably have to consider non-developer users too, if you want this player to be widely used. Convenience of usage is needed, for example stacking, ordering of shaders.
Already well into this chaining/ordering of scripts (current implementation of render script lacks the ability to stack/order multiple scripts). However, stacking/ordering "plain old" shaders can be easily created via the current implementation of render script. If anyone wants to have a go at creating such a script, I'll be happy to provide any assistance. If not, I'll be doing that when I get some time.

Render script is at its infancy now for end-users especially. The whole render script system was created in consultation with Shiandow based on his experience on what was lacking in other players / renderers' custom pixel shader system. I could use with a little help though especially with that plain old pixel shader as post processing filter stacking script.

Last edited by Zachs; 24th October 2014 at 06:24.
Zachs is offline   Reply With Quote
Old 24th October 2014, 10:58   #246  |  Link
chainik_svp
Registered User
 
Join Date: Mar 2012
Location: Saint-Petersburg
Posts: 239
Tested on i5-2430M + AMD 6470M

Common settings: LAV Video decoder (DXVA2 Copy-back mode) + ffdshow raw filter
MPC-HC setting: EVR-CP (bilinear resizer)
MPDN settings: D3D9 renderer on adapter 0, bilinear upscalers, no dithering
SVP settings: the very same in all cases, up to 60 fps, GPU accelerated through AMD's card
Video file: 1280x720 @24

Measured CPU load:

- MPC-HC x32, no SVP: 0-10%
- MPC-HC x64, no SVP: 5-15%
- MPDN x32, no SVP: 10-20%
- MPDN x64, no SVP: 20-30%

- MPC-HC x32, SVP: ~20%
- MPC-HC x64, SVP: ~30%
- MPDN x32, SVP: ~40%
- MPDN x64, SVP: 55-60%

o_O
__________________
SVPflow motion interpolation

Last edited by chainik_svp; 24th October 2014 at 11:09.
chainik_svp is offline   Reply With Quote
Old 24th October 2014, 11:22   #247  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
As said a few pages ago MPDN does a lot more than mpchc EVR CP even with the same settings. Among others there is frame drop / delay prevention that sacrifices some cpu cycles to guarantee smooth playback.

EDIT: I've just tested a 50fps HD clip on both MPC-HC EVR CP and MPDN, both without SVP. Both Bilinear. Both without ffdshow raw video filter. Both x86 builds.
GTX560, i5-3470

MPDN: 7-8%
MPC-HC: 6-7%

That's a difference of 1% in MPC-HC EVR CP's favour.

Letting the clip play further, both dropped MPC-HC and MPDN dropped to 5%. BTW, I used "low update speed" on my task manager to get a more stable average.

Last edited by Zachs; 24th October 2014 at 11:51.
Zachs is offline   Reply With Quote
Old 24th October 2014, 12:05   #248  |  Link
toniash
Registered User
 
Join Date: Oct 2010
Posts: 131
Quote:
Originally Posted by chainik_svp View Post
Tested on i5-2430M + AMD 6470M

Common settings: LAV Video decoder (DXVA2 Copy-back mode) + ffdshow raw filter
MPC-HC setting: EVR-CP (bilinear resizer)
MPDN settings: D3D9 renderer on adapter 0, bilinear upscalers, no dithering
SVP settings: the very same in all cases, up to 60 fps, GPU accelerated through AMD's card
Video file: 1280x720 @24

Measured CPU load:

- MPC-HC x32, no SVP: 0-10%
- MPC-HC x64, no SVP: 5-15%
- MPDN x32, no SVP: 10-20%
- MPDN x64, no SVP: 20-30%

- MPC-HC x32, SVP: ~20%
- MPC-HC x64, SVP: ~30%
- MPDN x32, SVP: ~40%
- MPDN x64, SVP: 55-60%

o_O
I can't understand why x32 builds are more efficient!
toniash is offline   Reply With Quote
Old 24th October 2014, 12:27   #249  |  Link
chainik_svp
Registered User
 
Join Date: Mar 2012
Location: Saint-Petersburg
Posts: 239
Seems like just adding "ffdshow raw" (w/o any filters) in MPDN increases CPU consumption by 10-15%.
That 60fps video file: 35-45% CPU load w/o ffdshow raw in both MPC-HC and MPDN and up to 60% in MPDN with ffdshow.


BTW it really doesn't like frame size changing in the post processor
__________________
SVPflow motion interpolation
chainik_svp is offline   Reply With Quote
Old 24th October 2014, 12:40   #250  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
Quote:
Originally Posted by chainik_svp View Post
Seems like just adding "ffdshow raw" (w/o any filters) in MPDN increases CPU consumption by 10-15%.
That 60fps video file: 35-45% CPU load w/o ffdshow raw in both MPC-HC and MPDN and up to 60% in MPDN with ffdshow.


BTW it really doesn't like frame size changing in the post processor
Can you elaborate on what you mean by the frame size changing part?

Anyway ffdshow raw filter increasing CPU consumption is really odd. But that's kind of out of my control really.
Zachs is offline   Reply With Quote
Old 24th October 2014, 12:41   #251  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
Quote:
Originally Posted by toniash View Post
I can't understand why x32 builds are more efficient!
Not with MPDN. The additional CPU consumption is from upstream filters (ffdshow / AviSynth). It's likely that they haven't coded x64 ASM functions, so the build is non-optimized unlike x86 where performance critical parts would've been fully optimized in ASM/SSE/SSE2 etc. Only a guess. Any SSE2 optimized functions I wrote for MPDN are used in both x64 and x86 builds.

Last edited by Zachs; 24th October 2014 at 12:44.
Zachs is offline   Reply With Quote
Old 24th October 2014, 12:47   #252  |  Link
chainik_svp
Registered User
 
Join Date: Mar 2012
Location: Saint-Petersburg
Posts: 239
Zachs
Can you elaborate on what you mean by the frame size changing part?

ffdshow raw -> Resize & aspect -> check "Resize"

frame size CAN change after opening the video and all standard renderers (plus madVR) handle this correctly


Anyway ffdshow raw filter increasing CPU consumption is really odd. But that's kind of out of my control really.

still it doesn't with MPC-HC
__________________
SVPflow motion interpolation
chainik_svp is offline   Reply With Quote
Old 24th October 2014, 13:09   #253  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
Quote:
Originally Posted by chainik_svp View Post
Zachs
Can you elaborate on what you mean by the frame size changing part?

ffdshow raw -> Resize & aspect -> check "Resize"

frame size CAN change after opening the video and all standard renderers (plus madVR) handle this correctly


Anyway ffdshow raw filter increasing CPU consumption is really odd. But that's kind of out of my control really.

still it doesn't with MPC-HC

Ah that frame size. What is it used for and how often is it used btw? Unlikely I'll be supporting that in the near future though.

It doesn't with MPC-HC doesn't mean there's no bug in ffdshow that is causing this problem. I don't do anything special to cater for ffdshow. In fact, I do the bare minimum after opening the media file, less than MPC-HC as you have noted with frame size changing.
Zachs is offline   Reply With Quote
Old 24th October 2014, 13:14   #254  |  Link
chainik_svp
Registered User
 
Join Date: Mar 2012
Location: Saint-Petersburg
Posts: 239
Ah that frame size. What is it used for.

frame cropping at the first place - to cut off black fields/borders or distorted edges, to change aspect ratio
to fill black fields with SVP


It doesn't with MPC-HC doesn't mean there's no bug in ffdshow that is causing this problem. I don't do anything special to cater for ffdshow.

just letting you know...
__________________
SVPflow motion interpolation
chainik_svp is offline   Reply With Quote
Old 24th October 2014, 13:22   #255  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
Quote:
Originally Posted by chainik_svp View Post
frame cropping at the first place - to cut off black fields/borders or distorted edges, to change aspect ratio
to fill black fields with SVP
Is it a must for SVP to work?
Zachs is offline   Reply With Quote
Old 24th October 2014, 13:34   #256  |  Link
chainik_svp
Registered User
 
Join Date: Mar 2012
Location: Saint-Petersburg
Posts: 239
SVP assumes it's working.
One more example is watching 1080p on a smaller screen. One can save a lot of horsepower downscaling the video to screen size before motion interpolation.

In fact I know only ONE video renderer that can't handle this - the one built into Stereoscopic Player.
__________________
SVPflow motion interpolation
chainik_svp is offline   Reply With Quote
Old 24th October 2014, 13:41   #257  |  Link
Zachs
Suptitle, MediaPlayer.NET
 
Join Date: Nov 2001
Posts: 1,721
Quote:
Originally Posted by chainik_svp View Post
SVP assumes it's working.
One more example is watching 1080p on a smaller screen. One can save a lot of horsepower downscaling the video to screen size before motion interpolation.

In fact I know only ONE video renderer that can't handle this - the one built into Stereoscopic Player.
Now you know two
Zachs is offline   Reply With Quote
Old 24th October 2014, 14:44   #258  |  Link
Anime Viewer
Troubleshooter
 
Anime Viewer's Avatar
 
Join Date: Feb 2014
Posts: 339
Quote:
Originally Posted by chainik_svp View Post
Tested on i5-2430M + AMD 6470M

Common settings: LAV Video decoder (DXVA2 Copy-back mode) + ffdshow raw filter
MPC-HC setting: EVR-CP (bilinear resizer)
MPDN settings: D3D9 renderer on adapter 0, bilinear upscalers, no dithering
SVP settings: the very same in all cases, up to 60 fps, GPU accelerated through AMD's card
Video file: 1280x720 @24

Measured CPU load:

- MPC-HC x32, no SVP: 0-10%
- MPC-HC x64, no SVP: 5-15%
- MPDN x32, no SVP: 10-20%
- MPDN x64, no SVP: 20-30%

- MPC-HC x32, SVP: ~20%
- MPC-HC x64, SVP: ~30%
- MPDN x32, SVP: ~40%
- MPDN x64, SVP: 55-60%

o_O
Are you sure SVP (and/or ffdshow64) is working with your x64 players. You're the only one I've seen report it running without crashing out with an error message. If you're share which files you installed, and what setup configurations you went with in ffdshow64 and MPDN x64 I'll see if Intel GPU report the same increase or not. For me MPDN x64 runs faster (render times) than x32...

Quote:
Originally Posted by Zachs View Post
Is it a must for SVP to work?
I'm still experimenting with SVP, but like you I think I may prefer MPDN with SVP off (too many graphic anomalies on subtitle video with it on which may be more annoying than any judder that may be present with it off) .
__________________
System specs: Sager NP9150 SE with i7-3630QM 2.40GHz, 16 GB RAM, 64-bit Windows 10 Pro, NVidia GTX 680M/Intel 4000 HD optimus dual GPU system. Video viewed on LG notebook screen and LG 3D passive TV.
Anime Viewer is offline   Reply With Quote
Old 25th October 2014, 00:32   #259  |  Link
chainik_svp
Registered User
 
Join Date: Mar 2012
Location: Saint-Petersburg
Posts: 239
Quote:
Originally Posted by Anime Viewer View Post
Are you sure SVP (and/or ffdshow64) is working with your x64 players
Yeah I'm pretty sure about it

Not so sure about the numbers as they were taken from one particular system. On my home rig with AMD Phenom X6 it not look so bad...
__________________
SVPflow motion interpolation
chainik_svp is offline   Reply With Quote
Old 25th October 2014, 11:08   #260  |  Link
toniash
Registered User
 
Join Date: Oct 2010
Posts: 131
new scripting

@Zachs So, what I've to do to insert lumasharpen as a postscaling shader?
toniash is offline   Reply With Quote
Reply

Tags
direct3d, mpdn, nnedi3, opencl, reclock


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 +1. The time now is 18:40.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.