Login
Username:

Password:

Remember me



Lost Password?

Register now!

Sections

Who's Online
46 user(s) are online (31 user(s) are browsing Forums)

Members: 0
Guests: 46

more...

Support us!

Headlines

 
  Register To Post  

« 1 2 (3) 4 »
Re: radeonHD video hw acceleration
Just popping in
Just popping in


See User information
Please add Compositing-Support to E-UAE and DVPlayer, MPlayer..etc


Edited by fingus on 2013/2/1 11:00:30
Go to top
Re: radeonHD video hw acceleration
Just popping in
Just popping in


See User information
@MickJT

Have you already tried to mix the "compositing code" in E-UAE ??


Sam 460EX, 2Gb Ram, Radeon R7 250, AmigaOS4.1 FE
A4000 PPC604@233, Mediator
A1200 PPC603@160, Mediator
uA1 G3@800, 512 Mb [sold]
Go to top
Re: radeonHD video hw acceleration
Quite a regular
Quite a regular


See User information
Quote:

Hans wrote:
Excluding the Radeon HD 7xxx+, as they have a new and nicer ISA.


How did I miss this?

Niiice

~Yes I am a Kiwi, No, I did not appear as an extra in 'Lord of the Rings'~
1x AmigaOne X5000 2.0GHz 2gM RadeonR9280X AOS4.x
3x AmigaOne X1000 1.8GHz 2gM RadeonHD7970 AOS4.x
Go to top
Re: radeonHD video hw acceleration
Not too shy to talk
Not too shy to talk


See User information
Hello

I have maded some code to try to decode the given YUV example with Warp3D
Unfortunately it dont works on my Sam440 as i dont have
W3D_SetDrawRegionTexture in my Warp3D.library (perhaps I need an update...)

Need real Warp3D V5 (not OS3 68k) (not Wazp3D)
Need to support rectangular textures
Need to support I8 format textures
Need to have W3D_SetDrawRegionTexture()

Finally I am not sure this code have any utility :-/


Alain Thellier


/* YUV to RGB using Warp3D - Alain Thellier - 2013 */
#define DODEBUG 1

/*==========================================================================*/
#ifdef __amigaos4__
#define OS4
#else
#define OS3
#endif
/*==================================================================================*/
#ifdef OS4
#define __USE_INLINE__
#define __USE_BASETYPE__
#define __USE_OLD_TIMEVAL__
#pragma pack(2)
#endif
/*==================================================================================*/
#ifdef DODEBUG
#define REM(message) printf(#message"\n");
#else
#define REM(message) ;
#endif

#include (((stdlib.h>
#include (((stdio.h>
#include (((strings.h>

#include (((proto/exec.h>
#include (((proto/intuition.h>
#include (((proto/dos.h>
#include (((proto/Warp3D.h>
#include (((proto/graphics.h>
#include (((graphics/gfx.h>
#include (((intuition/intuition.h>
#include (((intuition/intuitionbase.h>

