lazarus/components/opengl/glqtcontext.pas
2022-11-19 21:21:09 +01:00

591 lines
19 KiB
ObjectPascal

{
*****************************************************************************
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Author: Mattias Gaertner
}
unit GLQTContext;
{$mode objfpc}{$H+}
{$LinkLib GL}
{$PACKRECORDS C}
{$DEFINE ModernGL}
//{$DEFINE VerboseMultiSampling}
interface
uses
// Classes, SysUtils, ctypes, LCLProc, LCLType, X, XUtil, XLib, gl,
//InterfaceBase,
//glx,
//WSLCLClasses,
{$IFDEF ModernGL} ctypes,{$ENDIF}
Classes, SysUtils, Types, Controls, LCLProc, LCLType, X, XUtil, XLib, gl,
InterfaceBase,
WSLCLClasses,glx,
// Bindings
{$IFDEF LCLQt}qt4,{$ENDIF}
{$IFDEF LCLQt5}qt5, qlclopenglwidget,{$ENDIF}
{$IFDEF LCLQt6}qt6, qlclopenglwidget,{$ENDIF}
qtwidgets, qtobjects, qtproc, qtint,
QtWSControls;
// gdkgl
const
// enum _QT_GL_CONFIGS
QT_GL_NONE = 0;
QT_GL_USE_GL = 1;
QT_GL_BUFFER_SIZE = 2;
QT_GL_LEVEL = 3;
QT_GL_RGBA = 4;
QT_GL_DOUBLEBUFFER = 5;
QT_GL_STEREO = 6;
QT_GL_AUX_BUFFERS = 7;
QT_GL_RED_SIZE = 8;
QT_GL_GREEN_SIZE = 9;
QT_GL_BLUE_SIZE = 10;
QT_GL_ALPHA_SIZE = 11;
QT_GL_DEPTH_SIZE = 12;
QT_GL_STENCIL_SIZE = 13;
QT_GL_ACCUM_RED_SIZE = 14;
QT_GL_ACCUM_GREEN_SIZE = 15;
QT_GL_ACCUM_BLUE_SIZE = 16;
QT_GL_ACCUM_ALPHA_SIZE = 17;
// GLX_EXT_visual_info extension
QT_GL_X_VISUAL_TYPE_EXT = $22;
QT_GL_TRANSPARENT_TYPE_EXT = $23;
QT_GL_TRANSPARENT_INDEX_VALUE_EXT = $24;
QT_GL_TRANSPARENT_RED_VALUE_EXT = $25;
QT_GL_TRANSPARENT_GREEN_VALUE_EXT = $26;
QT_GL_TRANSPARENT_BLUE_VALUE_EXT = $27;
QT_GL_TRANSPARENT_ALPHA_VALUE_EXT = $28;
type
TGLXContext = pointer;
procedure LOpenGLViewport(Handle: HWND; Left, Top, Width, Height: integer);
procedure LOpenGLSwapBuffers(Handle: HWND);
function LOpenGLMakeCurrent(Handle: HWND): boolean;
function LOpenGLReleaseContext(Handle: HWND): boolean;
function LOpenGLCreateContext(AWinControl: TWinControl;
WSPrivate: TWSPrivateClass; SharedControl: TWinControl;
DoubleBuffered, RGBA: boolean;
const RedBits, GreenBits, BlueBits, MajorVersion, MinorVersion,
MultiSampling, AlphaBits, DepthBits, StencilBits, AUXBuffers: Cardinal;
const AParams: TCreateParams): HWND;
procedure LOpenGLDestroyContextInfo(AWinControl: TWinControl);
function CreateOpenGLContextAttrList(DoubleBuffered: boolean; RGBA: boolean;
const RedBits, GreenBits, BlueBits, AlphaBits, DepthBits,
StencilBits, AUXBuffers, MultiSampling: Cardinal): PInteger;
implementation
uses LMessages, Forms;
function XVisualAsString(AVisual: PVisual): string;
begin
if AVisual=nil then
begin
Result:='nil';
end else
begin
Result:=''
+' bits_per_rgb='+dbgs(AVisual^.bits_per_rgb)
+' red_mask='+hexstr(AVisual^.red_mask,8)
+' green_mask='+hexstr(AVisual^.green_mask,8)
+' blue_mask='+hexstr(AVisual^.blue_mask,8)
+' map_entries='+dbgs(AVisual^.map_entries)
+'';
end;
end;
function XDisplayAsString(ADisplay: PDisplay): string;
begin
if ADisplay=nil then
begin
Result:='nil';
end else
begin
Result:=''
+'';
end;
end;
type
{ TQtGLWidget }
TQtGLWidget = class({$IF DEFINED(LCLQt5) OR DEFINED(LCLQt6)}TQtOpenGLWidget{$ELSE}TQtWidget{$ENDIF})
protected
function PaintGLControl(Sender: QObjectH; Event: QEventH): boolean; cdecl;
{$IF DEFINED(LCLQt5) OR DEFINED(LCLQt6)}
procedure paintGL(); cdecl; override;
{$ENDIF}
public
xdisplay: PDisplay;
visual: PXVisualInfo;
glxcontext: TGLXContext;
ref_count: integer;
function EventFilter(Sender: QObjectH; Event: QEventH): Boolean; cdecl; override;
function GetGLXDrawable: GLXDrawable;
end;
{ TQtGLWidget }
function TQtGLWidget.PaintGLControl(Sender: QObjectH; Event: QEventH): boolean;
cdecl;
var
Msg: TLMPaint;
AStruct: PPaintStruct;
B: Boolean;
begin
Result := False;
QEvent_accept(Event);
if CanSendLCLMessage and (LCLObject is TWinControl) then
begin
FillChar(Msg{%H-}, SizeOf(Msg), #0);
Msg.Msg := LM_PAINT;
New(AStruct);
try
try
FillChar(AStruct^, SizeOf(TPaintStruct), 0);
// QWidget_geometry(Widget, @AStruct^.rcPaint);
QPaintEvent_rect(QPaintEventH(Event), @AStruct^.rcPaint);
AStruct^.hdc := PtrUInt(Self.glxcontext); // HDC(QOpenGLWidget_context(QOpenGLWidgetH(Sender)));
Msg.PaintStruct := AStruct;
Msg.DC := AStruct^.hdc;
LCLObject.WindowProc(TLMessage(Msg));
QEvent_ignore(Event);
Result := True; {let Qt finish}
finally
Dispose(AStruct);
end;
except
// prevent recursive repainting !
B := (Sender <> nil) and QtWidgetSet.IsValidHandle(HWND(Self));
if B then
QWidget_setUpdatesEnabled(QWidgetH(Sender), False);
try
Application.HandleException(nil);
finally
if B and Assigned(Application) and not Application.Terminated then
QWidget_setUpdatesEnabled(QWidgetH(Sender), True);
end;
end;
end else
begin
DebugLn('TQtGLWidget.PaintGLControl error CanSendLCLMessage=',dbgs(CanSendLCLMessage),' LCLObject=',dbgsName(LCLObject));
end;
end;
{$IF DEFINED(LCLQt5) OR DEFINED(LCLQt6)}
procedure TQtGLWidget.paintGL(); cdecl;
var
AEvent: QPaintEventH;
ARect: TRect;
begin
QWidget_rect(Widget, @ARect);
AEvent := QPaintEvent_Create(PRect(@ARect));
PaintGLControl(Widget, AEvent);
QPaintEvent_Destroy(AEvent);
end;
{$ENDIF}
function TQtGLWidget.EventFilter(Sender: QObjectH; Event: QEventH): Boolean;
cdecl;
begin
Result := False;
if QEvent_type(Event) = QEventPaint then
begin
{$IFDEF LCLQt}
PaintGLControl(Sender, Event);
{$ENDIF}
end else
Result := inherited EventFilter(Sender, Event);
end;
function TQtGLWidget.GetGLXDrawable: GLXDrawable;
begin
result := QWidget_winID(Widget);
end;
procedure LOpenGLViewport(Handle: HWND; Left, Top, Width, Height: integer);
begin
glViewport(Left,Top,Width,Height);
end;
procedure LOpenGLSwapBuffers(Handle: HWND);
var
Widget: TQtGLWidget;
begin
if Handle=0 then
RaiseGDBException('LOpenGLSwapBuffers Handle=0');
Widget:=TQtGLWidget(Handle);
glXSwapBuffers(Widget.xdisplay,
Widget.GetGLXDrawable
);
end;
function LOpenGLMakeCurrent(Handle: HWND): boolean;
var
Widget: TQtGLWidget;
begin
if Handle=0 then
RaiseGDBException('LOpenGLSwapBuffers Handle=0');
Result:=false;
Widget:=TQtGLWidget(Handle);
Result:=glXMakeCurrent(Widget.xdisplay,
Widget.GetGLXDrawable,
Widget.glxcontext);
end;
function LOpenGLReleaseContext(Handle: HWND): boolean;
var
Widget: TQtGLWidget;
begin
Result := false;
if Handle=0 then
RaiseGDBException('LOpenGLSwapBuffers Handle=0');
Widget:=TQtGLWidget(Handle);
Result := glXMakeCurrent(Widget.xdisplay, 0, nil);
end;
{$IFDEF ModernGL}
function CustomXErrorHandler({%H-}para1:PDisplay; para2:PXErrorEvent):cint;cdecl;
begin
if para2^.error_code=8 then begin
raise Exception.Create('A BadMatch X error occurred. Most likely the requested OpenGL version is invalid.');
end;
Result:=0;
end;
{$ENDIF}
type
TContextAttribs = record
AttributeList: PLongint;
MajorVersion: Cardinal;
MinorVersion: Cardinal;
MultiSampling: Cardinal;
ContextFlags: Cardinal;
end;
{function LOpenGLCreateContext(AWinControl: TWinControl;
WSPrivate: TWSPrivateClass; SharedControl: TWinControl;
DoubleBuffered, RGBA: boolean;
const MultiSampling, AlphaBits, DepthBits, StencilBits: Cardinal;
const AParams: TCreateParams): HWND;}
function LOpenGLCreateContext(AWinControl: TWinControl;
WSPrivate: TWSPrivateClass; SharedControl: TWinControl;
DoubleBuffered, RGBA: boolean;
const RedBits, GreenBits, BlueBits, MajorVersion, MinorVersion,
MultiSampling, AlphaBits, DepthBits, StencilBits, AUXBuffers: Cardinal;
const AParams: TCreateParams): HWND;
var
{$IFDEF ModernGL} { Used with glXCreateContextAttribsARB to select 3.X and above context }
Context3X: array [0..6] of Integer;
ScreenNum: cint;
FBConfig: TGLXFBConfig;
FBConfigs: PGLXFBConfig;
FBConfigsCount: Integer;
i: Integer;
Samples: cint;
BestSamples: Integer;
BestFBConfig: Integer;
XVInfo: PXVisualInfo;
XDisplay: PDisplay;
{$ENDIF}
AttrList: TContextAttribs;
NewQtWidget: TQtGLWidget;
direct: boolean;
{$IF DEFINED(LCLQt5) OR DEFINED(LCLQt6)}
AWinFormat: QSurfaceFormatH;
{$ENDIF}
{$IFDEF VerboseMultiSampling}
samp_buf, visual_id, red_size, blue_size, green_size, alpha_size: integer;
{$ENDIF}
SharedContext: TGLXContext;
begin
if WSPrivate=nil then ;
AttrList.AttributeList := CreateOpenGLContextAttrList(DoubleBuffered,RGBA,RedBits,GreenBits,BlueBits,AlphaBits,DepthBits,StencilBits,AUXBuffers, MultiSampling);
try
NewQtWidget:=TQtGLWidget.Create(AWinControl,AParams);
{$ifdef LCLQt}
NewQtWidget.setAttribute(QtWA_PaintOnScreen);
NewQtWidget.setAttribute(QtWA_NoSystemBackground);
NewQtWidget.setAttribute(QtWA_OpaquePaintEvent);
{$endif}
NewQtWidget.HasPaint := false;
{$IFDEF LCLQt6}
NewQtWidget.xdisplay := QtWidgetSet.x11Display;
{$ELSE}
NewQtWidget.xdisplay := QX11Info_display;
{$ENDIF}
NewQtWidget.visual := glXChooseVisual(NewQtWidget.xdisplay,
DefaultScreen(NewQtWidget.xdisplay), @attrList.AttributeList[0]);
direct := false;
{$IF DEFINED(LCLQt5) OR DEFINED(LCLQt6)}
QWindow_setSurfaceType(QWidget_windowHandle(NewQtWidget.Widget), QSurfaceSurfaceTypeOpenGLSurface);
AWinFormat := QSurfaceFormat_Create();
if MajorVersion > 0 then
begin
QSurfaceFormat_setMajorVersion(AWinFormat, MajorVersion);
QSurfaceFormat_setMinorVersion(AWinFormat, MinorVersion);
end;
if DoubleBuffered then
QSurfaceFormat_setSwapBehavior(AWinFormat, QSurfaceSwapBehaviorDoubleBuffer);
QSurfaceFormat_setSamples(AWinFormat, Integer(MultiSampling));
QSurfaceFormat_setDepthBufferSize(AWinFormat, DepthBits);
QSurfaceFormat_setStencilBufferSize(AWinFormat, Integer(StencilBits));
QSurfaceFormat_setRenderableType(AWinFormat, QSurfaceFormatRenderableTypeOpenGL);
QSurfaceFormat_setAlphaBufferSize(AWinFormat, AlphaBits);
QWindow_destroyPlatformResources(QWidget_windowHandle(NewQtWidget.Widget));
QWindow_setFormat(QWidget_windowHandle(NewQtWidget.Widget), AWinFormat);
QWindow_createPlatformResources(QWidget_windowHandle(NewQtWidget.Widget));
QSurfaceFormat_destroy(AWinFormat);
NewQtWidget.GetGLXDrawable;
{$ENDIF}
{ SharedContext will be passed to various glX routines,
to enable sharing OpenGL resources with SharedControl. }
if SharedControl <> nil then
SharedContext := TQtGLWidget(SharedControl.Handle).GLXContext
else
SharedContext := nil;
{$IFDEF ModernGL}
if GLX_version_1_3(NewQtWidget.xdisplay) then
begin
//use approach recommended since glX 1.3
direct := true;
XDisplay := NewQtWidget.xdisplay;
ScreenNum := DefaultScreen (XDisplay);
AttrList.MajorVersion:=MajorVersion;
AttrList.MinorVersion:=MinorVersion;
// fill in context flags
AttrList.ContextFlags:=0;
//if DebugContext then
// AttrList.ContextFlags:=Attribs.ContextFlags or GLX_CONTEXT_DEBUG_BIT_ARB;
if (MultiSampling > 1) and GLX_ARB_multisample(XDisplay,ScreenNum) then begin
AttrList.MultiSampling := MultiSampling;
end else
begin
AttrList.MultiSampling:=0;
if MultiSampling > 1 then
{$IFDEF UNIX}writeln('Multi-sampling not supported');{$ENDIF}
end;
FBConfigsCount:=0;
FBConfigs:=glXChooseFBConfig(XDisplay, ScreenNum, @AttrList.AttributeList[0], FBConfigsCount);
if FBConfigsCount = 0 then begin
{$IFDEF UNIX}writeln('Could not find FB config: will try without multi-sampling');{$ENDIF}
FreeMem(AttrList.AttributeList);
AttrList.AttributeList := CreateOpenGLContextAttrList(DoubleBuffered,RGBA,RedBits,GreenBits,BlueBits,AlphaBits,DepthBits,StencilBits,AUXBuffers, 0);
AttrList.MajorVersion:=MajorVersion;
AttrList.MinorVersion:=MinorVersion;
// fill in context flags
AttrList.ContextFlags:=0;
FBConfigs:=glXChooseFBConfig(XDisplay, ScreenNum, @AttrList.AttributeList[0], FBConfigsCount);
end;
if FBConfigsCount = 0 then begin
raise Exception.Create('Could not find FB config');
//{$IFDEF UNIX}writeln('Could not find FB config: this may end poorly.');{$ENDIF}
//NewQtWidget.glxcontext := glXCreateContext(NewQtWidget.xdisplay,NewQtWidget.visual, nil, direct);
//goto 123;
end;
// if multisampling is requested try to get a number of sample buffers as
// close to the specified number as possible
if AttrList.MultiSampling > 0 then
begin
BestSamples:=0;
for i := 0 to FBConfigsCount-1 do
begin
Samples:=0;
glXGetFBConfigAttrib(NewQtWidget.xdisplay, FBConfigs[i], GLX_SAMPLES_ARB, Samples);
if Samples = AttrList.MultiSampling then
begin
BestSamples := Samples;
BestFBConfig := i;
break;
end else
begin
if (Samples>BestSamples) and (Samples<AttrList.MultiSampling) then
begin
BestSamples := Samples;
BestFBConfig := i;
end;
end;
end;
//raise Exception.Create('BestFBConfig '+inttostr(BestFBConfig));
FBConfig := FBConfigs[BestFBConfig];
{$IFDEF VerboseMultiSampling}
//HTTPS://WWW.OPENGL.ORG/DISCUSSION_BOARDS/SHOWTHREAD.PHP/168359-OPENGL-3-X-AND-QT-FRAMEWORK?STYLEID=12
GLXGETFBCONFIGATTRIB(XDISPLAY, FBCONFIG, GLX_SAMPLE_BUFFERS, SAMP_BUF);
GLXGETFBCONFIGATTRIB(XDISPLAY, FBCONFIG, GLX_SAMPLES, SAMPLES);
GLXGETFBCONFIGATTRIB(XDISPLAY, FBCONFIG, GLX_VISUAL_ID, VISUAL_ID);
GLXGETFBCONFIGATTRIB(XDISPLAY, FBCONFIG, GLX_RED_SIZE, RED_SIZE);
GLXGETFBCONFIGATTRIB(XDISPLAY, FBCONFIG, GLX_BLUE_SIZE, BLUE_SIZE);
GLXGETFBCONFIGATTRIB(XDISPLAY, FBCONFIG, GLX_GREEN_SIZE, GREEN_SIZE);
GLXGETFBCONFIGATTRIB(XDISPLAY, FBCONFIG, GLX_ALPHA_SIZE, ALPHA_SIZE);
DEBUGLN(FORMAT('OPENGL FBCONFIG %D, ID 0X%X, SAMPLE_BUFFERS %D, SAMPLES %D, R%D G%D B%D A%D',
[BESTFBCONFIG, VISUAL_ID, SAMP_BUF, SAMPLES, RED_SIZE, BLUE_SIZE, GREEN_SIZE, ALPHA_SIZE]));
{$ENDIF}
end else
begin
{ just choose the first FB config from the FBConfigs list.
More involved selection possible. }
FBConfig := FBConfigs^;
end;
XVInfo:=glXGetVisualFromFBConfig(NewQtWidget.xdisplay, FBConfig);
if XVInfo=nil then
raise Exception.Create('QT no visual found');
if (GLX_ARB_create_context(NewQtWidget.xdisplay, DefaultScreen(NewQtWidget.xdisplay))) then
begin
// install custom X error handler
XSetErrorHandler(@CustomXErrorHandler);
Context3X[0] := GLX_CONTEXT_MAJOR_VERSION_ARB;
if AttrList.MajorVersion = 0 then
Context3X[1] := 1
else
Context3X[1] := AttrList.MajorVersion;
Context3X[2] := GLX_CONTEXT_MINOR_VERSION_ARB;
Context3X[3] := AttrList.MinorVersion;
Context3X[4] := GLX_CONTEXT_FLAGS_ARB;
Context3X[5] := AttrList.ContextFlags;
Context3X[6] := None;
NewQtWidget.glxcontext := glXCreateContextAttribsARB(NewQtWidget.xdisplay, FBConfig, SharedContext, direct, Context3X);
//raise Exception.Create('key '+inttostr(BestFBConfig));
// restore default error handler
XSetErrorHandler(nil);
end else
begin
NewQtWidget.glxcontext := glXCreateNewContext(NewQtWidget.xdisplay, FBConfig, GLX_RGBA_TYPE, SharedContext, direct);
end;
if FBConfigs<>nil then
XFree(FBConfigs);
end else
{$ENDIF}
NewQtWidget.glxcontext := glXCreateContext(NewQtWidget.xdisplay,
NewQtWidget.visual, SharedContext, direct);
//123:
NewQtWidget.ref_count := 1;
NewQtWidget.AttachEvents;
Result:=HWND(NewQtWidget);
finally
FreeMem(AttrList.AttributeList);
end;
end;
procedure LOpenGLDestroyContextInfo(AWinControl: TWinControl);
begin
if not AWinControl.HandleAllocated then exit;
// nothing to do
end;
function CreateOpenGLContextAttrList(DoubleBuffered: boolean; RGBA: boolean;
const RedBits, GreenBits, BlueBits, AlphaBits, DepthBits, StencilBits,
AUXBuffers, MultiSampling: Cardinal): PInteger;
var
p: integer;
UseFBConfig: boolean;
procedure Add(i: integer);
begin
if Result<>nil then
Result[p]:=i;
inc(p);
end;
procedure CreateList;
begin
p:=0;
//UseFBConfig := false;
if UseFBConfig then
begin
Add(GLX_X_RENDERABLE); Add(1);
Add(GLX_X_VISUAL_TYPE);
Add(GLX_TRUE_COLOR);
end;
if DoubleBuffered then
begin
if UseFBConfig then
begin Add(GLX_DOUBLEBUFFER); Add(1); end else
Add(GLX_DOUBLEBUFFER);
end;
if RGBA then
begin
if not UseFBConfig then Add(GLX_RGBA);
{ For UseFBConfig, glXChooseFBConfig already defaults to RGBA }
end;
Add(GLX_RED_SIZE); Add(RedBits);
Add(GLX_GREEN_SIZE); Add(GreenBits);
Add(GLX_BLUE_SIZE); Add(BlueBits);
if AlphaBits>0 then
begin
Add(GLX_ALPHA_SIZE); Add(AlphaBits);
end;
if DepthBits>0 then
begin
Add(GLX_DEPTH_SIZE); Add(DepthBits);
end;
if StencilBits>0 then
begin
Add(GLX_STENCIL_SIZE); Add(StencilBits);
end;
if AUXBuffers>0 then
begin
Add(GLX_AUX_BUFFERS); Add(AUXBuffers);
end;
{$IFDEF ModernGL}
if MultiSampling>1 then
begin
Add(GLX_SAMPLE_BUFFERS); Add(1);
Add(GLX_SAMPLES); Add(MultiSampling);
end;
{$ENDIF}
Add(0); { 0 = X.None (be careful: GLX_NONE is something different) }
end;
begin
{$IFDEF VerboseMultiSampling}
debugln(['CreateOpenGLContextAttrList MultiSampling=',MultiSampling]);
{$ENDIF}
{$IFDEF ModernGL}
{$IFDEF LCLQt6}
UseFBConfig := GLX_version_1_3(QtWidgetSet.x11Display());
{$ELSE}
UseFBConfig := GLX_version_1_3(QX11Info_display());
{$ENDIF}
{$ELSE}
UseFBConfig := false;
{$ENDIF}
Result:=nil;
CreateList;
GetMem(Result,SizeOf(integer)*p);
CreateList;
end;
end.