Hints for the OpenGL subsystem

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Hints for the OpenGL subsystem

Post by luis »

It would be good if when using the OpenGL subsystem were possible to programmatically give PB some "hints" before opening a PB screen.

The hints should be at least:

the depth buffer size ( 0, 16, 24 )
the stencil buffer bits ( 0, 8 )
the accumulation buffer bits ( 0, 32, 64 )
the multisample buffer bits ( 0, 2, 4, 8, 16, 32 )

and something to specify the OpenGL profile to be used (backward compatible, 3.2 or greater, debug profile, etc.)

the more... the better

The hints should be treated as such: PB should try to set the requested value, or the nearest possible.

This way could be feasible to write crossplatform OpenGL code using PB to provide the environment (full screen, screen inside a PB window and the rendering context).

At the moment, not having control on how the OpenGL rendering context is created, you have to dig yourself in a LOT of platform specific code to initialize OpenGL the way you want.

If you use the OpenGL screen created by PB there is no guarantee about the settings you will find enabled (and you can't change them anyway) and there is no guarantee these settings will be still the same in the next version, so it's not a reliable solution.

By the way: how the OpenGL RC is currently created by PB on the various OS ?
Natively directly by you with the appropriate code or using some third party library (SDL for example or something else) ?


EDIT after this post by Fred -> http://www.purebasic.fr/english/viewtop ... 92#p445892

1) I think what would be required is not something specific to the OpenglGadget, but to the opengl subsytem as a whole.

That way you could get the same flexibility for both the gadget and the pb screen (windowed and fullscreen).

What would be nice would be a method to specify the RC creation attributes before opening a screen or creating a gadget, something like, for example:

SetOpenglCreationAttribute (attribute, value)

where attribute could be: #PB_GL_DepthBufferSize, #PB_GL_StencilBufferBits, #PB_GL_AccumulationBufferBits, #PB_GL_MultisampleBufferBits.
For the possible range of accepted values see above.
The defaults for the attributes not specified could be whatever value you find sensible to make the standard PB graphical commands works.

This could be a very good start.

Even better would be the ability to decide if the context has to be backward compatible or just core profile (modern opengl commands only) but that could be done in a second moment after we have the mechanism above in place, tested and working.

The important thing is, please, this should not be for the opengl gadget only.

The command above should also contain a clear disclaimer in the manual explaining the settings specified here could have negative effects on the PB graphical commands and so the above should be used only when you want to use opengl by yourself and not mixing it with PB graphical commands. Or you should do it at your own risk.

That way PB programmers are happy and can use the usual PB command-set for games etc. Opengl programmers can have a RC created as they requires for any kind of opengl programming.

2) Complementary to that, would be nice to have a specular command to call AFTER we created the gadget or the screen, something like:

ret = GetOpenglCreationAttribute (attribute)

This should return the ACTUAL value you were able to set. For example I could have requested 32 bit accumulation buffer, but that value could have been not available at all or not available in conjunction with the other attributes I specified.
So I could find for example it has been set to 64 (the next best). Or 0 (if not available at all).

3) Another important thing, much needed just after this, would be a method to retrieve the opengl extensions functions addresses.

The equivalent of wglGetProcAddress() for Win, glXGetProcAddress() for linux etc, or alternatively you could made available a cross plaform lib like GLEW or GLEE to manage extensions.

Some work I know, I did all the above and more in my code so I know too well ! Thanks for considering it. :)
Last edited by luis on Wed Jun 04, 2014 1:04 pm, edited 2 times in total.
"Have you tried turning it off and on again ?"
A little PureBasic review
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Hints for the OpenGL subsystem

Post by IdeasVacuum »

+1
I don't know how important OpenGL is to the Games Industry but it is widely used for CAD/CAM, for which it is an excellent environment.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Hints for the OpenGL subsystem

Post by luis »

I put together a quick hack to test the attributes of the OpenGL RC created by PB 5.20 when using the opengl subsystem.

Code: Select all

