TextureClientOGL.cpp (10833B)
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 "GLContext.h" // for GLContext, etc 8 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc 9 #include "mozilla/layers/ISurfaceAllocator.h" 10 #include "mozilla/layers/TextureClientOGL.h" 11 #include "mozilla/gfx/2D.h" // for Factory 12 #include "mozilla/gfx/Point.h" // for IntSize 13 #include "GLLibraryEGL.h" 14 15 #ifdef MOZ_WIDGET_ANDROID 16 # include <jni.h> 17 # include <android/native_window.h> 18 # include <android/native_window_jni.h> 19 # include <sys/socket.h> 20 # include "mozilla/ipc/FileDescriptor.h" 21 # include "mozilla/java/GeckoSurfaceWrappers.h" 22 # include "mozilla/java/SurfaceAllocatorWrappers.h" 23 # include "mozilla/layers/AndroidHardwareBuffer.h" 24 # include "mozilla/UniquePtrExtensions.h" 25 #endif 26 27 using namespace mozilla::gl; 28 29 namespace mozilla { 30 namespace layers { 31 32 class CompositableForwarder; 33 34 //////////////////////////////////////////////////////////////////////// 35 // AndroidSurface 36 37 #ifdef MOZ_WIDGET_ANDROID 38 39 already_AddRefed<TextureClient> AndroidSurfaceTextureData::CreateTextureClient( 40 AndroidSurfaceTextureHandle aHandle, gfx::IntSize aSize, bool aContinuous, 41 gl::OriginPos aOriginPos, bool aHasAlpha, bool aForceBT709ColorSpace, 42 Maybe<gfx::Matrix4x4> aTransformOverride, LayersIPCChannel* aAllocator, 43 TextureFlags aFlags) { 44 if (aOriginPos == gl::OriginPos::BottomLeft) { 45 aFlags |= TextureFlags::ORIGIN_BOTTOM_LEFT; 46 } 47 48 return TextureClient::CreateWithData( 49 new AndroidSurfaceTextureData(aHandle, aSize, aContinuous, aHasAlpha, 50 aForceBT709ColorSpace, aTransformOverride), 51 aFlags, aAllocator); 52 } 53 54 AndroidSurfaceTextureData::AndroidSurfaceTextureData( 55 AndroidSurfaceTextureHandle aHandle, gfx::IntSize aSize, bool aContinuous, 56 bool aHasAlpha, bool aForceBT709ColorSpace, 57 Maybe<gfx::Matrix4x4> aTransformOverride) 58 : mHandle(aHandle), 59 mSize(aSize), 60 mContinuous(aContinuous), 61 mHasAlpha(aHasAlpha), 62 mForceBT709ColorSpace(aForceBT709ColorSpace), 63 mTransformOverride(aTransformOverride) { 64 MOZ_ASSERT(mHandle); 65 } 66 67 AndroidSurfaceTextureData::~AndroidSurfaceTextureData() {} 68 69 void AndroidSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const { 70 aInfo.size = mSize; 71 aInfo.format = gfx::SurfaceFormat::UNKNOWN; 72 aInfo.hasSynchronization = false; 73 aInfo.supportsMoz2D = false; 74 aInfo.canExposeMappedData = false; 75 } 76 77 bool AndroidSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) { 78 aOutDescriptor = SurfaceTextureDescriptor( 79 mHandle, mSize, 80 mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::R8G8B8X8, 81 mContinuous, mForceBT709ColorSpace, mTransformOverride); 82 return true; 83 } 84 85 #endif // MOZ_WIDGET_ANDROID 86 87 //////////////////////////////////////////////////////////////////////// 88 // AndroidNativeWindow 89 90 #ifdef MOZ_WIDGET_ANDROID 91 92 AndroidNativeWindowTextureData* AndroidNativeWindowTextureData::Create( 93 gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { 94 if (aFormat != gfx::SurfaceFormat::R8G8B8A8 && 95 aFormat != gfx::SurfaceFormat::R8G8B8X8 && 96 aFormat != gfx::SurfaceFormat::B8G8R8A8 && 97 aFormat != gfx::SurfaceFormat::B8G8R8X8 && 98 aFormat != gfx::SurfaceFormat::R5G6B5_UINT16) { 99 return nullptr; 100 } 101 102 auto surface = 103 java::GeckoSurface::LocalRef(java::SurfaceAllocator::AcquireSurface( 104 aSize.width, aSize.height, true /* single-buffer mode */)); 105 if (surface) { 106 return new AndroidNativeWindowTextureData(surface, aSize, aFormat); 107 } 108 109 return nullptr; 110 } 111 112 AndroidNativeWindowTextureData::AndroidNativeWindowTextureData( 113 java::GeckoSurface::Param aSurface, gfx::IntSize aSize, 114 gfx::SurfaceFormat aFormat) 115 : mSurface(aSurface), mIsLocked(false), mSize(aSize), mFormat(aFormat) { 116 mNativeWindow = ANativeWindow_fromSurface(jni::GetEnvForThread(), 117 mSurface->GetSurface().Get()); 118 MOZ_ASSERT(mNativeWindow, "Failed to create NativeWindow."); 119 120 // SurfaceTextures don't technically support BGR, but we can just pretend to 121 // be RGB. 122 int32_t format = WINDOW_FORMAT_RGBA_8888; 123 switch (aFormat) { 124 case gfx::SurfaceFormat::R8G8B8A8: 125 case gfx::SurfaceFormat::B8G8R8A8: 126 format = WINDOW_FORMAT_RGBA_8888; 127 break; 128 129 case gfx::SurfaceFormat::R8G8B8X8: 130 case gfx::SurfaceFormat::B8G8R8X8: 131 format = WINDOW_FORMAT_RGBX_8888; 132 break; 133 134 case gfx::SurfaceFormat::R5G6B5_UINT16: 135 format = WINDOW_FORMAT_RGB_565; 136 break; 137 138 default: 139 MOZ_ASSERT(false, "Unsupported AndroidNativeWindowTextureData format."); 140 } 141 142 DebugOnly<int32_t> r = ANativeWindow_setBuffersGeometry( 143 mNativeWindow, mSize.width, mSize.height, format); 144 MOZ_ASSERT(r == 0, "ANativeWindow_setBuffersGeometry failed."); 145 146 // Ideally here we'd call ANativeWindow_setBuffersTransform() with the 147 // identity transform, but that is only available on api level >= 26. 148 // Instead use SurfaceDescriptor's transformOverride flag when serializing. 149 } 150 151 void AndroidNativeWindowTextureData::FillInfo(TextureData::Info& aInfo) const { 152 aInfo.size = mSize; 153 aInfo.format = mFormat; 154 aInfo.hasSynchronization = false; 155 aInfo.supportsMoz2D = true; 156 aInfo.canExposeMappedData = false; 157 aInfo.canConcurrentlyReadLock = false; 158 } 159 160 bool AndroidNativeWindowTextureData::Serialize( 161 SurfaceDescriptor& aOutDescriptor) { 162 aOutDescriptor = SurfaceTextureDescriptor( 163 mSurface->GetHandle(), mSize, mFormat, false /* not continuous */, 164 false /* do not override colorspace */, 165 Some(gfx::Matrix4x4()) /* always use identity transform */); 166 return true; 167 } 168 169 bool AndroidNativeWindowTextureData::Lock(OpenMode) { 170 // ANativeWindows can only be locked and unlocked a single time, after which 171 // we must wait until they receive ownership back from the host. 172 // Therefore we must only actually call ANativeWindow_lock() once per cycle. 173 if (!mIsLocked) { 174 int32_t r = ANativeWindow_lock(mNativeWindow, &mBuffer, nullptr); 175 if (r == -ENOMEM) { 176 return false; 177 } else if (r < 0) { 178 MOZ_CRASH("ANativeWindow_lock failed."); 179 } 180 mIsLocked = true; 181 } 182 return true; 183 } 184 185 void AndroidNativeWindowTextureData::Unlock() { 186 // The TextureClient may want to call Lock again before handing ownership 187 // to the host, so we cannot call ANativeWindow_unlockAndPost yet. 188 } 189 190 void AndroidNativeWindowTextureData::Forget(LayersIPCChannel*) { 191 MOZ_ASSERT(!mIsLocked, 192 "ANativeWindow should not be released while locked.\n"); 193 ANativeWindow_release(mNativeWindow); 194 mNativeWindow = nullptr; 195 java::SurfaceAllocator::DisposeSurface(mSurface); 196 mSurface = nullptr; 197 } 198 199 already_AddRefed<gfx::DrawTarget> 200 AndroidNativeWindowTextureData::BorrowDrawTarget() { 201 const int bpp = (mFormat == gfx::SurfaceFormat::R5G6B5_UINT16) ? 2 : 4; 202 203 return gfx::Factory::CreateDrawTargetForData( 204 gfx::BackendType::SKIA, static_cast<unsigned char*>(mBuffer.bits), 205 gfx::IntSize(mBuffer.width, mBuffer.height), mBuffer.stride * bpp, 206 mFormat, true); 207 } 208 209 void AndroidNativeWindowTextureData::OnForwardedToHost() { 210 if (mIsLocked) { 211 int32_t r = ANativeWindow_unlockAndPost(mNativeWindow); 212 if (r < 0) { 213 MOZ_CRASH("ANativeWindow_unlockAndPost failed\n."); 214 } 215 mIsLocked = false; 216 } 217 } 218 219 AndroidHardwareBufferTextureData* AndroidHardwareBufferTextureData::Create( 220 gfx::IntSize aSize, gfx::SurfaceFormat aFormat) { 221 RefPtr<AndroidHardwareBuffer> buffer = 222 AndroidHardwareBuffer::Create(aSize, aFormat); 223 if (!buffer) { 224 return nullptr; 225 } 226 return new AndroidHardwareBufferTextureData(buffer, aSize, aFormat); 227 } 228 229 AndroidHardwareBufferTextureData::AndroidHardwareBufferTextureData( 230 AndroidHardwareBuffer* aAndroidHardwareBuffer, gfx::IntSize aSize, 231 gfx::SurfaceFormat aFormat) 232 : mAndroidHardwareBuffer(aAndroidHardwareBuffer), 233 mSize(aSize), 234 mFormat(aFormat), 235 mAddress(nullptr), 236 mIsLocked(false) {} 237 238 AndroidHardwareBufferTextureData::~AndroidHardwareBufferTextureData() {} 239 240 void AndroidHardwareBufferTextureData::FillInfo( 241 TextureData::Info& aInfo) const { 242 aInfo.size = mSize; 243 aInfo.format = mFormat; 244 aInfo.hasSynchronization = true; 245 aInfo.supportsMoz2D = true; 246 aInfo.canExposeMappedData = false; 247 aInfo.canConcurrentlyReadLock = true; 248 } 249 250 bool AndroidHardwareBufferTextureData::Serialize( 251 SurfaceDescriptor& aOutDescriptor) { 252 aOutDescriptor = SurfaceDescriptorAndroidHardwareBuffer( 253 mAndroidHardwareBuffer->mId, mSize, mFormat); 254 return true; 255 } 256 257 bool AndroidHardwareBufferTextureData::Lock(OpenMode aMode) { 258 if (!mIsLocked) { 259 MOZ_ASSERT(!mAddress); 260 261 uint64_t usage = 0; 262 if (aMode & OpenMode::OPEN_READ) { 263 usage |= AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN; 264 } 265 if (aMode & OpenMode::OPEN_WRITE) { 266 usage |= AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN; 267 } 268 269 int ret = mAndroidHardwareBuffer->Lock(usage, 0, &mAddress); 270 if (ret) { 271 mAddress = nullptr; 272 return false; 273 } 274 mIsLocked = true; 275 } 276 return true; 277 } 278 279 void AndroidHardwareBufferTextureData::Unlock() { 280 // The TextureClient may want to call Lock again before handing ownership 281 // to the host, so we cannot call AHardwareBuffer_unlock yet. 282 } 283 284 void AndroidHardwareBufferTextureData::Forget(LayersIPCChannel*) { 285 MOZ_ASSERT(!mIsLocked); 286 mAndroidHardwareBuffer = nullptr; 287 mAddress = nullptr; 288 } 289 290 already_AddRefed<gfx::DrawTarget> 291 AndroidHardwareBufferTextureData::BorrowDrawTarget() { 292 MOZ_ASSERT(mIsLocked); 293 294 const int bpp = (mFormat == gfx::SurfaceFormat::R5G6B5_UINT16) ? 2 : 4; 295 296 return gfx::Factory::CreateDrawTargetForData( 297 gfx::BackendType::SKIA, static_cast<unsigned char*>(mAddress), 298 gfx::IntSize(mAndroidHardwareBuffer->mSize.width, 299 mAndroidHardwareBuffer->mSize.height), 300 mAndroidHardwareBuffer->mStride * bpp, mFormat, true); 301 } 302 303 void AndroidHardwareBufferTextureData::OnForwardedToHost() { 304 if (mIsLocked) { 305 mAndroidHardwareBuffer->Unlock(); 306 mAddress = nullptr; 307 mIsLocked = false; 308 } 309 } 310 311 TextureFlags AndroidHardwareBufferTextureData::GetTextureFlags() const { 312 return TextureFlags::WAIT_HOST_USAGE_END; 313 } 314 315 Maybe<uint64_t> AndroidHardwareBufferTextureData::GetBufferId() const { 316 return Some(mAndroidHardwareBuffer->mId); 317 } 318 319 UniqueFileHandle AndroidHardwareBufferTextureData::GetAcquireFence() { 320 if (!mAndroidHardwareBuffer) { 321 return UniqueFileHandle(); 322 } 323 324 return mAndroidHardwareBuffer->GetAcquireFence(); 325 } 326 327 #endif // MOZ_WIDGET_ANDROID 328 329 } // namespace layers 330 } // namespace mozilla