/*==================================================================================*/
typedef struct _Point3D
{
float x,y,z;
float color[4];
float u,v,w;
} Point3D;
/*==================================================================================*/
struct Library *Warp3DBase;
W3D_Context *context = NULL;
struct Screen *screen;
struct Window *window;
ULONG OpenErr, CError;
ULONG ModeID,flags,ScreenBits;
W3D_Texture *tex2 ;
W3D_Texture *tex1 ;
void *pic1=NULL;
void *pic2=NULL;
ULONG error;
W3D_Scissor scissor1;
W3D_Scissor scissor2;
WORD winlarge,winhigh;
WORD texlarge,texhigh;
FILE *fp;
float onetexel;
Point3D P[18];
Point3D P2[18];
UBYTE *bmdata;
#define SWAP(x,y) {temp=x;x=y;y=temp;}
float temp;
/*==================================================================*/
struct GfxBase* GfxBase =NULL;
struct IntuitionBase* IntuitionBase =NULL;
struct Library * CyberGfxBase =NULL;
struct Library* Warp3DBase =NULL;
/*==================================================================*/
#ifdef OS4
struct GraphicsIFace* IGraphics =NULL;
struct IntuitionIFace* IIntuition =NULL;
struct CyberGfxIFace* ICyberGfx =NULL;
struct Warp3DIFace* IWarp3D =NULL;
#endif
/*==================================================================================*/
float White[4]={1.0 ,1.0 ,1.0 ,1.0 };
/*
From YUV to RGB :
R = Y + 1.13983*V
G = Y + 0.39465*U + 0.58060*V
B = Y + 2.03211*U

r = y + 1.402f*v;
g = y - (0.344f*u +0.714f*v);
b = y +1.772f*u;
*/
/*==================================================================================*/
float ColorY[4]={1.0 ,1.0 ,1.0 ,0.5 };
float ColorU[4]={0.0 ,-0.344 ,1.772 ,0.5 };
float ColorV[4]={1.402 ,-0.714 ,0.0 ,0.5 };
/*==================================================================================*/
BOOL OpenAmigaLibraries(void)
{
#define LIBOPEN(libbase,name,version) libbase =(void*)OpenLibrary(#name,(ULONG)version); if(libbase==NULL) return(FALSE);
#define LIBOPEN4(interface,libbase) interface=(void*)GetInterface((struct Library *)libbase, "main", 1, NULL); if(interface==NULL) return(FALSE);

LIBOPEN(GfxBase,graphics.library,0)
LIBOPEN(IntuitionBase,intuition.library,0)
LIBOPEN(CyberGfxBase,cybergraphics.library,0)
LIBOPEN(Warp3DBase,Warp3D.library,4)

#ifdef OS4
LIBOPEN(Warp3DBase,Warp3D.library,5)
LIBOPEN4(IExec,SysBase)

LIBOPEN4(IGraphics,GfxBase)
LIBOPEN4(IIntuition,IntuitionBase)
LIBOPEN4(ICyberGfx,CyberGfxBase)
LIBOPEN4(IWarp3D,Warp3DBase)
#endif

return(TRUE);
}
/*======================================================================================*/
void CloseAmigaLibraries()
{
#define LIBCLOSE(libbase) if(libbase !=NULL) { CloseLibrary((struct Library *)libbase ); libbase=NULL; }
#define LIBCLOSE4(interface) if(interface!=NULL) { DropInterface((struct Interface*)interface ); interface=NULL; }


#ifdef OS4
LIBCLOSE4(IGraphics)
LIBCLOSE4(IIntuition)
LIBCLOSE4(ICyberGfx)
LIBCLOSE4(IWarp3D)
#endif

LIBCLOSE(GfxBase)
LIBCLOSE(IntuitionBase)
LIBCLOSE(CyberGfxBase)
LIBCLOSE(Warp3DBase)

}
/*==================================================================================*/
BOOL StartWarp3D(void)
{
int x,y;
void *bmHandle;

if(!OpenAmigaLibraries())
return(FALSE);

screen = LockPubScreen("Workbench") ;
x=screen->Width;
y=screen->Height;

window = OpenWindowTags(NULL,
WA_Activate, TRUE,
WA_Width, winlarge,
WA_Height, winhigh,
WA_Left, x/2-winlarge/2,
WA_Top, y/2-winhigh/2,
WA_Title, (ULONG)"YUV to RGB - Thellier 2013",
WA_DragBar, TRUE,
WA_Backdrop, FALSE,
WA_GimmeZeroZero, TRUE,
WA_Borderless, FALSE,
TAG_DONE);

if (window==NULL)
{printf("Unable to open window\n");return FALSE; }

context = W3D_CreateContextTags(&CError,
W3D_CC_BITMAP,(ULONG)window->RPort->BitMap,
W3D_CC_YOFFSET,0,
W3D_CC_DRIVERTYPE,W3D_DRIVER_BEST,
W3D_CC_DOUBLEHEIGHT,FALSE, //DoubleHeightON,
W3D_CC_FAST,TRUE,
TAG_DONE);

if(CError==W3D_SUCCESS){printf("create Warp3D context success!\n");};
if(CError==W3D_ILLEGALINPUT){printf("Illigal input!\n");};
if(CError==W3D_NOMEMORY){printf("no memory\n");};
if(CError==W3D_NODRIVER){printf("no driver\n");};
if(CError==W3D_UNSUPPORTEDFMT){printf("usupportedfmt\n");};
if(CError==W3D_ILLEGALBITMAP){printf("illegal bitmap\n");};

W3D_ClearDrawRegion(context,0);
return TRUE;
}
/*==================================================================================*/
void SetP(Point3D *P,float x, float y, float z,float w,float u, float v,float *color)
{
P->x=x; P->y=y; P->z=z; P->w=w; P->u=u; P->v=v;
P->color[0]=color[0];
P->color[1]=color[1];
P->color[2]=color[2];
P->color[3]=color[3];
}
/*==================================================================================*/
void DoQuad(Point3D *P,float x, float y,float xlarge,float yhigh,float u, float v,float ularge,float vhigh,float *color)
{
float z=0.0;
float w=1.0;

w=0.001/(P->z+0.001);
SetP(&P[0],x,y+yhigh,z,w,u,v+vhigh,color);
SetP(&P[1],x+xlarge,y+yhigh,z,w,u+ularge,v+vhigh,color);
SetP(&P[2],x+xlarge,y,z,w,u+ularge,v,color);
SetP(&P[3],x,y+yhigh,z,w,u,v+vhigh,color);
SetP(&P[4],x+xlarge,y,z,w,u+ularge,v,color);
SetP(&P[5],x,y,z,w,u,v,color);
}
/*==================================================================================*/
void DrawArrWarp3D(Point3D *P,ULONG Pnb,ULONG primitive)
{
void *VertexPointer;
void *TexCoordPointer;
void *ColorPointer;
UWORD stride=sizeof(Point3D);
UWORD off_v,off_w;
ULONG format,result;


REM(DrawArrWarp3D)

W3D_Flush(context);
W3D_WaitIdle(context); /* dont modify points or pointers during drawing */
/* Warp3D V5 for Os4 ppc only implement W3D_InterleavedArray() not W3D_Vertex/TexCoord/ColorPointer() */
#ifdef OS4
#define ARRAYFORMAT (W3D_VFORMAT_COLOR | W3D_VFORMAT_TCOORD_0 )
VertexPointer =(void *)&(P->x);
W3D_InterleavedArray(context,VertexPointer,stride,ARRAYFORMAT,W3D_TEXCOORD_NORMALIZED);
#else
VertexPointer =(void *)&(P->x);
TexCoordPointer =(void *)&(P->u);
ColorPointer =(void *)&(P->color);
off_v=(UWORD)( (ULONG)&(P->v) - (ULONG)&(P->u));
off_w=(UWORD)( (ULONG)&(P->w) - (ULONG)&(P->u));
result=W3D_VertexPointer(context,VertexPointer,stride,W3D_VERTEX_F_F_F, 0);
result=W3D_TexCoordPointer(context,TexCoordPointer,stride,0, off_v, off_w,W3D_TEXCOORD_NORMALIZED);
result=W3D_ColorPointer(context,ColorPointer,stride,W3D_COLOR_FLOAT ,W3D_CMODE_RGBA,0);
#endif
if( W3D_SUCCESS != W3D_LockHardware(context) )
{REM(cant lock!) ; return;}
W3D_DrawArray(context,primitive,0,Pnb); /* draw with warp3d */
W3D_UnLockHardware(context);
}
/*==================================================================================*/
int main(int argc, char *argv[])
{
ULONG x,y,high,large;

REM( main )

texlarge=352;
texhigh=288;
winlarge=texlarge+100;
winhigh =texhigh+100;

if(FALSE==StartWarp3D())
goto panic;

REM( load tex )
pic1=malloc(texlarge*texhigh*16/8);
pic2=malloc(texlarge*texhigh*16/8);
if(pic1==NULL) {printf("cant alloc texture!\n");goto panic;}
if(pic1==NULL) {printf("cant alloc texture!\n");goto panic;}


if((fp = fopen("PROGDIR:test.yuv","rb")) == NULL)
{ printf("can't open picture\n");goto panic;}
fread(pic1,texlarge*texhigh*32/8,1,fp);
fclose(fp);

REM( YUV loaded)
tex1 = W3D_AllocTexObjTags(context, &error,
W3D_ATO_IMAGE, (ULONG) pic1,
W3D_ATO_FORMAT, W3D_I8,
W3D_ATO_WIDTH, texlarge*2,
W3D_ATO_HEIGHT, texhigh,
TAG_DONE);
if(tex1==NULL) {printf("Warp3D cant alloc I8 texture1!\n");goto panic;}

tex2 = W3D_AllocTexObjTags(context, &error,
W3D_ATO_IMAGE, (ULONG) pic2,
W3D_ATO_FORMAT, W3D_I8,
W3D_ATO_WIDTH, texlarge,
W3D_ATO_HEIGHT, texhigh*2,
TAG_DONE);
if(tex2==NULL) {printf("Warp3D cant alloc I8 texture2!\n");goto panic;}

W3D_SetState(context,W3D_TEXMAPPING,W3D_ENABLE);

REM(pass1: unfold interleaved YUV a 3 squares Y U V)
scissor2.left=0;
scissor2.top=0;
scissor2.width=texlarge;
scissor2.height=texhigh*2;
IWarp3D->W3D_SetDrawRegionTexture(context,tex2,&scissor2);
REM( w3d settings )
W3D_BindTexture(context,0,tex1);
W3D_SetFilter(context,tex1,W3D_NEAREST,W3D_NEAREST);
W3D_SetTexEnv(context,tex1,W3D_REPLACE,NULL);
W3D_SetState(context,W3D_BLENDING,W3D_DISABLE);
REM( Y U V to 3 quads )
onetexel=1.0/(float)(texlarge*4);
DoQuad(&P[0],0,0,texlarge,texhigh,0.0*onetexel,0.0,1.0,1.0,White);
DoQuad(&P[6],0,texhigh,texlarge/2,texhigh,1.0*onetexel,0.0,1.0,1.0,White);
DoQuad(&P[12],texlarge/2,texhigh,texlarge/2,texhigh,3.0*onetexel,0.0,1.0,1.0,White);
REM( draw the 3 quads )
DrawArrWarp3D(P,18,W3D_PRIMITIVE_TRIANGLES);
REM(flush)
W3D_FlushFrame(context);
W3D_WaitIdle(context);
Delay(500);

REM(pass2: Merge the 3 squares Y U V to an RGB picture in window-bitmap)
x =window->LeftEdge + window->BorderLeft ;
y =window->TopEdge + window->BorderTop ;
large =window->Width - window->BorderLeft - window->BorderRight ;
high =window->Height - window->BorderTop - window->BorderBottom;
scissor1.left=x;
scissor1.top=y;
scissor1.width=large;
scissor1.height=high;
W3D_SetDrawRegion(context,window->RPort->BitMap,0,&scissor1);
REM( w3d settings )
W3D_BindTexture(context,0,tex2);
W3D_SetFilter(context,tex2,W3D_LINEAR,W3D_LINEAR);
W3D_SetTexEnv(context,tex2,W3D_MODULATE,NULL);
W3D_SetState(context,W3D_BLENDING,W3D_ENABLE);
W3D_SetBlendMode(context,W3D_SRC_ALPHA,W3D_ONE_MINUS_SRC_ALPHA);
REM( Y U V from 3 quads )
DoQuad(&P2[0 ],x,y,large,high,0.0,0.0,1.0,0.5,ColorY);
DoQuad(&P2[6 ],x,y,large,high,0.0,0.5,0.5,0.5,ColorU);
DoQuad(&P2[12],x,y,large,high,0.5,0.5,0.5,0.5,ColorV);
REM( draw the 3 quads )
DrawArrWarp3D(P2,18,W3D_PRIMITIVE_TRIANGLES);
REM(flush)
W3D_FlushFrame(context);
W3D_WaitIdle(context);
Delay(500);

panic:
REM( Closing down...)
if (tex1) W3D_FreeTexObj(context, tex1);
if (tex2) W3D_FreeTexObj(context, tex2);
if (pic1) free(pic1);
if (pic2) free(pic2);
if (context) W3D_DestroyContext(context);
if (window) CloseWindow(window);
CloseAmigaLibraries();
exit(0);
}

