{ ***************************************************************************** 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 (Samplesnil 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.