RenderAndroidSurfaceTextureHost.cpp (10758B)
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 "RenderAndroidSurfaceTextureHost.h" 8 9 #include "GLReadTexImageHelper.h" 10 #include "mozilla/gfx/Logging.h" 11 #include "mozilla/webrender/RenderThread.h" 12 #include "GLContext.h" 13 #include "AndroidSurfaceTexture.h" 14 15 namespace mozilla { 16 namespace wr { 17 18 RenderAndroidSurfaceTextureHost::RenderAndroidSurfaceTextureHost( 19 const java::GeckoSurfaceTexture::GlobalRef& aSurfTex, gfx::IntSize aSize, 20 gfx::SurfaceFormat aFormat, bool aContinuousUpdate, 21 Maybe<gfx::Matrix4x4> aTransformOverride, bool aIsRemoteTexture) 22 : mSurfTex(aSurfTex), 23 mSize(aSize), 24 mFormat(aFormat), 25 mContinuousUpdate(aContinuousUpdate), 26 mTransformOverride(aTransformOverride), 27 mPrepareStatus(STATUS_NONE), 28 mAttachedToGLContext(false), 29 mIsRemoteTexture(aIsRemoteTexture) { 30 MOZ_COUNT_CTOR_INHERITED(RenderAndroidSurfaceTextureHost, RenderTextureHost); 31 32 if (mSurfTex) { 33 mSurfTex->IncrementUse(); 34 } 35 } 36 37 RenderAndroidSurfaceTextureHost::~RenderAndroidSurfaceTextureHost() { 38 MOZ_ASSERT(RenderThread::IsInRenderThread()); 39 MOZ_COUNT_DTOR_INHERITED(RenderAndroidSurfaceTextureHost, RenderTextureHost); 40 // The SurfaceTexture gets destroyed when its use count reaches zero. 41 if (mSurfTex) { 42 mSurfTex->DecrementUse(); 43 } 44 } 45 46 wr::WrExternalImage RenderAndroidSurfaceTextureHost::Lock(uint8_t aChannelIndex, 47 gl::GLContext* aGL) { 48 MOZ_ASSERT(aChannelIndex == 0); 49 MOZ_ASSERT((mPrepareStatus == STATUS_PREPARED) || 50 (!mSurfTex->IsSingleBuffer() && 51 mPrepareStatus == STATUS_UPDATE_TEX_IMAGE_NEEDED) || 52 mIsRemoteTexture); 53 54 if (mIsRemoteTexture) { 55 EnsureAttachedToGLContext(); 56 } 57 58 if (mGL.get() != aGL) { 59 // This should not happen. On android, SingletonGL is used. 60 MOZ_ASSERT_UNREACHABLE("Unexpected GL context"); 61 return InvalidToWrExternalImage(); 62 } 63 64 if (!mSurfTex || !mGL || !mGL->MakeCurrent()) { 65 return InvalidToWrExternalImage(); 66 } 67 68 MOZ_ASSERT(mAttachedToGLContext); 69 if (!mAttachedToGLContext) { 70 return InvalidToWrExternalImage(); 71 } 72 73 UpdateTexImageIfNecessary(); 74 75 const gfx::Matrix4x4 transform = GetTextureTransform(); 76 // We expect this transform to always be rectilinear, usually just a 77 // y-flip and sometimes an x and y scale/translation. This allows us 78 // to simply transform 2 points here instead of 4. 79 MOZ_ASSERT(transform.IsRectilinear(), 80 "Unexpected non-rectilinear transform returned from " 81 "SurfaceTexture.GetTransformMatrix()"); 82 gfx::Point uv0(0.0, 0.0); 83 gfx::Point uv1(1.0, 1.0); 84 uv0 = transform.TransformPoint(uv0); 85 uv1 = transform.TransformPoint(uv1); 86 87 return NativeTextureToWrExternalImage(mSurfTex->GetTexName(), uv0.x, uv0.y, 88 uv1.x, uv1.y); 89 } 90 91 void RenderAndroidSurfaceTextureHost::Unlock() {} 92 93 bool RenderAndroidSurfaceTextureHost::EnsureAttachedToGLContext() { 94 // During handling WebRenderError, GeckoSurfaceTexture should not be attached 95 // to GLContext. 96 if (RenderThread::Get()->IsHandlingWebRenderError()) { 97 return false; 98 } 99 100 if (mAttachedToGLContext) { 101 return true; 102 } 103 104 if (!mGL) { 105 mGL = RenderThread::Get()->SingletonGL(); 106 } 107 108 if (!mSurfTex || !mGL || !mGL->MakeCurrent()) { 109 return false; 110 } 111 112 if (!mSurfTex->IsAttachedToGLContext((int64_t)mGL.get())) { 113 GLuint texName; 114 mGL->fGenTextures(1, &texName); 115 ActivateBindAndTexParameteri(mGL, LOCAL_GL_TEXTURE0, 116 LOCAL_GL_TEXTURE_EXTERNAL_OES, texName); 117 118 if (NS_FAILED(mSurfTex->AttachToGLContext((int64_t)mGL.get(), texName))) { 119 MOZ_ASSERT(0); 120 mGL->fDeleteTextures(1, &texName); 121 return false; 122 } 123 } 124 125 mAttachedToGLContext = true; 126 return true; 127 } 128 129 void RenderAndroidSurfaceTextureHost::PrepareForUse() { 130 // When SurfaceTexture is single buffer mode, UpdateTexImage needs to be 131 // called only once for each publish. If UpdateTexImage is called more 132 // than once, it causes hang on puglish side. And UpdateTexImage needs to 133 // be called on render thread, since the SurfaceTexture is consumed on render 134 // thread. 135 MOZ_ASSERT(RenderThread::IsInRenderThread()); 136 MOZ_ASSERT(mPrepareStatus == STATUS_NONE); 137 138 if (mContinuousUpdate || !mSurfTex) { 139 return; 140 } 141 142 mPrepareStatus = STATUS_MIGHT_BE_USED_BY_WR; 143 144 if (mSurfTex->IsSingleBuffer()) { 145 EnsureAttachedToGLContext(); 146 // When SurfaceTexture is single buffer mode, it is OK to call 147 // UpdateTexImage() here. 148 mSurfTex->UpdateTexImage(); 149 mPrepareStatus = STATUS_PREPARED; 150 } 151 } 152 153 void RenderAndroidSurfaceTextureHost::NotifyForUse() { 154 MOZ_ASSERT(RenderThread::IsInRenderThread()); 155 156 if (mPrepareStatus == STATUS_NONE) { 157 // This happens either for RemoteTextureHost or when we lose a race to call 158 // PrepareForUse with a GPUVideoTextureHost when the content process 159 // attempts to use said texture host before the decoding process has setup 160 // the underlying SurfaceTextureHost. See bug 1986472. 161 PrepareForUse(); 162 } 163 164 if (mPrepareStatus == STATUS_MIGHT_BE_USED_BY_WR) { 165 // This happens when SurfaceTexture of video is rendered on WebRender. 166 // There is a case that SurfaceTexture is not rendered on WebRender, instead 167 // it is rendered to WebGL and the SurfaceTexture should not be attached to 168 // gl context of WebRender. It is ugly. But it is same as Compositor 169 // rendering. 170 MOZ_ASSERT(!mSurfTex->IsSingleBuffer()); 171 if (!EnsureAttachedToGLContext()) { 172 return; 173 } 174 mPrepareStatus = STATUS_UPDATE_TEX_IMAGE_NEEDED; 175 } 176 } 177 178 void RenderAndroidSurfaceTextureHost::NotifyNotUsed() { 179 MOZ_ASSERT(RenderThread::IsInRenderThread()); 180 181 if (!mSurfTex) { 182 MOZ_ASSERT(mPrepareStatus == STATUS_NONE); 183 return; 184 } 185 186 if (mIsRemoteTexture) { 187 UpdateTexImageIfNecessary(); 188 } 189 190 if (mSurfTex->IsSingleBuffer()) { 191 MOZ_ASSERT(mPrepareStatus == STATUS_PREPARED); 192 MOZ_ASSERT(mAttachedToGLContext); 193 // Release SurfaceTexture's buffer to client side. 194 mGL->MakeCurrent(); 195 mSurfTex->ReleaseTexImage(); 196 } else if (mPrepareStatus == STATUS_UPDATE_TEX_IMAGE_NEEDED) { 197 MOZ_ASSERT(mAttachedToGLContext); 198 // This could happen when video frame was skipped. UpdateTexImage() neeeds 199 // to be called for adjusting SurfaceTexture's buffer status. 200 mSurfTex->UpdateTexImage(); 201 } 202 203 mPrepareStatus = STATUS_NONE; 204 } 205 206 void RenderAndroidSurfaceTextureHost::UpdateTexImageIfNecessary() { 207 if (mIsRemoteTexture) { 208 EnsureAttachedToGLContext(); 209 NotifyForUse(); 210 } 211 212 if (mContinuousUpdate) { 213 MOZ_ASSERT(!mSurfTex->IsSingleBuffer()); 214 mSurfTex->UpdateTexImage(); 215 } else if (mPrepareStatus == STATUS_UPDATE_TEX_IMAGE_NEEDED) { 216 MOZ_ASSERT(!mSurfTex->IsSingleBuffer()); 217 // When SurfaceTexture is not single buffer mode, call UpdateTexImage() once 218 // just before rendering. During playing video, one SurfaceTexture is used 219 // for all RenderAndroidSurfaceTextureHosts of video. 220 mSurfTex->UpdateTexImage(); 221 mPrepareStatus = STATUS_PREPARED; 222 } 223 } 224 225 gfx::SurfaceFormat RenderAndroidSurfaceTextureHost::GetFormat() const { 226 MOZ_ASSERT(mFormat == gfx::SurfaceFormat::R8G8B8A8 || 227 mFormat == gfx::SurfaceFormat::R8G8B8X8); 228 229 if (mFormat == gfx::SurfaceFormat::R8G8B8A8) { 230 return gfx::SurfaceFormat::B8G8R8A8; 231 } 232 233 if (mFormat == gfx::SurfaceFormat::R8G8B8X8) { 234 return gfx::SurfaceFormat::B8G8R8X8; 235 } 236 237 gfxCriticalNoteOnce 238 << "Unexpected color format of RenderAndroidSurfaceTextureHost"; 239 240 return gfx::SurfaceFormat::UNKNOWN; 241 } 242 243 already_AddRefed<gfx::DataSourceSurface> 244 RenderAndroidSurfaceTextureHost::ReadTexImage() { 245 if (!mGL) { 246 mGL = RenderThread::Get()->SingletonGL(); 247 if (!mGL) { 248 return nullptr; 249 } 250 } 251 252 /* Allocate resulting image surface */ 253 int32_t stride = mSize.width * BytesPerPixel(GetFormat()); 254 RefPtr<gfx::DataSourceSurface> surf = 255 gfx::Factory::CreateDataSourceSurfaceWithStride(mSize, GetFormat(), 256 stride); 257 if (!surf) { 258 return nullptr; 259 } 260 261 layers::ShaderConfigOGL config = layers::ShaderConfigFromTargetAndFormat( 262 LOCAL_GL_TEXTURE_EXTERNAL, mFormat); 263 int shaderConfig = config.mFeatures; 264 265 bool ret = mGL->ReadTexImageHelper()->ReadTexImage( 266 surf, mSurfTex->GetTexName(), LOCAL_GL_TEXTURE_EXTERNAL, mSize, 267 GetTextureTransform(), shaderConfig, /* aYInvert */ false); 268 if (!ret) { 269 return nullptr; 270 } 271 272 return surf.forget(); 273 } 274 275 bool RenderAndroidSurfaceTextureHost::MapPlane(RenderCompositor* aCompositor, 276 uint8_t aChannelIndex, 277 PlaneInfo& aPlaneInfo) { 278 UpdateTexImageIfNecessary(); 279 280 RefPtr<gfx::DataSourceSurface> readback = ReadTexImage(); 281 if (!readback) { 282 return false; 283 } 284 285 gfx::DataSourceSurface::MappedSurface map; 286 if (!readback->Map(gfx::DataSourceSurface::MapType::READ, &map)) { 287 return false; 288 } 289 290 mReadback = readback; 291 aPlaneInfo.mSize = mSize; 292 aPlaneInfo.mStride = map.mStride; 293 aPlaneInfo.mData = map.mData; 294 return true; 295 } 296 297 void RenderAndroidSurfaceTextureHost::UnmapPlanes() { 298 if (mReadback) { 299 mReadback->Unmap(); 300 mReadback = nullptr; 301 } 302 } 303 304 gfx::Matrix4x4 RenderAndroidSurfaceTextureHost::GetTextureTransform() const { 305 gfx::Matrix4x4 transform; 306 307 // GetTransformMatrix() returns the transform set by the producer side of the 308 // SurfaceTexture that must be applied to texture coordinates when 309 // sampling. In some cases we may have set an override value, such as in 310 // AndroidNativeWindowTextureData where we own the producer side, or for 311 // MediaCodec output on devices where where we know the value is incorrect. 312 if (mTransformOverride) { 313 transform = *mTransformOverride; 314 } else if (mSurfTex) { 315 const auto& surf = java::sdk::SurfaceTexture::LocalRef( 316 java::sdk::SurfaceTexture::Ref::From(mSurfTex)); 317 gl::AndroidSurfaceTexture::GetTransformMatrix(surf, &transform); 318 } 319 320 return transform; 321 } 322 323 RefPtr<layers::TextureSource> 324 RenderAndroidSurfaceTextureHost::CreateTextureSource( 325 layers::TextureSourceProvider* aProvider) { 326 UpdateTexImageIfNecessary(); 327 return new layers::SurfaceTextureSource( 328 aProvider, mSurfTex, mFormat, LOCAL_GL_TEXTURE_EXTERNAL, 329 LOCAL_GL_CLAMP_TO_EDGE, mSize, mTransformOverride); 330 } 331 332 } // namespace wr 333 } // namespace mozilla