On Android we use SurfaceTextures to render content from sources such as the video decoder. These may have a transform set which is supposed to be applied to the texture coordinates used to sample the texture. Webrender (and software webrender), however, do not handle this correctly, meaning videos may be rendered at the incorrect size on some devices. SurfaceTextures should always be rendered with their bottom-left being their origin, eg vertically flipped. Additionally, the texture transform returned on most devices seems to be a simple y-flip transform with no scaling. Webrender currently just ignores the y-flip due to the texture origin, which cancels out us not handling the y-flip from the transform, meaning video looks correct on most devices. Some devices, however, do return a scaling transform which we must handle. This patch removes the override of WebRenderTextureHost::NeedsYFlip() that was causing us to ignore the y-flip due to the texture origin - since we will now apply the transform we must handle this correctly too. It adds a virtual method RenderTextureHost::GetUvCoords(), that returns the texture coordinates that should be used by webrender to sample from external textures. In most cases these are simply (0, 0) and (size.x, size.y), but in RenderAndroidSurfaceTextureHost we override this function to apply the transformation. This ensures we use the correct coordinates whenever the texture is rendered by webrender, eg in both software and hardware webrender when rendering in the non-compositing-path, and by hardware webrender's draw compositor. Additionally, the composite.glsl shader requires a fix to calculate the UV bounds correctly, as the coordinates may now be inverted. Lastly, we fix software webrender with the OpenGL compositor. CompositorOGL already has the required functionality to apply the texture transformation as it was used back in the layers days. We must simply ensure that we pass the value of the mIgnoreTransform flag from the original SurfaceTextureHost, through to the RenderAndroidSurfaceTextureHost, and finally to the SurfaceTextureSource which we hand to CompositorOGL. Differential Revision: https://phabricator.services.mozilla.com/D144306
235 lines
7.6 KiB
C++
235 lines
7.6 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "RenderTextureHostSWGL.h"
|
|
|
|
#include "mozilla/gfx/Logging.h"
|
|
#include "mozilla/layers/TextureHost.h"
|
|
#include "RenderThread.h"
|
|
|
|
namespace mozilla {
|
|
namespace wr {
|
|
|
|
bool RenderTextureHostSWGL::UpdatePlanes(RenderCompositor* aCompositor,
|
|
wr::ImageRendering aRendering) {
|
|
wr_swgl_make_current(mContext);
|
|
size_t planeCount = GetPlaneCount();
|
|
bool filterUpdate = IsFilterUpdateNecessary(aRendering);
|
|
if (mPlanes.size() < planeCount) {
|
|
mPlanes.reserve(planeCount);
|
|
while (mPlanes.size() < planeCount) {
|
|
mPlanes.push_back(PlaneInfo(wr_swgl_gen_texture(mContext)));
|
|
}
|
|
filterUpdate = true;
|
|
}
|
|
gfx::SurfaceFormat format = GetFormat();
|
|
gfx::ColorDepth colorDepth = GetColorDepth();
|
|
for (size_t i = 0; i < planeCount; i++) {
|
|
PlaneInfo& plane = mPlanes[i];
|
|
if (!MapPlane(aCompositor, i, plane)) {
|
|
if (i > 0) {
|
|
UnmapPlanes();
|
|
}
|
|
return false;
|
|
}
|
|
GLenum internalFormat = 0;
|
|
switch (format) {
|
|
case gfx::SurfaceFormat::B8G8R8A8:
|
|
case gfx::SurfaceFormat::B8G8R8X8:
|
|
MOZ_ASSERT(colorDepth == gfx::ColorDepth::COLOR_8);
|
|
internalFormat = LOCAL_GL_RGBA8;
|
|
break;
|
|
case gfx::SurfaceFormat::YUV:
|
|
switch (colorDepth) {
|
|
case gfx::ColorDepth::COLOR_8:
|
|
internalFormat = LOCAL_GL_R8;
|
|
break;
|
|
case gfx::ColorDepth::COLOR_10:
|
|
case gfx::ColorDepth::COLOR_12:
|
|
case gfx::ColorDepth::COLOR_16:
|
|
internalFormat = LOCAL_GL_R16;
|
|
break;
|
|
}
|
|
break;
|
|
case gfx::SurfaceFormat::NV12:
|
|
switch (colorDepth) {
|
|
case gfx::ColorDepth::COLOR_8:
|
|
internalFormat = i > 0 ? LOCAL_GL_RG8 : LOCAL_GL_R8;
|
|
break;
|
|
case gfx::ColorDepth::COLOR_10:
|
|
case gfx::ColorDepth::COLOR_12:
|
|
case gfx::ColorDepth::COLOR_16:
|
|
internalFormat = i > 0 ? LOCAL_GL_RG16 : LOCAL_GL_R16;
|
|
break;
|
|
}
|
|
break;
|
|
case gfx::SurfaceFormat::P010:
|
|
MOZ_ASSERT(colorDepth == gfx::ColorDepth::COLOR_10);
|
|
internalFormat = i > 0 ? LOCAL_GL_RG16 : LOCAL_GL_R16;
|
|
break;
|
|
case gfx::SurfaceFormat::YUV422:
|
|
MOZ_ASSERT(colorDepth == gfx::ColorDepth::COLOR_8);
|
|
internalFormat = LOCAL_GL_RGB_RAW_422_APPLE;
|
|
break;
|
|
default:
|
|
MOZ_RELEASE_ASSERT(false, "Unhandled external image format");
|
|
break;
|
|
}
|
|
wr_swgl_set_texture_buffer(mContext, plane.mTexture, internalFormat,
|
|
plane.mSize.width, plane.mSize.height,
|
|
plane.mStride, plane.mData, 0, 0);
|
|
}
|
|
if (filterUpdate) {
|
|
mCachedRendering = aRendering;
|
|
GLenum filter = aRendering == wr::ImageRendering::Pixelated
|
|
? LOCAL_GL_NEAREST
|
|
: LOCAL_GL_LINEAR;
|
|
for (const auto& plane : mPlanes) {
|
|
wr_swgl_set_texture_parameter(mContext, plane.mTexture,
|
|
LOCAL_GL_TEXTURE_MIN_FILTER, filter);
|
|
wr_swgl_set_texture_parameter(mContext, plane.mTexture,
|
|
LOCAL_GL_TEXTURE_MAG_FILTER, filter);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool RenderTextureHostSWGL::SetContext(void* aContext) {
|
|
if (mContext != aContext) {
|
|
CleanupPlanes();
|
|
mContext = aContext;
|
|
wr_swgl_reference_context(mContext);
|
|
}
|
|
return mContext != nullptr;
|
|
}
|
|
|
|
wr::WrExternalImage RenderTextureHostSWGL::LockSWGL(
|
|
uint8_t aChannelIndex, void* aContext, RenderCompositor* aCompositor,
|
|
wr::ImageRendering aRendering) {
|
|
if (!SetContext(aContext)) {
|
|
return InvalidToWrExternalImage();
|
|
}
|
|
if (!mLocked) {
|
|
if (!UpdatePlanes(aCompositor, aRendering)) {
|
|
return InvalidToWrExternalImage();
|
|
}
|
|
mLocked = true;
|
|
}
|
|
if (aChannelIndex >= mPlanes.size()) {
|
|
return InvalidToWrExternalImage();
|
|
}
|
|
const PlaneInfo& plane = mPlanes[aChannelIndex];
|
|
|
|
const auto uvs = GetUvCoords(plane.mSize);
|
|
|
|
// Prefer native textures, unless our backend forbids it.
|
|
// If the GetUvCoords call above returned anything other than the default,
|
|
// for example if this is a RenderAndroidSurfaceTextureHost, then this won't
|
|
// be handled correctly in the RawDataToWrExternalImage path. But we shouldn't
|
|
// hit this path in practice with a RenderAndroidSurfaceTextureHost.
|
|
layers::TextureHost::NativeTexturePolicy policy =
|
|
layers::TextureHost::BackendNativeTexturePolicy(
|
|
layers::WebRenderBackend::SOFTWARE, plane.mSize);
|
|
return policy == layers::TextureHost::NativeTexturePolicy::FORBID
|
|
? RawDataToWrExternalImage((uint8_t*)plane.mData,
|
|
plane.mStride * plane.mSize.height)
|
|
: NativeTextureToWrExternalImage(plane.mTexture, uvs.first.x,
|
|
uvs.first.y, uvs.second.x,
|
|
uvs.second.y);
|
|
}
|
|
|
|
void RenderTextureHostSWGL::UnlockSWGL() {
|
|
if (mLocked) {
|
|
mLocked = false;
|
|
UnmapPlanes();
|
|
}
|
|
}
|
|
|
|
void RenderTextureHostSWGL::CleanupPlanes() {
|
|
if (!mContext) {
|
|
return;
|
|
}
|
|
if (!mPlanes.empty()) {
|
|
wr_swgl_make_current(mContext);
|
|
for (const auto& plane : mPlanes) {
|
|
wr_swgl_delete_texture(mContext, plane.mTexture);
|
|
}
|
|
mPlanes.clear();
|
|
}
|
|
wr_swgl_destroy_context(mContext);
|
|
mContext = nullptr;
|
|
}
|
|
|
|
RenderTextureHostSWGL::~RenderTextureHostSWGL() { CleanupPlanes(); }
|
|
|
|
bool RenderTextureHostSWGL::LockSWGLCompositeSurface(
|
|
void* aContext, wr::SWGLCompositeSurfaceInfo* aInfo) {
|
|
if (!SetContext(aContext)) {
|
|
return false;
|
|
}
|
|
if (!mLocked) {
|
|
if (!UpdatePlanes(nullptr, mCachedRendering)) {
|
|
return false;
|
|
}
|
|
mLocked = true;
|
|
}
|
|
MOZ_ASSERT(mPlanes.size() <= 3);
|
|
for (size_t i = 0; i < mPlanes.size(); i++) {
|
|
aInfo->textures[i] = mPlanes[i].mTexture;
|
|
}
|
|
switch (GetFormat()) {
|
|
case gfx::SurfaceFormat::YUV:
|
|
case gfx::SurfaceFormat::NV12:
|
|
case gfx::SurfaceFormat::P010:
|
|
case gfx::SurfaceFormat::YUV422: {
|
|
aInfo->yuv_planes = mPlanes.size();
|
|
auto colorSpace = GetYUVColorSpace();
|
|
aInfo->color_space = ToWrYuvRangedColorSpace(colorSpace);
|
|
auto colorDepth = GetColorDepth();
|
|
aInfo->color_depth = ToWrColorDepth(colorDepth);
|
|
break;
|
|
}
|
|
case gfx::SurfaceFormat::B8G8R8A8:
|
|
case gfx::SurfaceFormat::B8G8R8X8:
|
|
break;
|
|
default:
|
|
gfxCriticalNote << "Unhandled external image format: " << GetFormat();
|
|
MOZ_RELEASE_ASSERT(false, "Unhandled external image format");
|
|
break;
|
|
}
|
|
aInfo->size.width = mPlanes[0].mSize.width;
|
|
aInfo->size.height = mPlanes[0].mSize.height;
|
|
return true;
|
|
}
|
|
|
|
bool wr_swgl_lock_composite_surface(void* aContext, wr::ExternalImageId aId,
|
|
wr::SWGLCompositeSurfaceInfo* aInfo) {
|
|
RenderTextureHost* texture = RenderThread::Get()->GetRenderTexture(aId);
|
|
if (!texture) {
|
|
return false;
|
|
}
|
|
RenderTextureHostSWGL* swglTex = texture->AsRenderTextureHostSWGL();
|
|
if (!swglTex) {
|
|
return false;
|
|
}
|
|
return swglTex->LockSWGLCompositeSurface(aContext, aInfo);
|
|
}
|
|
|
|
void wr_swgl_unlock_composite_surface(void* aContext, wr::ExternalImageId aId) {
|
|
RenderTextureHost* texture = RenderThread::Get()->GetRenderTexture(aId);
|
|
if (!texture) {
|
|
return;
|
|
}
|
|
RenderTextureHostSWGL* swglTex = texture->AsRenderTextureHostSWGL();
|
|
if (!swglTex) {
|
|
return;
|
|
}
|
|
swglTex->UnlockSWGL();
|
|
}
|
|
|
|
} // namespace wr
|
|
} // namespace mozilla
|