diff --git a/gfx/gl/GLUploadHelpers.cpp b/gfx/gl/GLUploadHelpers.cpp index 6080eb00d3ae..3532ff1a0846 100644 --- a/gfx/gl/GLUploadHelpers.cpp +++ b/gfx/gl/GLUploadHelpers.cpp @@ -126,13 +126,20 @@ static void TexSubImage2DWithUnpackSubimageGLES( // row. We only upload the first height-1 rows using GL_UNPACK_ROW_LENGTH, // and then we upload the final row separately. See bug 697990. int rowLength = stride / pixelsize; - gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); - gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height - 1, format, - type, pixels); - gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); - gl->fTexSubImage2D(target, level, xoffset, yoffset + height - 1, width, 1, - format, type, - (const unsigned char*)pixels + (height - 1) * stride); + if (gl->HasPBOState()) { + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); + gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, + type, pixels); + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); + } else { + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, rowLength); + gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height - 1, + format, type, pixels); + gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0); + gl->fTexSubImage2D(target, level, xoffset, yoffset + height - 1, width, 1, + format, type, + (const unsigned char*)pixels + (height - 1) * stride); + } gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); } @@ -211,7 +218,8 @@ static void TexSubImage2DHelper(GLContext* gl, GLenum target, GLint level, gl->fTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); - } else if (gl->IsExtensionSupported(GLContext::EXT_unpack_subimage)) { + } else if (gl->IsExtensionSupported(GLContext::EXT_unpack_subimage) || + gl->HasPBOState()) { TexSubImage2DWithUnpackSubimageGLES(gl, target, level, xoffset, yoffset, width, height, stride, pixelsize, format, type, pixels); diff --git a/gfx/layers/opengl/TextureHostOGL.h b/gfx/layers/opengl/TextureHostOGL.h index 70fbfe56a4d6..35683dcea0c3 100644 --- a/gfx/layers/opengl/TextureHostOGL.h +++ b/gfx/layers/opengl/TextureHostOGL.h @@ -195,6 +195,8 @@ class TextureImageTextureSourceOGL final : public DataTextureSource, bool NextTile() override { return mTexImage->NextTile(); } + gl::GLContext* gl() const { return mGL; } + protected: ~TextureImageTextureSourceOGL(); diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp index e83d87b32d5c..d88c61c05662 100644 --- a/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp +++ b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp @@ -249,16 +249,10 @@ LayoutDeviceIntSize RenderCompositorOGLSWGL::GetBufferSize() { UniquePtr RenderCompositorOGLSWGL::DoCreateTile(Surface* aSurface) { - const auto tileSize = aSurface->TileSize(); - - RefPtr source = new TextureImageTextureSourceOGL( + auto source = MakeRefPtr( mCompositor->AsCompositorOGL(), layers::TextureFlags::NO_FLAGS); - RefPtr surf = gfx::Factory::CreateDataSourceSurface( - gfx::IntSize(tileSize.width, tileSize.height), - gfx::SurfaceFormat::B8G8R8A8); - - return MakeUnique(source, surf); + return MakeUnique(std::move(source), aSurface->TileSize()); } bool RenderCompositorOGLSWGL::MaybeReadback( @@ -283,27 +277,133 @@ bool RenderCompositorOGLSWGL::MaybeReadback( return true; } -RenderCompositorOGLSWGL::TileOGL::TileOGL(layers::DataTextureSource* aTexture, - gfx::DataSourceSurface* aSurface) - : Tile(), mTexture(aTexture), mSurface(aSurface) {} +// This is a DataSourceSurface that represents a 0-based PBO for GLTextureImage. +class PBOUnpackSurface : public gfx::DataSourceSurface { + public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PBOUnpackSurface, override) + + explicit PBOUnpackSurface(const gfx::IntSize& aSize) : mSize(aSize) {} + + uint8_t* GetData() override { return nullptr; } + int32_t Stride() override { return mSize.width * sizeof(uint32_t); } + gfx::SurfaceType GetType() const override { + return gfx::SurfaceType::DATA_ALIGNED; + } + gfx::IntSize GetSize() const override { return mSize; } + gfx::SurfaceFormat GetFormat() const override { + return gfx::SurfaceFormat::B8G8R8A8; + } + + // PBO offsets need to start from a 0 address, but DataSourceSurface::Map + // checks for failure by comparing the address against nullptr. Override Map + // to work around this. Due to DataSourceSurface::Map checking for failure via + bool Map(MapType, MappedSurface* aMappedSurface) override { + aMappedSurface->mData = GetData(); + aMappedSurface->mStride = Stride(); + return true; + } + + void Unmap() override {} + + private: + gfx::IntSize mSize; +}; + +RenderCompositorOGLSWGL::TileOGL::TileOGL( + RefPtr&& aTexture, + const gfx::IntSize& aSize) + : mTexture(aTexture) { + auto* gl = mTexture->gl(); + if (gl && gl->HasPBOState() && gl->MakeCurrent()) { + mSurface = new PBOUnpackSurface(aSize); + // Create a PBO large enough to encompass any valid rects within the tile. + gl->fGenBuffers(1, &mPBO); + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO); + gl->fBufferData(LOCAL_GL_PIXEL_UNPACK_BUFFER, + mSurface->Stride() * aSize.height, nullptr, + LOCAL_GL_DYNAMIC_DRAW); + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); + } else { + // Couldn't allocate a PBO, so just use a memory surface instead. + mSurface = gfx::Factory::CreateDataSourceSurface( + aSize, gfx::SurfaceFormat::B8G8R8A8); + } +} + +RenderCompositorOGLSWGL::TileOGL::~TileOGL() { + if (mPBO) { + auto* gl = mTexture->gl(); + if (gl && gl->MakeCurrent()) { + gl->fDeleteBuffers(1, &mPBO); + mPBO = 0; + } + } +} + +layers::DataTextureSource* +RenderCompositorOGLSWGL::TileOGL::GetTextureSource() { + return mTexture.get(); +} bool RenderCompositorOGLSWGL::TileOGL::Map(wr::DeviceIntRect aDirtyRect, wr::DeviceIntRect aValidRect, void** aData, int32_t* aStride) { - gfx::DataSourceSurface::MappedSurface map; - if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) { - return false; + if (mPBO) { + auto* gl = mTexture->gl(); + if (!gl) { + return false; + } + // Map the PBO, but only within the range of the buffer that spans from the + // linear start offset to the linear end offset. Since we don't care about + // the previous contents of the buffer, we can just tell OpenGL to + // invalidate the entire buffer, even though we're only mapping a sub-range. + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO); + size_t stride = mSurface->Stride(); + size_t offset = + stride * aValidRect.origin.y + aValidRect.origin.x * sizeof(uint32_t); + size_t length = stride * (aValidRect.size.height - 1) + + aValidRect.size.width * sizeof(uint32_t); + void* data = gl->fMapBufferRange( + LOCAL_GL_PIXEL_UNPACK_BUFFER, offset, length, + LOCAL_GL_MAP_WRITE_BIT | LOCAL_GL_MAP_INVALIDATE_BUFFER_BIT); + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); + if (!data) { + return false; + } + *aData = data; + *aStride = stride; + } else { + // No PBO is available, so just directly write to the memory surface. + gfx::DataSourceSurface::MappedSurface map; + if (!mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) { + return false; + } + // Verify that we're not somehow using a PBOUnpackSurface. + MOZ_ASSERT(map.mData != nullptr); + *aData = map.mData + aValidRect.origin.y * map.mStride + + aValidRect.origin.x * sizeof(uint32_t); + *aStride = map.mStride; } - *aData = - map.mData + aValidRect.origin.y * map.mStride + aValidRect.origin.x * 4; - *aStride = map.mStride; return true; } void RenderCompositorOGLSWGL::TileOGL::Unmap(const gfx::IntRect& aDirtyRect) { - mSurface->Unmap(); nsIntRegion dirty(aDirtyRect); - mTexture->Update(mSurface, &dirty); + if (mPBO) { + // If there is a PBO, it must be unmapped before it can be sourced from. + // Leave the PBO bound before the call to Update so that the texture uploads + // will source from it. + auto* gl = mTexture->gl(); + if (!gl) { + return; + } + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, mPBO); + gl->fUnmapBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER); + mTexture->Update(mSurface, &dirty); + gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); + } else { + mTexture->Update(mSurface, &dirty); + } } } // namespace wr diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.h b/gfx/webrender_bindings/RenderCompositorOGLSWGL.h index 55605b9be374..cd8e4f323f2b 100644 --- a/gfx/webrender_bindings/RenderCompositorOGLSWGL.h +++ b/gfx/webrender_bindings/RenderCompositorOGLSWGL.h @@ -12,6 +12,10 @@ namespace mozilla { +namespace layers { +class TextureImageTextureSourceOGL; +} + namespace wr { class RenderCompositorOGLSWGL : public RenderCompositorLayersSWGL { @@ -60,19 +64,20 @@ class RenderCompositorOGLSWGL : public RenderCompositorLayersSWGL { class TileOGL : public RenderCompositorLayersSWGL::Tile { public: - TileOGL(layers::DataTextureSource* aTexture, - gfx::DataSourceSurface* aSurface); - virtual ~TileOGL() = default; + TileOGL(RefPtr&& aTexture, + const gfx::IntSize& aSize); + virtual ~TileOGL(); bool Map(wr::DeviceIntRect aDirtyRect, wr::DeviceIntRect aValidRect, void** aData, int32_t* aStride) override; void Unmap(const gfx::IntRect& aDirtyRect) override; - layers::DataTextureSource* GetTextureSource() override { return mTexture; } + layers::DataTextureSource* GetTextureSource() override; bool IsValid() override { return true; } private: - RefPtr mTexture; + RefPtr mTexture; RefPtr mSurface; + GLuint mPBO = 0; }; };