Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "mozilla/layers/PLayerTransaction.h" |
michael@0 | 7 | |
michael@0 | 8 | // This must occur *after* layers/PLayerTransaction.h to avoid |
michael@0 | 9 | // typedefs conflicts. |
michael@0 | 10 | #include "mozilla/ArrayUtils.h" |
michael@0 | 11 | |
michael@0 | 12 | #include "ThebesLayerD3D9.h" |
michael@0 | 13 | #include "gfxPlatform.h" |
michael@0 | 14 | |
michael@0 | 15 | #include "gfxWindowsPlatform.h" |
michael@0 | 16 | #include "gfxTeeSurface.h" |
michael@0 | 17 | #include "gfxUtils.h" |
michael@0 | 18 | #include "ReadbackProcessor.h" |
michael@0 | 19 | #include "ReadbackLayer.h" |
michael@0 | 20 | #include "mozilla/gfx/2D.h" |
michael@0 | 21 | |
michael@0 | 22 | namespace mozilla { |
michael@0 | 23 | namespace layers { |
michael@0 | 24 | |
michael@0 | 25 | using namespace gfx; |
michael@0 | 26 | |
michael@0 | 27 | ThebesLayerD3D9::ThebesLayerD3D9(LayerManagerD3D9 *aManager) |
michael@0 | 28 | : ThebesLayer(aManager, nullptr) |
michael@0 | 29 | , LayerD3D9(aManager) |
michael@0 | 30 | { |
michael@0 | 31 | mImplData = static_cast<LayerD3D9*>(this); |
michael@0 | 32 | aManager->deviceManager()->mLayersWithResources.AppendElement(this); |
michael@0 | 33 | } |
michael@0 | 34 | |
michael@0 | 35 | ThebesLayerD3D9::~ThebesLayerD3D9() |
michael@0 | 36 | { |
michael@0 | 37 | if (mD3DManager) { |
michael@0 | 38 | mD3DManager->deviceManager()->mLayersWithResources.RemoveElement(this); |
michael@0 | 39 | } |
michael@0 | 40 | } |
michael@0 | 41 | |
michael@0 | 42 | /** |
michael@0 | 43 | * Retention threshold - amount of pixels intersection required to enable |
michael@0 | 44 | * layer content retention. This is a guesstimate. Profiling could be done to |
michael@0 | 45 | * figure out the optimal threshold. |
michael@0 | 46 | */ |
michael@0 | 47 | #define RETENTION_THRESHOLD 16384 |
michael@0 | 48 | |
michael@0 | 49 | void |
michael@0 | 50 | ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion) |
michael@0 | 51 | { |
michael@0 | 52 | mInvalidRegion.Or(mInvalidRegion, aRegion); |
michael@0 | 53 | mInvalidRegion.SimplifyOutward(20); |
michael@0 | 54 | mValidRegion.Sub(mValidRegion, mInvalidRegion); |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | void |
michael@0 | 58 | ThebesLayerD3D9::CopyRegion(IDirect3DTexture9* aSrc, const nsIntPoint &aSrcOffset, |
michael@0 | 59 | IDirect3DTexture9* aDest, const nsIntPoint &aDestOffset, |
michael@0 | 60 | const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion) |
michael@0 | 61 | { |
michael@0 | 62 | nsRefPtr<IDirect3DSurface9> srcSurface, dstSurface; |
michael@0 | 63 | aSrc->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); |
michael@0 | 64 | aDest->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); |
michael@0 | 65 | |
michael@0 | 66 | nsIntRegion retainedRegion; |
michael@0 | 67 | nsIntRegionRectIterator iter(aCopyRegion); |
michael@0 | 68 | const nsIntRect *r; |
michael@0 | 69 | while ((r = iter.Next())) { |
michael@0 | 70 | if (r->width * r->height > RETENTION_THRESHOLD) { |
michael@0 | 71 | RECT oldRect, newRect; |
michael@0 | 72 | |
michael@0 | 73 | // Calculate the retained rectangle's position on the old and the new |
michael@0 | 74 | // surface. |
michael@0 | 75 | oldRect.left = r->x - aSrcOffset.x; |
michael@0 | 76 | oldRect.top = r->y - aSrcOffset.y; |
michael@0 | 77 | oldRect.right = oldRect.left + r->width; |
michael@0 | 78 | oldRect.bottom = oldRect.top + r->height; |
michael@0 | 79 | |
michael@0 | 80 | newRect.left = r->x - aDestOffset.x; |
michael@0 | 81 | newRect.top = r->y - aDestOffset.y; |
michael@0 | 82 | newRect.right = newRect.left + r->width; |
michael@0 | 83 | newRect.bottom = newRect.top + r->height; |
michael@0 | 84 | |
michael@0 | 85 | // Copy data from our old texture to the new one |
michael@0 | 86 | HRESULT hr = device()-> |
michael@0 | 87 | StretchRect(srcSurface, &oldRect, dstSurface, &newRect, D3DTEXF_NONE); |
michael@0 | 88 | |
michael@0 | 89 | if (SUCCEEDED(hr)) { |
michael@0 | 90 | retainedRegion.Or(retainedRegion, *r); |
michael@0 | 91 | } |
michael@0 | 92 | } |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | // Areas which were valid and were retained are still valid |
michael@0 | 96 | aValidRegion->And(*aValidRegion, retainedRegion); |
michael@0 | 97 | } |
michael@0 | 98 | |
michael@0 | 99 | static uint64_t RectArea(const nsIntRect& aRect) |
michael@0 | 100 | { |
michael@0 | 101 | return aRect.width*uint64_t(aRect.height); |
michael@0 | 102 | } |
michael@0 | 103 | |
michael@0 | 104 | void |
michael@0 | 105 | ThebesLayerD3D9::UpdateTextures(SurfaceMode aMode) |
michael@0 | 106 | { |
michael@0 | 107 | nsIntRect visibleRect = mVisibleRegion.GetBounds(); |
michael@0 | 108 | |
michael@0 | 109 | if (HaveTextures(aMode)) { |
michael@0 | 110 | if (!mTextureRect.IsEqualInterior(visibleRect)) { |
michael@0 | 111 | nsRefPtr<IDirect3DTexture9> oldTexture = mTexture; |
michael@0 | 112 | nsRefPtr<IDirect3DTexture9> oldTextureOnWhite = mTextureOnWhite; |
michael@0 | 113 | |
michael@0 | 114 | NS_ASSERTION(mTextureRect.Contains(mValidRegion.GetBounds()), |
michael@0 | 115 | "How can we have valid data outside the texture?"); |
michael@0 | 116 | nsIntRegion retainRegion; |
michael@0 | 117 | // The region we want to retain is the valid data that is inside |
michael@0 | 118 | // the new visible region |
michael@0 | 119 | retainRegion.And(mValidRegion, mVisibleRegion); |
michael@0 | 120 | |
michael@0 | 121 | CreateNewTextures(gfx::IntSize(visibleRect.width, visibleRect.height), aMode); |
michael@0 | 122 | |
michael@0 | 123 | // If our texture creation failed this can mean a device reset is pending and we |
michael@0 | 124 | // should silently ignore the failure. In the future when device failures |
michael@0 | 125 | // are properly handled we should test for the type of failure and gracefully |
michael@0 | 126 | // handle different failures. See bug 569081. |
michael@0 | 127 | if (!HaveTextures(aMode)) { |
michael@0 | 128 | mValidRegion.SetEmpty(); |
michael@0 | 129 | } else { |
michael@0 | 130 | CopyRegion(oldTexture, mTextureRect.TopLeft(), mTexture, visibleRect.TopLeft(), |
michael@0 | 131 | retainRegion, &mValidRegion); |
michael@0 | 132 | if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
michael@0 | 133 | CopyRegion(oldTextureOnWhite, mTextureRect.TopLeft(), mTextureOnWhite, visibleRect.TopLeft(), |
michael@0 | 134 | retainRegion, &mValidRegion); |
michael@0 | 135 | } |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | mTextureRect = visibleRect; |
michael@0 | 139 | } |
michael@0 | 140 | } else { |
michael@0 | 141 | CreateNewTextures(gfx::IntSize(visibleRect.width, visibleRect.height), aMode); |
michael@0 | 142 | mTextureRect = visibleRect; |
michael@0 | 143 | |
michael@0 | 144 | NS_ASSERTION(mValidRegion.IsEmpty(), "Someone forgot to empty the region"); |
michael@0 | 145 | } |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | void |
michael@0 | 149 | ThebesLayerD3D9::RenderRegion(const nsIntRegion& aRegion) |
michael@0 | 150 | { |
michael@0 | 151 | nsIntRegionRectIterator iter(aRegion); |
michael@0 | 152 | |
michael@0 | 153 | const nsIntRect *iterRect; |
michael@0 | 154 | while ((iterRect = iter.Next())) { |
michael@0 | 155 | device()->SetVertexShaderConstantF(CBvLayerQuad, |
michael@0 | 156 | ShaderConstantRect(iterRect->x, |
michael@0 | 157 | iterRect->y, |
michael@0 | 158 | iterRect->width, |
michael@0 | 159 | iterRect->height), |
michael@0 | 160 | 1); |
michael@0 | 161 | |
michael@0 | 162 | device()->SetVertexShaderConstantF(CBvTextureCoords, |
michael@0 | 163 | ShaderConstantRect( |
michael@0 | 164 | (float)(iterRect->x - mTextureRect.x) / (float)mTextureRect.width, |
michael@0 | 165 | (float)(iterRect->y - mTextureRect.y) / (float)mTextureRect.height, |
michael@0 | 166 | (float)iterRect->width / (float)mTextureRect.width, |
michael@0 | 167 | (float)iterRect->height / (float)mTextureRect.height), 1); |
michael@0 | 168 | |
michael@0 | 169 | device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); |
michael@0 | 170 | } |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | void |
michael@0 | 174 | ThebesLayerD3D9::RenderThebesLayer(ReadbackProcessor* aReadback) |
michael@0 | 175 | { |
michael@0 | 176 | if (mVisibleRegion.IsEmpty()) { |
michael@0 | 177 | return; |
michael@0 | 178 | } |
michael@0 | 179 | |
michael@0 | 180 | nsIntRect newTextureRect = mVisibleRegion.GetBounds(); |
michael@0 | 181 | |
michael@0 | 182 | SurfaceMode mode = GetSurfaceMode(); |
michael@0 | 183 | if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA && |
michael@0 | 184 | (!mParent || !mParent->SupportsComponentAlphaChildren())) { |
michael@0 | 185 | mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; |
michael@0 | 186 | } |
michael@0 | 187 | // If we have a transform that requires resampling of our texture, then |
michael@0 | 188 | // we need to make sure we don't sample pixels that haven't been drawn. |
michael@0 | 189 | // We clamp sample coordinates to the texture rect, but when the visible region |
michael@0 | 190 | // doesn't fill the entire texture rect we need to make sure we draw all the |
michael@0 | 191 | // pixels in the texture rect anyway in case they get sampled. |
michael@0 | 192 | nsIntRegion neededRegion = mVisibleRegion; |
michael@0 | 193 | if (!neededRegion.GetBounds().IsEqualInterior(newTextureRect) || |
michael@0 | 194 | neededRegion.GetNumRects() > 1) { |
michael@0 | 195 | if (MayResample()) { |
michael@0 | 196 | neededRegion = newTextureRect; |
michael@0 | 197 | if (mode == SurfaceMode::SURFACE_OPAQUE) { |
michael@0 | 198 | // We're going to paint outside the visible region, but layout hasn't |
michael@0 | 199 | // promised that it will paint opaquely there, so we'll have to |
michael@0 | 200 | // treat this layer as transparent. |
michael@0 | 201 | mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA; |
michael@0 | 202 | } |
michael@0 | 203 | } |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | VerifyContentType(mode); |
michael@0 | 207 | UpdateTextures(mode); |
michael@0 | 208 | if (!HaveTextures(mode)) { |
michael@0 | 209 | NS_WARNING("Texture creation failed"); |
michael@0 | 210 | return; |
michael@0 | 211 | } |
michael@0 | 212 | |
michael@0 | 213 | nsTArray<ReadbackProcessor::Update> readbackUpdates; |
michael@0 | 214 | nsIntRegion readbackRegion; |
michael@0 | 215 | if (aReadback && UsedForReadback()) { |
michael@0 | 216 | aReadback->GetThebesLayerUpdates(this, &readbackUpdates, &readbackRegion); |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | // Because updates to D3D9 ThebesLayers are rendered with the CPU, we don't |
michael@0 | 220 | // have to do readback from D3D9 surfaces. Instead we make sure that any area |
michael@0 | 221 | // needed for readback is included in the drawRegion we ask layout to render. |
michael@0 | 222 | // Then the readback areas we need can be copied out of the temporary |
michael@0 | 223 | // destinationSurface in DrawRegion. |
michael@0 | 224 | nsIntRegion drawRegion; |
michael@0 | 225 | drawRegion.Sub(neededRegion, mValidRegion); |
michael@0 | 226 | drawRegion.Or(drawRegion, readbackRegion); |
michael@0 | 227 | // NS_ASSERTION(mVisibleRegion.Contains(region), "Bad readback region!"); |
michael@0 | 228 | |
michael@0 | 229 | if (!drawRegion.IsEmpty()) { |
michael@0 | 230 | LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); |
michael@0 | 231 | if (!cbInfo.Callback) { |
michael@0 | 232 | NS_ERROR("D3D9 should never need to update ThebesLayers in an empty transaction"); |
michael@0 | 233 | return; |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | DrawRegion(drawRegion, mode, readbackUpdates); |
michael@0 | 237 | |
michael@0 | 238 | mValidRegion = neededRegion; |
michael@0 | 239 | } |
michael@0 | 240 | |
michael@0 | 241 | if (mD3DManager->CompositingDisabled()) { |
michael@0 | 242 | return; |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | SetShaderTransformAndOpacity(); |
michael@0 | 246 | |
michael@0 | 247 | if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
michael@0 | 248 | mD3DManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS1, |
michael@0 | 249 | GetMaskLayer()); |
michael@0 | 250 | device()->SetTexture(0, mTexture); |
michael@0 | 251 | device()->SetTexture(1, mTextureOnWhite); |
michael@0 | 252 | device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); |
michael@0 | 253 | device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR); |
michael@0 | 254 | RenderRegion(neededRegion); |
michael@0 | 255 | |
michael@0 | 256 | mD3DManager->SetShaderMode(DeviceManagerD3D9::COMPONENTLAYERPASS2, |
michael@0 | 257 | GetMaskLayer()); |
michael@0 | 258 | device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); |
michael@0 | 259 | device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); |
michael@0 | 260 | RenderRegion(neededRegion); |
michael@0 | 261 | |
michael@0 | 262 | // Restore defaults |
michael@0 | 263 | device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); |
michael@0 | 264 | device()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); |
michael@0 | 265 | device()->SetTexture(1, nullptr); |
michael@0 | 266 | } else { |
michael@0 | 267 | mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER, |
michael@0 | 268 | GetMaskLayer()); |
michael@0 | 269 | device()->SetTexture(0, mTexture); |
michael@0 | 270 | RenderRegion(neededRegion); |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | // Set back to default. |
michael@0 | 274 | device()->SetVertexShaderConstantF(CBvTextureCoords, |
michael@0 | 275 | ShaderConstantRect(0, 0, 1.0f, 1.0f), |
michael@0 | 276 | 1); |
michael@0 | 277 | } |
michael@0 | 278 | |
michael@0 | 279 | void |
michael@0 | 280 | ThebesLayerD3D9::CleanResources() |
michael@0 | 281 | { |
michael@0 | 282 | mTexture = nullptr; |
michael@0 | 283 | mTextureOnWhite = nullptr; |
michael@0 | 284 | mValidRegion.SetEmpty(); |
michael@0 | 285 | } |
michael@0 | 286 | |
michael@0 | 287 | void |
michael@0 | 288 | ThebesLayerD3D9::LayerManagerDestroyed() |
michael@0 | 289 | { |
michael@0 | 290 | mD3DManager->deviceManager()->mLayersWithResources.RemoveElement(this); |
michael@0 | 291 | mD3DManager = nullptr; |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | Layer* |
michael@0 | 295 | ThebesLayerD3D9::GetLayer() |
michael@0 | 296 | { |
michael@0 | 297 | return this; |
michael@0 | 298 | } |
michael@0 | 299 | |
michael@0 | 300 | bool |
michael@0 | 301 | ThebesLayerD3D9::IsEmpty() |
michael@0 | 302 | { |
michael@0 | 303 | return !mTexture; |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | void |
michael@0 | 307 | ThebesLayerD3D9::VerifyContentType(SurfaceMode aMode) |
michael@0 | 308 | { |
michael@0 | 309 | if (!mTexture) |
michael@0 | 310 | return; |
michael@0 | 311 | |
michael@0 | 312 | D3DSURFACE_DESC desc; |
michael@0 | 313 | mTexture->GetLevelDesc(0, &desc); |
michael@0 | 314 | |
michael@0 | 315 | switch (aMode) { |
michael@0 | 316 | case SurfaceMode::SURFACE_OPAQUE: |
michael@0 | 317 | if (desc.Format == D3DFMT_X8R8G8B8 && !mTextureOnWhite) |
michael@0 | 318 | return; |
michael@0 | 319 | break; |
michael@0 | 320 | |
michael@0 | 321 | case SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA: |
michael@0 | 322 | if (desc.Format == D3DFMT_A8R8G8B8 && !mTextureOnWhite) |
michael@0 | 323 | return; |
michael@0 | 324 | break; |
michael@0 | 325 | |
michael@0 | 326 | case SurfaceMode::SURFACE_COMPONENT_ALPHA: |
michael@0 | 327 | if (mTextureOnWhite) { |
michael@0 | 328 | NS_ASSERTION(desc.Format == D3DFMT_X8R8G8B8, "Wrong format for component alpha texture"); |
michael@0 | 329 | return; |
michael@0 | 330 | } |
michael@0 | 331 | break; |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | // The new format isn't compatible with the old texture(s), toss out the old |
michael@0 | 335 | // texture(s). |
michael@0 | 336 | mTexture = nullptr; |
michael@0 | 337 | mTextureOnWhite = nullptr; |
michael@0 | 338 | mValidRegion.SetEmpty(); |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | class OpaqueRenderer { |
michael@0 | 342 | public: |
michael@0 | 343 | OpaqueRenderer(const nsIntRegion& aUpdateRegion) : |
michael@0 | 344 | mUpdateRegion(aUpdateRegion) {} |
michael@0 | 345 | ~OpaqueRenderer() { End(); } |
michael@0 | 346 | already_AddRefed<gfxWindowsSurface> Begin(LayerD3D9* aLayer); |
michael@0 | 347 | void End(); |
michael@0 | 348 | IDirect3DTexture9* GetTexture() { return mTmpTexture; } |
michael@0 | 349 | |
michael@0 | 350 | private: |
michael@0 | 351 | const nsIntRegion& mUpdateRegion; |
michael@0 | 352 | nsRefPtr<IDirect3DTexture9> mTmpTexture; |
michael@0 | 353 | nsRefPtr<IDirect3DSurface9> mSurface; |
michael@0 | 354 | nsRefPtr<gfxWindowsSurface> mD3D9ThebesSurface; |
michael@0 | 355 | }; |
michael@0 | 356 | |
michael@0 | 357 | already_AddRefed<gfxWindowsSurface> |
michael@0 | 358 | OpaqueRenderer::Begin(LayerD3D9* aLayer) |
michael@0 | 359 | { |
michael@0 | 360 | nsIntRect bounds = mUpdateRegion.GetBounds(); |
michael@0 | 361 | |
michael@0 | 362 | HRESULT hr = aLayer->device()-> |
michael@0 | 363 | CreateTexture(bounds.width, bounds.height, 1, 0, D3DFMT_X8R8G8B8, |
michael@0 | 364 | D3DPOOL_SYSTEMMEM, getter_AddRefs(mTmpTexture), nullptr); |
michael@0 | 365 | |
michael@0 | 366 | if (FAILED(hr)) { |
michael@0 | 367 | aLayer->ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr); |
michael@0 | 368 | return nullptr; |
michael@0 | 369 | } |
michael@0 | 370 | |
michael@0 | 371 | hr = mTmpTexture->GetSurfaceLevel(0, getter_AddRefs(mSurface)); |
michael@0 | 372 | |
michael@0 | 373 | if (FAILED(hr)) { |
michael@0 | 374 | // Uh-oh, bail. |
michael@0 | 375 | NS_WARNING("Failed to get texture surface level."); |
michael@0 | 376 | return nullptr; |
michael@0 | 377 | } |
michael@0 | 378 | |
michael@0 | 379 | nsRefPtr<gfxWindowsSurface> result = new gfxWindowsSurface(mSurface); |
michael@0 | 380 | if (!result || result->CairoStatus()) { |
michael@0 | 381 | NS_WARNING("Failed to d3d9 cairo surface."); |
michael@0 | 382 | return nullptr; |
michael@0 | 383 | } |
michael@0 | 384 | mD3D9ThebesSurface = result; |
michael@0 | 385 | |
michael@0 | 386 | return result.forget(); |
michael@0 | 387 | } |
michael@0 | 388 | |
michael@0 | 389 | void |
michael@0 | 390 | OpaqueRenderer::End() |
michael@0 | 391 | { |
michael@0 | 392 | mSurface = nullptr; |
michael@0 | 393 | // gfxWindowsSurface returned from ::Begin() should be released before the |
michael@0 | 394 | // texture is used. This will assert that this is the case |
michael@0 | 395 | #if 1 |
michael@0 | 396 | if (mD3D9ThebesSurface) { |
michael@0 | 397 | mD3D9ThebesSurface->AddRef(); |
michael@0 | 398 | nsrefcnt c = mD3D9ThebesSurface->Release(); |
michael@0 | 399 | if (c != 1) |
michael@0 | 400 | NS_RUNTIMEABORT("Reference mD3D9ThebesSurface must be released by caller of Begin() before calling End()"); |
michael@0 | 401 | } |
michael@0 | 402 | #endif |
michael@0 | 403 | mD3D9ThebesSurface = nullptr; |
michael@0 | 404 | |
michael@0 | 405 | } |
michael@0 | 406 | |
michael@0 | 407 | static void |
michael@0 | 408 | FillSurface(gfxASurface* aSurface, const nsIntRegion& aRegion, |
michael@0 | 409 | const nsIntPoint& aOffset, const gfxRGBA& aColor) |
michael@0 | 410 | { |
michael@0 | 411 | nsRefPtr<gfxContext> ctx = new gfxContext(aSurface); |
michael@0 | 412 | ctx->Translate(-gfxPoint(aOffset.x, aOffset.y)); |
michael@0 | 413 | gfxUtils::ClipToRegion(ctx, aRegion); |
michael@0 | 414 | ctx->SetColor(aColor); |
michael@0 | 415 | ctx->Paint(); |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | void |
michael@0 | 419 | ThebesLayerD3D9::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode, |
michael@0 | 420 | const nsTArray<ReadbackProcessor::Update>& aReadbackUpdates) |
michael@0 | 421 | { |
michael@0 | 422 | HRESULT hr; |
michael@0 | 423 | nsIntRect visibleRect = mVisibleRegion.GetBounds(); |
michael@0 | 424 | |
michael@0 | 425 | nsRefPtr<gfxASurface> destinationSurface; |
michael@0 | 426 | nsIntRect bounds = aRegion.GetBounds(); |
michael@0 | 427 | nsRefPtr<IDirect3DTexture9> tmpTexture; |
michael@0 | 428 | OpaqueRenderer opaqueRenderer(aRegion); |
michael@0 | 429 | OpaqueRenderer opaqueRendererOnWhite(aRegion); |
michael@0 | 430 | |
michael@0 | 431 | switch (aMode) |
michael@0 | 432 | { |
michael@0 | 433 | case SurfaceMode::SURFACE_OPAQUE: |
michael@0 | 434 | destinationSurface = opaqueRenderer.Begin(this); |
michael@0 | 435 | break; |
michael@0 | 436 | |
michael@0 | 437 | case SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA: { |
michael@0 | 438 | hr = device()->CreateTexture(bounds.width, bounds.height, 1, |
michael@0 | 439 | 0, D3DFMT_A8R8G8B8, |
michael@0 | 440 | D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), nullptr); |
michael@0 | 441 | |
michael@0 | 442 | if (FAILED(hr)) { |
michael@0 | 443 | ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr); |
michael@0 | 444 | return; |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | // XXX - We may consider retaining a SYSTEMMEM texture texture the size |
michael@0 | 448 | // of our DEFAULT texture and then use UpdateTexture and add dirty rects |
michael@0 | 449 | // to update in a single call. |
michael@0 | 450 | nsRefPtr<gfxWindowsSurface> dest = new gfxWindowsSurface( |
michael@0 | 451 | gfxIntSize(bounds.width, bounds.height), gfxImageFormat::ARGB32); |
michael@0 | 452 | // If the contents of this layer don't require component alpha in the |
michael@0 | 453 | // end of rendering, it's safe to enable Cleartype since all the Cleartype |
michael@0 | 454 | // glyphs must be over (or under) opaque pixels. |
michael@0 | 455 | dest->SetSubpixelAntialiasingEnabled(!(mContentFlags & CONTENT_COMPONENT_ALPHA)); |
michael@0 | 456 | destinationSurface = dest.forget(); |
michael@0 | 457 | break; |
michael@0 | 458 | } |
michael@0 | 459 | |
michael@0 | 460 | case SurfaceMode::SURFACE_COMPONENT_ALPHA: { |
michael@0 | 461 | nsRefPtr<gfxWindowsSurface> onBlack = opaqueRenderer.Begin(this); |
michael@0 | 462 | nsRefPtr<gfxWindowsSurface> onWhite = opaqueRendererOnWhite.Begin(this); |
michael@0 | 463 | if (onBlack && onWhite) { |
michael@0 | 464 | FillSurface(onBlack, aRegion, bounds.TopLeft(), gfxRGBA(0.0, 0.0, 0.0, 1.0)); |
michael@0 | 465 | FillSurface(onWhite, aRegion, bounds.TopLeft(), gfxRGBA(1.0, 1.0, 1.0, 1.0)); |
michael@0 | 466 | gfxASurface* surfaces[2] = { onBlack.get(), onWhite.get() }; |
michael@0 | 467 | destinationSurface = new gfxTeeSurface(surfaces, ArrayLength(surfaces)); |
michael@0 | 468 | // Using this surface as a source will likely go horribly wrong, since |
michael@0 | 469 | // only the onBlack surface will really be used, so alpha information will |
michael@0 | 470 | // be incorrect. |
michael@0 | 471 | destinationSurface->SetAllowUseAsSource(false); |
michael@0 | 472 | } |
michael@0 | 473 | break; |
michael@0 | 474 | } |
michael@0 | 475 | } |
michael@0 | 476 | |
michael@0 | 477 | if (!destinationSurface) |
michael@0 | 478 | return; |
michael@0 | 479 | |
michael@0 | 480 | nsRefPtr<gfxContext> context; |
michael@0 | 481 | if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(BackendType::CAIRO)) { |
michael@0 | 482 | RefPtr<DrawTarget> dt = |
michael@0 | 483 | gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(destinationSurface, |
michael@0 | 484 | IntSize(destinationSurface->GetSize().width, |
michael@0 | 485 | destinationSurface->GetSize().height)); |
michael@0 | 486 | |
michael@0 | 487 | context = new gfxContext(dt); |
michael@0 | 488 | } else { |
michael@0 | 489 | context = new gfxContext(destinationSurface); |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | context->Translate(gfxPoint(-bounds.x, -bounds.y)); |
michael@0 | 493 | LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); |
michael@0 | 494 | cbInfo.Callback(this, context, aRegion, DrawRegionClip::CLIP_NONE, nsIntRegion(), cbInfo.CallbackData); |
michael@0 | 495 | |
michael@0 | 496 | for (uint32_t i = 0; i < aReadbackUpdates.Length(); ++i) { |
michael@0 | 497 | NS_ASSERTION(aMode == SurfaceMode::SURFACE_OPAQUE, |
michael@0 | 498 | "Transparent surfaces should not be used for readback"); |
michael@0 | 499 | const ReadbackProcessor::Update& update = aReadbackUpdates[i]; |
michael@0 | 500 | nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset(); |
michael@0 | 501 | nsRefPtr<gfxContext> ctx = |
michael@0 | 502 | update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset, |
michael@0 | 503 | update.mSequenceCounter); |
michael@0 | 504 | if (ctx) { |
michael@0 | 505 | ctx->Translate(gfxPoint(offset.x, offset.y)); |
michael@0 | 506 | ctx->SetSource(destinationSurface, gfxPoint(bounds.x, bounds.y)); |
michael@0 | 507 | ctx->Paint(); |
michael@0 | 508 | update.mLayer->GetSink()->EndUpdate(ctx, update.mUpdateRect + offset); |
michael@0 | 509 | } |
michael@0 | 510 | } |
michael@0 | 511 | |
michael@0 | 512 | // Release the cairo d3d9 surface before we try to composite it |
michael@0 | 513 | context = nullptr; |
michael@0 | 514 | |
michael@0 | 515 | nsAutoTArray<IDirect3DTexture9*,2> srcTextures; |
michael@0 | 516 | nsAutoTArray<IDirect3DTexture9*,2> destTextures; |
michael@0 | 517 | switch (aMode) |
michael@0 | 518 | { |
michael@0 | 519 | case SurfaceMode::SURFACE_OPAQUE: |
michael@0 | 520 | // Must release reference to dest surface before ending drawing |
michael@0 | 521 | destinationSurface = nullptr; |
michael@0 | 522 | opaqueRenderer.End(); |
michael@0 | 523 | srcTextures.AppendElement(opaqueRenderer.GetTexture()); |
michael@0 | 524 | destTextures.AppendElement(mTexture); |
michael@0 | 525 | break; |
michael@0 | 526 | |
michael@0 | 527 | case SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA: { |
michael@0 | 528 | LockTextureRectD3D9 textureLock(tmpTexture); |
michael@0 | 529 | if (!textureLock.HasLock()) { |
michael@0 | 530 | NS_WARNING("Failed to lock ThebesLayer tmpTexture texture."); |
michael@0 | 531 | return; |
michael@0 | 532 | } |
michael@0 | 533 | |
michael@0 | 534 | D3DLOCKED_RECT r = textureLock.GetLockRect(); |
michael@0 | 535 | |
michael@0 | 536 | nsRefPtr<gfxImageSurface> imgSurface = |
michael@0 | 537 | new gfxImageSurface((unsigned char *)r.pBits, |
michael@0 | 538 | bounds.Size(), |
michael@0 | 539 | r.Pitch, |
michael@0 | 540 | gfxImageFormat::ARGB32); |
michael@0 | 541 | |
michael@0 | 542 | if (destinationSurface) { |
michael@0 | 543 | nsRefPtr<gfxContext> context = new gfxContext(imgSurface); |
michael@0 | 544 | context->SetSource(destinationSurface); |
michael@0 | 545 | context->SetOperator(gfxContext::OPERATOR_SOURCE); |
michael@0 | 546 | context->Paint(); |
michael@0 | 547 | } |
michael@0 | 548 | |
michael@0 | 549 | // Must release reference to dest surface before ending drawing |
michael@0 | 550 | destinationSurface = nullptr; |
michael@0 | 551 | imgSurface = nullptr; |
michael@0 | 552 | |
michael@0 | 553 | srcTextures.AppendElement(tmpTexture); |
michael@0 | 554 | destTextures.AppendElement(mTexture); |
michael@0 | 555 | break; |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | case SurfaceMode::SURFACE_COMPONENT_ALPHA: { |
michael@0 | 559 | // Must release reference to dest surface before ending drawing |
michael@0 | 560 | destinationSurface = nullptr; |
michael@0 | 561 | opaqueRenderer.End(); |
michael@0 | 562 | opaqueRendererOnWhite.End(); |
michael@0 | 563 | srcTextures.AppendElement(opaqueRenderer.GetTexture()); |
michael@0 | 564 | destTextures.AppendElement(mTexture); |
michael@0 | 565 | srcTextures.AppendElement(opaqueRendererOnWhite.GetTexture()); |
michael@0 | 566 | destTextures.AppendElement(mTextureOnWhite); |
michael@0 | 567 | break; |
michael@0 | 568 | } |
michael@0 | 569 | } |
michael@0 | 570 | NS_ASSERTION(srcTextures.Length() == destTextures.Length(), "Mismatched lengths"); |
michael@0 | 571 | |
michael@0 | 572 | |
michael@0 | 573 | // Copy to the texture. |
michael@0 | 574 | for (uint32_t i = 0; i < srcTextures.Length(); ++i) { |
michael@0 | 575 | nsRefPtr<IDirect3DSurface9> srcSurface; |
michael@0 | 576 | nsRefPtr<IDirect3DSurface9> dstSurface; |
michael@0 | 577 | |
michael@0 | 578 | destTextures[i]->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); |
michael@0 | 579 | srcTextures[i]->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); |
michael@0 | 580 | |
michael@0 | 581 | nsIntRegionRectIterator iter(aRegion); |
michael@0 | 582 | const nsIntRect *iterRect; |
michael@0 | 583 | while ((iterRect = iter.Next())) { |
michael@0 | 584 | RECT rect; |
michael@0 | 585 | rect.left = iterRect->x - bounds.x; |
michael@0 | 586 | rect.top = iterRect->y - bounds.y; |
michael@0 | 587 | rect.right = iterRect->XMost() - bounds.x; |
michael@0 | 588 | rect.bottom = iterRect->YMost() - bounds.y; |
michael@0 | 589 | |
michael@0 | 590 | POINT point; |
michael@0 | 591 | point.x = iterRect->x - visibleRect.x; |
michael@0 | 592 | point.y = iterRect->y - visibleRect.y; |
michael@0 | 593 | device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); |
michael@0 | 594 | } |
michael@0 | 595 | } |
michael@0 | 596 | } |
michael@0 | 597 | |
michael@0 | 598 | void |
michael@0 | 599 | ThebesLayerD3D9::CreateNewTextures(const gfx::IntSize &aSize, |
michael@0 | 600 | SurfaceMode aMode) |
michael@0 | 601 | { |
michael@0 | 602 | if (aSize.width == 0 || aSize.height == 0) { |
michael@0 | 603 | // Nothing to do. |
michael@0 | 604 | return; |
michael@0 | 605 | } |
michael@0 | 606 | |
michael@0 | 607 | mTexture = nullptr; |
michael@0 | 608 | mTextureOnWhite = nullptr; |
michael@0 | 609 | HRESULT hr = device()->CreateTexture(aSize.width, aSize.height, 1, |
michael@0 | 610 | D3DUSAGE_RENDERTARGET, |
michael@0 | 611 | aMode != SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8, |
michael@0 | 612 | D3DPOOL_DEFAULT, getter_AddRefs(mTexture), nullptr); |
michael@0 | 613 | if (FAILED(hr)) { |
michael@0 | 614 | ReportFailure(NS_LITERAL_CSTRING("ThebesLayerD3D9::CreateNewTextures(): Failed to create texture"), |
michael@0 | 615 | hr); |
michael@0 | 616 | return; |
michael@0 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) { |
michael@0 | 620 | hr = device()->CreateTexture(aSize.width, aSize.height, 1, |
michael@0 | 621 | D3DUSAGE_RENDERTARGET, |
michael@0 | 622 | D3DFMT_X8R8G8B8, |
michael@0 | 623 | D3DPOOL_DEFAULT, getter_AddRefs(mTextureOnWhite), nullptr); |
michael@0 | 624 | if (FAILED(hr)) { |
michael@0 | 625 | ReportFailure(NS_LITERAL_CSTRING("ThebesLayerD3D9::CreateNewTextures(): Failed to create texture (2)"), |
michael@0 | 626 | hr); |
michael@0 | 627 | return; |
michael@0 | 628 | } |
michael@0 | 629 | } |
michael@0 | 630 | } |
michael@0 | 631 | |
michael@0 | 632 | } /* namespace layers */ |
michael@0 | 633 | } /* namespace mozilla */ |