1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/layers/basic/BasicCompositor.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,495 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 +* This Source Code Form is subject to the terms of the Mozilla Public 1.6 +* License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "BasicCompositor.h" 1.10 +#include "BasicLayersImpl.h" // for FillRectWithMask 1.11 +#include "TextureHostBasic.h" 1.12 +#include "mozilla/layers/Effects.h" 1.13 +#include "mozilla/layers/YCbCrImageDataSerializer.h" 1.14 +#include "nsIWidget.h" 1.15 +#include "gfx2DGlue.h" 1.16 +#include "mozilla/gfx/2D.h" 1.17 +#include "mozilla/gfx/Helpers.h" 1.18 +#include "gfxUtils.h" 1.19 +#include "YCbCrUtils.h" 1.20 +#include <algorithm> 1.21 +#include "ImageContainer.h" 1.22 +#include "gfxPrefs.h" 1.23 +#define PIXMAN_DONT_DEFINE_STDINT 1.24 +#include "pixman.h" // for pixman_f_transform, etc 1.25 + 1.26 +namespace mozilla { 1.27 +using namespace mozilla::gfx; 1.28 + 1.29 +namespace layers { 1.30 + 1.31 +class DataTextureSourceBasic : public DataTextureSource 1.32 + , public TextureSourceBasic 1.33 +{ 1.34 +public: 1.35 + 1.36 + virtual TextureSourceBasic* AsSourceBasic() MOZ_OVERRIDE { return this; } 1.37 + 1.38 + virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) MOZ_OVERRIDE { return mSurface; } 1.39 + 1.40 + SurfaceFormat GetFormat() const MOZ_OVERRIDE 1.41 + { 1.42 + return mSurface->GetFormat(); 1.43 + } 1.44 + 1.45 + virtual IntSize GetSize() const MOZ_OVERRIDE 1.46 + { 1.47 + return mSurface->GetSize(); 1.48 + } 1.49 + 1.50 + virtual bool Update(gfx::DataSourceSurface* aSurface, 1.51 + nsIntRegion* aDestRegion = nullptr, 1.52 + gfx::IntPoint* aSrcOffset = nullptr) MOZ_OVERRIDE 1.53 + { 1.54 + // XXX - For this to work with IncrementalContentHost we will need to support 1.55 + // the aDestRegion and aSrcOffset parameters properly; 1.56 + mSurface = aSurface; 1.57 + return true; 1.58 + } 1.59 + 1.60 + virtual void DeallocateDeviceData() MOZ_OVERRIDE 1.61 + { 1.62 + mSurface = nullptr; 1.63 + SetUpdateSerial(0); 1.64 + } 1.65 + 1.66 +public: 1.67 + RefPtr<gfx::DataSourceSurface> mSurface; 1.68 +}; 1.69 + 1.70 +BasicCompositor::BasicCompositor(nsIWidget *aWidget) 1.71 + : mWidget(aWidget) 1.72 +{ 1.73 + MOZ_COUNT_CTOR(BasicCompositor); 1.74 + SetBackend(LayersBackend::LAYERS_BASIC); 1.75 +} 1.76 + 1.77 +BasicCompositor::~BasicCompositor() 1.78 +{ 1.79 + MOZ_COUNT_DTOR(BasicCompositor); 1.80 +} 1.81 + 1.82 +void BasicCompositor::Destroy() 1.83 +{ 1.84 + mWidget->CleanupRemoteDrawing(); 1.85 + mWidget = nullptr; 1.86 +} 1.87 + 1.88 +TemporaryRef<CompositingRenderTarget> 1.89 +BasicCompositor::CreateRenderTarget(const IntRect& aRect, SurfaceInitMode aInit) 1.90 +{ 1.91 + RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); 1.92 + 1.93 + RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect); 1.94 + 1.95 + return rt.forget(); 1.96 +} 1.97 + 1.98 +TemporaryRef<CompositingRenderTarget> 1.99 +BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect, 1.100 + const CompositingRenderTarget *aSource, 1.101 + const IntPoint &aSourcePoint) 1.102 +{ 1.103 + RefPtr<DrawTarget> target = mDrawTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8); 1.104 + RefPtr<BasicCompositingRenderTarget> rt = new BasicCompositingRenderTarget(target, aRect); 1.105 + 1.106 + DrawTarget *source; 1.107 + if (aSource) { 1.108 + const BasicCompositingRenderTarget* sourceSurface = 1.109 + static_cast<const BasicCompositingRenderTarget*>(aSource); 1.110 + source = sourceSurface->mDrawTarget; 1.111 + } else { 1.112 + source = mDrawTarget; 1.113 + } 1.114 + 1.115 + RefPtr<SourceSurface> snapshot = source->Snapshot(); 1.116 + 1.117 + IntRect sourceRect(aSourcePoint, aRect.Size()); 1.118 + rt->mDrawTarget->CopySurface(snapshot, sourceRect, IntPoint(0, 0)); 1.119 + return rt.forget(); 1.120 +} 1.121 + 1.122 +TemporaryRef<DataTextureSource> 1.123 +BasicCompositor::CreateDataTextureSource(TextureFlags aFlags) 1.124 +{ 1.125 + RefPtr<DataTextureSource> result = new DataTextureSourceBasic(); 1.126 + return result.forget(); 1.127 +} 1.128 + 1.129 +bool 1.130 +BasicCompositor::SupportsEffect(EffectTypes aEffect) 1.131 +{ 1.132 + return static_cast<EffectTypes>(aEffect) != EFFECT_YCBCR; 1.133 +} 1.134 + 1.135 +static void 1.136 +DrawSurfaceWithTextureCoords(DrawTarget *aDest, 1.137 + const gfx::Rect& aDestRect, 1.138 + SourceSurface *aSource, 1.139 + const gfx::Rect& aTextureCoords, 1.140 + gfx::Filter aFilter, 1.141 + float aOpacity, 1.142 + SourceSurface *aMask, 1.143 + const Matrix* aMaskTransform) 1.144 +{ 1.145 + // Convert aTextureCoords into aSource's coordinate space 1.146 + gfxRect sourceRect(aTextureCoords.x * aSource->GetSize().width, 1.147 + aTextureCoords.y * aSource->GetSize().height, 1.148 + aTextureCoords.width * aSource->GetSize().width, 1.149 + aTextureCoords.height * aSource->GetSize().height); 1.150 + // Compute a transform that maps sourceRect to aDestRect. 1.151 + gfxMatrix transform = 1.152 + gfxUtils::TransformRectToRect(sourceRect, 1.153 + gfxPoint(aDestRect.x, aDestRect.y), 1.154 + gfxPoint(aDestRect.XMost(), aDestRect.y), 1.155 + gfxPoint(aDestRect.XMost(), aDestRect.YMost())); 1.156 + Matrix matrix = ToMatrix(transform); 1.157 + 1.158 + // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1). 1.159 + gfx::Rect unitRect(0, 0, 1, 1); 1.160 + ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT; 1.161 + 1.162 + FillRectWithMask(aDest, aDestRect, aSource, aFilter, DrawOptions(aOpacity), 1.163 + mode, aMask, aMaskTransform, &matrix); 1.164 +} 1.165 + 1.166 +static pixman_transform 1.167 +Matrix3DToPixman(const gfx3DMatrix& aMatrix) 1.168 +{ 1.169 + pixman_f_transform transform; 1.170 + 1.171 + transform.m[0][0] = aMatrix._11; 1.172 + transform.m[0][1] = aMatrix._21; 1.173 + transform.m[0][2] = aMatrix._41; 1.174 + transform.m[1][0] = aMatrix._12; 1.175 + transform.m[1][1] = aMatrix._22; 1.176 + transform.m[1][2] = aMatrix._42; 1.177 + transform.m[2][0] = aMatrix._14; 1.178 + transform.m[2][1] = aMatrix._24; 1.179 + transform.m[2][2] = aMatrix._44; 1.180 + 1.181 + pixman_transform result; 1.182 + pixman_transform_from_pixman_f_transform(&result, &transform); 1.183 + 1.184 + return result; 1.185 +} 1.186 + 1.187 +static void 1.188 +PixmanTransform(DataSourceSurface* aDest, 1.189 + DataSourceSurface* aSource, 1.190 + const gfx3DMatrix& aTransform, 1.191 + gfxPoint aDestOffset) 1.192 +{ 1.193 + IntSize destSize = aDest->GetSize(); 1.194 + pixman_image_t* dest = pixman_image_create_bits(PIXMAN_a8r8g8b8, 1.195 + destSize.width, 1.196 + destSize.height, 1.197 + (uint32_t*)aDest->GetData(), 1.198 + aDest->Stride()); 1.199 + 1.200 + IntSize srcSize = aSource->GetSize(); 1.201 + pixman_image_t* src = pixman_image_create_bits(PIXMAN_a8r8g8b8, 1.202 + srcSize.width, 1.203 + srcSize.height, 1.204 + (uint32_t*)aSource->GetData(), 1.205 + aSource->Stride()); 1.206 + 1.207 + NS_ABORT_IF_FALSE(src && dest, "Failed to create pixman images?"); 1.208 + 1.209 + pixman_transform pixTransform = Matrix3DToPixman(aTransform); 1.210 + pixman_transform pixTransformInverted; 1.211 + 1.212 + // If the transform is singular then nothing would be drawn anyway, return here 1.213 + if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) { 1.214 + pixman_image_unref(dest); 1.215 + pixman_image_unref(src); 1.216 + return; 1.217 + } 1.218 + pixman_image_set_transform(src, &pixTransformInverted); 1.219 + 1.220 + pixman_image_composite32(PIXMAN_OP_SRC, 1.221 + src, 1.222 + nullptr, 1.223 + dest, 1.224 + aDestOffset.x, 1.225 + aDestOffset.y, 1.226 + 0, 1.227 + 0, 1.228 + 0, 1.229 + 0, 1.230 + destSize.width, 1.231 + destSize.height); 1.232 + 1.233 + pixman_image_unref(dest); 1.234 + pixman_image_unref(src); 1.235 +} 1.236 + 1.237 +static inline IntRect 1.238 +RoundOut(Rect r) 1.239 +{ 1.240 + r.RoundOut(); 1.241 + return IntRect(r.x, r.y, r.width, r.height); 1.242 +} 1.243 + 1.244 +void 1.245 +BasicCompositor::DrawQuad(const gfx::Rect& aRect, 1.246 + const gfx::Rect& aClipRect, 1.247 + const EffectChain &aEffectChain, 1.248 + gfx::Float aOpacity, 1.249 + const gfx::Matrix4x4 &aTransform) 1.250 +{ 1.251 + RefPtr<DrawTarget> buffer = mRenderTarget->mDrawTarget; 1.252 + 1.253 + // For 2D drawing, |dest| and |buffer| are the same surface. For 3D drawing, 1.254 + // |dest| is a temporary surface. 1.255 + RefPtr<DrawTarget> dest = buffer; 1.256 + 1.257 + buffer->PushClipRect(aClipRect); 1.258 + AutoSaveTransform autoSaveTransform(dest); 1.259 + 1.260 + Matrix newTransform; 1.261 + Rect transformBounds; 1.262 + gfx3DMatrix new3DTransform; 1.263 + IntPoint offset = mRenderTarget->GetOrigin(); 1.264 + 1.265 + if (aTransform.Is2D()) { 1.266 + newTransform = aTransform.As2D(); 1.267 + } else { 1.268 + // Create a temporary surface for the transform. 1.269 + dest = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(RoundOut(aRect).Size(), SurfaceFormat::B8G8R8A8); 1.270 + if (!dest) { 1.271 + return; 1.272 + } 1.273 + 1.274 + // Get the bounds post-transform. 1.275 + To3DMatrix(aTransform, new3DTransform); 1.276 + gfxRect bounds = new3DTransform.TransformBounds(ThebesRect(aRect)); 1.277 + bounds.IntersectRect(bounds, gfxRect(offset.x, offset.y, buffer->GetSize().width, buffer->GetSize().height)); 1.278 + 1.279 + transformBounds = ToRect(bounds); 1.280 + transformBounds.RoundOut(); 1.281 + 1.282 + // Propagate the coordinate offset to our 2D draw target. 1.283 + newTransform.Translate(transformBounds.x, transformBounds.y); 1.284 + 1.285 + // When we apply the 3D transformation, we do it against a temporary 1.286 + // surface, so undo the coordinate offset. 1.287 + new3DTransform = new3DTransform * gfx3DMatrix::Translation(-transformBounds.x, -transformBounds.y, 0); 1.288 + 1.289 + transformBounds.MoveTo(0, 0); 1.290 + } 1.291 + 1.292 + newTransform.PostTranslate(-offset.x, -offset.y); 1.293 + buffer->SetTransform(newTransform); 1.294 + 1.295 + RefPtr<SourceSurface> sourceMask; 1.296 + Matrix maskTransform; 1.297 + if (aEffectChain.mSecondaryEffects[EFFECT_MASK]) { 1.298 + EffectMask *effectMask = static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EFFECT_MASK].get()); 1.299 + sourceMask = effectMask->mMaskTexture->AsSourceBasic()->GetSurface(dest); 1.300 + MOZ_ASSERT(effectMask->mMaskTransform.Is2D(), "How did we end up with a 3D transform here?!"); 1.301 + MOZ_ASSERT(!effectMask->mIs3D); 1.302 + maskTransform = effectMask->mMaskTransform.As2D(); 1.303 + maskTransform.Translate(-offset.x, -offset.y); 1.304 + } 1.305 + 1.306 + switch (aEffectChain.mPrimaryEffect->mType) { 1.307 + case EFFECT_SOLID_COLOR: { 1.308 + EffectSolidColor* effectSolidColor = 1.309 + static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get()); 1.310 + 1.311 + FillRectWithMask(dest, aRect, effectSolidColor->mColor, 1.312 + DrawOptions(aOpacity), sourceMask, &maskTransform); 1.313 + break; 1.314 + } 1.315 + case EFFECT_RGB: { 1.316 + TexturedEffect* texturedEffect = 1.317 + static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get()); 1.318 + TextureSourceBasic* source = texturedEffect->mTexture->AsSourceBasic(); 1.319 + 1.320 + DrawSurfaceWithTextureCoords(dest, aRect, 1.321 + source->GetSurface(dest), 1.322 + texturedEffect->mTextureCoords, 1.323 + texturedEffect->mFilter, 1.324 + aOpacity, sourceMask, &maskTransform); 1.325 + break; 1.326 + } 1.327 + case EFFECT_YCBCR: { 1.328 + NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!"); 1.329 + break; 1.330 + } 1.331 + case EFFECT_RENDER_TARGET: { 1.332 + EffectRenderTarget* effectRenderTarget = 1.333 + static_cast<EffectRenderTarget*>(aEffectChain.mPrimaryEffect.get()); 1.334 + RefPtr<BasicCompositingRenderTarget> surface 1.335 + = static_cast<BasicCompositingRenderTarget*>(effectRenderTarget->mRenderTarget.get()); 1.336 + RefPtr<SourceSurface> sourceSurf = surface->mDrawTarget->Snapshot(); 1.337 + 1.338 + DrawSurfaceWithTextureCoords(dest, aRect, 1.339 + sourceSurf, 1.340 + effectRenderTarget->mTextureCoords, 1.341 + effectRenderTarget->mFilter, 1.342 + aOpacity, sourceMask, &maskTransform); 1.343 + break; 1.344 + } 1.345 + case EFFECT_COMPONENT_ALPHA: { 1.346 + NS_RUNTIMEABORT("Can't (easily) support component alpha with BasicCompositor!"); 1.347 + break; 1.348 + } 1.349 + default: { 1.350 + NS_RUNTIMEABORT("Invalid effect type!"); 1.351 + break; 1.352 + } 1.353 + } 1.354 + 1.355 + if (!aTransform.Is2D()) { 1.356 + dest->Flush(); 1.357 + 1.358 + RefPtr<SourceSurface> snapshot = dest->Snapshot(); 1.359 + RefPtr<DataSourceSurface> source = snapshot->GetDataSurface(); 1.360 + RefPtr<DataSourceSurface> temp = 1.361 + Factory::CreateDataSourceSurface(RoundOut(transformBounds).Size(), SurfaceFormat::B8G8R8A8); 1.362 + if (!temp) { 1.363 + return; 1.364 + } 1.365 + 1.366 + PixmanTransform(temp, source, new3DTransform, gfxPoint(0, 0)); 1.367 + 1.368 + buffer->DrawSurface(temp, transformBounds, transformBounds); 1.369 + } 1.370 + 1.371 + buffer->PopClip(); 1.372 +} 1.373 + 1.374 +void 1.375 +BasicCompositor::BeginFrame(const nsIntRegion& aInvalidRegion, 1.376 + const gfx::Rect *aClipRectIn, 1.377 + const gfx::Matrix& aTransform, 1.378 + const gfx::Rect& aRenderBounds, 1.379 + gfx::Rect *aClipRectOut /* = nullptr */, 1.380 + gfx::Rect *aRenderBoundsOut /* = nullptr */) 1.381 +{ 1.382 + nsIntRect intRect; 1.383 + mWidget->GetClientBounds(intRect); 1.384 + mWidgetSize = gfx::ToIntSize(intRect.Size()); 1.385 + 1.386 + // The result of GetClientBounds is shifted over by the size of the window 1.387 + // manager styling. We want to ignore that. 1.388 + intRect.MoveTo(0, 0); 1.389 + Rect rect = Rect(0, 0, intRect.width, intRect.height); 1.390 + 1.391 + // Sometimes the invalid region is larger than we want to draw. 1.392 + nsIntRegion invalidRegionSafe; 1.393 + invalidRegionSafe.And(aInvalidRegion, intRect); 1.394 + 1.395 + // FIXME: Redraw the whole screen in every frame to work around bug 972728. 1.396 + invalidRegionSafe = intRect; 1.397 + 1.398 + nsIntRect invalidRect = invalidRegionSafe.GetBounds(); 1.399 + mInvalidRect = IntRect(invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height); 1.400 + mInvalidRegion = invalidRegionSafe; 1.401 + 1.402 + if (aRenderBoundsOut) { 1.403 + *aRenderBoundsOut = Rect(); 1.404 + } 1.405 + 1.406 + if (mInvalidRect.width <= 0 || mInvalidRect.height <= 0) { 1.407 + return; 1.408 + } 1.409 + 1.410 + if (mCopyTarget) { 1.411 + // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Create a dummy 1.412 + // placeholder so that CreateRenderTarget() works. 1.413 + mDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1,1), SurfaceFormat::B8G8R8A8); 1.414 + } else { 1.415 + mDrawTarget = mWidget->StartRemoteDrawing(); 1.416 + } 1.417 + if (!mDrawTarget) { 1.418 + return; 1.419 + } 1.420 + 1.421 + // Setup an intermediate render target to buffer all compositing. We will 1.422 + // copy this into mDrawTarget (the widget), and/or mCopyTarget in EndFrame() 1.423 + RefPtr<CompositingRenderTarget> target = CreateRenderTarget(mInvalidRect, INIT_MODE_CLEAR); 1.424 + SetRenderTarget(target); 1.425 + 1.426 + // We only allocate a surface sized to the invalidated region, so we need to 1.427 + // translate future coordinates. 1.428 + Matrix transform; 1.429 + transform.Translate(-invalidRect.x, -invalidRect.y); 1.430 + mRenderTarget->mDrawTarget->SetTransform(transform); 1.431 + 1.432 + gfxUtils::ClipToRegion(mRenderTarget->mDrawTarget, invalidRegionSafe); 1.433 + 1.434 + if (aRenderBoundsOut) { 1.435 + *aRenderBoundsOut = rect; 1.436 + } 1.437 + 1.438 + if (aClipRectIn) { 1.439 + mRenderTarget->mDrawTarget->PushClipRect(*aClipRectIn); 1.440 + } else { 1.441 + mRenderTarget->mDrawTarget->PushClipRect(rect); 1.442 + if (aClipRectOut) { 1.443 + *aClipRectOut = rect; 1.444 + } 1.445 + } 1.446 +} 1.447 + 1.448 +void 1.449 +BasicCompositor::EndFrame() 1.450 +{ 1.451 + // Pop aClipRectIn/bounds rect 1.452 + mRenderTarget->mDrawTarget->PopClip(); 1.453 + 1.454 + if (gfxPrefs::WidgetUpdateFlashing()) { 1.455 + float r = float(rand()) / RAND_MAX; 1.456 + float g = float(rand()) / RAND_MAX; 1.457 + float b = float(rand()) / RAND_MAX; 1.458 + // We're still clipped to mInvalidRegion, so just fill the bounds. 1.459 + mRenderTarget->mDrawTarget->FillRect(ToRect(mInvalidRegion.GetBounds()), 1.460 + ColorPattern(Color(r, g, b, 0.2f))); 1.461 + } 1.462 + 1.463 + // Pop aInvalidregion 1.464 + mRenderTarget->mDrawTarget->PopClip(); 1.465 + 1.466 + // Note: Most platforms require us to buffer drawing to the widget surface. 1.467 + // That's why we don't draw to mDrawTarget directly. 1.468 + RefPtr<SourceSurface> source = mRenderTarget->mDrawTarget->Snapshot(); 1.469 + RefPtr<DrawTarget> dest(mCopyTarget ? mCopyTarget : mDrawTarget); 1.470 + 1.471 + // The source DrawTarget is clipped to the invalidation region, so we have 1.472 + // to copy the individual rectangles in the region or else we'll draw blank 1.473 + // pixels. 1.474 + nsIntRegionRectIterator iter(mInvalidRegion); 1.475 + for (const nsIntRect *r = iter.Next(); r; r = iter.Next()) { 1.476 + dest->CopySurface(source, 1.477 + IntRect(r->x - mInvalidRect.x, r->y - mInvalidRect.y, r->width, r->height), 1.478 + IntPoint(r->x, r->y)); 1.479 + } 1.480 + if (!mCopyTarget) { 1.481 + mWidget->EndRemoteDrawing(); 1.482 + } 1.483 + 1.484 + mDrawTarget = nullptr; 1.485 + mRenderTarget = nullptr; 1.486 +} 1.487 + 1.488 +void 1.489 +BasicCompositor::AbortFrame() 1.490 +{ 1.491 + mRenderTarget->mDrawTarget->PopClip(); 1.492 + mRenderTarget->mDrawTarget->PopClip(); 1.493 + mDrawTarget = nullptr; 1.494 + mRenderTarget = nullptr; 1.495 +} 1.496 + 1.497 +} 1.498 +}