GLS.SDLContext.pas 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. //
  2. // The graphics rendering engine GLScene http://glscene.org
  3. //
  4. unit GLS.SDLContext;
  5. (*
  6. SDL specific Context and Viewer.
  7. NOTA: SDL notifies use of context destruction *after* it happened, this prevents
  8. clean release of allocated stuff and requires a temporary switch to
  9. "ignore OpenGL errors" mode during destruction, thus potentially
  10. leaking memory (depending on hardware drivers willingness to perform
  11. automatic releases)
  12. *)
  13. interface
  14. uses
  15. Winapi.Windows,
  16. System.Classes,
  17. System.SysUtils,
  18. GLS.OpenGLTokens,
  19. GLS.OpenGLAdapter,
  20. GLS.XOpenGL,
  21. GLS.Context,
  22. GLS.Scene,
  23. SDL2.Import,
  24. GLS.SDLWindow;
  25. type
  26. (* A viewer using SDL.
  27. Beware: only one at a time, no other viewers allowed!
  28. Will also close the application when the window is closed! *)
  29. TSDLViewer = class(TGLNonVisualViewer)
  30. private
  31. FCaption: string;
  32. FOnSDLEvent: TSDLEvent;
  33. FOnEventPollDone: TNotifyEvent;
  34. FOnResize: TNotifyEvent;
  35. protected
  36. procedure SetCaption(const val: string);
  37. procedure DoOnOpen(sender: TObject);
  38. procedure DoOnClose(sender: TObject);
  39. procedure DoOnResize(sender: TObject);
  40. procedure DoOnSDLEvent(sender: TObject; const event: TSDL_Event);
  41. procedure DoOnEventPollDone(sender: TObject);
  42. procedure DoBufferStructuralChange(Sender: TObject); override;
  43. procedure PrepareGLContext; override;
  44. public
  45. constructor Create(AOwner: TComponent); override;
  46. destructor Destroy; override;
  47. procedure Render(baseObject: TGLBaseSceneObject = nil); override;
  48. function Active: Boolean;
  49. published
  50. property Caption: string read FCaption write SetCaption;
  51. property OnResize: TNotifyEvent read FOnResize write FOnResize;
  52. (* Fired whenever an SDL Event is polled.
  53. SDL_QUITEV and SDL_VIDEORESIZE are not passed to this event handler,
  54. they are passed via OnClose and OnResize respectively. *)
  55. property OnSDLEvent: TSDLEvent read FOnSDLEvent write FOnSDLEvent;
  56. // Fired whenever an event polling completes with no events left to poll.
  57. property OnEventPollDone: TNotifyEvent read FOnEventPollDone write FOnEventPollDone;
  58. end;
  59. (* A context driver for OpenGL via SDL (libsdl.org).
  60. Due to limitations of SDL:
  61. you may have only one SDL window opened at any time (you cannot have memory viewers)
  62. closing the SDL window will terminate the application *)
  63. TSDLContext = class(TGLScreenControlingContext)
  64. private
  65. FSDLWin: TSDLWindow;
  66. FSimulatedValidity: Boolean; // Hack around SDL's post-notified destruction of context
  67. protected
  68. procedure DoCreateContext(outputDevice: HDC); override;
  69. procedure DoCreateMemoryContext(outputDevice: HWND; width, height: Integer; BufferCount: integer); override;
  70. function DoShareLists(aContext: TGLContext): Boolean; override;
  71. procedure DoDestroyContext; override;
  72. procedure DoActivate; override;
  73. procedure DoDeactivate; override;
  74. public
  75. constructor Create; override;
  76. destructor Destroy; override;
  77. function IsValid: Boolean; override;
  78. procedure SwapBuffers; override;
  79. function RenderOutputDevice: Pointer; override;
  80. property SDLWindow: TSDLWindow read FSDLWin;
  81. end;
  82. procedure Register;
  83. // ------------------------------------------------------------------
  84. implementation
  85. // ------------------------------------------------------------------
  86. // ------------------
  87. // ------------------ TSDLViewer ------------------
  88. // ------------------
  89. constructor TSDLViewer.Create(AOwner: TComponent);
  90. begin
  91. inherited Create(AOwner);
  92. Width := 640;
  93. Height := 480;
  94. end;
  95. destructor TSDLViewer.Destroy;
  96. begin
  97. inherited Destroy;
  98. end;
  99. procedure TSDLViewer.DoBufferStructuralChange(Sender: TObject);
  100. begin
  101. // ignore that, supporting it with SDL is not very praticable as of now...
  102. end;
  103. procedure TSDLViewer.PrepareGLContext;
  104. begin
  105. with Buffer.RenderingContext as TSDLContext do
  106. begin
  107. Width := Self.Width;
  108. Height := Self.Height;
  109. with FSDLWin do
  110. begin
  111. Caption := Self.Caption;
  112. OnOpen := DoOnOpen;
  113. OnClose := DoOnClose;
  114. OnResize := DoOnResize;
  115. OnSDLEvent := DoOnSDLEvent;
  116. OnEventPollDone := DoOnEventPollDone;
  117. end;
  118. end;
  119. end;
  120. procedure TSDLViewer.Render(baseObject: TGLBaseSceneObject = nil);
  121. begin
  122. LoadOpenGL;
  123. if Buffer.RenderingContext = nil then
  124. begin
  125. Buffer.CreateRC(0, False);
  126. end;
  127. Buffer.Render(baseObject);
  128. end;
  129. function TSDLViewer.Active: Boolean;
  130. begin
  131. Result := Assigned(Buffer.RenderingContext) and Buffer.RenderingContext.IsValid;
  132. end;
  133. procedure TSDLViewer.SetCaption(const val: string);
  134. begin
  135. if val <> FCaption then
  136. begin
  137. FCaption := val;
  138. if Buffer.RenderingContext <> nil then
  139. with Buffer.RenderingContext as TSDLContext do
  140. if Assigned(FSDLWin) then
  141. FSDLWin.Caption := FCaption;
  142. end;
  143. end;
  144. procedure TSDLViewer.DoOnOpen(sender: TObject);
  145. begin
  146. // nothing yet
  147. end;
  148. procedure TSDLViewer.DoOnClose(sender: TObject);
  149. begin
  150. // nothing yet
  151. end;
  152. procedure TSDLViewer.DoOnResize(sender: TObject);
  153. begin
  154. with Buffer.RenderingContext as TSDLContext do
  155. begin
  156. Self.Width := FSDLWin.Width;
  157. Self.Height := FSDLWin.Height;
  158. Buffer.Resize(0, 0, FSDLWin.Width, FSDLWin.Height);
  159. end;
  160. if Assigned(FOnResize) then
  161. FOnResize(Self);
  162. end;
  163. procedure TSDLViewer.DoOnSDLEvent(sender: TObject; const event: TSDL_Event);
  164. begin
  165. if Assigned(FOnSDLEvent) then
  166. FOnSDLEvent(sender, event);
  167. end;
  168. procedure TSDLViewer.DoOnEventPollDone(sender: TObject);
  169. begin
  170. if Assigned(FOnEventPollDone) then
  171. FOnEventPollDone(sender);
  172. end;
  173. // ------------------
  174. // ------------------ TSDLContext ------------------
  175. // ------------------
  176. constructor TSDLContext.Create;
  177. begin
  178. inherited Create;
  179. FSDLWin := TSDLWindow.Create(nil);
  180. end;
  181. destructor TSDLContext.Destroy;
  182. var
  183. oldIgnore: Boolean;
  184. begin
  185. oldIgnore := vIgnoreOpenGLErrors;
  186. FSimulatedValidity := True;
  187. vIgnoreOpenGLErrors := True;
  188. try
  189. inherited Destroy;
  190. finally
  191. vIgnoreOpenGLErrors := oldIgnore;
  192. FSimulatedValidity := False;
  193. end;
  194. FreeAndNil(FSDLWin);
  195. end;
  196. procedure TSDLContext.DoCreateContext(outputDevice: HDC);
  197. var
  198. sdlOpt: TSDLWindowOptions;
  199. begin
  200. // Just in case it didn't happen already.
  201. if not InitOpenGL then
  202. RaiseLastOSError;
  203. FSDLWin.Width := Width;
  204. FSDLWin.Height := Height;
  205. if ColorBits > 16 then
  206. FSDLWin.PixelDepth := vpd24bits
  207. else
  208. FSDLWin.PixelDepth := vpd16bits;
  209. sdlOpt := [voOpenGL, voHardwareAccel];
  210. if FullScreen then
  211. sdlOpt := sdlOpt + [voFullScreen]
  212. else
  213. sdlOpt := sdlOpt + [voResizable];
  214. if rcoDoubleBuffered in Options then
  215. sdlOpt := sdlOpt + [voDoubleBuffer];
  216. if StencilBits > 0 then
  217. sdlOpt := sdlOpt + [voStencilBuffer];
  218. FSDLWin.Open;
  219. if not FSDLWin.Active then
  220. raise Exception.Create('SDLWindow open failed.');
  221. FGL.Initialize;
  222. MakeGLCurrent;
  223. end;
  224. procedure TSDLContext.DoCreateMemoryContext(outputDevice: HWND; width, height: Integer; BufferCount: integer);
  225. begin
  226. raise Exception.Create(ClassName + ': Memory contexts not supported');
  227. end;
  228. function TSDLContext.DoShareLists(aContext: TGLContext): Boolean;
  229. begin
  230. // nothing (only one context at all times... no need to share)
  231. Result := False;
  232. end;
  233. procedure TSDLContext.DoDestroyContext;
  234. begin
  235. // Beware, SDL will also terminate the application
  236. FGL.Close;
  237. FSDLWin.Close;
  238. end;
  239. procedure TSDLContext.DoActivate;
  240. begin
  241. if not FGL.IsInitialized then
  242. FGL.Initialize;
  243. end;
  244. procedure TSDLContext.DoDeactivate;
  245. begin
  246. // nothing particular (only one context, always active)
  247. end;
  248. function TSDLContext.IsValid: Boolean;
  249. begin
  250. Result := (Assigned(FSDLWin) and (FSDLWin.Active)) or FSimulatedValidity;
  251. end;
  252. procedure TSDLContext.SwapBuffers;
  253. begin
  254. FSDLWin.SwapBuffers;
  255. end;
  256. function TSDLContext.RenderOutputDevice: Pointer;
  257. begin
  258. // unsupported
  259. Result := nil;
  260. end;
  261. procedure Register;
  262. begin
  263. RegisterComponents('GLScene', [TSDLViewer]);
  264. end;
  265. // ------------------------------------------------------------------
  266. initialization
  267. // ------------------------------------------------------------------
  268. RegisterClass(TSDLViewer);
  269. RegisterGLContextClass(TSDLContext);
  270. end.