Go to top
Re: radeonHD video hw acceleration
Just popping in
Just popping in


See User information
@thellier

W3D_SetDrawRegionTexture() is not implemented and moreover is removed in the latest versions of the main library and drivers. Calling it due to an outdated interface definition is probably not a great idea.

Go to top
Re: radeonHD video hw acceleration
Quite a regular
Quite a regular


See User information
I'll add

But we still appreciate your endevours =)

perhaps a swap out is in order or how they say "wrapper"

~Yes I am a Kiwi, No, I did not appear as an extra in 'Lord of the Rings'~
1x AmigaOne X5000 2.0GHz 2gM RadeonR9280X AOS4.x
3x AmigaOne X1000 1.8GHz 2gM RadeonHD7970 AOS4.x
Go to top
Re: radeonHD video hw acceleration
Not too shy to talk
Not too shy to talk


See User information
Quote:

Hans wrote:

Awesome. Thanks for that. I see from the code that the U and V planes are interleaved. I wasn't aware of this. That's not how the official format is specified. Fortunately, this isn't going to be a problem, as the plane alignment requirements remain the same.

Hans


It's done that way in the Radeon 2xx driver because it appears to have been some commonly used format (e.g. Y on top, U/V side-by-side below) and I didn't want to differ. As far as the Picasso96 API is concerned it's just 3 pointers with three BytesPerRow values, this gives freedom in driver development so you can physically arrange them any way you want and it will work since the applications will simply use the three pointers and use the three bytesPerRow values to get to other rows.

