TexUnpackBlob.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. /* -*- Mode: C++; tab-width: 20; 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 "TexUnpackBlob.h"
  6. #include "GLBlitHelper.h"
  7. #include "GLContext.h"
  8. #include "mozilla/dom/Element.h"
  9. #include "mozilla/dom/HTMLCanvasElement.h"
  10. #include "mozilla/RefPtr.h"
  11. #include "nsLayoutUtils.h"
  12. #include "WebGLBuffer.h"
  13. #include "WebGLContext.h"
  14. #include "WebGLTexelConversions.h"
  15. #include "WebGLTexture.h"
  16. namespace mozilla {
  17. namespace webgl {
  18. static bool
  19. IsPIValidForDOM(const webgl::PackingInfo& pi)
  20. {
  21. // https://www.khronos.org/registry/webgl/specs/latest/2.0/#TEXTURE_TYPES_FORMATS_FROM_DOM_ELEMENTS_TABLE
  22. // Just check for invalid individual formats and types, not combinations.
  23. switch (pi.format) {
  24. case LOCAL_GL_RGB:
  25. case LOCAL_GL_RGBA:
  26. case LOCAL_GL_LUMINANCE_ALPHA:
  27. case LOCAL_GL_LUMINANCE:
  28. case LOCAL_GL_ALPHA:
  29. case LOCAL_GL_RED:
  30. case LOCAL_GL_RED_INTEGER:
  31. case LOCAL_GL_RG:
  32. case LOCAL_GL_RG_INTEGER:
  33. case LOCAL_GL_RGB_INTEGER:
  34. case LOCAL_GL_RGBA_INTEGER:
  35. break;
  36. case LOCAL_GL_SRGB:
  37. case LOCAL_GL_SRGB_ALPHA:
  38. // Allowed in WebGL1+EXT_srgb
  39. break;
  40. default:
  41. return false;
  42. }
  43. switch (pi.type) {
  44. case LOCAL_GL_UNSIGNED_BYTE:
  45. case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
  46. case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
  47. case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
  48. case LOCAL_GL_HALF_FLOAT:
  49. case LOCAL_GL_HALF_FLOAT_OES:
  50. case LOCAL_GL_FLOAT:
  51. case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
  52. break;
  53. default:
  54. return false;
  55. }
  56. return true;
  57. }
  58. static bool
  59. ValidatePIForDOM(WebGLContext* webgl, const char* funcName,
  60. const webgl::PackingInfo& pi)
  61. {
  62. if (!IsPIValidForDOM(pi)) {
  63. webgl->ErrorInvalidOperation("%s: Format or type is invalid for DOM sources.",
  64. funcName);
  65. return false;
  66. }
  67. return true;
  68. }
  69. static WebGLTexelFormat
  70. FormatForPackingInfo(const PackingInfo& pi)
  71. {
  72. switch (pi.type) {
  73. case LOCAL_GL_UNSIGNED_BYTE:
  74. switch (pi.format) {
  75. case LOCAL_GL_RED:
  76. case LOCAL_GL_LUMINANCE:
  77. case LOCAL_GL_RED_INTEGER:
  78. return WebGLTexelFormat::R8;
  79. case LOCAL_GL_ALPHA:
  80. return WebGLTexelFormat::A8;
  81. case LOCAL_GL_LUMINANCE_ALPHA:
  82. return WebGLTexelFormat::RA8;
  83. case LOCAL_GL_RGB:
  84. case LOCAL_GL_RGB_INTEGER:
  85. return WebGLTexelFormat::RGB8;
  86. case LOCAL_GL_RGBA:
  87. case LOCAL_GL_RGBA_INTEGER:
  88. return WebGLTexelFormat::RGBA8;
  89. case LOCAL_GL_RG:
  90. case LOCAL_GL_RG_INTEGER:
  91. return WebGLTexelFormat::RG8;
  92. default:
  93. break;
  94. }
  95. break;
  96. case LOCAL_GL_UNSIGNED_SHORT_5_6_5:
  97. if (pi.format == LOCAL_GL_RGB)
  98. return WebGLTexelFormat::RGB565;
  99. break;
  100. case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1:
  101. if (pi.format == LOCAL_GL_RGBA)
  102. return WebGLTexelFormat::RGBA5551;
  103. break;
  104. case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4:
  105. if (pi.format == LOCAL_GL_RGBA)
  106. return WebGLTexelFormat::RGBA4444;
  107. break;
  108. case LOCAL_GL_HALF_FLOAT:
  109. case LOCAL_GL_HALF_FLOAT_OES:
  110. switch (pi.format) {
  111. case LOCAL_GL_RED:
  112. case LOCAL_GL_LUMINANCE:
  113. return WebGLTexelFormat::R16F;
  114. case LOCAL_GL_ALPHA: return WebGLTexelFormat::A16F;
  115. case LOCAL_GL_LUMINANCE_ALPHA: return WebGLTexelFormat::RA16F;
  116. case LOCAL_GL_RG: return WebGLTexelFormat::RG16F;
  117. case LOCAL_GL_RGB: return WebGLTexelFormat::RGB16F;
  118. case LOCAL_GL_RGBA: return WebGLTexelFormat::RGBA16F;
  119. default:
  120. break;
  121. }
  122. break;
  123. case LOCAL_GL_FLOAT:
  124. switch (pi.format) {
  125. case LOCAL_GL_RED:
  126. case LOCAL_GL_LUMINANCE:
  127. return WebGLTexelFormat::R32F;
  128. case LOCAL_GL_ALPHA: return WebGLTexelFormat::A32F;
  129. case LOCAL_GL_LUMINANCE_ALPHA: return WebGLTexelFormat::RA32F;
  130. case LOCAL_GL_RG: return WebGLTexelFormat::RG32F;
  131. case LOCAL_GL_RGB: return WebGLTexelFormat::RGB32F;
  132. case LOCAL_GL_RGBA: return WebGLTexelFormat::RGBA32F;
  133. default:
  134. break;
  135. }
  136. break;
  137. case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV:
  138. if (pi.format == LOCAL_GL_RGB)
  139. return WebGLTexelFormat::RGB11F11F10F;
  140. break;
  141. default:
  142. break;
  143. }
  144. return WebGLTexelFormat::FormatNotSupportingAnyConversion;
  145. }
  146. ////////////////////
  147. static bool
  148. ValidateUnpackPixels(WebGLContext* webgl, const char* funcName, uint32_t fullRows,
  149. uint32_t tailPixels, webgl::TexUnpackBlob* blob)
  150. {
  151. if (!blob->mWidth || !blob->mHeight || !blob->mDepth)
  152. return true;
  153. const auto usedPixelsPerRow = CheckedUint32(blob->mSkipPixels) + blob->mWidth;
  154. if (!usedPixelsPerRow.isValid() || usedPixelsPerRow.value() > blob->mRowLength) {
  155. webgl->ErrorInvalidOperation("%s: UNPACK_SKIP_PIXELS + width >"
  156. " UNPACK_ROW_LENGTH.",
  157. funcName);
  158. return false;
  159. }
  160. if (blob->mHeight > blob->mImageHeight) {
  161. webgl->ErrorInvalidOperation("%s: height > UNPACK_IMAGE_HEIGHT.", funcName);
  162. return false;
  163. }
  164. //////
  165. // The spec doesn't bound SKIP_ROWS + height <= IMAGE_HEIGHT, unfortunately.
  166. auto skipFullRows = CheckedUint32(blob->mSkipImages) * blob->mImageHeight;
  167. skipFullRows += blob->mSkipRows;
  168. MOZ_ASSERT(blob->mDepth >= 1);
  169. MOZ_ASSERT(blob->mHeight >= 1);
  170. auto usedFullRows = CheckedUint32(blob->mDepth - 1) * blob->mImageHeight;
  171. usedFullRows += blob->mHeight - 1; // Full rows in the final image, excluding the tail.
  172. const auto fullRowsNeeded = skipFullRows + usedFullRows;
  173. if (!fullRowsNeeded.isValid()) {
  174. webgl->ErrorOutOfMemory("%s: Invalid calculation for required row count.",
  175. funcName);
  176. return false;
  177. }
  178. if (fullRows > fullRowsNeeded.value())
  179. return true;
  180. if (fullRows == fullRowsNeeded.value() && tailPixels >= usedPixelsPerRow.value()) {
  181. blob->mNeedsExactUpload = true;
  182. return true;
  183. }
  184. webgl->ErrorInvalidOperation("%s: Desired upload requires more data than is"
  185. " available: (%u rows plus %u pixels needed, %u rows"
  186. " plus %u pixels available)",
  187. funcName, fullRowsNeeded.value(),
  188. usedPixelsPerRow.value(), fullRows, tailPixels);
  189. return false;
  190. }
  191. static bool
  192. ValidateUnpackBytes(WebGLContext* webgl, const char* funcName,
  193. const webgl::PackingInfo& pi, size_t availByteCount,
  194. webgl::TexUnpackBlob* blob)
  195. {
  196. if (!blob->mWidth || !blob->mHeight || !blob->mDepth)
  197. return true;
  198. const auto bytesPerPixel = webgl::BytesPerPixel(pi);
  199. const auto bytesPerRow = CheckedUint32(blob->mRowLength) * bytesPerPixel;
  200. const auto rowStride = RoundUpToMultipleOf(bytesPerRow, blob->mAlignment);
  201. const auto fullRows = availByteCount / rowStride;
  202. if (!fullRows.isValid()) {
  203. webgl->ErrorOutOfMemory("%s: Unacceptable upload size calculated.");
  204. return false;
  205. }
  206. const auto bodyBytes = fullRows.value() * rowStride.value();
  207. const auto tailPixels = (availByteCount - bodyBytes) / bytesPerPixel;
  208. return ValidateUnpackPixels(webgl, funcName, fullRows.value(), tailPixels, blob);
  209. }
  210. ////////////////////
  211. static uint32_t
  212. ZeroOn2D(TexImageTarget target, uint32_t val)
  213. {
  214. return (IsTarget3D(target) ? val : 0);
  215. }
  216. static uint32_t
  217. FallbackOnZero(uint32_t val, uint32_t fallback)
  218. {
  219. return (val ? val : fallback);
  220. }
  221. TexUnpackBlob::TexUnpackBlob(const WebGLContext* webgl, TexImageTarget target,
  222. uint32_t rowLength, uint32_t width, uint32_t height,
  223. uint32_t depth, bool srcIsPremult)
  224. : mAlignment(webgl->mPixelStore_UnpackAlignment)
  225. , mRowLength(rowLength)
  226. , mImageHeight(FallbackOnZero(ZeroOn2D(target, webgl->mPixelStore_UnpackImageHeight),
  227. height))
  228. , mSkipPixels(webgl->mPixelStore_UnpackSkipPixels)
  229. , mSkipRows(webgl->mPixelStore_UnpackSkipRows)
  230. , mSkipImages(ZeroOn2D(target, webgl->mPixelStore_UnpackSkipImages))
  231. , mWidth(width)
  232. , mHeight(height)
  233. , mDepth(depth)
  234. , mSrcIsPremult(srcIsPremult)
  235. , mNeedsExactUpload(false)
  236. {
  237. MOZ_ASSERT_IF(!IsTarget3D(target), mDepth == 1);
  238. }
  239. bool
  240. TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
  241. const uint32_t rowLength, const uint32_t rowCount,
  242. WebGLTexelFormat srcFormat,
  243. const uint8_t* const srcBegin, const ptrdiff_t srcStride,
  244. WebGLTexelFormat dstFormat, const ptrdiff_t dstStride,
  245. const uint8_t** const out_begin,
  246. UniqueBuffer* const out_anchoredBuffer) const
  247. {
  248. MOZ_ASSERT(srcFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
  249. MOZ_ASSERT(dstFormat != WebGLTexelFormat::FormatNotSupportingAnyConversion);
  250. *out_begin = srcBegin;
  251. if (!rowLength || !rowCount)
  252. return true;
  253. const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
  254. const auto srcOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
  255. : gl::OriginPos::BottomLeft);
  256. const auto dstOrigin = gl::OriginPos::BottomLeft;
  257. if (srcFormat != dstFormat) {
  258. webgl->GenerateWarning("%s: Conversion requires pixel reformatting.", funcName);
  259. } else if (mSrcIsPremult != dstIsPremult) {
  260. webgl->GenerateWarning("%s: Conversion requires change in"
  261. "alpha-premultiplication.",
  262. funcName);
  263. } else if (srcOrigin != dstOrigin) {
  264. webgl->GenerateWarning("%s: Conversion requires y-flip.", funcName);
  265. } else if (srcStride != dstStride) {
  266. webgl->GenerateWarning("%s: Conversion requires change in stride.", funcName);
  267. } else {
  268. return true;
  269. }
  270. ////
  271. const auto dstTotalBytes = CheckedUint32(rowCount) * dstStride;
  272. if (!dstTotalBytes.isValid()) {
  273. webgl->ErrorOutOfMemory("%s: Calculation failed.", funcName);
  274. return false;
  275. }
  276. UniqueBuffer dstBuffer = calloc(1, dstTotalBytes.value());
  277. if (!dstBuffer.get()) {
  278. webgl->ErrorOutOfMemory("%s: Failed to allocate dest buffer.", funcName);
  279. return false;
  280. }
  281. const auto dstBegin = static_cast<uint8_t*>(dstBuffer.get());
  282. ////
  283. // And go!:
  284. bool wasTrivial;
  285. if (!ConvertImage(rowLength, rowCount,
  286. srcBegin, srcStride, srcOrigin, srcFormat, mSrcIsPremult,
  287. dstBegin, dstStride, dstOrigin, dstFormat, dstIsPremult,
  288. &wasTrivial))
  289. {
  290. webgl->ErrorImplementationBug("%s: ConvertImage failed.", funcName);
  291. return false;
  292. }
  293. *out_begin = dstBegin;
  294. *out_anchoredBuffer = Move(dstBuffer);
  295. return true;
  296. }
  297. static GLenum
  298. DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, TexImageTarget target, GLint level,
  299. const DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset,
  300. GLsizei width, GLsizei height, GLsizei depth, const void* data)
  301. {
  302. if (isSubImage) {
  303. return DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height,
  304. depth, dui->ToPacking(), data);
  305. } else {
  306. return DoTexImage(gl, target, level, dui, width, height, depth, data);
  307. }
  308. }
  309. //////////////////////////////////////////////////////////////////////////////////////////
  310. // TexUnpackBytes
  311. TexUnpackBytes::TexUnpackBytes(const WebGLContext* webgl, TexImageTarget target,
  312. uint32_t width, uint32_t height, uint32_t depth,
  313. bool isClientData, const uint8_t* ptr, size_t availBytes)
  314. : TexUnpackBlob(webgl, target,
  315. FallbackOnZero(webgl->mPixelStore_UnpackRowLength, width),
  316. width, height, depth, false)
  317. , mIsClientData(isClientData)
  318. , mPtr(ptr)
  319. , mAvailBytes(availBytes)
  320. { }
  321. bool
  322. TexUnpackBytes::Validate(WebGLContext* webgl, const char* funcName,
  323. const webgl::PackingInfo& pi)
  324. {
  325. if (mIsClientData && !mPtr)
  326. return true;
  327. return ValidateUnpackBytes(webgl, funcName, pi, mAvailBytes, this);
  328. }
  329. bool
  330. TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
  331. WebGLTexture* tex, TexImageTarget target, GLint level,
  332. const webgl::DriverUnpackInfo* dui, GLint xOffset,
  333. GLint yOffset, GLint zOffset, GLenum* const out_error) const
  334. {
  335. WebGLContext* webgl = tex->mContext;
  336. const auto pi = dui->ToPacking();
  337. const auto format = FormatForPackingInfo(pi);
  338. const auto bytesPerPixel = webgl::BytesPerPixel(pi);
  339. const uint8_t* uploadPtr = mPtr;
  340. UniqueBuffer tempBuffer;
  341. do {
  342. if (!mIsClientData || !mPtr)
  343. break;
  344. if (!webgl->mPixelStore_FlipY &&
  345. !webgl->mPixelStore_PremultiplyAlpha)
  346. {
  347. break;
  348. }
  349. if (webgl->mPixelStore_UnpackImageHeight ||
  350. webgl->mPixelStore_UnpackSkipImages ||
  351. webgl->mPixelStore_UnpackRowLength ||
  352. webgl->mPixelStore_UnpackSkipRows ||
  353. webgl->mPixelStore_UnpackSkipPixels)
  354. {
  355. webgl->ErrorInvalidOperation("%s: Non-DOM-Element uploads with alpha-premult"
  356. " or y-flip do not support subrect selection.",
  357. funcName);
  358. return false;
  359. }
  360. webgl->GenerateWarning("%s: Alpha-premult and y-flip are deprecated for"
  361. " non-DOM-Element uploads.",
  362. funcName);
  363. const uint32_t rowLength = mWidth;
  364. const uint32_t rowCount = mHeight * mDepth;
  365. const auto stride = RoundUpToMultipleOf(rowLength * bytesPerPixel, mAlignment);
  366. if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, format, mPtr, stride,
  367. format, stride, &uploadPtr, &tempBuffer))
  368. {
  369. return false;
  370. }
  371. } while (false);
  372. //////
  373. const auto& gl = webgl->gl;
  374. bool useParanoidHandling = false;
  375. if (mNeedsExactUpload && webgl->mBoundPixelUnpackBuffer) {
  376. webgl->GenerateWarning("%s: Uploads from a buffer with a final row with a byte"
  377. " count smaller than the row stride can incur extra"
  378. " overhead.",
  379. funcName);
  380. if (gl->WorkAroundDriverBugs()) {
  381. useParanoidHandling |= (gl->Vendor() == gl::GLVendor::NVIDIA);
  382. }
  383. }
  384. if (!useParanoidHandling) {
  385. if (webgl->mBoundPixelUnpackBuffer) {
  386. gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER,
  387. webgl->mBoundPixelUnpackBuffer->mGLName);
  388. }
  389. *out_error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset,
  390. zOffset, mWidth, mHeight, mDepth, uploadPtr);
  391. if (webgl->mBoundPixelUnpackBuffer) {
  392. gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
  393. }
  394. return true;
  395. }
  396. //////
  397. MOZ_ASSERT(webgl->mBoundPixelUnpackBuffer);
  398. if (!isSubImage) {
  399. // Alloc first to catch OOMs.
  400. AssertUintParamCorrect(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
  401. *out_error = DoTexOrSubImage(false, gl, target, level, dui, xOffset, yOffset,
  402. zOffset, mWidth, mHeight, mDepth, nullptr);
  403. if (*out_error)
  404. return true;
  405. }
  406. const ScopedLazyBind bindPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER,
  407. webgl->mBoundPixelUnpackBuffer);
  408. //////
  409. // Make our sometimes-implicit values explicit. Also this keeps them constant when we
  410. // ask for height=mHeight-1 and such.
  411. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, mRowLength);
  412. gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mImageHeight);
  413. if (mDepth > 1) {
  414. *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
  415. zOffset, mWidth, mHeight, mDepth-1, uploadPtr);
  416. }
  417. // Skip the images we uploaded.
  418. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, mSkipImages + mDepth - 1);
  419. if (mHeight > 1) {
  420. *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset, yOffset,
  421. zOffset+mDepth-1, mWidth, mHeight-1, 1, uploadPtr);
  422. }
  423. const auto totalSkipRows = CheckedUint32(mSkipImages) * mImageHeight + mSkipRows;
  424. const auto totalFullRows = CheckedUint32(mDepth - 1) * mImageHeight + mHeight - 1;
  425. const auto tailOffsetRows = totalSkipRows + totalFullRows;
  426. const auto bytesPerRow = CheckedUint32(mRowLength) * bytesPerPixel;
  427. const auto rowStride = RoundUpToMultipleOf(bytesPerRow, mAlignment);
  428. if (!rowStride.isValid()) {
  429. MOZ_CRASH("Should be checked earlier.");
  430. }
  431. const auto tailOffsetBytes = tailOffsetRows * rowStride;
  432. uploadPtr += tailOffsetBytes.value();
  433. //////
  434. gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // No stride padding.
  435. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); // No padding in general.
  436. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, 0); // Don't skip images,
  437. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, 0); // or rows.
  438. // Keep skipping pixels though!
  439. *out_error = DoTexOrSubImage(true, gl, target, level, dui, xOffset,
  440. yOffset+mHeight-1, zOffset+mDepth-1, mWidth, 1, 1,
  441. uploadPtr);
  442. // Reset all our modified state.
  443. gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
  444. gl->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, webgl->mPixelStore_UnpackImageHeight);
  445. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength);
  446. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES, webgl->mPixelStore_UnpackSkipImages);
  447. gl->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS, webgl->mPixelStore_UnpackSkipRows);
  448. return true;
  449. }
  450. ////////////////////////////////////////////////////////////////////////////////
  451. ////////////////////////////////////////////////////////////////////////////////
  452. // TexUnpackImage
  453. TexUnpackImage::TexUnpackImage(const WebGLContext* webgl, TexImageTarget target,
  454. uint32_t width, uint32_t height, uint32_t depth,
  455. layers::Image* image, bool isAlphaPremult)
  456. : TexUnpackBlob(webgl, target, image->GetSize().width, width, height, depth,
  457. isAlphaPremult)
  458. , mImage(image)
  459. { }
  460. TexUnpackImage::~TexUnpackImage()
  461. { }
  462. bool
  463. TexUnpackImage::Validate(WebGLContext* webgl, const char* funcName,
  464. const webgl::PackingInfo& pi)
  465. {
  466. if (!ValidatePIForDOM(webgl, funcName, pi))
  467. return false;
  468. const auto fullRows = mImage->GetSize().height;
  469. return ValidateUnpackPixels(webgl, funcName, fullRows, 0, this);
  470. }
  471. bool
  472. TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
  473. WebGLTexture* tex, TexImageTarget target, GLint level,
  474. const webgl::DriverUnpackInfo* dui, GLint xOffset,
  475. GLint yOffset, GLint zOffset, GLenum* const out_error) const
  476. {
  477. MOZ_ASSERT_IF(needsRespec, !isSubImage);
  478. WebGLContext* webgl = tex->mContext;
  479. gl::GLContext* gl = webgl->GL();
  480. gl->MakeCurrent();
  481. if (needsRespec) {
  482. *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset,
  483. yOffset, zOffset, mWidth, mHeight, mDepth,
  484. nullptr);
  485. if (*out_error)
  486. return true;
  487. }
  488. do {
  489. if (mDepth != 1)
  490. break;
  491. const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
  492. if (mSrcIsPremult != dstIsPremult)
  493. break;
  494. if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA)
  495. break;
  496. if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE)
  497. break;
  498. gl::ScopedFramebuffer scopedFB(gl);
  499. gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());
  500. {
  501. gl::GLContext::LocalErrorScope errorScope(*gl);
  502. gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
  503. target.get(), tex->mGLName, level);
  504. if (errorScope.GetError())
  505. break;
  506. }
  507. const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
  508. if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
  509. break;
  510. const gfx::IntSize destSize(mWidth, mHeight);
  511. const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
  512. : gl::OriginPos::BottomLeft);
  513. if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
  514. dstOrigin))
  515. {
  516. break;
  517. }
  518. // Blitting was successful, so we're done!
  519. *out_error = 0;
  520. return true;
  521. } while (false);
  522. webgl->GenerateWarning("%s: Failed to hit GPU-copy fast-path. Falling back to CPU"
  523. " upload.",
  524. funcName);
  525. const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface();
  526. RefPtr<gfx::DataSourceSurface> dataSurf;
  527. if (surf) {
  528. // WARNING: OSX can lose our MakeCurrent here.
  529. dataSurf = surf->GetDataSurface();
  530. }
  531. if (!dataSurf) {
  532. webgl->ErrorOutOfMemory("%s: GetAsSourceSurface or GetDataSurface failed after"
  533. " blit failed for TexUnpackImage.",
  534. funcName);
  535. return false;
  536. }
  537. const TexUnpackSurface surfBlob(webgl, target, mWidth, mHeight, mDepth, dataSurf,
  538. mSrcIsPremult);
  539. return surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level,
  540. dui, xOffset, yOffset, zOffset, out_error);
  541. }
  542. ////////////////////////////////////////////////////////////////////////////////
  543. ////////////////////////////////////////////////////////////////////////////////
  544. // TexUnpackSurface
  545. TexUnpackSurface::TexUnpackSurface(const WebGLContext* webgl, TexImageTarget target,
  546. uint32_t width, uint32_t height, uint32_t depth,
  547. gfx::DataSourceSurface* surf, bool isAlphaPremult)
  548. : TexUnpackBlob(webgl, target, surf->GetSize().width, width, height, depth,
  549. isAlphaPremult)
  550. , mSurf(surf)
  551. { }
  552. //////////
  553. static bool
  554. GetFormatForSurf(gfx::SourceSurface* surf, WebGLTexelFormat* const out_texelFormat,
  555. uint8_t* const out_bpp)
  556. {
  557. const auto surfFormat = surf->GetFormat();
  558. switch (surfFormat) {
  559. case gfx::SurfaceFormat::B8G8R8A8:
  560. *out_texelFormat = WebGLTexelFormat::BGRA8;
  561. *out_bpp = 4;
  562. return true;
  563. case gfx::SurfaceFormat::B8G8R8X8:
  564. *out_texelFormat = WebGLTexelFormat::BGRX8;
  565. *out_bpp = 4;
  566. return true;
  567. case gfx::SurfaceFormat::R8G8B8A8:
  568. *out_texelFormat = WebGLTexelFormat::RGBA8;
  569. *out_bpp = 4;
  570. return true;
  571. case gfx::SurfaceFormat::R8G8B8X8:
  572. *out_texelFormat = WebGLTexelFormat::RGBX8;
  573. *out_bpp = 4;
  574. return true;
  575. case gfx::SurfaceFormat::R5G6B5_UINT16:
  576. *out_texelFormat = WebGLTexelFormat::RGB565;
  577. *out_bpp = 2;
  578. return true;
  579. case gfx::SurfaceFormat::A8:
  580. *out_texelFormat = WebGLTexelFormat::A8;
  581. *out_bpp = 1;
  582. return true;
  583. case gfx::SurfaceFormat::YUV:
  584. // Ugh...
  585. NS_ERROR("We don't handle uploads from YUV sources yet.");
  586. // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically
  587. // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB)
  588. return false;
  589. default:
  590. return false;
  591. }
  592. }
  593. //////////
  594. bool
  595. TexUnpackSurface::Validate(WebGLContext* webgl, const char* funcName,
  596. const webgl::PackingInfo& pi)
  597. {
  598. if (!ValidatePIForDOM(webgl, funcName, pi))
  599. return false;
  600. const auto fullRows = mSurf->GetSize().height;
  601. return ValidateUnpackPixels(webgl, funcName, fullRows, 0, this);
  602. }
  603. bool
  604. TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName,
  605. WebGLTexture* tex, TexImageTarget target, GLint level,
  606. const webgl::DriverUnpackInfo* dstDUI, GLint xOffset,
  607. GLint yOffset, GLint zOffset,
  608. GLenum* const out_error) const
  609. {
  610. const auto& webgl = tex->mContext;
  611. ////
  612. const auto rowLength = mSurf->GetSize().width;
  613. const auto rowCount = mSurf->GetSize().height;
  614. const auto& dstPI = dstDUI->ToPacking();
  615. const auto& dstBPP = webgl::BytesPerPixel(dstPI);
  616. const auto dstFormat = FormatForPackingInfo(dstPI);
  617. ////
  618. WebGLTexelFormat srcFormat;
  619. uint8_t srcBPP;
  620. if (!GetFormatForSurf(mSurf, &srcFormat, &srcBPP)) {
  621. webgl->ErrorImplementationBug("%s: GetFormatForSurf failed for"
  622. " WebGLTexelFormat::%u.",
  623. funcName, uint32_t(mSurf->GetFormat()));
  624. return false;
  625. }
  626. gfx::DataSourceSurface::ScopedMap map(mSurf, gfx::DataSourceSurface::MapType::READ);
  627. if (!map.IsMapped()) {
  628. webgl->ErrorOutOfMemory("%s: Failed to map source surface for upload.", funcName);
  629. return false;
  630. }
  631. const auto& srcBegin = map.GetData();
  632. const auto& srcStride = map.GetStride();
  633. ////
  634. const auto srcRowLengthBytes = rowLength * srcBPP;
  635. const uint8_t maxGLAlignment = 8;
  636. uint8_t srcAlignment = 1;
  637. for (; srcAlignment <= maxGLAlignment; srcAlignment *= 2) {
  638. const auto strideGuess = RoundUpToMultipleOf(srcRowLengthBytes, srcAlignment);
  639. if (strideGuess == srcStride)
  640. break;
  641. }
  642. const uint32_t dstAlignment = (srcAlignment > maxGLAlignment) ? 1 : srcAlignment;
  643. const auto dstRowLengthBytes = rowLength * dstBPP;
  644. const auto dstStride = RoundUpToMultipleOf(dstRowLengthBytes, dstAlignment);
  645. ////
  646. const uint8_t* dstBegin = srcBegin;
  647. UniqueBuffer tempBuffer;
  648. if (!ConvertIfNeeded(webgl, funcName, rowLength, rowCount, srcFormat, srcBegin,
  649. srcStride, dstFormat, dstStride, &dstBegin, &tempBuffer))
  650. {
  651. return false;
  652. }
  653. ////
  654. const auto& gl = webgl->gl;
  655. MOZ_ALWAYS_TRUE( gl->MakeCurrent() );
  656. gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, dstAlignment);
  657. if (webgl->IsWebGL2()) {
  658. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength);
  659. }
  660. *out_error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dstDUI, xOffset,
  661. yOffset, zOffset, mWidth, mHeight, mDepth, dstBegin);
  662. gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, webgl->mPixelStore_UnpackAlignment);
  663. if (webgl->IsWebGL2()) {
  664. gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, webgl->mPixelStore_UnpackRowLength);
  665. }
  666. return true;
  667. }
  668. } // namespace webgl
  669. } // namespace mozilla