GLS.DynamicTexture.pas 7.9 KB

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