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/. */
7 #include "RenderAndroidSurfaceTextureHost.h"
9 #include "GLReadTexImageHelper.h"
10 #include "mozilla/gfx/Logging.h"
11 #include "mozilla/webrender/RenderThread.h"
12 #include "GLContext.h"
13 #include "AndroidSurfaceTexture.h"
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
)
25 mContinuousUpdate(aContinuousUpdate
),
26 mTransformOverride(aTransformOverride
),
27 mPrepareStatus(STATUS_NONE
),
28 mAttachedToGLContext(false),
29 mIsRemoteTexture(aIsRemoteTexture
) {
30 MOZ_COUNT_CTOR_INHERITED(RenderAndroidSurfaceTextureHost
, RenderTextureHost
);
33 mSurfTex
->IncrementUse();
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.
42 mSurfTex
->DecrementUse();
46 wr::WrExternalImage
RenderAndroidSurfaceTextureHost::Lock(uint8_t aChannelIndex
,
48 MOZ_ASSERT(aChannelIndex
== 0);
49 MOZ_ASSERT((mPrepareStatus
== STATUS_PREPARED
) ||
50 (!mSurfTex
->IsSingleBuffer() &&
51 mPrepareStatus
== STATUS_UPDATE_TEX_IMAGE_NEEDED
) ||
54 if (mIsRemoteTexture
) {
55 EnsureAttachedToGLContext();
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();
64 if (!mSurfTex
|| !mGL
|| !mGL
->MakeCurrent()) {
65 return InvalidToWrExternalImage();
68 MOZ_ASSERT(mAttachedToGLContext
);
69 if (!mAttachedToGLContext
) {
70 return InvalidToWrExternalImage();
73 UpdateTexImageIfNecessary();
75 const auto uvs
= GetUvCoords(mSize
);
76 return NativeTextureToWrExternalImage(mSurfTex
->GetTexName(), uvs
.first
.x
,
77 uvs
.first
.y
, uvs
.second
.x
,
81 void RenderAndroidSurfaceTextureHost::Unlock() {}
83 bool RenderAndroidSurfaceTextureHost::EnsureAttachedToGLContext() {
84 // During handling WebRenderError, GeckoSurfaceTexture should not be attached
86 if (RenderThread::Get()->IsHandlingWebRenderError()) {
90 if (mAttachedToGLContext
) {
95 mGL
= RenderThread::Get()->SingletonGL();
98 if (!mSurfTex
|| !mGL
|| !mGL
->MakeCurrent()) {
102 if (!mSurfTex
->IsAttachedToGLContext((int64_t)mGL
.get())) {
104 mGL
->fGenTextures(1, &texName
);
105 ActivateBindAndTexParameteri(mGL
, LOCAL_GL_TEXTURE0
,
106 LOCAL_GL_TEXTURE_EXTERNAL_OES
, texName
);
108 if (NS_FAILED(mSurfTex
->AttachToGLContext((int64_t)mGL
.get(), texName
))) {
110 mGL
->fDeleteTextures(1, &texName
);
115 mAttachedToGLContext
= true;
119 void RenderAndroidSurfaceTextureHost::PrepareForUse() {
120 // When SurfaceTexture is single buffer mode, UpdateTexImage needs to be
121 // called only once for each publish. If UpdateTexImage is called more
122 // than once, it causes hang on puglish side. And UpdateTexImage needs to
123 // be called on render thread, since the SurfaceTexture is consumed on render
125 MOZ_ASSERT(RenderThread::IsInRenderThread());
126 MOZ_ASSERT(mPrepareStatus
== STATUS_NONE
);
128 if (mContinuousUpdate
|| !mSurfTex
) {
132 mPrepareStatus
= STATUS_MIGHT_BE_USED_BY_WR
;
134 if (mSurfTex
->IsSingleBuffer()) {
135 EnsureAttachedToGLContext();
136 // When SurfaceTexture is single buffer mode, it is OK to call
137 // UpdateTexImage() here.
138 mSurfTex
->UpdateTexImage();
139 mPrepareStatus
= STATUS_PREPARED
;
143 void RenderAndroidSurfaceTextureHost::NotifyForUse() {
144 MOZ_ASSERT(RenderThread::IsInRenderThread());
146 if (mPrepareStatus
== STATUS_MIGHT_BE_USED_BY_WR
) {
147 // This happens when SurfaceTexture of video is rendered on WebRender.
148 // There is a case that SurfaceTexture is not rendered on WebRender, instead
149 // it is rendered to WebGL and the SurfaceTexture should not be attached to
150 // gl context of WebRender. It is ugly. But it is same as Compositor
152 MOZ_ASSERT(!mSurfTex
->IsSingleBuffer());
153 if (!EnsureAttachedToGLContext()) {
156 mPrepareStatus
= STATUS_UPDATE_TEX_IMAGE_NEEDED
;
160 void RenderAndroidSurfaceTextureHost::NotifyNotUsed() {
161 MOZ_ASSERT(RenderThread::IsInRenderThread());
164 MOZ_ASSERT(mPrepareStatus
== STATUS_NONE
);
168 if (mIsRemoteTexture
) {
169 UpdateTexImageIfNecessary();
172 if (mSurfTex
->IsSingleBuffer()) {
173 MOZ_ASSERT(mPrepareStatus
== STATUS_PREPARED
);
174 MOZ_ASSERT(mAttachedToGLContext
);
175 // Release SurfaceTexture's buffer to client side.
177 mSurfTex
->ReleaseTexImage();
178 } else if (mPrepareStatus
== STATUS_UPDATE_TEX_IMAGE_NEEDED
) {
179 MOZ_ASSERT(mAttachedToGLContext
);
180 // This could happen when video frame was skipped. UpdateTexImage() neeeds
181 // to be called for adjusting SurfaceTexture's buffer status.
182 mSurfTex
->UpdateTexImage();
185 mPrepareStatus
= STATUS_NONE
;
188 void RenderAndroidSurfaceTextureHost::UpdateTexImageIfNecessary() {
189 if (mIsRemoteTexture
) {
190 EnsureAttachedToGLContext();
191 if (mPrepareStatus
== STATUS_NONE
) {
194 if (mPrepareStatus
== STATUS_MIGHT_BE_USED_BY_WR
) {
199 if (mContinuousUpdate
) {
200 MOZ_ASSERT(!mSurfTex
->IsSingleBuffer());
201 mSurfTex
->UpdateTexImage();
202 } else if (mPrepareStatus
== STATUS_UPDATE_TEX_IMAGE_NEEDED
) {
203 MOZ_ASSERT(!mSurfTex
->IsSingleBuffer());
204 // When SurfaceTexture is not single buffer mode, call UpdateTexImage() once
205 // just before rendering. During playing video, one SurfaceTexture is used
206 // for all RenderAndroidSurfaceTextureHosts of video.
207 mSurfTex
->UpdateTexImage();
208 mPrepareStatus
= STATUS_PREPARED
;
212 gfx::SurfaceFormat
RenderAndroidSurfaceTextureHost::GetFormat() const {
213 MOZ_ASSERT(mFormat
== gfx::SurfaceFormat::R8G8B8A8
||
214 mFormat
== gfx::SurfaceFormat::R8G8B8X8
);
216 if (mFormat
== gfx::SurfaceFormat::R8G8B8A8
) {
217 return gfx::SurfaceFormat::B8G8R8A8
;
220 if (mFormat
== gfx::SurfaceFormat::R8G8B8X8
) {
221 return gfx::SurfaceFormat::B8G8R8X8
;
225 << "Unexpected color format of RenderAndroidSurfaceTextureHost";
227 return gfx::SurfaceFormat::UNKNOWN
;
230 already_AddRefed
<gfx::DataSourceSurface
>
231 RenderAndroidSurfaceTextureHost::ReadTexImage() {
233 mGL
= RenderThread::Get()->SingletonGL();
239 /* Allocate resulting image surface */
240 int32_t stride
= mSize
.width
* BytesPerPixel(GetFormat());
241 RefPtr
<gfx::DataSourceSurface
> surf
=
242 gfx::Factory::CreateDataSourceSurfaceWithStride(mSize
, GetFormat(),
248 layers::ShaderConfigOGL config
= layers::ShaderConfigFromTargetAndFormat(
249 LOCAL_GL_TEXTURE_EXTERNAL
, mFormat
);
250 int shaderConfig
= config
.mFeatures
;
252 bool ret
= mGL
->ReadTexImageHelper()->ReadTexImage(
253 surf
, mSurfTex
->GetTexName(), LOCAL_GL_TEXTURE_EXTERNAL
, mSize
,
254 shaderConfig
, /* aYInvert */ false);
259 return surf
.forget();
262 bool RenderAndroidSurfaceTextureHost::MapPlane(RenderCompositor
* aCompositor
,
263 uint8_t aChannelIndex
,
264 PlaneInfo
& aPlaneInfo
) {
265 UpdateTexImageIfNecessary();
267 RefPtr
<gfx::DataSourceSurface
> readback
= ReadTexImage();
272 gfx::DataSourceSurface::MappedSurface map
;
273 if (!readback
->Map(gfx::DataSourceSurface::MapType::READ
, &map
)) {
277 mReadback
= readback
;
278 aPlaneInfo
.mSize
= mSize
;
279 aPlaneInfo
.mStride
= map
.mStride
;
280 aPlaneInfo
.mData
= map
.mData
;
284 void RenderAndroidSurfaceTextureHost::UnmapPlanes() {
291 std::pair
<gfx::Point
, gfx::Point
> RenderAndroidSurfaceTextureHost::GetUvCoords(
292 gfx::IntSize aTextureSize
) const {
293 gfx::Matrix4x4 transform
;
295 // GetTransformMatrix() returns the transform set by the producer side of the
296 // SurfaceTexture that must be applied to texture coordinates when
297 // sampling. In some cases we may have set an override value, such as in
298 // AndroidNativeWindowTextureData where we own the producer side, or for
299 // MediaCodec output on devices where where we know the value is incorrect.
300 if (mTransformOverride
) {
301 transform
= *mTransformOverride
;
302 } else if (mSurfTex
) {
303 const auto& surf
= java::sdk::SurfaceTexture::LocalRef(
304 java::sdk::SurfaceTexture::Ref::From(mSurfTex
));
305 gl::AndroidSurfaceTexture::GetTransformMatrix(surf
, &transform
);
308 // We expect this transform to always be rectilinear, usually just a
309 // y-flip and sometimes an x and y scale. This allows this function
310 // to simply transform and return 2 points here instead of 4.
311 MOZ_ASSERT(transform
.IsRectilinear(),
312 "Unexpected non-rectilinear transform returned from "
313 "SurfaceTexture.GetTransformMatrix()");
315 transform
.PostScale(aTextureSize
.width
, aTextureSize
.height
, 0.0);
317 gfx::Point uv0
= gfx::Point(0.0, 0.0);
318 gfx::Point uv1
= gfx::Point(1.0, 1.0);
319 uv0
= transform
.TransformPoint(uv0
);
320 uv1
= transform
.TransformPoint(uv1
);
322 return std::make_pair(uv0
, uv1
);
326 } // namespace mozilla