WebGLContextBuffers.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "WebGLContext.h"
  6. #include "GLContext.h"
  7. #include "WebGLBuffer.h"
  8. #include "WebGLTransformFeedback.h"
  9. #include "WebGLVertexArray.h"
  10. #include "mozilla/CheckedInt.h"
  11. namespace mozilla {
  12. WebGLRefPtr<WebGLBuffer>*
  13. WebGLContext::ValidateBufferSlot(const char* funcName, GLenum target)
  14. {
  15. WebGLRefPtr<WebGLBuffer>* slot = nullptr;
  16. switch (target) {
  17. case LOCAL_GL_ARRAY_BUFFER:
  18. slot = &mBoundArrayBuffer;
  19. break;
  20. case LOCAL_GL_ELEMENT_ARRAY_BUFFER:
  21. slot = &(mBoundVertexArray->mElementArrayBuffer);
  22. break;
  23. }
  24. if (IsWebGL2()) {
  25. switch (target) {
  26. case LOCAL_GL_COPY_READ_BUFFER:
  27. slot = &mBoundCopyReadBuffer;
  28. break;
  29. case LOCAL_GL_COPY_WRITE_BUFFER:
  30. slot = &mBoundCopyWriteBuffer;
  31. break;
  32. case LOCAL_GL_PIXEL_PACK_BUFFER:
  33. slot = &mBoundPixelPackBuffer;
  34. break;
  35. case LOCAL_GL_PIXEL_UNPACK_BUFFER:
  36. slot = &mBoundPixelUnpackBuffer;
  37. break;
  38. case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
  39. slot = &(mBoundTransformFeedback->mGenericBufferBinding);
  40. break;
  41. case LOCAL_GL_UNIFORM_BUFFER:
  42. slot = &mBoundUniformBuffer;
  43. break;
  44. }
  45. }
  46. if (!slot) {
  47. ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
  48. return nullptr;
  49. }
  50. return slot;
  51. }
  52. WebGLBuffer*
  53. WebGLContext::ValidateBufferSelection(const char* funcName, GLenum target)
  54. {
  55. const auto& slot = ValidateBufferSlot(funcName, target);
  56. if (!slot)
  57. return nullptr;
  58. const auto& buffer = *slot;
  59. if (!buffer) {
  60. ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
  61. return nullptr;
  62. }
  63. if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER) {
  64. if (mBoundTransformFeedback->IsActiveAndNotPaused()) {
  65. ErrorInvalidOperation("%s: Cannot select TRANSFORM_FEEDBACK_BUFFER when"
  66. " transform feedback is active and unpaused.",
  67. funcName);
  68. return nullptr;
  69. }
  70. if (buffer->IsBoundForNonTF()) {
  71. ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for"
  72. " non-transform-feedback.",
  73. funcName);
  74. return nullptr;
  75. }
  76. } else {
  77. if (buffer->IsBoundForTF()) {
  78. ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for"
  79. " transform feedback.",
  80. funcName);
  81. return nullptr;
  82. }
  83. }
  84. return buffer.get();
  85. }
  86. IndexedBufferBinding*
  87. WebGLContext::ValidateIndexedBufferSlot(const char* funcName, GLenum target, GLuint index)
  88. {
  89. decltype(mIndexedUniformBufferBindings)* bindings;
  90. const char* maxIndexEnum;
  91. switch (target) {
  92. case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
  93. bindings = &(mBoundTransformFeedback->mIndexedBindings);
  94. maxIndexEnum = "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS";
  95. break;
  96. case LOCAL_GL_UNIFORM_BUFFER:
  97. bindings = &mIndexedUniformBufferBindings;
  98. maxIndexEnum = "MAX_UNIFORM_BUFFER_BINDINGS";
  99. break;
  100. default:
  101. ErrorInvalidEnum("%s: Bad `target`: 0x%04x", funcName, target);
  102. return nullptr;
  103. }
  104. if (index >= bindings->size()) {
  105. ErrorInvalidValue("%s: `index` >= %s.", funcName, maxIndexEnum);
  106. return nullptr;
  107. }
  108. return &(*bindings)[index];
  109. }
  110. ////////////////////////////////////////
  111. void
  112. WebGLContext::BindBuffer(GLenum target, WebGLBuffer* buffer)
  113. {
  114. const char funcName[] = "bindBuffer";
  115. if (IsContextLost())
  116. return;
  117. if (buffer && !ValidateObject(funcName, *buffer))
  118. return;
  119. const auto& slot = ValidateBufferSlot(funcName, target);
  120. if (!slot)
  121. return;
  122. if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
  123. return;
  124. gl->MakeCurrent();
  125. gl->fBindBuffer(target, buffer ? buffer->mGLName : 0);
  126. WebGLBuffer::SetSlot(target, buffer, slot);
  127. if (buffer) {
  128. buffer->SetContentAfterBind(target);
  129. }
  130. switch (target) {
  131. case LOCAL_GL_PIXEL_PACK_BUFFER:
  132. case LOCAL_GL_PIXEL_UNPACK_BUFFER:
  133. gl->fBindBuffer(target, 0);
  134. break;
  135. }
  136. }
  137. ////////////////////////////////////////
  138. bool
  139. WebGLContext::ValidateIndexedBufferBinding(const char* funcName, GLenum target,
  140. GLuint index,
  141. WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
  142. IndexedBufferBinding** const out_indexedBinding)
  143. {
  144. *out_genericBinding = ValidateBufferSlot(funcName, target);
  145. if (!*out_genericBinding)
  146. return false;
  147. *out_indexedBinding = ValidateIndexedBufferSlot(funcName, target, index);
  148. if (!*out_indexedBinding)
  149. return false;
  150. if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
  151. mBoundTransformFeedback->mIsActive)
  152. {
  153. ErrorInvalidOperation("%s: Cannot update indexed buffer bindings on active"
  154. " transform feedback objects.",
  155. funcName);
  156. return false;
  157. }
  158. return true;
  159. }
  160. void
  161. WebGLContext::BindBufferBase(GLenum target, GLuint index, WebGLBuffer* buffer)
  162. {
  163. const char funcName[] = "bindBufferBase";
  164. if (IsContextLost())
  165. return;
  166. if (buffer && !ValidateObject(funcName, *buffer))
  167. return;
  168. WebGLRefPtr<WebGLBuffer>* genericBinding;
  169. IndexedBufferBinding* indexedBinding;
  170. if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
  171. &indexedBinding))
  172. {
  173. return;
  174. }
  175. if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
  176. return;
  177. ////
  178. gl->MakeCurrent();
  179. gl->fBindBufferBase(target, index, buffer ? buffer->mGLName : 0);
  180. ////
  181. WebGLBuffer::SetSlot(target, buffer, genericBinding);
  182. WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
  183. indexedBinding->mRangeStart = 0;
  184. indexedBinding->mRangeSize = 0;
  185. if (buffer) {
  186. buffer->SetContentAfterBind(target);
  187. }
  188. }
  189. void
  190. WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
  191. WebGLintptr offset, WebGLsizeiptr size)
  192. {
  193. const char funcName[] = "bindBufferRange";
  194. if (IsContextLost())
  195. return;
  196. if (buffer && !ValidateObject(funcName, *buffer))
  197. return;
  198. if (!ValidateNonNegative(funcName, "offset", offset) ||
  199. !ValidateNonNegative(funcName, "size", size))
  200. {
  201. return;
  202. }
  203. WebGLRefPtr<WebGLBuffer>* genericBinding;
  204. IndexedBufferBinding* indexedBinding;
  205. if (!ValidateIndexedBufferBinding(funcName, target, index, &genericBinding,
  206. &indexedBinding))
  207. {
  208. return;
  209. }
  210. if (buffer && !buffer->ValidateCanBindToTarget(funcName, target))
  211. return;
  212. if (buffer && !size) {
  213. ErrorInvalidValue("%s: size must be non-zero for non-null buffer.", funcName);
  214. return;
  215. }
  216. ////
  217. gl->MakeCurrent();
  218. switch (target) {
  219. case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
  220. if (offset % 4 != 0 || size % 4 != 0) {
  221. ErrorInvalidValue("%s: For %s, `offset` and `size` must be multiples of 4.",
  222. funcName, "TRANSFORM_FEEDBACK_BUFFER");
  223. return;
  224. }
  225. break;
  226. case LOCAL_GL_UNIFORM_BUFFER:
  227. {
  228. GLuint offsetAlignment = 0;
  229. gl->GetUIntegerv(LOCAL_GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &offsetAlignment);
  230. if (offset % offsetAlignment != 0) {
  231. ErrorInvalidValue("%s: For %s, `offset` must be a multiple of %s.",
  232. funcName, "UNIFORM_BUFFER",
  233. "UNIFORM_BUFFER_OFFSET_ALIGNMENT");
  234. return;
  235. }
  236. }
  237. break;
  238. }
  239. ////
  240. gl->fBindBufferRange(target, index, buffer ? buffer->mGLName : 0, offset, size);
  241. ////
  242. WebGLBuffer::SetSlot(target, buffer, genericBinding);
  243. WebGLBuffer::SetSlot(target, buffer, &indexedBinding->mBufferBinding);
  244. indexedBinding->mRangeStart = offset;
  245. indexedBinding->mRangeSize = size;
  246. if (buffer) {
  247. buffer->SetContentAfterBind(target);
  248. }
  249. }
  250. ////////////////////////////////////////
  251. void
  252. WebGLContext::BufferDataImpl(GLenum target, size_t dataLen, const uint8_t* data,
  253. GLenum usage)
  254. {
  255. const char funcName[] = "bufferData";
  256. const auto& buffer = ValidateBufferSelection(funcName, target);
  257. if (!buffer)
  258. return;
  259. buffer->BufferData(target, dataLen, data, usage);
  260. }
  261. ////
  262. void
  263. WebGLContext::BufferData(GLenum target, WebGLsizeiptr size, GLenum usage)
  264. {
  265. const char funcName[] = "bufferData";
  266. if (IsContextLost())
  267. return;
  268. if (!ValidateNonNegative(funcName, "size", size))
  269. return;
  270. ////
  271. const auto checkedSize = CheckedInt<size_t>(size);
  272. if (!checkedSize.isValid())
  273. return ErrorOutOfMemory("%s: Size too large for platform.", funcName);
  274. const UniqueBuffer zeroBuffer(calloc(size, 1));
  275. if (!zeroBuffer)
  276. return ErrorOutOfMemory("%s: Failed to allocate zeros.", funcName);
  277. BufferDataImpl(target, size_t(size), (const uint8_t*)zeroBuffer.get(), usage);
  278. }
  279. void
  280. WebGLContext::BufferData(GLenum target, const dom::Nullable<dom::ArrayBuffer>& maybeSrc,
  281. GLenum usage)
  282. {
  283. if (IsContextLost())
  284. return;
  285. if (!ValidateNonNull("bufferData", maybeSrc))
  286. return;
  287. const auto& src = maybeSrc.Value();
  288. src.ComputeLengthAndData();
  289. BufferDataImpl(target, src.LengthAllowShared(), src.DataAllowShared(), usage);
  290. }
  291. void
  292. WebGLContext::BufferData(GLenum target, const dom::ArrayBufferView& src, GLenum usage,
  293. GLuint srcElemOffset, GLuint srcElemCountOverride)
  294. {
  295. const char funcName[] = "bufferData";
  296. if (IsContextLost())
  297. return;
  298. uint8_t* bytes;
  299. size_t byteLen;
  300. if (!ValidateArrayBufferView(funcName, src, srcElemOffset, srcElemCountOverride,
  301. &bytes, &byteLen))
  302. {
  303. return;
  304. }
  305. BufferDataImpl(target, byteLen, bytes, usage);
  306. }
  307. ////////////////////////////////////////
  308. void
  309. WebGLContext::BufferSubDataImpl(GLenum target, WebGLsizeiptr dstByteOffset,
  310. size_t dataLen, const uint8_t* data)
  311. {
  312. const char funcName[] = "bufferSubData";
  313. if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset))
  314. return;
  315. const auto& buffer = ValidateBufferSelection(funcName, target);
  316. if (!buffer)
  317. return;
  318. if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen))
  319. return;
  320. if (!CheckedInt<GLintptr>(dataLen).isValid()) {
  321. ErrorOutOfMemory("%s: Size too large.", funcName);
  322. return;
  323. }
  324. const GLintptr glDataLen(dataLen);
  325. ////
  326. MakeContextCurrent();
  327. const ScopedLazyBind lazyBind(gl, target, buffer);
  328. // Warning: Possibly shared memory. See bug 1225033.
  329. gl->fBufferSubData(target, dstByteOffset, glDataLen, data);
  330. // Warning: Possibly shared memory. See bug 1225033.
  331. buffer->ElementArrayCacheBufferSubData(dstByteOffset, data, size_t(glDataLen));
  332. }
  333. void
  334. WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
  335. const dom::ArrayBuffer& src)
  336. {
  337. if (IsContextLost())
  338. return;
  339. src.ComputeLengthAndData();
  340. BufferSubDataImpl(target, dstByteOffset, src.LengthAllowShared(),
  341. src.DataAllowShared());
  342. }
  343. void
  344. WebGLContext::BufferSubData(GLenum target, WebGLsizeiptr dstByteOffset,
  345. const dom::ArrayBufferView& src, GLuint srcElemOffset,
  346. GLuint srcElemCountOverride)
  347. {
  348. const char funcName[] = "bufferSubData";
  349. if (IsContextLost())
  350. return;
  351. uint8_t* bytes;
  352. size_t byteLen;
  353. if (!ValidateArrayBufferView(funcName, src, srcElemOffset, srcElemCountOverride,
  354. &bytes, &byteLen))
  355. {
  356. return;
  357. }
  358. BufferSubDataImpl(target, dstByteOffset, byteLen, bytes);
  359. }
  360. ////////////////////////////////////////
  361. already_AddRefed<WebGLBuffer>
  362. WebGLContext::CreateBuffer()
  363. {
  364. if (IsContextLost())
  365. return nullptr;
  366. GLuint buf = 0;
  367. MakeContextCurrent();
  368. gl->fGenBuffers(1, &buf);
  369. RefPtr<WebGLBuffer> globj = new WebGLBuffer(this, buf);
  370. return globj.forget();
  371. }
  372. void
  373. WebGLContext::DeleteBuffer(WebGLBuffer* buffer)
  374. {
  375. if (!ValidateDeleteObject("deleteBuffer", buffer))
  376. return;
  377. ////
  378. const auto fnClearIfBuffer = [&](GLenum target, WebGLRefPtr<WebGLBuffer>& bindPoint) {
  379. if (bindPoint == buffer) {
  380. WebGLBuffer::SetSlot(target, nullptr, &bindPoint);
  381. }
  382. };
  383. fnClearIfBuffer(0, mBoundArrayBuffer);
  384. fnClearIfBuffer(0, mBoundVertexArray->mElementArrayBuffer);
  385. for (auto& cur : mBoundVertexArray->mAttribs) {
  386. fnClearIfBuffer(0, cur.mBuf);
  387. }
  388. // WebGL binding points
  389. if (IsWebGL2()) {
  390. fnClearIfBuffer(0, mBoundCopyReadBuffer);
  391. fnClearIfBuffer(0, mBoundCopyWriteBuffer);
  392. fnClearIfBuffer(0, mBoundPixelPackBuffer);
  393. fnClearIfBuffer(0, mBoundPixelUnpackBuffer);
  394. fnClearIfBuffer(0, mBoundUniformBuffer);
  395. fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER,
  396. mBoundTransformFeedback->mGenericBufferBinding);
  397. if (!mBoundTransformFeedback->mIsActive) {
  398. for (auto& binding : mBoundTransformFeedback->mIndexedBindings) {
  399. fnClearIfBuffer(LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER,
  400. binding.mBufferBinding);
  401. }
  402. }
  403. for (auto& binding : mIndexedUniformBufferBindings) {
  404. fnClearIfBuffer(0, binding.mBufferBinding);
  405. }
  406. }
  407. ////
  408. buffer->RequestDelete();
  409. }
  410. bool
  411. WebGLContext::IsBuffer(WebGLBuffer* buffer)
  412. {
  413. if (!ValidateIsObject("isBuffer", buffer))
  414. return false;
  415. MakeContextCurrent();
  416. return gl->fIsBuffer(buffer->mGLName);
  417. }
  418. } // namespace mozilla