TileClient ClientTiledLayerBuffer::ValidateTile(TileClient aTile, const nsIntPoint& aTileOrigin, const nsIntRegion& aDirtyRegion) { PROFILER_LABEL("ClientTiledLayerBuffer", "ValidateTile"); #ifdef GFX_TILEDLAYER_PREF_WARNINGS if (aDirtyRegion.IsComplex()) { printf_stderr("Complex region\n"); } #endif if (aTile.IsPlaceholderTile()) { aTile.SetLayerManager(mManager); } // Discard our front and backbuffers if our contents changed. In this case // the calling code will already have taken care of invalidating the entire // layer. if (HasFormatChanged()) { aTile.DiscardBackBuffer(); aTile.DiscardFrontBuffer(); } bool createdTextureClient = false; nsIntRegion offsetDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin); bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget; RefPtr<TextureClient> backBuffer = aTile.GetBackBuffer(offsetDirtyRegion, mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())), &createdTextureClient, !usingSinglePaintBuffer); if (!backBuffer->Lock(OPEN_READ_WRITE)) { NS_WARNING("Failed to lock tile TextureClient for updating."); aTile.DiscardFrontBuffer(); return aTile; } // We must not keep a reference to the DrawTarget after it has been unlocked, // make sure these are null'd before unlocking as destruction of the context // may cause the target to be flushed. RefPtr<DrawTarget> drawTarget = backBuffer->AsTextureClientDrawTarget()->GetAsDrawTarget(); drawTarget->SetTransform(Matrix()); RefPtr<gfxContext> ctxt = new gfxContext(drawTarget); if (usingSinglePaintBuffer) { // XXX Perhaps we should just copy the bounding rectangle here? RefPtr<gfx::SourceSurface> source = mSinglePaintDrawTarget->Snapshot(); nsIntRegionRectIterator it(aDirtyRegion); for (const nsIntRect* dirtyRect = it.Next(); dirtyRect != nullptr; dirtyRect = it.Next()) { #ifdef GFX_TILEDLAYER_PREF_WARNINGS printf_stderr(" break into subdirtyRect %i, %i, %i, %i\n", dirtyRect->x, dirtyRect->y, dirtyRect->width, dirtyRect->height); #endif gfx::Rect drawRect(dirtyRect->x - aTileOrigin.x, dirtyRect->y - aTileOrigin.y, dirtyRect->width, dirtyRect->height); drawRect.Scale(mResolution); gfx::IntRect copyRect(NS_roundf((dirtyRect->x - mSinglePaintBufferOffset.x) * mResolution), NS_roundf((dirtyRect->y - mSinglePaintBufferOffset.y) * mResolution), drawRect.width, drawRect.height); gfx::IntPoint copyTarget(NS_roundf(drawRect.x), NS_roundf(drawRect.y)); drawTarget->CopySurface(source, copyRect, copyTarget); // Mark the newly updated area as invalid in the front buffer aTile.mInvalidFront.Or(aTile.mInvalidFront, nsIntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height)); } // The new buffer is now validated, remove the dirty region from it. aTile.mInvalidBack.Sub(nsIntRect(0, 0, TILEDLAYERBUFFER_TILE_SIZE, TILEDLAYERBUFFER_TILE_SIZE), offsetDirtyRegion); } else { // Area of the full tile... nsIntRegion tileRegion = nsIntRect(aTileOrigin.x, aTileOrigin.y, TILEDLAYERBUFFER_TILE_SIZE, TILEDLAYERBUFFER_TILE_SIZE); // Intersect this area with the portion that's dirty. tileRegion = tileRegion.Intersect(aDirtyRegion); // Move invalid areas into layer space. aTile.mInvalidFront.MoveBy(aTileOrigin); aTile.mInvalidBack.MoveBy(aTileOrigin); // Add the area that's going to be redrawn to the invalid area of the // front region. aTile.mInvalidFront.Or(aTile.mInvalidFront, tileRegion); // Add invalid areas of the backbuffer to the area to redraw. tileRegion.Or(tileRegion, aTile.mInvalidBack); // Move invalid areas back into tile space. aTile.mInvalidFront.MoveBy(-aTileOrigin); // This will be validated now. aTile.mInvalidBack.SetEmpty(); nsIntRect bounds = tileRegion.GetBounds(); bounds.ScaleRoundOut(mResolution, mResolution); bounds.MoveBy(-aTileOrigin); if (GetContentType() != gfxContentType::COLOR) { drawTarget->ClearRect(Rect(bounds.x, bounds.y, bounds.width, bounds.height)); } ctxt->NewPath(); ctxt->Clip(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)); ctxt->Scale(mResolution, mResolution); ctxt->Translate(gfxPoint(-aTileOrigin.x, -aTileOrigin.y)); mCallback(mThebesLayer, ctxt, tileRegion.GetBounds(), DrawRegionClip::CLIP_NONE, nsIntRegion(), mCallbackData); } #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution, aTileOrigin.y * mResolution, GetTileLength(), GetTileLength()); #endif ctxt = nullptr; drawTarget = nullptr; backBuffer->Unlock(); aTile.Flip(); if (createdTextureClient) { if (!mCompositableClient->AddTextureClient(backBuffer)) { NS_WARNING("Failed to add tile TextureClient."); aTile.DiscardFrontBuffer(); aTile.DiscardBackBuffer(); return aTile; } } // Note, we don't call UpdatedTexture. The Updated function is called manually // by the TiledContentHost before composition. if (backBuffer->HasInternalBuffer()) { // If our new buffer has an internal buffer, we don't want to keep another // TextureClient around unnecessarily, so discard the back-buffer. aTile.DiscardBackBuffer(); } return aTile; }
// |aTexCoordRect| is the rectangle from the texture that we want to // draw using the given program. The program already has a necessary // offset and scale, so the geometry that needs to be drawn is a unit // square from 0,0 to 1,1. // // |aTexSize| is the actual size of the texture, as it can be larger // than the rectangle given by |aTexCoordRect|. void CompositorOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg, const Rect& aTexCoordRect, TextureSource *aTexture) { NS_ASSERTION(aProg->HasInitialized(), "Shader program not correctly initialized"); GLuint vertAttribIndex = aProg->AttribLocation(ShaderProgramOGL::VertexCoordAttrib); GLuint texCoordAttribIndex = aProg->AttribLocation(ShaderProgramOGL::TexCoordAttrib); NS_ASSERTION(texCoordAttribIndex != GLuint(-1), "no texture coords?"); // Given what we know about these textures and coordinates, we can // compute fmod(t, 1.0f) to get the same texture coordinate out. If // the texCoordRect dimension is < 0 or > width/height, then we have // wraparound that we need to deal with by drawing multiple quads, // because we can't rely on full non-power-of-two texture support // (which is required for the REPEAT wrap mode). GLContext::RectTriangles rects; GLenum wrapMode = aTexture->AsSourceOGL()->GetWrapMode(); IntSize realTexSize = aTexture->GetSize(); if (!mGLContext->CanUploadNonPowerOfTwo()) { realTexSize = IntSize(NextPowerOfTwo(realTexSize.width), NextPowerOfTwo(realTexSize.height)); } // We need to convert back to actual texels here to get proper behaviour with // our GL helper functions. Should fix this sometime. // I want to vomit. IntRect texCoordRect = IntRect(NS_roundf(aTexCoordRect.x * aTexture->GetSize().width), NS_roundf(aTexCoordRect.y * aTexture->GetSize().height), NS_roundf(aTexCoordRect.width * aTexture->GetSize().width), NS_roundf(aTexCoordRect.height * aTexture->GetSize().height)); // This is fairly disgusting - if the texture should be flipped it will have a // negative height, in which case we un-invert the texture coords and pass the // flipped 'flag' to the functions below. We can't just use the inverted coords // because our GL funtions use an explicit flag. bool flipped = false; if (texCoordRect.height < 0) { flipped = true; texCoordRect.y = texCoordRect.YMost(); texCoordRect.height = -texCoordRect.height; } if (wrapMode == LOCAL_GL_REPEAT) { rects.addRect(/* dest rectangle */ 0.0f, 0.0f, 1.0f, 1.0f, /* tex coords */ texCoordRect.x / GLfloat(realTexSize.width), texCoordRect.y / GLfloat(realTexSize.height), texCoordRect.XMost() / GLfloat(realTexSize.width), texCoordRect.YMost() / GLfloat(realTexSize.height), flipped); } else { nsIntRect tcRect(texCoordRect.x, texCoordRect.y, texCoordRect.width, texCoordRect.height); GLContext::DecomposeIntoNoRepeatTriangles(tcRect, nsIntSize(realTexSize.width, realTexSize.height), rects, flipped); } DrawWithVertexBuffer2(mGLContext, mVBOs, LOCAL_GL_TRIANGLES, rects.elements(), vertAttribIndex, rects.vertexPointer(), texCoordAttribIndex, rects.texCoordPointer()); }