Go to top
Re: radeonHD video hw acceleration
Just popping in
Just popping in


See User information
@Cobra

nice to see you again !

Sam 460EX, 2Gb Ram, Radeon R7 250, AmigaOS4.1 FE
A4000 PPC604@233, Mediator
A1200 PPC603@160, Mediator
uA1 G3@800, 512 Mb [sold]
Go to top
Re: radeonHD video hw acceleration
Not too shy to talk
Not too shy to talk


See User information
>the U and V planes are interleaved
>you can physically arrange them any way you want

Yes I agree. But desinterleaving them on the fly allow to obtain 3 rectangular textures
a big one with Y
a small with U
a small with V
so allowing to use hardware filtering/resizing as Y values are now sequential (same for U same for V)

Interleaved
YUYV YUYV YUYV YUYV
YUYV YUYV YUYV YUYV
YUYV YUYV YUYV YUYV
YUYV YUYV YUYV YUYV

Desinterleaved
YYYYYYY
YYYYYYY
YYYYYYY
YYYYYYY

UUUU
UUUU
UUUU
UUUU

VVVV
VVVV
VVVV
VVVV




Go to top
Re: radeonHD video hw acceleration
Home away from home
Home away from home


See User information
Quote:

COBRA wrote:
... As far as the Picasso96 API is concerned it's just 3 pointers with three BytesPerRow values, this gives freedom in driver development so you can physically arrange them any way you want and it will work since the applications will simply use the three pointers and use the three bytesPerRow values to get to other rows.


