SourceSurfaceWebgl.cpp (7968B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "SourceSurfaceWebgl.h" 8 9 #include "DrawTargetWebglInternal.h" 10 #include "WebGLBuffer.h" 11 #include "mozilla/gfx/Swizzle.h" 12 13 namespace mozilla::gfx { 14 15 SourceSurfaceWebgl::SourceSurfaceWebgl(DrawTargetWebgl* aDT) 16 : mFormat(aDT->GetFormat()), 17 mSize(aDT->GetSize()), 18 mDT(aDT), 19 mSharedContext(aDT->mSharedContext) {} 20 21 SourceSurfaceWebgl::SourceSurfaceWebgl( 22 const RefPtr<SharedContextWebgl>& aSharedContext) 23 : mSharedContext(aSharedContext) {} 24 25 SourceSurfaceWebgl::~SourceSurfaceWebgl() { 26 if (mHandle) { 27 // Signal that the texture handle is not being used now. 28 mHandle->ClearSurface(); 29 } 30 if (mReadBuffer) { 31 if (RefPtr<SharedContextWebgl> sharedContext = {mSharedContext}) { 32 sharedContext->RemoveSnapshotPBO(this, mReadBuffer.forget()); 33 } 34 mReadBuffer = nullptr; 35 } 36 } 37 38 // Read back the contents of the target or texture handle for data use. This 39 // may attempt a readback into a PBO for performance, unless forced to 40 // immediately read into data. 41 inline bool SourceSurfaceWebgl::EnsureData(bool aForce, uint8_t* aData, 42 int32_t aStride) { 43 if (mData) { 44 if (aData) { 45 DataSourceSurface::ScopedMap map(mData, MapType::READ); 46 if (!map.IsMapped()) { 47 return false; 48 } 49 SwizzleData(map.GetData(), map.GetStride(), mFormat, aData, aStride, 50 mFormat, mSize); 51 } 52 return true; 53 } 54 55 if (mReadBuffer) { 56 // If there is a pending PBO readback, then copy the contents of the PBO. 57 if (RefPtr<SharedContextWebgl> sharedContext = {mSharedContext}) { 58 mData = sharedContext->ReadSnapshotFromPBO(mReadBuffer, mFormat, mSize, 59 aData, aStride); 60 mOwnsData = !mData || !aData; 61 sharedContext->RemoveSnapshotPBO(this, mReadBuffer.forget()); 62 } 63 mReadBuffer = nullptr; 64 return !!mData; 65 } 66 67 if (RefPtr<DrawTargetWebgl> dt = {mDT}) { 68 if (!aForce) { 69 mReadBuffer = dt->ReadSnapshotIntoPBO(this); 70 } 71 if (!mReadBuffer) { 72 mData = dt->ReadSnapshot(aData, aStride); 73 mOwnsData = !mData || !aData; 74 } 75 } else if (mHandle) { 76 // Assume that the target changed, so there should be a texture handle 77 // holding a copy. Try to read data from the copy since we can't read 78 // from the target. 79 if (RefPtr<SharedContextWebgl> sharedContext = {mSharedContext}) { 80 if (!aForce) { 81 mReadBuffer = sharedContext->ReadSnapshotIntoPBO(this, mHandle); 82 } 83 if (!mReadBuffer) { 84 mData = sharedContext->ReadSnapshot(mHandle, aData, aStride); 85 mOwnsData = !mData || !aData; 86 } 87 } 88 } 89 return mData || mReadBuffer; 90 } 91 92 bool SourceSurfaceWebgl::ReadDataInto(uint8_t* aData, int32_t aStride) { 93 return EnsureData(true, aData, aStride); 94 } 95 96 bool SourceSurfaceWebgl::ForceReadFromPBO() { 97 if (mReadBuffer && EnsureData()) { 98 MOZ_ASSERT(!mReadBuffer); 99 return true; 100 } 101 return false; 102 } 103 104 uint8_t* SourceSurfaceWebgl::GetData() { 105 if (!EnsureData()) { 106 return nullptr; 107 } 108 if (!mOwnsData) { 109 mData = Factory::CopyDataSourceSurface(mData); 110 mOwnsData = true; 111 } 112 return mData ? mData->GetData() : nullptr; 113 } 114 115 int32_t SourceSurfaceWebgl::Stride() { 116 if (!EnsureData()) { 117 return 0; 118 } 119 return mData->Stride(); 120 } 121 122 bool SourceSurfaceWebgl::Map(MapType aType, MappedSurface* aMappedSurface) { 123 if (!EnsureData()) { 124 return false; 125 } 126 if (!mOwnsData && aType != MapType::READ) { 127 mData = Factory::CopyDataSourceSurface(mData); 128 mOwnsData = true; 129 } 130 return mData && mData->Map(aType, aMappedSurface); 131 } 132 133 void SourceSurfaceWebgl::Unmap() { 134 if (mData) { 135 mData->Unmap(); 136 } 137 } 138 139 // Handler for when the owner DrawTargetWebgl is about to modify its internal 140 // framebuffer, and so this snapshot must be copied into a new texture, if 141 // possible, or read back into data, if necessary, to preserve this particular 142 // version of the framebuffer. 143 void SourceSurfaceWebgl::DrawTargetWillChange(bool aNeedHandle) { 144 RefPtr<DrawTargetWebgl> dt(mDT); 145 if (!dt) { 146 MOZ_ASSERT_UNREACHABLE("No DrawTargetWebgl for SourceSurfaceWebgl"); 147 return; 148 } 149 // Only try to copy into a new texture handle if we don't already have data. 150 // However, we still might need to immediately draw this snapshot to a WebGL 151 // target, which would require a subsequent upload, so also copy into a new 152 // handle even if we already have data in that case since it is faster than 153 // uploading. 154 if ((aNeedHandle || (!mData && !mReadBuffer)) && !mHandle) { 155 // Prefer copying the framebuffer to a texture if possible. 156 mHandle = dt->CopySnapshot(); 157 if (mHandle) { 158 // Link this surface to the handle. 159 mHandle->SetSurface(this); 160 } else { 161 // If that fails, then try to just read the data to a surface. 162 EnsureData(false); 163 } 164 } 165 mDT = nullptr; 166 } 167 168 // Handler for when the owner DrawTargetWebgl is itself being destroyed and 169 // needs to transfer ownership of its internal backing texture to the snapshot. 170 void SourceSurfaceWebgl::GiveTexture(RefPtr<TextureHandle> aHandle) { 171 // If we get here, then the target still points to this surface as its 172 // snapshot and needs to hand off its backing texture before it is destroyed. 173 MOZ_ASSERT(mDT); 174 MOZ_ASSERT(!mHandle); 175 mHandle = aHandle.forget(); 176 mHandle->SetSurface(this); 177 mDT = nullptr; 178 } 179 180 void SourceSurfaceWebgl::SetHandle(TextureHandle* aHandle) { 181 MOZ_ASSERT(!mHandle); 182 mFormat = aHandle->GetFormat(); 183 mSize = aHandle->GetSize(); 184 mHandle = aHandle; 185 mHandle->SetSurface(this); 186 } 187 188 // Handler for when the owner DrawTargetWebgl is destroying the cached texture 189 // handle that has been allocated for this snapshot, or if the surface has 190 // uploaded data. 191 void SourceSurfaceWebgl::OnUnlinkTexture(SharedContextWebgl* aContext, 192 TextureHandle* aHandle, bool aForce) { 193 // If the snapshot was mapped before the target changed, we may have read 194 // data instead of holding a copied texture handle. If subsequently we then 195 // try to draw with this snapshot, we might have allocated an external texture 196 // handle in the texture cache that still links to this snapshot and can cause 197 // us to end up here inside OnUnlinkTexture. 198 if (mHandle != aHandle) { 199 return; 200 } 201 if (!mData && !mReadBuffer) { 202 if (!aForce) { 203 mReadBuffer = aContext->ReadSnapshotIntoPBO(this, mHandle); 204 } 205 if (!mReadBuffer) { 206 mData = aContext->ReadSnapshot(mHandle); 207 mOwnsData = true; 208 } 209 } 210 mHandle = nullptr; 211 } 212 213 already_AddRefed<SourceSurface> SourceSurfaceWebgl::ExtractSubrect( 214 const IntRect& aRect) { 215 // Ensure the subrect is actually in bounds. 216 if (aRect.IsEmpty() || !GetRect().Contains(aRect)) { 217 return nullptr; 218 } 219 RefPtr<TextureHandle> subHandle; 220 RefPtr<SharedContextWebgl> sharedContext; 221 if (RefPtr<DrawTargetWebgl> dt = {mDT}) { 222 // If this is still a snapshot linked to a target, then copy from the 223 // target. 224 subHandle = dt->CopySnapshot(aRect); 225 sharedContext = dt->mSharedContext; 226 } else if (mHandle) { 227 // Otherwise, we have a handle, but we need to verify it is still linked to 228 // a valid context. 229 sharedContext = mSharedContext; 230 if (sharedContext) { 231 // Try to copy directly from the handle using the context. 232 subHandle = sharedContext->CopySnapshot(aRect, mHandle); 233 } 234 } 235 if (subHandle && sharedContext) { 236 RefPtr<SourceSurfaceWebgl> surface = new SourceSurfaceWebgl(sharedContext); 237 surface->SetHandle(subHandle); 238 return surface.forget(); 239 } 240 return nullptr; 241 } 242 243 } // namespace mozilla::gfx