#GL_VENDOR = $1F00
#GL_RENDERER = $1F01
#GL_VERSION = $1F02

#GL_RED_BITS = $0D52
#GL_GREEN_BITS = $0D53
#GL_BLUE_BITS = $0D54
#GL_ALPHA_BITS = $0D55
#GL_DEPTH_BITS = $0D56
#GL_STENCIL_BITS = $0D57
#GL_ACCUM_RED_BITS = $0D58
#GL_ACCUM_GREEN_BITS = $0D59
#GL_ACCUM_BLUE_BITS = $0D5A
#GL_ACCUM_ALPHA_BITS = $0D5B

#GL_COLOR_BUFFER_BIT = $00004000

#GL_CONTEXT_FLAGS = $821E
#GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT = $0001
#GL_CONTEXT_FLAG_DEBUG_BIT = $00000002

If InitSprite() = 0 Or InitKeyboard() = 0
 MessageRequester("Error", "Init error")
 End
EndIf

CompilerIf (#PB_Compiler_OS = #PB_OS_Windows)

CompilerIf Subsystem("OpenGL") = 0
 CompilerError "Specify " + #DQUOTE$ + "OpenGL" + #DQUOTE$ + " in Compiler -> Compiler Options - > Library Subsystem"
CompilerEndIf

CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86)

Debug "Win x86"
Import "Opengl32.lib" ; x86
 glClear_(a.i) As "_glClear@4"   
 glGetString_(a.i) As "_glGetString@4"
 glGetIntegerv_(a.i,b.i) As "_glGetIntegerv@8"
EndImport

CompilerElse  

Debug "Win x64" 
Import "Opengl32.lib" ; x64
 glClear_(a.i) As "glClear"
 glGetString_(a.i) As "glGetString"
 glGetIntegerv_(a.i,b.i) As "glGetIntegerv"
EndImport

CompilerEndIf
CompilerEndIf

CompilerIf (#PB_Compiler_OS = #PB_OS_MacOS)

CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86)
Debug "OSX x32"
CompilerElse  
Debug "OSX x64"
CompilerEndIf

ImportC "/System/Library/Frameworks/OpenGL.framework/OpenGL"
 glClear_(a.i) As "_glClear"
 glGetString_(a.i) As "_glGetString"
 glGetIntegerv_(a.i,b.i) As "_glGetIntegerv"
EndImport
CompilerEndIf