So, how do you get the three pointers, etc.? M3x's example doesn't show that.

Hans

Join Kea Campus' Amiga Corner and support Amiga content creation
https://keasigmadelta.com/ - see more of my work
Go to top
Re: radeonHD video hw acceleration
Not too shy to talk
Not too shy to talk


See User information
@thellier

When decoding video the decoder gives the bitmap in planar format (Y, U and V planes separately). Originally we only had overlay using interleaved YUV422 format so I would convert the planar format to interleaved format in software and transfer it to videomem. It's not an issue to convert the format, but the interleaved YUV422 uses 16 bits per pixel (U/V is shared only for pairs of pixels) while most video codecs are 12 bits per pixel (U/V is shared by groups of 4 pixels, 2x2 areas) so when we implemented YUV420P planar mode it gave a significant performance increase due to the fact that less data had to be transferred to video memory, not because data did not have to be interleaved.

However with planar formats you could have a further speed increase by using DMA hardware to transfer the data. This would not be possible with interleaved formats because DMA hardware can't rearrange planar data to interleaved data.

Go to top
Re: radeonHD video hw acceleration
Not too shy to talk
Not too shy to talk


See User information
@Hans

m3x's example uses the interleaved YUV422 format, not the planar YUV420 format. I have a test application which generates a YUV420P test bitmap (colour gradient thing) and outputs it on overlay, do you want that? If you need one that displays an actual image, I can make that but you have to wait for it.

Go to top
Re: radeonHD video hw acceleration
Home away from home
Home away from home


See User information
Quote:

COBRA wrote:
@Hans

m3x's example uses the interleaved YUV422 format, not the planar YUV420 format. I have a test application which generates a YUV420P test bitmap (colour gradient thing) and outputs it on overlay, do you want that? If you need one that displays an actual image, I can make that but you have to wait for it.


YUV422 is only the first example. M3x also posted a YUV420P example in post #30 of this thread. However, his example hard-codes the format to what th R2xx driver uses.

