BufferTexture.cpp (18703B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "BufferTexture.h" 8 9 #include <utility> 10 11 #include "libyuv.h" 12 #include "mozilla/fallible.h" 13 #include "mozilla/gfx/2D.h" 14 #include "mozilla/gfx/Logging.h" 15 #include "mozilla/layers/CompositableForwarder.h" 16 #include "mozilla/layers/ISurfaceAllocator.h" 17 #include "mozilla/layers/ImageDataSerializer.h" 18 #include "mozilla/layers/TextureForwarder.h" 19 20 #include "gfxPlatform.h" 21 22 #ifdef MOZ_WIDGET_GTK 23 # include "gfxPlatformGtk.h" 24 #endif 25 26 using mozilla::ipc::IShmemAllocator; 27 28 namespace mozilla { 29 namespace layers { 30 31 class MemoryTextureData : public BufferTextureData { 32 public: 33 static MemoryTextureData* Create(gfx::IntSize aSize, 34 gfx::SurfaceFormat aFormat, 35 gfx::BackendType aMoz2DBackend, 36 LayersBackend aLayersBackend, 37 TextureFlags aFlags, 38 TextureAllocationFlags aAllocFlags, 39 IShmemAllocator* aAllocator); 40 41 virtual TextureData* CreateSimilar( 42 LayersIPCChannel* aAllocator, LayersBackend aLayersBackend, 43 TextureFlags aFlags = TextureFlags::DEFAULT, 44 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override; 45 46 virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override; 47 48 virtual void Deallocate(LayersIPCChannel*) override; 49 50 MemoryTextureData(const BufferDescriptor& aDesc, 51 gfx::BackendType aMoz2DBackend, uint8_t* aBuffer, 52 size_t aBufferSize, bool aAutoDeallocate = false, 53 bool aIsClear = false) 54 : BufferTextureData(aDesc, aMoz2DBackend, aIsClear), 55 mBuffer(aBuffer), 56 mBufferSize(aBufferSize), 57 mAutoDeallocate(aAutoDeallocate) { 58 MOZ_ASSERT(aBuffer); 59 MOZ_ASSERT(aBufferSize); 60 } 61 62 virtual ~MemoryTextureData() override { 63 if (mAutoDeallocate) { 64 Deallocate(nullptr); 65 } 66 } 67 68 virtual uint8_t* GetBuffer() override { return mBuffer; } 69 70 virtual size_t GetBufferSize() override { return mBufferSize; } 71 72 TextureType GetTextureType() const override { return TextureType::Unknown; } 73 74 protected: 75 uint8_t* mBuffer; 76 size_t mBufferSize; 77 bool mAutoDeallocate; 78 }; 79 80 class ShmemTextureData : public BufferTextureData { 81 public: 82 static ShmemTextureData* Create(gfx::IntSize aSize, 83 gfx::SurfaceFormat aFormat, 84 gfx::BackendType aMoz2DBackend, 85 LayersBackend aLayersBackend, 86 TextureFlags aFlags, 87 TextureAllocationFlags aAllocFlags, 88 IShmemAllocator* aAllocator); 89 90 virtual TextureData* CreateSimilar( 91 LayersIPCChannel* aAllocator, LayersBackend aLayersBackend, 92 TextureFlags aFlags = TextureFlags::DEFAULT, 93 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override; 94 95 virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override; 96 97 virtual void Deallocate(LayersIPCChannel* aAllocator) override; 98 99 ShmemTextureData(const BufferDescriptor& aDesc, 100 gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem, 101 bool aIsClear = false) 102 : BufferTextureData(aDesc, aMoz2DBackend, aIsClear), mShmem(aShmem) { 103 MOZ_ASSERT(mShmem.Size<uint8_t>()); 104 } 105 106 virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); } 107 108 virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); } 109 110 protected: 111 mozilla::ipc::Shmem mShmem; 112 }; 113 114 BufferTextureData* BufferTextureData::Create( 115 gfx::IntSize aSize, gfx::SurfaceFormat aFormat, 116 gfx::BackendType aMoz2DBackend, LayersBackend aLayersBackend, 117 TextureFlags aFlags, TextureAllocationFlags aAllocFlags, 118 mozilla::ipc::IShmemAllocator* aAllocator, bool aIsSameProcess) { 119 if (!aAllocator || aIsSameProcess) { 120 return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, 121 aLayersBackend, aFlags, aAllocFlags, 122 aAllocator); 123 } else { 124 return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, 125 aLayersBackend, aFlags, aAllocFlags, 126 aAllocator); 127 } 128 } 129 130 BufferTextureData* BufferTextureData::CreateInternal( 131 LayersIPCChannel* aAllocator, const BufferDescriptor& aDesc, 132 gfx::BackendType aMoz2DBackend, int32_t aBufferSize, 133 TextureFlags aTextureFlags) { 134 if (!aAllocator || aAllocator->IsSameProcess()) { 135 uint8_t* buffer = new (fallible) uint8_t[aBufferSize]; 136 if (!buffer) { 137 return nullptr; 138 } 139 140 GfxMemoryImageReporter::DidAlloc(buffer); 141 142 return new MemoryTextureData(aDesc, aMoz2DBackend, buffer, aBufferSize); 143 } else { 144 ipc::Shmem shm; 145 if (!aAllocator->AllocUnsafeShmem(aBufferSize, &shm)) { 146 return nullptr; 147 } 148 149 bool isClear = aDesc.type() == BufferDescriptor::TRGBDescriptor && 150 !IsOpaque(aDesc.get_RGBDescriptor().format()); 151 return new ShmemTextureData(aDesc, aMoz2DBackend, shm, isClear); 152 } 153 } 154 155 BufferTextureData* BufferTextureData::CreateForYCbCr( 156 KnowsCompositor* aAllocator, const gfx::IntRect& aDisplay, 157 const gfx::IntSize& aYSize, uint32_t aYStride, 158 const gfx::IntSize& aCbCrSize, uint32_t aCbCrStride, StereoMode aStereoMode, 159 gfx::ColorDepth aColorDepth, gfx::YUVColorSpace aYUVColorSpace, 160 gfx::ColorRange aColorRange, gfx::ChromaSubsampling aSubsampling, 161 TextureFlags aTextureFlags) { 162 uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize( 163 aYSize, aYStride, aCbCrSize, aCbCrStride); 164 if (bufSize == 0) { 165 return nullptr; 166 } 167 168 uint32_t yOffset; 169 uint32_t cbOffset; 170 uint32_t crOffset; 171 ImageDataSerializer::ComputeYCbCrOffsets(aYStride, aYSize.height, aCbCrStride, 172 aCbCrSize.height, yOffset, cbOffset, 173 crOffset); 174 175 YCbCrDescriptor descriptor = 176 YCbCrDescriptor(aDisplay, aYSize, aYStride, aCbCrSize, aCbCrStride, 177 yOffset, cbOffset, crOffset, aStereoMode, aColorDepth, 178 aYUVColorSpace, aColorRange, aSubsampling); 179 180 return CreateInternal( 181 aAllocator ? aAllocator->GetTextureForwarder() : nullptr, descriptor, 182 gfx::BackendType::NONE, bufSize, aTextureFlags); 183 } 184 185 void BufferTextureData::FillInfo(TextureData::Info& aInfo) const { 186 aInfo.size = GetSize(); 187 aInfo.format = GetFormat(); 188 aInfo.hasSynchronization = false; 189 aInfo.canExposeMappedData = true; 190 191 switch (aInfo.format) { 192 case gfx::SurfaceFormat::YUV420: 193 case gfx::SurfaceFormat::UNKNOWN: 194 aInfo.supportsMoz2D = false; 195 break; 196 default: 197 aInfo.supportsMoz2D = true; 198 } 199 } 200 201 gfx::IntSize BufferTextureData::GetSize() const { 202 return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor); 203 } 204 205 gfx::IntRect BufferTextureData::GetPictureRect() const { 206 return ImageDataSerializer::RectFromBufferDescriptor(mDescriptor); 207 } 208 209 Maybe<gfx::IntSize> BufferTextureData::GetYSize() const { 210 return ImageDataSerializer::YSizeFromBufferDescriptor(mDescriptor); 211 } 212 213 Maybe<gfx::IntSize> BufferTextureData::GetCbCrSize() const { 214 return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor); 215 } 216 217 Maybe<int32_t> BufferTextureData::GetYStride() const { 218 return ImageDataSerializer::YStrideFromBufferDescriptor(mDescriptor); 219 } 220 221 Maybe<int32_t> BufferTextureData::GetCbCrStride() const { 222 return ImageDataSerializer::CbCrStrideFromBufferDescriptor(mDescriptor); 223 } 224 225 Maybe<gfx::YUVColorSpace> BufferTextureData::GetYUVColorSpace() const { 226 return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor); 227 } 228 229 Maybe<gfx::ColorDepth> BufferTextureData::GetColorDepth() const { 230 return ImageDataSerializer::ColorDepthFromBufferDescriptor(mDescriptor); 231 } 232 233 Maybe<StereoMode> BufferTextureData::GetStereoMode() const { 234 return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor); 235 } 236 237 Maybe<gfx::ChromaSubsampling> BufferTextureData::GetChromaSubsampling() const { 238 return ImageDataSerializer::ChromaSubsamplingFromBufferDescriptor( 239 mDescriptor); 240 } 241 242 gfx::SurfaceFormat BufferTextureData::GetFormat() const { 243 return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor); 244 } 245 246 already_AddRefed<gfx::DrawTarget> BufferTextureData::BorrowDrawTarget() { 247 if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) { 248 return nullptr; 249 } 250 251 const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor(); 252 253 uint32_t stride = ImageDataSerializer::GetRGBStride(rgb); 254 RefPtr<gfx::DrawTarget> dt; 255 if (gfx::Factory::DoesBackendSupportDataDrawtarget(mMoz2DBackend)) { 256 dt = gfx::Factory::CreateDrawTargetForData(mMoz2DBackend, GetBuffer(), 257 rgb.size(), stride, rgb.format(), 258 true, mIsClear); 259 } 260 if (!dt) { 261 // Fall back to supported platform backend. Note that mMoz2DBackend 262 // does not match the draw target type. 263 dt = gfxPlatform::CreateDrawTargetForData(GetBuffer(), rgb.size(), stride, 264 rgb.format(), true, mIsClear); 265 } 266 267 if (!dt) { 268 gfxCriticalNote << "BorrowDrawTarget failure, original backend " 269 << (int)mMoz2DBackend; 270 } 271 272 return dt.forget(); 273 } 274 275 bool BufferTextureData::BorrowMappedData(MappedTextureData& aData) { 276 if (GetFormat() == gfx::SurfaceFormat::YUV420) { 277 return false; 278 } 279 280 gfx::IntSize size = GetSize(); 281 282 aData.data = GetBuffer(); 283 aData.size = size; 284 aData.format = GetFormat(); 285 aData.stride = 286 ImageDataSerializer::ComputeRGBStride(aData.format, size.width); 287 mIsClear = false; 288 289 return true; 290 } 291 292 bool BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) { 293 if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) { 294 return false; 295 } 296 297 const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor(); 298 299 uint8_t* data = GetBuffer(); 300 auto ySize = desc.ySize(); 301 auto cbCrSize = desc.cbCrSize(); 302 303 aMap.stereoMode = desc.stereoMode(); 304 aMap.metadata = nullptr; 305 uint32_t bytesPerPixel = 306 BytesPerPixel(SurfaceFormatForColorDepth(desc.colorDepth())); 307 308 aMap.y.data = data + desc.yOffset(); 309 aMap.y.size = ySize; 310 aMap.y.stride = desc.yStride(); 311 aMap.y.skip = 0; 312 aMap.y.bytesPerPixel = bytesPerPixel; 313 314 aMap.cb.data = data + desc.cbOffset(); 315 aMap.cb.size = cbCrSize; 316 aMap.cb.stride = desc.cbCrStride(); 317 aMap.cb.skip = 0; 318 aMap.cb.bytesPerPixel = bytesPerPixel; 319 320 aMap.cr.data = data + desc.crOffset(); 321 aMap.cr.size = cbCrSize; 322 aMap.cr.stride = desc.cbCrStride(); 323 aMap.cr.skip = 0; 324 aMap.cr.bytesPerPixel = bytesPerPixel; 325 326 return true; 327 } 328 329 bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) { 330 if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) { 331 return false; 332 } 333 const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor(); 334 335 uint32_t stride = ImageDataSerializer::GetRGBStride(rgb); 336 RefPtr<gfx::DataSourceSurface> surface = 337 gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride, 338 rgb.size(), rgb.format()); 339 340 if (!surface) { 341 gfxCriticalError() << "Failed to get serializer as surface!"; 342 return false; 343 } 344 345 RefPtr<gfx::DataSourceSurface> srcSurf = aSurface->GetDataSurface(); 346 347 if (!srcSurf) { 348 gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT)."; 349 return false; 350 } 351 352 if (surface->GetSize() != srcSurf->GetSize() || 353 surface->GetFormat() != srcSurf->GetFormat()) { 354 gfxCriticalError() << "Attempt to update texture client from a surface " 355 "with a different size or format (BT)! This: " 356 << surface->GetSize() << " " << surface->GetFormat() 357 << " Other: " << aSurface->GetSize() << " " 358 << aSurface->GetFormat(); 359 return false; 360 } 361 362 gfx::DataSourceSurface::MappedSurface sourceMap; 363 gfx::DataSourceSurface::MappedSurface destMap; 364 if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) { 365 gfxCriticalError() 366 << "Failed to map source surface for UpdateFromSurface (BT)."; 367 return false; 368 } 369 370 if (!surface->Map(gfx::DataSourceSurface::WRITE, &destMap)) { 371 srcSurf->Unmap(); 372 gfxCriticalError() 373 << "Failed to map destination surface for UpdateFromSurface."; 374 return false; 375 } 376 377 for (int y = 0; y < srcSurf->GetSize().height; y++) { 378 memcpy(destMap.mData + destMap.mStride * y, 379 sourceMap.mData + sourceMap.mStride * y, 380 srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat())); 381 } 382 383 srcSurf->Unmap(); 384 surface->Unmap(); 385 386 mIsClear = false; 387 388 return true; 389 } 390 391 bool MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { 392 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN); 393 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) { 394 return false; 395 } 396 397 uintptr_t ptr = reinterpret_cast<uintptr_t>(mBuffer); 398 aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(ptr)); 399 400 return true; 401 } 402 403 static bool InitBuffer(uint8_t* buf, size_t bufSize, gfx::SurfaceFormat aFormat, 404 TextureAllocationFlags aAllocFlags, bool aAlreadyZero) { 405 if (!buf) { 406 gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize 407 << " bytes"; 408 return false; 409 } 410 411 if (aAllocFlags & ALLOC_CLEAR_BUFFER) { 412 if (aFormat == gfx::SurfaceFormat::B8G8R8X8) { 413 // Even though BGRX was requested, XRGB_UINT32 is what is meant, 414 // so use 0xFF000000 to put alpha in the right place. 415 libyuv::ARGBRect(buf, bufSize, 0, 0, bufSize / sizeof(uint32_t), 1, 416 0xFF000000); 417 } else if (!aAlreadyZero) { 418 memset(buf, 0, bufSize); 419 } 420 } 421 422 return true; 423 } 424 425 MemoryTextureData* MemoryTextureData::Create(gfx::IntSize aSize, 426 gfx::SurfaceFormat aFormat, 427 gfx::BackendType aMoz2DBackend, 428 LayersBackend aLayersBackend, 429 TextureFlags aFlags, 430 TextureAllocationFlags aAllocFlags, 431 IShmemAllocator* aAllocator) { 432 // Should have used CreateForYCbCr. 433 MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV420); 434 435 if (aSize.width <= 0 || aSize.height <= 0) { 436 gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" 437 << aSize.height; 438 return nullptr; 439 } 440 441 uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat); 442 if (!bufSize) { 443 return nullptr; 444 } 445 446 uint8_t* buf = new (fallible) uint8_t[bufSize]; 447 if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, false)) { 448 return nullptr; 449 } 450 451 GfxMemoryImageReporter::DidAlloc(buf); 452 453 BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat); 454 455 // Remote textures are not managed by a texture client, so we need to ensure 456 // that memory is freed when the owning MemoryTextureData goes away. 457 bool autoDeallocate = !!(aFlags & TextureFlags::REMOTE_TEXTURE); 458 bool isClear = (aAllocFlags & ALLOC_CLEAR_BUFFER) != 0; 459 return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize, 460 autoDeallocate, isClear); 461 } 462 463 void MemoryTextureData::Deallocate(LayersIPCChannel*) { 464 MOZ_ASSERT(mBuffer); 465 GfxMemoryImageReporter::WillFree(mBuffer); 466 delete[] mBuffer; 467 mBuffer = nullptr; 468 } 469 470 TextureData* MemoryTextureData::CreateSimilar( 471 LayersIPCChannel* aAllocator, LayersBackend aLayersBackend, 472 TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const { 473 return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend, 474 aLayersBackend, aFlags, aAllocFlags, 475 aAllocator); 476 } 477 478 bool ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { 479 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN); 480 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) { 481 return false; 482 } 483 484 aOutDescriptor = 485 SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(std::move(mShmem))); 486 487 return true; 488 } 489 490 ShmemTextureData* ShmemTextureData::Create(gfx::IntSize aSize, 491 gfx::SurfaceFormat aFormat, 492 gfx::BackendType aMoz2DBackend, 493 LayersBackend aLayersBackend, 494 TextureFlags aFlags, 495 TextureAllocationFlags aAllocFlags, 496 IShmemAllocator* aAllocator) { 497 MOZ_ASSERT(aAllocator); 498 // Should have used CreateForYCbCr. 499 MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV420); 500 501 if (!aAllocator) { 502 return nullptr; 503 } 504 505 if (aSize.width <= 0 || aSize.height <= 0) { 506 gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" 507 << aSize.height; 508 return nullptr; 509 } 510 511 uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat); 512 if (!bufSize) { 513 return nullptr; 514 } 515 516 mozilla::ipc::Shmem shm; 517 if (!aAllocator->AllocUnsafeShmem(bufSize, &shm)) { 518 return nullptr; 519 } 520 521 uint8_t* buf = shm.get<uint8_t>(); 522 if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, true)) { 523 return nullptr; 524 } 525 526 BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat); 527 bool isClear = (aAllocFlags & ALLOC_CLEAR_BUFFER) || !IsOpaque(aFormat); 528 return new ShmemTextureData(descriptor, aMoz2DBackend, shm, isClear); 529 } 530 531 TextureData* ShmemTextureData::CreateSimilar( 532 LayersIPCChannel* aAllocator, LayersBackend aLayersBackend, 533 TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const { 534 return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend, 535 aLayersBackend, aFlags, aAllocFlags, 536 aAllocator); 537 } 538 539 void ShmemTextureData::Deallocate(LayersIPCChannel* aAllocator) { 540 if (!aAllocator) { 541 gfxCriticalNote << "No allocator in ShmemTextureData::Deallocate"; 542 return; 543 } 544 aAllocator->DeallocShmem(mShmem); 545 } 546 547 } // namespace layers 548 } // namespace mozilla