CompilerIf (#PB_Compiler_OS = #PB_OS_Linux)

CompilerIf (#PB_Compiler_Processor = #PB_Processor_x86)
Debug "Linux x32"
CompilerElse  
Debug "Linux x64"
CompilerEndIf

ImportC "-lGL" 
 glClear_(a.i) As "glClear"
 glGetString_(a.i) As "glGetString"
 glGetIntegerv_(a.i,b.i) As "glGetIntegerv"
EndImport
CompilerEndIf

OpenWindow(0, 0, 0, 320, 240, "OpenGL Context Test", #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)

OpenWindowedScreen(WindowID(0), 0, 0, 320, 240)

Debug PeekS(glGetString_(#GL_VENDOR),-1,#PB_Ascii)
Debug PeekS(glGetString_(#GL_RENDERER),-1,#PB_Ascii)

GL_Version$ = StringField(PeekS(glGetString_(#GL_VERSION),-1,#PB_Ascii), 1, " ")
GL_Major = Val(StringField(GL_Version$, 1, "."))
GL_Minor = Val(StringField(GL_Version$, 2, "."))
GL_Release = Val(StringField(GL_Version$, 3, "."))

Debug GL_Version$
 
iGLInt = -1
glGetIntegerv_(#GL_CONTEXT_FLAGS, @iGLInt)
If GL_Major >= 3 And iGLInt <> -1 ; dirty way... cannot check extensions in this proggy, but it should work
    If (iGLInt & #GL_CONTEXT_FLAG_DEBUG_BIT)
        Debug "Debug Context is ON"
    Else
        Debug "Debug Context is OFF"
    EndIf
Else
    Debug "Debug Context is not supported"
EndIf

iGLInt = 0
glGetIntegerv_(#GL_RED_BITS, @iGLInt) : iColorDepth = iGLInt
glGetIntegerv_(#GL_GREEN_BITS, @iGLInt) : iColorDepth + iGLInt
glGetIntegerv_(#GL_BLUE_BITS, @iGLInt) : iColorDepth + iGLInt
glGetIntegerv_(#GL_ALPHA_BITS, @iGLInt) : iColorDepth + iGLInt
Debug "ColorBufferBits = " + iColorDepth
 
iGLInt = 0
glGetIntegerv_(#GL_DEPTH_BITS, @iGLInt)
Debug "DepthBufferBits = " + iGLInt  
 
iGLInt = 0
glGetIntegerv_(#GL_STENCIL_BITS, @iGLInt)
Debug "StencilBufferBits = " + iGLInt  
 
iGLInt = 0
glGetIntegerv_(#GL_ACCUM_RED_BITS, @iGLInt) : iAccumDepth = iGLInt
glGetIntegerv_(#GL_ACCUM_GREEN_BITS, @iGLInt) : iAccumDepth + iGLInt
glGetIntegerv_(#GL_ACCUM_BLUE_BITS, @iGLInt) : iAccumDepth + iGLInt
glGetIntegerv_(#GL_ACCUM_ALPHA_BITS, @iGLInt) : iAccumDepth + iGLInt
Debug "AccumBufferBits = " + iAccumDepth

Repeat
    glClear_(#GL_COLOR_BUFFER_BIT)
    ExamineKeyboard()  
    Repeat
        Event = WindowEvent()    
        Select Event
            Case #PB_Event_CloseWindow : Quit = 1    
        EndSelect    
    Until Event = 0 
    
    FlipBuffers()       
Until Quit Or KeyboardPushed(#PB_Key_Escape)
These are the results I could get on two real computers (Win7 X64 and OSX 10.8.5) and a VM running a Linux 64 bits.

Code: Select all

Win x86
NVIDIA Corporation
GeForce GTX 560 Ti/PCIe/SSE2
4.3.0
Debug Context is OFF
ColorBufferBits = 24
DepthBufferBits = 24
StencilBufferBits = 0
AccumBufferBits = 64

Win x64
NVIDIA Corporation
GeForce GTX 560 Ti/PCIe/SSE2
4.3.0
Debug Context is OFF
ColorBufferBits = 24
DepthBufferBits = 24
StencilBufferBits = 0
AccumBufferBits = 64

OSX x32
Intel Inc.
Intel HD Graphics 4000 OpenGL Engine
2.1
Debug Context is not supported
ColorBufferBits = 32
DepthBufferBits = 0
StencilBufferBits = 0
AccumBufferBits = 0

OSX x64
Intel Inc.
Intel HD Graphics 4000 OpenGL Engine
2.1
Debug Context is not supported
ColorBufferBits = 32
DepthBufferBits = 0
StencilBufferBits = 0
AccumBufferBits = 0

Linux x64
Humper
Chromium
2.1
Debug Context is not supported
ColorBufferBits = 24
DepthBufferBits = 24
StencilBufferBits = 0
AccumBufferBits = 64
So, apart the debug context not available on older GL versions and disabled on my OpenGL 4.3 nVidia driver (as it should be, since there is no way to enable/disable it right now using PB and it could have impact on the performances) I see the following main problems:

The stencil buffer is 0 everywhere.
On OSX you have nothing. No stencil buffer, no accumulation buffer, no depth buffer.

So, as I requested in the first post the nice thing to have would be a way to hint/request the desired settings.

If this is not possible for some reason AT LEAST on all the platforms PB should try to set the same values...
By the way: how the OpenGL RC is currently created by PB on the various OS ?
Natively directly by you with the appropriate code or using some third party library (SDL for example or something else) ?
... or reasonably similar and usable values (not all drivers/gpu are the same so you take what you can), to make possible to use all the buffers. No buffers with '0' as their size.

I know someone think this could be a waste of resources, but I would choose this possibility over the current situation nevertheless, if nothing better can be done.

I understand the idea behind the opengl subsystem is for the use of PB itself and its 2D commands, but thinking that with some more work we could also have a pre-initialized OpenGL environment (at least as "compatible profile") coherent on all platforms usable for anything and we don't just for some small details it's really sad :(

If possible... thanks.
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Hints for the OpenGL subsystem

Post by idle »

here's what I got
Linux x64
ATI Technologies Inc.
AMD Radeon HD 6670
4.2.11627
Debug Context is OFF
ColorBufferBits = 32
DepthBufferBits = 24
StencilBufferBits = 8
AccumBufferBits = 0
Windows 11, Manjaro, Raspberry Pi OS
Image
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Hints for the OpenGL subsystem

Post by IdeasVacuum »

From my aged WinXP system:

Code: Select all

Win x86
NVIDIA Corporation
GeForce 7900 GT/GTO/PCIe/SSE2
2.1.2
Debug Context is not supported
ColorBufferBits = 24
DepthBufferBits = 24
StencilBufferBits = 0
AccumBufferBits = 64
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
Shardik
Addict
Addict
Posts: 2058
Joined: Thu Apr 21, 2005 2:38 pm
Location: Germany

Re: Hints for the OpenGL subsystem

Post by Shardik »

These are the results on my iMac (summer 2010) for the following operating systems all running natively on the same hardware:
- OS X 10.6.8 (Snow Leopard)
- Windows 7 SP1 Home premium
- Ubuntu 12.04 LTS with KDE
OSX x32
ATI Technologies Inc.
ATI Radeon HD 5670 OpenGL Engine
2.1
Debug Context is not supported
ColorBufferBits = 32
DepthBufferBits = 0
StencilBufferBits = 0
AccumBufferBits = 0

OSX x64
ATI Technologies Inc.
ATI Radeon HD 5670 OpenGL Engine
2.1
Debug Context is not supported
ColorBufferBits = 32
DepthBufferBits = 0
StencilBufferBits = 0
AccumBufferBits = 0

Win x86
ATI Technologies Inc.
ATI Radeon HD 5670
4.0.10061
Debug Context is OFF
ColorBufferBits = 32
DepthBufferBits = 24
StencilBufferBits = 8
AccumBufferBits = 0

Win x64
ATI Technologies Inc.
ATI Radeon HD 5670
4.0.10061
Debug Context is OFF
ColorBufferBits = 32
DepthBufferBits = 24
StencilBufferBits = 8
AccumBufferBits = 0

Linux x64
X.Org
Gallium 0.4 on AMD REDWOOD
2.1
Debug Context is not supported
ColorBufferBits = 24
DepthBufferBits = 24
StencilBufferBits = 0
AccumBufferBits = 0
Deluxe0321
User
User
Posts: 69
Joined: Tue Sep 16, 2008 6:11 am
Location: ger

Re: Hints for the OpenGL subsystem

Post by Deluxe0321 »

Windows 8.1 Pro with latest AMD beta drivers
Win x86
ATI Technologies Inc.
AMD Radeon HD 7800 Series
4.2.12422
Debug Context is OFF
ColorBufferBits = 32
DepthBufferBits = 24
StencilBufferBits = 8
AccumBufferBits = 0
User avatar
flaith
Enthusiast
Enthusiast
Posts: 704
Joined: Mon Apr 25, 2005 9:28 pm
Location: $300:20 58 FC 60 - Rennes
Contact:

Re: Hints for the OpenGL subsystem

Post by flaith »

Win x86
Intel
Intel 945GM
1.4.0
Debug Context is not supported
ColorBufferBits = 32
DepthBufferBits = 16
StencilBufferBits = 0
AccumBufferBits = 64
I know... :oops:
“Fear is a reaction. Courage is a decision.” - WC
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Hints for the OpenGL subsystem

Post by IdeasVacuum »

... a lot of variations.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Hints for the OpenGL subsystem

Post by luis »

IdeasVacuum wrote:... a lot of variations.
It's true different hw/sw have different capabilities, but practically every hw/sw can set those buffers to something usable. Clearly on OSX the reason for all buffers set to zero it's not because they are all not available.

So, if we could have a mechanism to hint the desired settings, that coupled with the fact we can check afterwards what has been actually set could make us happy enough.

That, or at least the highest set of settings available (even if more expensive) available by default.

Thanks for the reports :wink:


EDIT: subject somewhat discussed again here -> http://www.purebasic.fr/english/viewtop ... 49#p444249
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
grabiller
Enthusiast
Enthusiast
Posts: 309
Joined: Wed Jun 01, 2011 9:38 am
Location: France - 89220 Rogny-Les-Septs-Ecluses
Contact:

Re: Hints for the OpenGL subsystem

Post by grabiller »

luis wrote:It would be good if when using the OpenGL subsystem were possible to programmatically give PB some "hints" before opening a PB screen.
The hints should be at least:
the depth buffer size ( 0, 16, 24 )
the stencil buffer bits ( 0, 8 )
the accumulation buffer bits ( 0, 32, 64 )
the multisample buffer bits ( 0, 2, 4, 8, 16, 32 )
and something to specify the OpenGL profile to be used (backward compatible, 3.2 or greater, debug profile, etc.)
../..
The important thing is, please, this should not be for the opengl gadget only.
../..
This should return the ACTUAL value you were able to set. For example I could have requested 32 bit accumulation buffer, but that value could have been not available at all or not available in conjunction with the other attributes I specified. So I could find for example it has been set to 64 (the next best). Or 0 (if not available at all).
../..
3) Another important thing, much needed just after this, would be a method to retrieve the opengl extensions functions addresses.
The equivalent of wglGetProcAddress() for Win, glXGetProcAddress() for linux etc, or alternatively you could made available a cross plaform lib like GLEW or GLEE to manage extensions.
Luis, thanks for discussing about this. It is important - I believe it too - to have more control about OpenGL contexts indeed.

However, I'm not totally agree with the details of your request, here is why:

Nowadays, users should be encouraged to use "modern" OpenGL, that is at least OpenGL v3.2+. While it is a little bit more difficult at first than using the "old" OpenGL way, it is worth the pain, really.

So the first thing, using "modern" OpenGL, is to forget about the Topology of the display buffer at creation time. The depth buffer, stencyl buffer, accumulation buffer should be forgotten with the OS created OpenGL context, default values are fine. This is because with modern OpenGL you do your drawing in a FrameBuffer, and simply display it to the user through the OS created display buffer. And the FrameBuffer is created and used by you through pure OpenGL commands, you don't need any specific OS (glx/wgl/etc..) command for this. In short, you can create any FrameBuffer with any depth buffer size, stencyl buffer size, etc.. with only pure OpenGL commands on any context created by the OS. For instance you can create a Multisampled FrameBuffer with 32 samples per pixel and display it on a non Multisampled OS created display buffer. With modern OpenGL you don't need to mess with OS specific function to do that.

The only left important configuration flags are the Core/Compatibility/Debug Profiles and OpenGL version. Although, with current drivers, in general you always get - by default - the highest OpenGL version with the Compatibility Profile. And it's perfectly ok to not change that, you'll still get full OpenGL capabilities.

What is mandatory though, is indeed access to the "GetProcAddress" function, in order to retrieve all the needed (and modern) OpenGL functions. Also, a very important one, access to the swap interval function (wglSwapIntervalEXT on Windows).

But there is a catch: Functions you retrieve through the famous "GetProcAddress" depend on the created OpenGL context. That means you can't have this "globally" in PureBasic as it should be retrieved and used only on the current context. Depending on the Profile you choose or the OpenGL version you choose, you may or may not get the same functions. Some functions having the same name may point to different internal functions too.

For instance, the usual mistake that crashes a "modern" OpenGL program is assuming you always get a glGetString function even with highest OpenGL versions. Well, if you choose Core/OpenGLv4.4 for instance, and rely on glGetString to evaluate available extensions, then your program will crash because "GetProcAddress" glGetString will return #Null. Indeed, glGetString is now deprecated on Core profiles v4.4+.

That's why that makes sense to retrieve a GetProcAddress from the OpenGLGadget directly, after the context has been created and made current, because you may have a different OpenGL contexts on different Gadgets (not recommended of course, but possible).

Cheers,
Guy.
guy rabiller | radfac founder / ceo | raafal.org
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Hints for the OpenGL subsystem

Post by luis »

Guy, valid points. Yes it's true a framebuffer can be created independently from the default display buffer and so the initial screen buffer settings can be somewhat irrelevant (that can be done in legacy opengl too BTW through extensions). And your point about the extensions being tied to a specific context is absolutely correct but in practice it should be a rare occurrence to have different types of RC in the same program, so for 99% of the uses I think it's not a problem but nevertheless you are right. So if any addition to the PB commandset can take that too into account obviously it would be only for the better. It should be sufficient to call the PB GetProcAddress after a context has been made current.

About the use of the old, legacy opengl I wrote my opinion here -> http://www.purebasic.fr/english/viewtop ... 05#p444205
I think those points have some merits too, if one is interested in those aspects.

It would be nice to be able to do BOTH with PB, no question.
But PB uses opengl 1.2 and a handful of extensions, so I thought asking about something not so distant from what currently already available but not fully tweakable (yet) would have given to the request more chances to be implemented. That and the fact I wouldn't like to be forced to use only modern GL, so the creation of the context is for me important.

In time... who knows.

Anyway, I'll take any kind improvement gladly, in the meantime I just code all from scratch by myself so I can do as I please and have the control I like to have. Even if "crappy" is my "crappy" library and knowing how everything work is priceless.
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
grabiller
Enthusiast
Enthusiast
Posts: 309
Joined: Wed Jun 01, 2011 9:38 am
Location: France - 89220 Rogny-Les-Septs-Ecluses
Contact:

Re: Hints for the OpenGL subsystem

Post by grabiller »

luis wrote:../.. It would be nice to be able to do BOTH with PB, no question.
But PB uses opengl 1.2 and a handful of extensions, so I thought asking about something not so distant from what currently already available but not fully tweakable (yet) would have given to the request more chances to be implemented. That and the fact I wouldn't like to be forced to use only modern GL, so the creation of the context is for me important../..
Fair enough Luis.

I was just thinking that only providing options for Profiles/Version for "modern" OpenGL would require less work to implement in PB rather than having a fully customizable context (so having more chances to see it implemented).

Also enforcing the use of modern OpenGL, although a little bit harsh at first, I admit it, is something - I believe - that would benefit everyone.

The thing is that the OpenGL API in its "Compatibility" profile mode (allowing "old" OpenGL way as well as "modern" way at the same time) is becoming such a mess that even new OpenGL developers are completely lost nowadays. And to a point that - I believe - getting straight into the "Core" profile is now much more easy and elegant, even for beginners.

But I understand your point nonetheless.
guy rabiller | radfac founder / ceo | raafal.org
Jimbob81
User
User
Posts: 18
Joined: Thu Jun 25, 2020 7:17 am

Re: Hints for the OpenGL subsystem

Post by Jimbob81 »

Old post I know but this is what I get when running the above test code...

[17:43:02] Waiting for executable to start...
[17:43:02] Executable type: Linux - x64 (64bit, Unicode)
[17:43:02] Executable started.
[17:43:05] The debugged executable quit unexpectedly.

If I compile the example code without a debugger then I get a window with a black background and obviously no results.

Debian Linux 9 and MATE desktop, PB 5.62 (x64)
Post Reply