The source code to your YUV420P test app would be useful. It doesn't have to be an actual image, so long as there is enough detail that it's obvious if something is wrong.

Hans

Join Kea Campus' Amiga Corner and support Amiga content creation
https://keasigmadelta.com/ - see more of my work
Go to top
Re: radeonHD video hw acceleration
Not too shy to talk
Not too shy to talk


See User information
@Hans

OK, sent you an E-Mail with the test code I found on my hard drive but to be honest I didn't have time to check it maybe it's the same as m3x's one, will look at it tonight after work and if it's not doing it the correct way I'll make some test code for you based on the dvplayer code.

Go to top
Re: radeonHD video hw acceleration
Home away from home
Home away from home


See User information
Quote:

COBRA wrote:
@Hans

OK, sent you an E-Mail with the test code I found on my hard drive but to be honest I didn't have time to check it maybe it's the same as m3x's one, will look at it tonight after work and if it's not doing it the correct way I'll make some test code for you based on the dvplayer code.


Thanks. I just had a look at it, and it's different from m3x's. Most importantly, it shows how to get the pointers to each plane in the bitmap.

Hans

Join Kea Campus' Amiga Corner and support Amiga content creation
https://keasigmadelta.com/ - see more of my work
Go to top
Re: radeonHD video hw acceleration
Not too shy to talk
Not too shy to talk


See User information
Any use of this?

Within the next few hours AMD will be publishing open-source driver code that exposes their Unified Video Decoder (UVD) engine on modern Radeon HD graphics cards

http://www.phoronix.com/scan.php?page ... =amd_opensource_uvd&num=1

CD32/A500/A600/A600+Furia/A1200/A4000D+A2320+PiccoloSD64/Sam440 flex 800MHz RAM 1GB HD7750 128MB OS4.1 SBLive! ->
Go to top
Re: radeonHD video hw acceleration
Home away from home
Home away from home


See User information
@mr2
Let's hope they release the required documentation for it aswell. If so, i'd be very interested in helping for a bounty to get this. It would mean that even the sam460 would playback fullhd video.

X5000
Go to top
Re: radeonHD video hw acceleration
Home away from home
Home away from home


See User information
Quote:

mr2 wrote:
Any use of this?

Within the next few hours AMD will be publishing open-source driver code that exposes their Unified Video Decoder (UVD) engine on modern Radeon HD graphics cards

http://www.phoronix.com/scan.php?page ... =amd_opensource_uvd&num=1


Yes, that will absolutely be useful, provided that there are other developers who are willing to work on all the bits that we'll need. Driver support is just a small part of what's needed. We need an AmigaOS version of VDPAU (Video Decode and Presentation API for Unix), and avcodec needs to be updated to use it too.

BTW, is there an easier way to grab the patches than copying and pasting from the linked to DRI mailing list archives?

Hans


P.S. Implementing all that is needed for HW video decoding is quite an undertaking, so expect it to take a long while.

Join Kea Campus' Amiga Corner and support Amiga content creation
https://keasigmadelta.com/ - see more of my work
Go to top
Re: radeonHD video hw acceleration
Just popping in
Just popping in


See User information
Very interesting news!! The code has just arrived ! And there are the Mesa patches too !!

Sam 460EX, 2Gb Ram, Radeon R7 250, AmigaOS4.1 FE
A4000 PPC604@233, Mediator
A1200 PPC603@160, Mediator
uA1 G3@800, 512 Mb [sold]
Go to top
Re: radeonHD video hw acceleration
Not too shy to talk
Not too shy to talk


See User information
Quote:
Yes, that will absolutely be useful, provided that there are other developers who are willing to work on all the bits that we'll need. Driver support is just a small part of what's needed. We need an AmigaOS version of ....


Great! The only way to speed things up (by community) is to donate some cash, IMO. Does anyone smart enough want to divide this complicated task to the stages? Is Amigabounty still around?

CD32/A500/A600/A600+Furia/A1200/A4000D+A2320+PiccoloSD64/Sam440 flex 800MHz RAM 1GB HD7750 128MB OS4.1 SBLive! ->
Go to top

  Register To Post
« 1 2 (3) 4 »

 




Currently Active Users Viewing This Thread: 1 ( 0 members and 1 Anonymous Users )




Powered by XOOPS 2.0 © 2001-2024 The XOOPS Project