GLDynamicTexture.pas 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLDynamicTexture;
  5. (*
  6. Adds a dynamic texture image, which allows for easy updating of
  7. texture data.
  8. *)
  9. interface
  10. {$I GLScene.inc}
  11. uses
  12. Winapi.OpenGL,
  13. Winapi.OpenGLext,
  14. System.Types,
  15. System.Classes,
  16. System.SysUtils,
  17. OpenGLTOkens,
  18. GLCrossPlatform,
  19. GLContext,
  20. GLTexture,
  21. GLTextureFormat,
  22. GLGraphics;
  23. type
  24. // Allows for fast updating of the texture at runtime
  25. TGLDynamicTextureImage = class(TGLBlankImage)
  26. private
  27. FUpdating: integer;
  28. FTexSize: integer;
  29. FBuffer: pointer;
  30. FPBO: TGLBufferObjectHandle;
  31. FData: pointer;
  32. FDirtyRect: TRect;
  33. FUseBGR: boolean;
  34. FUsePBO: boolean;
  35. procedure SetDirtyRectangle(const Value: TRect);
  36. procedure SetUsePBO(const Value: boolean);
  37. protected
  38. function GetTexSize: integer;
  39. function GetBitsPerPixel: integer;
  40. function GetDataFormat: integer;
  41. function GetTextureFormat: integer;
  42. procedure FreePBO;
  43. procedure FreeBuffer;
  44. property BitsPerPixel: integer read GetBitsPerPixel;
  45. property DataFormat: integer read GetDataFormat;
  46. property TextureFormat: integer read GetTextureFormat;
  47. public
  48. constructor Create(AOwner: TPersistent); override;
  49. class function FriendlyName : String; override;
  50. class function FriendlyDescription : String; override;
  51. procedure NotifyChange(Sender: TObject); override;
  52. // Must be called before using the Data pointer. Rendering context must be active!
  53. procedure BeginUpdate;
  54. // Must be called after data is changed. This will upload the new data
  55. procedure EndUpdate;
  56. // Pointer to buffer data. Will be nil outside a BeginUpdate / EndUpdate block
  57. property Data: pointer read FData;
  58. (* Marks the dirty rectangle inside the texture. BeginUpdate sets
  59. it to ((0, 0), (Width, Height)), ie the entire texture.
  60. Override it if you're only changing a small piece of the texture.
  61. Note that the Data pointer is relative to the DirtyRectangle,
  62. NOT the entire texture. *)
  63. property DirtyRectangle: TRect read FDirtyRect write SetDirtyRectangle;
  64. // Indicates that the data is stored as BGR(A) instead of RGB(A). The default is BGR(A)
  65. property UseBGR: boolean read FUseBGR write FUseBGR;
  66. // Enables or disables use of a PBO. Default is true
  67. property UsePBO: boolean read FUsePBO write SetUsePBO;
  68. end;
  69. //---------------------------------------------------------
  70. implementation
  71. //---------------------------------------------------------
  72. uses
  73. GLVectorGeometry;
  74. //----------------------------------
  75. // TGLDynamicTextureImage
  76. //----------------------------------
  77. procedure TGLDynamicTextureImage.BeginUpdate;
  78. var
  79. LTarget: TGLTextureTarget;
  80. begin
  81. Assert(FUpdating >= 0, 'Unbalanced begin/end update');
  82. FUpdating:= FUpdating + 1;
  83. if FUpdating > 1 then
  84. exit;
  85. // initialization
  86. if not (assigned(FPBO) or assigned(FBuffer)) then
  87. begin
  88. // cache so we know if it's changed
  89. FTexSize:= GetTexSize;
  90. if FUsePBO and TGLUnpackPBOHandle.IsSupported then
  91. begin
  92. FPBO:= TGLUnpackPBOHandle.CreateAndAllocate;
  93. // initialize buffer
  94. FPBO.BindBufferData(nil, FTexSize, GL_STREAM_DRAW_ARB);
  95. // unbind so we don't upload the data from it, which is unnecessary
  96. FPBO.UnBind;
  97. end
  98. else
  99. begin
  100. // fall back to regular memory buffer if PBO's aren't supported
  101. FBuffer:= AllocMem(FTexSize);
  102. end;
  103. // Force creation of texture
  104. // This is a bit of a hack, should be a better way...
  105. LTarget := TGLTexture(OwnerTexture).TextureHandle.Target;
  106. CurrentGLContext.GLStates.TextureBinding[0, LTarget] := TGLTexture(OwnerTexture).Handle;
  107. case LTarget of
  108. ttNoShape: ;
  109. ttTexture1D: ;
  110. ttTexture2D:
  111. gl.TexImage2D(GL_TEXTURE_2D, 0, TGLTexture(OwnerTexture).OpenGLTextureFormat, Width, Height, 0, TextureFormat, GL_UNSIGNED_BYTE, nil);
  112. ttTexture3D: ;
  113. ttTexture1DArray: ;
  114. ttTexture2DArray: ;
  115. ttTextureRect: ;
  116. ttTextureBuffer: ;
  117. ttTextureCube: ;
  118. ttTexture2DMultisample: ;
  119. ttTexture2DMultisampleArray: ;
  120. ttTextureCubeArray: ;
  121. end;
  122. end;
  123. gl.CheckError;
  124. if assigned(FPBO) then
  125. begin
  126. FPBO.Bind;
  127. FData:= FPBO.MapBuffer(GL_WRITE_ONLY_ARB);
  128. end
  129. else
  130. begin
  131. FData:= FBuffer;
  132. end;
  133. gl.CheckError;
  134. FDirtyRect:= GetGLRect(0, 0, Width, Height);
  135. end;
  136. constructor TGLDynamicTextureImage.Create(AOwner: TPersistent);
  137. begin
  138. inherited Create(AOwner);
  139. FUseBGR:= true;
  140. FUsePBO:= true;
  141. end;
  142. procedure TGLDynamicTextureImage.EndUpdate;
  143. var
  144. d: pointer;
  145. LTarget: TGLTextureTarget;
  146. begin
  147. Assert(FUpdating > 0, 'Unbalanced begin/end update');
  148. FUpdating:= FUpdating - 1;
  149. if FUpdating > 0 then
  150. exit;
  151. if assigned(FPBO) then
  152. begin
  153. FPBO.UnmapBuffer;
  154. // pointer will act as an offset when using PBO
  155. d:= nil;
  156. end
  157. else
  158. begin
  159. d:= FBuffer;
  160. end;
  161. LTarget := TGLTexture(OwnerTexture).TextureHandle.Target;
  162. CurrentGLContext.GLStates.TextureBinding[0, LTarget] := TGLTexture(OwnerTexture).Handle;
  163. case LTarget of
  164. ttNoShape: ;
  165. ttTexture1D: ;
  166. ttTexture2D:
  167. begin
  168. gl.TexSubImage2D(GL_TEXTURE_2D, 0,
  169. FDirtyRect.Left, FDirtyRect.Top,
  170. FDirtyRect.Right-FDirtyRect.Left,
  171. FDirtyRect.Bottom-FDirtyRect.Top,
  172. TextureFormat, DataFormat, d);
  173. end;
  174. ttTexture3D: ;
  175. ttTexture1DArray: ;
  176. ttTexture2DArray: ;
  177. ttTextureRect: ;
  178. ttTextureBuffer: ;
  179. ttTextureCube: ;
  180. ttTexture2DMultisample: ;
  181. ttTexture2DMultisampleArray: ;
  182. ttTextureCubeArray: ;
  183. end;
  184. if assigned(FPBO) then
  185. FPBO.UnBind;
  186. FData:= nil;
  187. gl.CheckError;
  188. end;
  189. procedure TGLDynamicTextureImage.FreeBuffer;
  190. begin
  191. if assigned(FBuffer) then
  192. begin
  193. FreeMem(FBuffer);
  194. FBuffer:= nil;
  195. end;
  196. end;
  197. procedure TGLDynamicTextureImage.FreePBO;
  198. begin
  199. if assigned(FPBO) then
  200. begin
  201. FPBO.Free;
  202. FPBO:= nil;
  203. end;
  204. end;
  205. class function TGLDynamicTextureImage.FriendlyName : String;
  206. begin
  207. Result:='Dynamic Texture';
  208. end;
  209. class function TGLDynamicTextureImage.FriendlyDescription : String;
  210. begin
  211. Result:='Dynamic Texture - optimised for changes at runtime';
  212. end;
  213. function TGLDynamicTextureImage.GetBitsPerPixel: integer;
  214. begin
  215. Result := 8 * GetTextureElementSize( TGLTexture(OwnerTexture).TextureFormatEx );
  216. end;
  217. function TGLDynamicTextureImage.GetDataFormat: integer;
  218. var
  219. data, color: Cardinal;
  220. begin
  221. FindCompatibleDataFormat(TGLTexture(OwnerTexture).TextureFormatEx, color, data);
  222. Result := data;
  223. end;
  224. function TGLDynamicTextureImage.GetTexSize: integer;
  225. begin
  226. result:= Width * Height * BitsPerPixel div 8;
  227. end;
  228. function TGLDynamicTextureImage.GetTextureFormat: integer;
  229. var
  230. data, color: Cardinal;
  231. begin
  232. FindCompatibleDataFormat(TGLTexture(OwnerTexture).TextureFormatEx, color, data);
  233. if FUseBGR then
  234. case color of
  235. GL_RGB: color := GL_BGR;
  236. GL_RGBA: color := GL_BGRA;
  237. end;
  238. Result := color;
  239. end;
  240. procedure TGLDynamicTextureImage.NotifyChange(Sender: TObject);
  241. begin
  242. if FTexSize <> GetTexSize then
  243. begin
  244. FreePBO;
  245. FreeBuffer;
  246. end;
  247. inherited;
  248. end;
  249. procedure TGLDynamicTextureImage.SetDirtyRectangle(const Value: TRect);
  250. begin
  251. FDirtyRect.Left:= MaxInteger(Value.Left, 0);
  252. FDirtyRect.Top:= MaxInteger(Value.Top, 0);
  253. FDirtyRect.Right:= MinInteger(Value.Right, Width);
  254. FDirtyRect.Bottom:= MinInteger(Value.Bottom, Height);
  255. end;
  256. procedure TGLDynamicTextureImage.SetUsePBO(const Value: boolean);
  257. begin
  258. Assert(FUpdating = 0, 'Cannot change PBO settings while updating');
  259. if FUsePBO <> Value then
  260. begin
  261. FUsePBO := Value;
  262. if not FUsePBO then
  263. FreePBO
  264. else
  265. FreeBuffer;
  266. end;
  267. end;
  268. //----------------------------------
  269. initialization
  270. //----------------------------------
  271. RegisterGLTextureImageClass(TGLDynamicTextureImage);
  272. end.