GLS.SDL.Context.pas 7.8 KB

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