RenderCompositorSWGL.cpp (11716B)
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 "RenderCompositorSWGL.h" 8 9 #include "mozilla/gfx/Logging.h" 10 #include "mozilla/widget/CompositorWidget.h" 11 12 #ifdef MOZ_WIDGET_GTK 13 # include "mozilla/WidgetUtilsGtk.h" 14 #endif 15 16 namespace mozilla { 17 using namespace gfx; 18 19 namespace wr { 20 21 extern LazyLogModule gRenderThreadLog; 22 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__)) 23 24 /* static */ 25 UniquePtr<RenderCompositor> RenderCompositorSWGL::Create( 26 const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) { 27 void* ctx = wr_swgl_create_context(); 28 if (!ctx) { 29 gfxCriticalNote << "Failed SWGL context creation for WebRender"; 30 return nullptr; 31 } 32 return MakeUnique<RenderCompositorSWGL>(aWidget, ctx); 33 } 34 35 RenderCompositorSWGL::RenderCompositorSWGL( 36 const RefPtr<widget::CompositorWidget>& aWidget, void* aContext) 37 : RenderCompositor(aWidget), mContext(aContext) { 38 MOZ_ASSERT(mContext); 39 LOG("RenderCompositorSWGL::RenderCompositorSWGL()"); 40 } 41 42 RenderCompositorSWGL::~RenderCompositorSWGL() { 43 LOG("RenderCompositorSWGL::~RenderCompositorSWGL()"); 44 45 wr_swgl_destroy_context(mContext); 46 } 47 48 void RenderCompositorSWGL::ClearMappedBuffer() { 49 mMappedData = nullptr; 50 mMappedStride = 0; 51 mDT = nullptr; 52 } 53 54 bool RenderCompositorSWGL::MakeCurrent() { 55 wr_swgl_make_current(mContext); 56 return true; 57 } 58 59 bool RenderCompositorSWGL::BeginFrame() { 60 mRenderWidgetSize = Some(mWidget->GetClientSize()); 61 #ifdef MOZ_WIDGET_GTK 62 if (mLastRenderWidgetSize != mRenderWidgetSize.value()) { 63 mLastRenderWidgetSize = mRenderWidgetSize.value(); 64 mRequestFullRender = true; 65 } 66 #endif 67 // Set up a temporary region representing the entire window surface in case a 68 // dirty region is not supplied. 69 ClearMappedBuffer(); 70 mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize()); 71 wr_swgl_make_current(mContext); 72 return true; 73 } 74 75 bool RenderCompositorSWGL::AllocateMappedBuffer( 76 const wr::DeviceIntRect* aOpaqueRects, size_t aNumOpaqueRects) { 77 // Request a new draw target to use from the widget... 78 MOZ_ASSERT(!mDT); 79 mDT = mWidget->StartRemoteDrawingInRegion(mDirtyRegion); 80 if (!mDT) { 81 #if !defined(MOZ_WAYLAND) 82 gfxCriticalNoteOnce 83 << "RenderCompositorSWGL failed mapping default framebuffer, no dt"; 84 #endif 85 return false; 86 } 87 // Attempt to lock the underlying buffer directly from the draw target. 88 // Verify that the size at least matches what the widget claims and that 89 // the format is BGRA8 as SWGL requires. 90 uint8_t* data = nullptr; 91 gfx::IntSize size; 92 int32_t stride = 0; 93 gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN; 94 if (!mSurface && mDT->LockBits(&data, &size, &stride, &format) && 95 (format != gfx::SurfaceFormat::B8G8R8A8 && 96 format != gfx::SurfaceFormat::B8G8R8X8)) { 97 // We tried to lock the DT and it succeeded, but the size or format 98 // of the data is not compatible, so just release it and fall back below... 99 mDT->ReleaseBits(data); 100 data = nullptr; 101 } 102 LayoutDeviceIntRect bounds = mDirtyRegion.GetBounds(); 103 // If locking succeeded above, just use that. 104 if (data) { 105 mMappedData = data; 106 mMappedStride = stride; 107 // Disambiguate whether the widget's draw target has its origin at zero or 108 // if it is offset to the dirty region origin. The DT might either enclose 109 // only the region itself, the region including the origin, or the entire 110 // widget. Thus, if the DT doesn't only enclose the region, we assume it 111 // contains the origin. 112 if (size != bounds.Size().ToUnknownSize()) { 113 // Update the bounds to include zero if the origin is at zero. 114 bounds.ExpandToEnclose(LayoutDeviceIntPoint(0, 0)); 115 } 116 // Sometimes we end up racing on the widget size, and it can shrink between 117 // BeginFrame and StartCompositing. We calculated our dirty region based on 118 // the previous widget size, so we need to clamp the bounds here to ensure 119 // we remain within the buffer. 120 bounds.IntersectRect( 121 bounds, 122 LayoutDeviceIntRect(bounds.TopLeft(), 123 LayoutDeviceIntSize(size.width, size.height))); 124 } else { 125 // If we couldn't lock the DT above, then allocate a data surface and map 126 // that for usage with SWGL. 127 size = bounds.Size().ToUnknownSize(); 128 if (!mSurface || mSurface->GetSize() != size) { 129 mSurface = gfx::Factory::CreateDataSourceSurface( 130 size, gfx::SurfaceFormat::B8G8R8A8); 131 } 132 gfx::DataSourceSurface::MappedSurface map = {nullptr, 0}; 133 if (!mSurface || !mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) { 134 // We failed mapping the data surface, so need to cancel the frame. 135 mWidget->EndRemoteDrawingInRegion(mDT, mDirtyRegion); 136 ClearMappedBuffer(); 137 gfxCriticalNoteOnce 138 << "RenderCompositorSWGL failed mapping default framebuffer, no surf"; 139 return false; 140 } 141 mMappedData = map.mData; 142 mMappedStride = map.mStride; 143 } 144 MOZ_ASSERT(mMappedData != nullptr && mMappedStride > 0); 145 wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width, 146 bounds.height, mMappedStride, mMappedData); 147 148 LayoutDeviceIntRegion opaque; 149 for (size_t i = 0; i < aNumOpaqueRects; i++) { 150 const auto& rect = aOpaqueRects[i]; 151 opaque.OrWith(LayoutDeviceIntRect(rect.min.x, rect.min.y, rect.width(), 152 rect.height())); 153 } 154 155 LayoutDeviceIntRegion clear = mWidget->GetTransparentRegion(); 156 clear.AndWith(mDirtyRegion); 157 clear.SubOut(opaque); 158 for (auto iter = clear.RectIter(); !iter.Done(); iter.Next()) { 159 const auto& rect = iter.Get(); 160 wr_swgl_clear_color_rect(mContext, 0, rect.x, rect.y, rect.width, 161 rect.height, 0, 0, 0, 0); 162 } 163 164 return true; 165 } 166 167 void RenderCompositorSWGL::StartCompositing( 168 wr::ColorF aClearColor, const wr::DeviceIntRect* aDirtyRects, 169 size_t aNumDirtyRects, const wr::DeviceIntRect* aOpaqueRects, 170 size_t aNumOpaqueRects) { 171 if (mDT) { 172 // Cancel any existing buffers that might accidentally be left from updates 173 CommitMappedBuffer(false); 174 // Reset the region to the widget bounds 175 mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize()); 176 } 177 if (aNumDirtyRects) { 178 // Install the dirty rects into the bounds of the existing region 179 auto bounds = mDirtyRegion.GetBounds(); 180 mDirtyRegion.SetEmpty(); 181 for (size_t i = 0; i < aNumDirtyRects; i++) { 182 const auto& rect = aDirtyRects[i]; 183 mDirtyRegion.OrWith(LayoutDeviceIntRect(rect.min.x, rect.min.y, 184 rect.width(), rect.height())); 185 } 186 // Ensure the region lies within the widget bounds 187 mDirtyRegion.AndWith(bounds); 188 } 189 // Now that the dirty rects have been supplied and the composition region 190 // is known, allocate and install a framebuffer encompassing the composition 191 // region. 192 if (mDirtyRegion.IsEmpty() || 193 !AllocateMappedBuffer(aOpaqueRects, aNumOpaqueRects)) { 194 // If allocation of the mapped default framebuffer failed, then just install 195 // a temporary framebuffer (with a minimum size of 2x2) so compositing can 196 // still proceed. 197 auto bounds = mDirtyRegion.GetBounds(); 198 bounds.width = std::max(bounds.width, 2); 199 bounds.height = std::max(bounds.height, 2); 200 wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width, 201 bounds.height, 0, nullptr); 202 } 203 } 204 205 void RenderCompositorSWGL::CommitMappedBuffer(bool aDirty) { 206 if (!mDT) { 207 mDirtyRegion.SetEmpty(); 208 return; 209 } 210 // Force any delayed clears to resolve. 211 if (aDirty) { 212 wr_swgl_resolve_framebuffer(mContext, 0); 213 } 214 // Clear out the old framebuffer in case something tries to access it after 215 // the frame. 216 wr_swgl_init_default_framebuffer(mContext, 0, 0, 0, 0, 0, nullptr); 217 // If we have a draw target at this point, mapping must have succeeded. 218 MOZ_ASSERT(mMappedData != nullptr); 219 if (mSurface) { 220 // If we're using a data surface, unmap it and draw it to the DT if there 221 // are any supplied dirty rects. 222 mSurface->Unmap(); 223 if (aDirty) { 224 // The temporary source surface is always a partial region of the widget 225 // that is offset from the origin to the actual bounds of the dirty 226 // region. The destination DT may also be an offset partial region, but we 227 // must check to see if its size matches the region bounds to verify this. 228 LayoutDeviceIntRect bounds = mDirtyRegion.GetBounds(); 229 gfx::IntPoint srcOffset = bounds.TopLeft().ToUnknownPoint(); 230 gfx::IntPoint dstOffset = mDT->GetSize() == bounds.Size().ToUnknownSize() 231 ? srcOffset 232 : gfx::IntPoint(0, 0); 233 for (auto iter = mDirtyRegion.RectIter(); !iter.Done(); iter.Next()) { 234 gfx::IntRect dirtyRect = iter.Get().ToUnknownRect(); 235 mDT->CopySurface(mSurface, dirtyRect - srcOffset, 236 dirtyRect.TopLeft() - dstOffset); 237 } 238 } 239 } else { 240 // Otherwise, we had locked the DT directly. Just release the data. 241 mDT->ReleaseBits(mMappedData); 242 } 243 mDT->Flush(); 244 245 // Done with the DT. Hand it back to the widget and clear out any trace of it. 246 mWidget->EndRemoteDrawingInRegion(mDT, mDirtyRegion); 247 mDirtyRegion.SetEmpty(); 248 ClearMappedBuffer(); 249 } 250 251 void RenderCompositorSWGL::CancelFrame() { 252 CommitMappedBuffer(false); 253 mRenderWidgetSize = Nothing(); 254 } 255 256 RenderedFrameId RenderCompositorSWGL::EndFrame( 257 const nsTArray<DeviceIntRect>& aDirtyRects) { 258 // Dirty rects have already been set inside StartCompositing. We need to keep 259 // those dirty rects exactly the same here so we supply the same exact region 260 // to EndRemoteDrawingInRegion as for StartRemoteDrawingInRegion. 261 RenderedFrameId frameId = GetNextRenderFrameId(); 262 CommitMappedBuffer(); 263 mRenderWidgetSize = Nothing(); 264 return frameId; 265 } 266 267 bool RenderCompositorSWGL::RequestFullRender() { 268 #if defined(MOZ_WIDGET_ANDROID) 269 // XXX Add partial present support. 270 return true; 271 #elif defined(MOZ_WIDGET_GTK) 272 // We're requested to do full render after Resume() on Wayland. 273 if (mRequestFullRender) { 274 mRequestFullRender = false; 275 return true; 276 } 277 return false; 278 #else 279 return false; 280 #endif 281 } 282 283 void RenderCompositorSWGL::Pause() {} 284 285 bool RenderCompositorSWGL::Resume() { 286 #ifdef MOZ_WIDGET_GTK 287 mRequestFullRender = true; 288 #endif 289 return true; 290 } 291 292 LayoutDeviceIntSize RenderCompositorSWGL::GetBufferSize() { 293 // If we're between BeginFrame() and EndFrame()/CancelFrame() calls 294 // return recent rendering size instead of actual underlying widget 295 // size. It prevents possible rendering artifacts if widget size was changed. 296 return mRenderWidgetSize ? mRenderWidgetSize.value() 297 : mWidget->GetClientSize(); 298 } 299 300 void RenderCompositorSWGL::GetCompositorCapabilities( 301 CompositorCapabilities* aCaps) { 302 // Always support a single update rect for SwCompositor 303 aCaps->max_update_rects = 1; 304 305 // On uncomposited desktops such as X11 without compositor or Window 7 with 306 // Aero disabled we need to force a full redraw when the window contents may 307 // be damaged. 308 #ifdef MOZ_WIDGET_GTK 309 aCaps->redraw_on_invalidation = widget::GdkIsX11Display(); 310 #else 311 aCaps->redraw_on_invalidation = true; 312 #endif 313 } 314 315 } // namespace wr 316 } // namespace mozilla