From 50dc71d1c236be40117260d623314d33b01d05b3 Mon Sep 17 00:00:00 2001 From: gellert Date: Mon, 18 Jul 2016 16:16:23 +0200 Subject: [PATCH] gpu acclereted osr added --- atom/browser/api/atom_api_web_contents.cc | 24 +- atom/browser/api/atom_api_web_contents.h | 7 + atom/browser/api/frame_subscriber.cc | 3 +- atom/browser/common_web_contents_delegate.cc | 7 +- atom/browser/native_window_views.h | 6 +- atom/browser/osr_window.cc | 567 +++++++++++++++---- atom/browser/osr_window.h | 269 ++++++--- atom/browser/osr_window_mac.mm | 112 ++++ default_app/default_app.js | 4 + default_app/index.html | 8 + default_app/main.js | 2 +- filenames.gypi | 1 + 12 files changed, 812 insertions(+), 198 deletions(-) create mode 100644 atom/browser/osr_window_mac.mm diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 99cb2e007482..36b42ec78eb4 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1194,12 +1194,30 @@ void WebContents::BeginFrameSubscription(mate::Arguments* args) { const auto view = web_contents()->GetRenderWidgetHostView(); if (view) { - std::unique_ptr frame_subscriber(new FrameSubscriber( - isolate(), view, callback, only_dirty)); - view->BeginFrameSubscription(std::move(frame_subscriber)); + // std::unique_ptr frame_subscriber(new FrameSubscriber( + // isolate(), view, callback, only_dirty)); + // view->BeginFrameSubscription(std::move(frame_subscriber)); + paint_isolate_ = args->isolate(); + + auto v = static_cast(view); + paint_callback_ = base::Bind(&WebContents::OnPaint, + base::Unretained(this)); + v->SetPaintCallback(&paint_callback_); } } +void WebContents::OnPaint( + const gfx::Rect& damage_rect, + int bitmap_width, + int bitmap_height, + void* bitmap_pixels) { + + v8::MaybeLocal buffer = node::Buffer::New(paint_isolate_ + , (char *)bitmap_pixels, sizeof(bitmap_pixels)); + + Emit("paint", damage_rect, bitmap_width, bitmap_height, buffer.ToLocalChecked()); +} + void WebContents::EndFrameSubscription() { const auto view = web_contents()->GetRenderWidgetHostView(); if (view) diff --git a/atom/browser/api/atom_api_web_contents.h b/atom/browser/api/atom_api_web_contents.h index 2917aa860a81..2d97a0146e0e 100644 --- a/atom/browser/api/atom_api_web_contents.h +++ b/atom/browser/api/atom_api_web_contents.h @@ -18,6 +18,8 @@ #include "native_mate/handle.h" #include "ui/gfx/image/image.h" +#include "atom/browser/osr_window.h" + namespace blink { struct WebDeviceEmulationParams; } @@ -141,6 +143,7 @@ class WebContents : public mate::TrackableObject, // Subscribe to the frame updates. void BeginFrameSubscription(mate::Arguments* args); void EndFrameSubscription(); + void OnPaint(const gfx::Rect&, int, int, void*); // Dragging native items. void StartDrag(const mate::Dictionary& item, mate::Arguments* args); @@ -279,6 +282,8 @@ class WebContents : public mate::TrackableObject, private: AtomBrowserContext* GetBrowserContext() const; + OffScreenWindow::OnPaintCallback paint_callback_; + uint32_t GetNextRequestId() { return ++request_id_; } @@ -299,6 +304,8 @@ class WebContents : public mate::TrackableObject, v8::Global devtools_web_contents_; v8::Global debugger_; + v8::Isolate* paint_isolate_; + std::unique_ptr guest_delegate_; // The host webcontents that may contain this webcontents. diff --git a/atom/browser/api/frame_subscriber.cc b/atom/browser/api/frame_subscriber.cc index 456a535d608b..b8bfae2241bd 100644 --- a/atom/browser/api/frame_subscriber.cc +++ b/atom/browser/api/frame_subscriber.cc @@ -74,7 +74,8 @@ bool FrameSubscriber::ShouldCaptureFrame( base::Bind(&FrameSubscriber::ReadbackResultAsBitmap, base::Unretained(this))); - surface->RequestCopyOfOutput(std::move(request)); + // surface->RequestCopyOfOutput(std::move(request)); + std::cout << request.get() << surface << std::endl; return false; } diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index 80a9bc211be3..0b59d513d4ed 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -191,11 +191,12 @@ void CommonWebContentsDelegate::InitWithWebContents( content::WebContentsImpl* impl = reinterpret_cast(web_contents); - impl->SetView(new OffScreenWebContentsView); std::cout << "end" << std::endl; // Create InspectableWebContents. - /*web_contents_.reset(brightray::InspectableWebContents::Create(web_contents)); - web_contents_->SetDelegate(this);*/ + web_contents_.reset(brightray::InspectableWebContents::Create(web_contents)); + web_contents_->SetDelegate(this); + + impl->SetView(new OffScreenWebContentsView); std::cout << "end" << std::endl; } diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index a3bea9b97acb..a4ea107194e1 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -171,9 +171,9 @@ class NativeWindowViews : public NativeWindow, content::WebContents*, const content::NativeWebKeyboardEvent& event) override; - // views::View: - gfx::Size GetMinimumSize() override; - gfx::Size GetMaximumSize() override; + // // views::View: + // gfx::Size GetMinimumSize() const override; + // gfx::Size GetMaximumSize() const override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override; // Register accelerators supported by the menu model. diff --git a/atom/browser/osr_window.cc b/atom/browser/osr_window.cc index ed4eeb691e11..2676c5350bd7 100644 --- a/atom/browser/osr_window.cc +++ b/atom/browser/osr_window.cc @@ -4,6 +4,7 @@ #include "atom/browser/osr_window.h" #include "third_party/WebKit/public/platform/WebScreenInfo.h" +#include "content/browser/compositor/gl_helper.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/renderer_host/render_widget_host_delegate.h" #include "content/public/browser/render_widget_host_view_frame_subscriber.h" @@ -24,6 +25,31 @@ #include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkPixmap.h" #include "cc/output/output_surface_client.h" +#include "cc/output/copy_output_request.h" + +#include "base/callback_helpers.h" +#include "base/location.h" +#include "base/logging.h" +#include "content/public/browser/browser_thread.h" +#include "cc/scheduler/delay_based_time_source.h" + +// const float kDefaultScaleFactor = 1.0; + +// The maximum number of retry counts if frame capture fails. +const int kFrameRetryLimit = 2; + +// When accelerated compositing is enabled and a widget resize is pending, +// we delay further resizes of the UI. The following constant is the maximum +// length of time that we should delay further UI resizes while waiting for a +// resized frame from a renderer. +// const int kResizeLockTimeoutMs = 67; + +#define CEF_UIT content::BrowserThread::UI +#define CEF_POST_TASK(id, task) \ + content::BrowserThread::PostTask(id, FROM_HERE, task) +#define CEF_POST_DELAYED_TASK(id, task, delay_ms) \ + content::BrowserThread::PostDelayedTask(id, FROM_HERE, task, \ + base::TimeDelta::FromMilliseconds(delay_ms)) namespace atom { @@ -166,10 +192,21 @@ void OffScreenWebContentsView::SetOverscrollControllerEnabled(bool enabled){ std::cout << "SetOverscrollControllerEnabled" << std::endl; } +#if defined(OS_MACOSX) +void OffScreenWebContentsView::SetAllowOtherViews(bool allow) { +} +bool OffScreenWebContentsView::GetAllowOtherViews() const { + return false; +} +bool OffScreenWebContentsView::IsEventTracking() const { + return false; +} - +void OffScreenWebContentsView::CloseTabAfterEventTracking() { +} +#endif // defined(OS_MACOSX) OffScreenOutputDevice::OffScreenOutputDevice() { std::cout << "OffScreenOutputDevice" << std::endl; @@ -215,99 +252,6 @@ SkCanvas* OffScreenOutputDevice::BeginPaint(const gfx::Rect& damage_rect) { return canvas_.get(); } -void OffScreenOutputDevice::saveSkBitmapToBMPFile(const SkBitmap& skBitmap, const char* path){ - typedef unsigned char UINT8; - typedef signed char SINT8; - typedef unsigned short UINT16; - typedef signed short SINT16; - typedef unsigned int UINT32; - typedef signed int SINT32; - - struct BMP_FILEHDR // BMP file header - { - UINT32 bfSize; // size of file - UINT16 bfReserved1; - UINT16 bfReserved2; - UINT32 bfOffBits; // pointer to the pixmap bits - }; - - struct BMP_INFOHDR // BMP information header - { - UINT32 biSize; // size of this struct - UINT32 biWidth; // pixmap width - UINT32 biHeight; // pixmap height - UINT16 biPlanes; // should be 1 - UINT16 biBitCount; // number of bits per pixel - UINT32 biCompression; // compression method - UINT32 biSizeImage; // size of image - UINT32 biXPelsPerMeter; // horizontal resolution - UINT32 biYPelsPerMeter; // vertical resolution - UINT32 biClrUsed; // number of colors used - UINT32 biClrImportant; // number of important colors - }; - #define BitmapColorGetA(color) (((color) >> 24) & 0xFF) - #define BitmapColorGetR(color) (((color) >> 16) & 0xFF) - #define BitmapColorGetG(color) (((color) >> 8) & 0xFF) - #define BitmapColorGetB(color) (((color) >> 0) & 0xFF) - - int bmpWidth = skBitmap.width(); - int bmpHeight = skBitmap.height(); - int stride = skBitmap.rowBytes(); - char* m_pmap = (char*)skBitmap.getPixels(); - //virtual PixelFormat& GetPixelFormat() =0; //assume pf is ARGB; - FILE* fp = fopen(path, "wb"); - if(!fp){ - printf("saveSkBitmapToBMPFile: fopen %s Error!\n", path); - } - SINT32 bpl=bmpWidth*4; - // BMP file header. - BMP_FILEHDR fhdr; - fputc('B', fp); - fputc('M', fp); - fhdr.bfReserved1=fhdr.bfReserved2=0; - fhdr.bfOffBits=14+40; // File header size + header size. - fhdr.bfSize=fhdr.bfOffBits+bpl*bmpHeight; - fwrite(&fhdr, 1, 12, fp); - - // BMP header. - BMP_INFOHDR bhdr; - bhdr.biSize=40; - bhdr.biBitCount=32; - bhdr.biCompression=0; // RGB Format. - bhdr.biPlanes=1; - bhdr.biWidth=bmpWidth; - bhdr.biHeight=bmpHeight; - bhdr.biClrImportant=0; - bhdr.biClrUsed=0; - bhdr.biXPelsPerMeter=2384; - bhdr.biYPelsPerMeter=2384; - bhdr.biSizeImage=bpl*bmpHeight; - fwrite(&bhdr, 1, 40, fp); - - // BMP data. - //for(UINT32 y=0; y=0; y--) - { - SINT32 base=y*stride; - for(SINT32 x=0; x<(SINT32)bmpWidth; x++) - { - UINT32 i=base+x*4; - UINT32 pixelData = *(UINT32*)(m_pmap+i); - UINT8 b1=BitmapColorGetB(pixelData); - UINT8 g1=BitmapColorGetG(pixelData); - UINT8 r1=BitmapColorGetR(pixelData); - UINT8 a1=BitmapColorGetA(pixelData); - r1=r1*a1/255; - g1=g1*a1/255; - b1=b1*a1/255; - UINT32 temp=(a1<<24)|(r1<<16)|(g1<<8)|b1;//a bmp pixel in little endian is B、G、R、A - fwrite(&temp, 4, 1, fp); - } - } - fflush(fp); - fclose(fp); -} - void OffScreenOutputDevice::EndPaint() { std::cout << "EndPaint" << std::endl; @@ -322,19 +266,351 @@ void OffScreenOutputDevice::EndPaint() { //saveSkBitmapToBMPFile(*(bitmap_.get()), "test.bmp"); uint8_t* pixels = reinterpret_cast(bitmap_->getPixels()); - for (int i = 0; i<4; i++) { + for (int i = 0; i<16; i++) { int x = static_cast(pixels[i]); std::cout << std::hex << x << std::dec << std::endl; } } +// Used for managing copy requests when GPU compositing is enabled. Based on +// RendererOverridesHandler::InnerSwapCompositorFrame and +// DelegatedFrameHost::CopyFromCompositingSurface. +class CefCopyFrameGenerator { + public: + CefCopyFrameGenerator(int frame_rate_threshold_ms, + OffScreenWindow* view) + : frame_rate_threshold_ms_(frame_rate_threshold_ms), + view_(view), + frame_pending_(false), + frame_in_progress_(false), + frame_retry_count_(0), + weak_ptr_factory_(this) { + } + + void GenerateCopyFrame( + bool force_frame, + const gfx::Rect& damage_rect) { + if (force_frame && !frame_pending_) + frame_pending_ = true; + + // No frame needs to be generated at this time. + if (!frame_pending_) + return; + + // Keep track of |damage_rect| for when the next frame is generated. + if (!damage_rect.IsEmpty()) + pending_damage_rect_.Union(damage_rect); + + // Don't attempt to generate a frame while one is currently in-progress. + if (frame_in_progress_) + return; + frame_in_progress_ = true; + + // Don't exceed the frame rate threshold. + const int64_t frame_rate_delta = + (base::TimeTicks::Now() - frame_start_time_).InMilliseconds(); + if (frame_rate_delta < frame_rate_threshold_ms_) { + // Generate the frame after the necessary time has passed. + CEF_POST_DELAYED_TASK(CEF_UIT, + base::Bind(&CefCopyFrameGenerator::InternalGenerateCopyFrame, + weak_ptr_factory_.GetWeakPtr()), + frame_rate_threshold_ms_ - frame_rate_delta); + return; + } + + InternalGenerateCopyFrame(); + } + + bool frame_pending() const { return frame_pending_; } + + void set_frame_rate_threshold_ms(int frame_rate_threshold_ms) { + frame_rate_threshold_ms_ = frame_rate_threshold_ms; + } + + private: + void InternalGenerateCopyFrame() { + frame_pending_ = false; + frame_start_time_ = base::TimeTicks::Now(); + + if (!view_->render_widget_host()) + return; + + const gfx::Rect damage_rect = pending_damage_rect_; + pending_damage_rect_.SetRect(0, 0, 0, 0); + + // The below code is similar in functionality to + // DelegatedFrameHost::CopyFromCompositingSurface but we reuse the same + // SkBitmap in the GPU codepath and avoid scaling where possible. + std::unique_ptr request = + cc::CopyOutputRequest::CreateRequest(base::Bind( + &CefCopyFrameGenerator::CopyFromCompositingSurfaceHasResult, + weak_ptr_factory_.GetWeakPtr(), + gfx::Rect(view_->GetPhysicalBackingSize()))); + + // request->set_area(gfx::Rect(view_->GetPhysicalBackingSize())); + + view_->DelegatedFrameHostGetLayer()->RequestCopyOfOutput( + std::move(request)); + } + + void CopyFromCompositingSurfaceHasResult( + const gfx::Rect& damage_rect, + std::unique_ptr result) { + std::cout << "has result" << std::endl; + if (result->IsEmpty() || result->size().IsEmpty() || + !view_->render_widget_host()) { + OnCopyFrameCaptureFailure(damage_rect); + return; + } + + if (result->HasTexture()) { + PrepareTextureCopyOutputResult(damage_rect, std::move(result)); + return; + } + + DCHECK(result->HasBitmap()); + PrepareBitmapCopyOutputResult(damage_rect, std::move(result)); + } + + void PrepareTextureCopyOutputResult( + const gfx::Rect& damage_rect, + std::unique_ptr result) { + DCHECK(result->HasTexture()); + base::ScopedClosureRunner scoped_callback_runner( + base::Bind(&CefCopyFrameGenerator::OnCopyFrameCaptureFailure, + weak_ptr_factory_.GetWeakPtr(), + damage_rect)); + + const gfx::Size& result_size = result->size(); + SkIRect bitmap_size; + if (bitmap_) + bitmap_->getBounds(&bitmap_size); + + if (!bitmap_ || + bitmap_size.width() != result_size.width() || + bitmap_size.height() != result_size.height()) { + // Create a new bitmap if the size has changed. + bitmap_.reset(new SkBitmap); + bitmap_->allocN32Pixels(result_size.width(), + result_size.height(), + true); + if (bitmap_->drawsNothing()) + return; + } + + content::ImageTransportFactory* factory = + content::ImageTransportFactory::GetInstance(); + content::GLHelper* gl_helper = factory->GetGLHelper(); + if (!gl_helper) + return; + + std::unique_ptr bitmap_pixels_lock( + new SkAutoLockPixels(*bitmap_)); + uint8_t* pixels = static_cast(bitmap_->getPixels()); + + cc::TextureMailbox texture_mailbox; + std::unique_ptr release_callback; + result->TakeTexture(&texture_mailbox, &release_callback); + DCHECK(texture_mailbox.IsTexture()); + if (!texture_mailbox.IsTexture()) + return; + + ignore_result(scoped_callback_runner.Release()); + + gl_helper->CropScaleReadbackAndCleanMailbox( + texture_mailbox.mailbox(), + texture_mailbox.sync_token(), + result_size, + gfx::Rect(result_size), + result_size, + pixels, + kN32_SkColorType, + base::Bind( + &CefCopyFrameGenerator::CopyFromCompositingSurfaceFinishedProxy, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(&release_callback), + damage_rect, + base::Passed(&bitmap_), + base::Passed(&bitmap_pixels_lock)), + content::GLHelper::SCALER_QUALITY_FAST); + } + + static void CopyFromCompositingSurfaceFinishedProxy( + base::WeakPtr generator, + std::unique_ptr release_callback, + const gfx::Rect& damage_rect, + std::unique_ptr bitmap, + std::unique_ptr bitmap_pixels_lock, + bool result) { + // This method may be called after the view has been deleted. + gpu::SyncToken sync_token; + if (result) { + content::GLHelper* gl_helper = + content::ImageTransportFactory::GetInstance()->GetGLHelper(); + if (gl_helper) + gl_helper->GenerateSyncToken(&sync_token); + } + const bool lost_resource = !sync_token.HasData(); + release_callback->Run(sync_token, lost_resource); + + if (generator) { + generator->CopyFromCompositingSurfaceFinished( + damage_rect, std::move(bitmap), std::move(bitmap_pixels_lock), + result); + } else { + bitmap_pixels_lock.reset(); + bitmap.reset(); + } + } + + void CopyFromCompositingSurfaceFinished( + const gfx::Rect& damage_rect, + std::unique_ptr bitmap, + std::unique_ptr bitmap_pixels_lock, + bool result) { + // Restore ownership of the bitmap to the view. + DCHECK(!bitmap_); + bitmap_ = std::move(bitmap); + + if (result) { + OnCopyFrameCaptureSuccess(damage_rect, *bitmap_, + std::move(bitmap_pixels_lock)); + } else { + bitmap_pixels_lock.reset(); + OnCopyFrameCaptureFailure(damage_rect); + } + } + + void PrepareBitmapCopyOutputResult( + const gfx::Rect& damage_rect, + std::unique_ptr result) { + DCHECK(result->HasBitmap()); + std::unique_ptr source = result->TakeBitmap(); + DCHECK(source); + if (source) { + std::unique_ptr bitmap_pixels_lock( + new SkAutoLockPixels(*source)); + OnCopyFrameCaptureSuccess(damage_rect, *source, + std::move(bitmap_pixels_lock)); + } else { + OnCopyFrameCaptureFailure(damage_rect); + } + } + + void OnCopyFrameCaptureFailure( + const gfx::Rect& damage_rect) { + // Retry with the same |damage_rect|. + pending_damage_rect_.Union(damage_rect); + + const bool force_frame = (++frame_retry_count_ <= kFrameRetryLimit); + OnCopyFrameCaptureCompletion(force_frame); + } + + void OnCopyFrameCaptureSuccess( + const gfx::Rect& damage_rect, + const SkBitmap& bitmap, + std::unique_ptr bitmap_pixels_lock) { + + + // view_->OnPaint(damage_rect, bitmap.width(), bitmap.height(), + // bitmap.getPixels()); + + uint8_t* pixels = reinterpret_cast(bitmap.getPixels()); + for (int i = 0; i<4; i++) { + int x = static_cast(pixels[i]); + std::cout << std::hex << x << std::dec << std::endl; + } + if (view_->paintCallback) { + std::cout << "FRAME COPY ARRIVED" << std::endl; + view_->paintCallback->Run(damage_rect, bitmap.width(), bitmap.height(), + pixels); + } + + bitmap_pixels_lock.reset(); + + // Reset the frame retry count on successful frame generation. + if (frame_retry_count_ > 0) + frame_retry_count_ = 0; + + OnCopyFrameCaptureCompletion(false); + } + + void OnCopyFrameCaptureCompletion(bool force_frame) { + frame_in_progress_ = false; + + if (frame_pending_) { + // Another frame was requested while the current frame was in-progress. + // Generate the pending frame now. + CEF_POST_TASK(CEF_UIT, + base::Bind(&CefCopyFrameGenerator::GenerateCopyFrame, + weak_ptr_factory_.GetWeakPtr(), + force_frame, + gfx::Rect())); + } + } + + int frame_rate_threshold_ms_; + OffScreenWindow* view_; + + base::TimeTicks frame_start_time_; + bool frame_pending_; + bool frame_in_progress_; + int frame_retry_count_; + std::unique_ptr bitmap_; + gfx::Rect pending_damage_rect_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(CefCopyFrameGenerator); +}; + +// Used to control the VSync rate in subprocesses when BeginFrame scheduling is +// enabled. +class CefBeginFrameTimer : public cc::DelayBasedTimeSourceClient { + public: + CefBeginFrameTimer(int frame_rate_threshold_ms, + const base::Closure& callback) + : callback_(callback) { + time_source_ = cc::DelayBasedTimeSource::Create( + base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms), + content::BrowserThread::GetMessageLoopProxyForThread(CEF_UIT).get()); + time_source_->SetClient(this); + } + + void SetActive(bool active) { + time_source_->SetActive(active); + } + + bool IsActive() const { + return time_source_->Active(); + } + + void SetFrameRateThresholdMs(int frame_rate_threshold_ms) { + time_source_->SetTimebaseAndInterval( + base::TimeTicks::Now(), + base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms)); + } + + private: + // cc::TimerSourceClient implementation. + void OnTimerTick() override { + callback_.Run(); + } + + const base::Closure callback_; + std::unique_ptr time_source_; + + DISALLOW_COPY_AND_ASSIGN(CefBeginFrameTimer); +}; + OffScreenWindow::OffScreenWindow(content::RenderWidgetHost* host) : render_widget_host_(content::RenderWidgetHostImpl::From(host)), - delegated_frame_host_(new content::DelegatedFrameHost(this)), - compositor_widget_(gfx::kNullAcceleratedWidget), + frame_rate_threshold_ms_(0), scale_factor_(1.0f), is_showing_(!render_widget_host_->is_hidden()), size_(gfx::Size(800, 600)), + delegated_frame_host_(new content::DelegatedFrameHost(this)), + compositor_widget_(gfx::kNullAcceleratedWidget), weak_ptr_factory_(this) { DCHECK(render_widget_host_); std::cout << "OffScreenWindow" << std::endl; @@ -344,16 +620,61 @@ OffScreenWindow::OffScreenWindow(content::RenderWidgetHost* host) CreatePlatformWidget(); - compositor_.reset(new ui::Compositor(content::GetContextFactory(), - base::ThreadTaskRunnerHandle::Get())); +#if !defined(OS_MACOSX) + // On OS X the ui::Compositor is created/owned by the platform view. + compositor_.reset( + new ui::Compositor(content::GetContextFactory(), + base::ThreadTaskRunnerHandle::Get())); compositor_->SetAcceleratedWidget(compositor_widget_); - compositor_->SetDelegate(this); +#endif + // compositor_->SetDelegate(this); compositor_->SetRootLayer(root_layer_.get()); + + frame_rate_threshold_ms_ = 1000 / 24; + begin_frame_timer_.reset(new CefBeginFrameTimer( + frame_rate_threshold_ms_, + base::Bind(&OffScreenWindow::OnBeginFrameTimerTick, + weak_ptr_factory_.GetWeakPtr()))); + + // begin_frame_timer_->SetActive(true); +} + +void OffScreenWindow::OnBeginFrameTimerTick() { + const base::TimeTicks frame_time = base::TimeTicks::Now(); + const base::TimeDelta vsync_period = + base::TimeDelta::FromMilliseconds(frame_rate_threshold_ms_); + SendBeginFrame(frame_time, vsync_period); + + std::cout << "tickkkk" << std::endl; + // copy_frame_generator_->GenerateCopyFrame(true, damage_rect); +} + +void OffScreenWindow::SendBeginFrame(base::TimeTicks frame_time, + base::TimeDelta vsync_period) { + base::TimeTicks display_time = frame_time + vsync_period; + + // TODO(brianderson): Use adaptive draw-time estimation. + base::TimeDelta estimated_browser_composite_time = + base::TimeDelta::FromMicroseconds( + (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60)); + + base::TimeTicks deadline = display_time - estimated_browser_composite_time; + + render_widget_host_->Send(new ViewMsg_BeginFrame( + render_widget_host_->GetRoutingID(), + cc::BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline, + vsync_period, cc::BeginFrameArgs::NORMAL))); +} + +void OffScreenWindow::SetPaintCallback(const OnPaintCallback *callback) { + paintCallback.reset(callback); } OffScreenWindow::~OffScreenWindow() { std::cout << "~OffScreenWindow" << std::endl; - if (is_showing_) delegated_frame_host_->WasHidden(); + + if (is_showing_) + delegated_frame_host_->WasHidden(); delegated_frame_host_->ResetCompositor(); delegated_frame_host_.reset(NULL); @@ -447,7 +768,8 @@ void OffScreenWindow::Show() { return; is_showing_ = true; - if (render_widget_host_) render_widget_host_->WasShown(ui::LatencyInfo()); + if (render_widget_host_) + render_widget_host_->WasShown(ui::LatencyInfo()); delegated_frame_host_->SetCompositor(compositor_.get()); delegated_frame_host_->WasShown(ui::LatencyInfo()); } @@ -457,7 +779,8 @@ void OffScreenWindow::Hide() { if (!is_showing_) return; - if (render_widget_host_) render_widget_host_->WasHidden(); + if (render_widget_host_) + render_widget_host_->WasHidden(); delegated_frame_host_->WasHidden(); delegated_frame_host_->ResetCompositor(); is_showing_ = false; @@ -512,9 +835,43 @@ void OffScreenWindow::OnSwapCompositorFrame( last_scroll_offset_ = frame->metadata.root_scroll_offset; } + if (!copy_frame_generator_.get()) { + copy_frame_generator_.reset( + new CefCopyFrameGenerator(frame_rate_threshold_ms_, this)); + } + + // Determine the damage rectangle for the current frame. This is the same + // calculation that SwapDelegatedFrame uses. + // cc::RenderPass* root_pass = + // frame->delegated_frame_data->render_pass_list.back().get(); + // gfx::Size frame_size = root_pass->output_rect.size(); + // gfx::Rect damage_rect = + // gfx::ToEnclosingRect(gfx::RectF(root_pass->damage_rect)); + // damage_rect.Intersect(gfx::Rect(frame_size)); + gfx::Rect damage_rect = gfx::Rect(GetVisibleViewportSize()); + if (frame->delegated_frame_data) - delegated_frame_host_->SwapDelegatedFrame( - output_surface_id, std::move(frame)); + delegated_frame_host_->SwapDelegatedFrame(output_surface_id, + std::move(frame)); + + // Request a copy of the last compositor frame which will eventually call + // OnPaint asynchronously. + std::cout << "FRAME COPY REQUESTED" << std::endl; + copy_frame_generator_->GenerateCopyFrame(true, damage_rect); + + // gfx::Rect rect = gfx::Rect(GetVisibleViewportSize()); + // // The below code is similar in functionality to + // // DelegatedFrameHost::CopyFromCompositingSurface but we reuse the same + // // SkBitmap in the GPU codepath and avoid scaling where possible. + // std::unique_ptr request = + // cc::CopyOutputRequest::CreateRequest(base::Bind( + // &OffScreenWindow::CopyFromCompositingSurfaceHasResult, + // weak_ptr_factory_.GetWeakPtr(), + // rect)); + // + // request->set_area(gfx::Rect(GetPhysicalBackingSize())); + // DelegatedFrameHostGetLayer()->RequestCopyOfOutput( + // std::move(request)); } void OffScreenWindow::ClearCompositorFrame() { diff --git a/atom/browser/osr_window.h b/atom/browser/osr_window.h index b00802c7f8d6..d566cdda4604 100644 --- a/atom/browser/osr_window.h +++ b/atom/browser/osr_window.h @@ -25,43 +25,78 @@ #include "cc/output/output_surface_client.h" #include "cc/scheduler/begin_frame_source.h" +#include "content/browser/web_contents/web_contents_view.h" +// #include "atom/browser/native_window_views.h" + +#if defined(OS_WIN) #include #include "ui/gfx/win/window_impl.h" -#include "content/browser/web_contents/web_contents_view.h" -#include "atom/browser/native_window_views.h" +#endif + +#if defined(OS_MACOSX) +#include "content/browser/renderer_host/browser_compositor_view_mac.h" +#include "ui/accelerated_widget_mac/accelerated_widget_mac.h" +#endif + +#if defined(OS_MACOSX) +#ifdef __OBJC__ +@class CALayer; +@class NSWindow; +@class NSTextInputContext; +#else +class CALayer; +class NSWindow; +class NSTextInputContext; +#endif +#endif + +#if defined(OS_MACOSX) +class AcceleratedWidgetMacNSViewHelper; +#endif namespace atom { +class CefCopyFrameGenerator; +class CefBeginFrameTimer; + class OffScreenWebContentsView : public content::WebContentsView { public: OffScreenWebContentsView(); ~OffScreenWebContentsView(); - gfx::NativeView GetNativeView() const; - gfx::NativeView GetContentNativeView() const; - gfx::NativeWindow GetTopLevelNativeWindow() const; + gfx::NativeView GetNativeView() const override; + gfx::NativeView GetContentNativeView() const override; + gfx::NativeWindow GetTopLevelNativeWindow() const override; - void GetContainerBounds(gfx::Rect* out) const; - void SizeContents(const gfx::Size& size); - void Focus(); - void SetInitialFocus(); - void StoreFocus(); - void RestoreFocus(); - content::DropData* GetDropData() const; - gfx::Rect GetViewBounds() const; + void GetContainerBounds(gfx::Rect* out) const override; + void SizeContents(const gfx::Size& size) override; + void Focus() override; + void SetInitialFocus() override; + void StoreFocus() override; + void RestoreFocus() override; + content::DropData* GetDropData() const override; + gfx::Rect GetViewBounds() const override; void CreateView( - const gfx::Size& initial_size, gfx::NativeView context); + const gfx::Size& initial_size, gfx::NativeView context) override; content::RenderWidgetHostViewBase* CreateViewForWidget( - content::RenderWidgetHost* render_widget_host, bool is_guest_view_hack); + content::RenderWidgetHost* render_widget_host, bool is_guest_view_hack) override; content::RenderWidgetHostViewBase* CreateViewForPopupWidget( - content::RenderWidgetHost* render_widget_host); + content::RenderWidgetHost* render_widget_host) override; + + void SetPageTitle(const base::string16& title) override; + void RenderViewCreated(content::RenderViewHost* host) override; + void RenderViewSwappedIn(content::RenderViewHost* host) override; + void SetOverscrollControllerEnabled(bool enabled) override; + +#if defined(OS_MACOSX) + void SetAllowOtherViews(bool allow) override; + bool GetAllowOtherViews() const override; + bool IsEventTracking() const override; + void CloseTabAfterEventTracking() override; +#endif - void SetPageTitle(const base::string16& title); - void RenderViewCreated(content::RenderViewHost* host); - void RenderViewSwappedIn(content::RenderViewHost* host); - void SetOverscrollControllerEnabled(bool enabled); private: content::RenderWidgetHostViewBase* view_; }; @@ -72,7 +107,7 @@ public: ~OffScreenOutputDevice(); - void saveSkBitmapToBMPFile(const SkBitmap& skBitmap, const char* path); + // void saveSkBitmapToBMPFile(const SkBitmap& skBitmap, const char* path); void Resize(const gfx::Size& pixel_size, float scale_factor) override; SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override; void EndPaint() override; @@ -91,16 +126,22 @@ public: ~AtomCompositorDelegate() {}; std::unique_ptr CreateSoftwareOutputDevice( - ui::Compositor* compositor) { + ui::Compositor* compositor) override { return std::unique_ptr(new OffScreenOutputDevice); } }; class OffScreenWindow - : public content::RenderWidgetHostViewBase, - public content::DelegatedFrameHostClient, - public ui::CompositorDelegate { + : public content::RenderWidgetHostViewBase, + #if defined(OS_MACOSX) + public ui::AcceleratedWidgetMacNSView, + #endif + public ui::CompositorDelegate, + public content::DelegatedFrameHostClient { public: + typedef base::Callback + OnPaintCallback; + OffScreenWindow(content::RenderWidgetHost*); ~OffScreenWindow(); @@ -108,100 +149,164 @@ public: //content::RenderWidgetHostView bool OnMessageReceived(const IPC::Message&) override; - void InitAsChild(gfx::NativeView); - content::RenderWidgetHost* GetRenderWidgetHost(void) const; - void SetSize(const gfx::Size &); - void SetBounds(const gfx::Rect &); - gfx::Vector2dF GetLastScrollOffset(void) const; - gfx::NativeView GetNativeView(void) const; - gfx::NativeViewId GetNativeViewId(void) const; - gfx::NativeViewAccessible GetNativeViewAccessible(void); + void InitAsChild(gfx::NativeView) override; + content::RenderWidgetHost* GetRenderWidgetHost(void) const override; + void SetSize(const gfx::Size &) override; + void SetBounds(const gfx::Rect &) override; + gfx::Vector2dF GetLastScrollOffset(void) const override; + gfx::NativeView GetNativeView(void) const override; + gfx::NativeViewId GetNativeViewId(void) const override; + gfx::NativeViewAccessible GetNativeViewAccessible(void) override; ui::TextInputClient* GetTextInputClient() override; - void Focus(void); - bool HasFocus(void) const; - bool IsSurfaceAvailableForCopy(void) const; - void Show(void); - void Hide(void); - bool IsShowing(void); - gfx::Rect GetViewBounds(void) const; + void Focus(void) override; + bool HasFocus(void) const override; + bool IsSurfaceAvailableForCopy(void) const override; + void Show(void) override; + void Hide(void) override; + bool IsShowing(void) override; + gfx::Rect GetViewBounds(void) const override; gfx::Size GetVisibleViewportSize() const override; void SetInsets(const gfx::Insets&) override; - bool LockMouse(void); - void UnlockMouse(void); - bool GetScreenColorProfile(std::vector*); + bool LockMouse(void) override; + void UnlockMouse(void) override; + bool GetScreenColorProfile(std::vector*) override; + +#if defined(OS_MACOSX) + void SetActive(bool active) override; + void ShowDefinitionForSelection() override; + bool SupportsSpeech() const override; + void SpeakSelection() override; + bool IsSpeaking() const override; + void StopSpeaking() override; +#endif // defined(OS_MACOSX) //content::RenderWidgetHostViewBase - void OnSwapCompositorFrame(uint32_t, std::unique_ptr); - void ClearCompositorFrame(void); - void InitAsPopup(content::RenderWidgetHostView *, const gfx::Rect &); - void InitAsFullscreen(content::RenderWidgetHostView *); - void UpdateCursor(const content::WebCursor &); - void SetIsLoading(bool); - void TextInputStateChanged(const ViewHostMsg_TextInputState_Params &); - void ImeCancelComposition(void); - void RenderProcessGone(base::TerminationStatus,int); - void Destroy(void); - void SetTooltipText(const base::string16 &); - void SelectionBoundsChanged(const ViewHostMsg_SelectionBounds_Params &); + void OnSwapCompositorFrame(uint32_t, std::unique_ptr) override; + void ClearCompositorFrame(void) override; + void InitAsPopup(content::RenderWidgetHostView *, const gfx::Rect &) override; + void InitAsFullscreen(content::RenderWidgetHostView *) override; + void UpdateCursor(const content::WebCursor &) override; + void SetIsLoading(bool) override; + void TextInputStateChanged(const ViewHostMsg_TextInputState_Params &) override; + void ImeCancelComposition(void) override; + void RenderProcessGone(base::TerminationStatus,int) override; + void Destroy(void) override; + void SetTooltipText(const base::string16 &) override; + +#if defined(OS_MACOSX) + void SelectionChanged(const base::string16& text, + size_t offset, + const gfx::Range& range) override; +#endif + + void SelectionBoundsChanged(const ViewHostMsg_SelectionBounds_Params &) override; void CopyFromCompositingSurface(const gfx::Rect &, const gfx::Size &, const content::ReadbackRequestCallback &, - const SkColorType); + const SkColorType) override; void CopyFromCompositingSurfaceToVideoFrame( const gfx::Rect &, const scoped_refptr &, const base::Callback &); - bool CanCopyToVideoFrame(void) const; + base::internal::CopyMode::Copyable> &) override; + bool CanCopyToVideoFrame(void) const override; void BeginFrameSubscription( - std::unique_ptr); - void EndFrameSubscription(); - bool HasAcceleratedSurface(const gfx::Size &); - void GetScreenInfo(blink::WebScreenInfo *); + std::unique_ptr) override; + void EndFrameSubscription() override; + bool HasAcceleratedSurface(const gfx::Size &) override; + void GetScreenInfo(blink::WebScreenInfo *) override; bool GetScreenColorProfile(blink::WebVector*); - gfx::Rect GetBoundsInRootWindow(void); - void LockCompositingSurface(void); - void UnlockCompositingSurface(void); + gfx::Rect GetBoundsInRootWindow(void) override; + void LockCompositingSurface(void) override; + void UnlockCompositingSurface(void) override; void ImeCompositionRangeChanged( - const gfx::Range &, const std::vector&); + const gfx::Range &, const std::vector&) override; gfx::Size GetPhysicalBackingSize() const override; gfx::Size GetRequestedRendererSize() const override; //content::DelegatedFrameHostClient int DelegatedFrameHostGetGpuMemoryBufferClientId(void) const; - ui::Layer *DelegatedFrameHostGetLayer(void) const; - bool DelegatedFrameHostIsVisible(void) const; - SkColor DelegatedFrameHostGetGutterColor(SkColor) const; - gfx::Size DelegatedFrameHostDesiredSizeInDIP(void) const; - bool DelegatedFrameCanCreateResizeLock(void) const; - std::unique_ptr DelegatedFrameHostCreateResizeLock(bool); - void DelegatedFrameHostResizeLockWasReleased(void); + ui::Layer *DelegatedFrameHostGetLayer(void) const override; + bool DelegatedFrameHostIsVisible(void) const override; + SkColor DelegatedFrameHostGetGutterColor(SkColor) const override; + gfx::Size DelegatedFrameHostDesiredSizeInDIP(void) const override; + bool DelegatedFrameCanCreateResizeLock(void) const override; + std::unique_ptr DelegatedFrameHostCreateResizeLock(bool) override; + void DelegatedFrameHostResizeLockWasReleased(void) override; void DelegatedFrameHostSendCompositorSwapAck( - int, const cc::CompositorFrameAck &); + int, const cc::CompositorFrameAck &) override; void DelegatedFrameHostSendReclaimCompositorResources( - int, const cc::CompositorFrameAck &); - void DelegatedFrameHostOnLostCompositorResources(void); + int, const cc::CompositorFrameAck &) override; + void DelegatedFrameHostOnLostCompositorResources(void) override; void DelegatedFrameHostUpdateVSyncParameters( - const base::TimeTicks &, const base::TimeDelta &); + const base::TimeTicks &, const base::TimeDelta &) override; std::unique_ptr CreateSoftwareOutputDevice( - ui::Compositor* compositor); + ui::Compositor* compositor) override; void OnSetNeedsBeginFrames(bool enabled); + +#if defined(OS_MACOSX) + // AcceleratedWidgetMacNSView implementation. + NSView* AcceleratedWidgetGetNSView() const override; + void AcceleratedWidgetGetVSyncParameters( + base::TimeTicks* timebase, base::TimeDelta* interval) const override; + void AcceleratedWidgetSwapCompleted() override; +#endif // defined(OS_MACOSX) + + ui::Compositor* compositor() const { return compositor_.get(); } + content::RenderWidgetHostImpl* render_widget_host() const + { return render_widget_host_; } + + void OnBeginFrameTimerTick(); + void SendBeginFrame(base::TimeTicks frame_time, + base::TimeDelta vsync_period); + + std::unique_ptr paintCallback; + void SetPaintCallback(const OnPaintCallback*); private: content::RenderWidgetHostImpl* render_widget_host_; - std::unique_ptr delegated_frame_host_; - std::unique_ptr compositor_; - gfx::AcceleratedWidget compositor_widget_; - std::unique_ptr root_layer_; + std::unique_ptr copy_frame_generator_; + std::unique_ptr begin_frame_timer_; + + int frame_rate_threshold_ms_; float scale_factor_; bool is_showing_; gfx::Vector2dF last_scroll_offset_; gfx::Size size_; + std::unique_ptr delegated_frame_host_; + std::unique_ptr compositor_; + gfx::AcceleratedWidget compositor_widget_; + std::unique_ptr root_layer_; + #if defined(OS_WIN) std::unique_ptr window_; +#elif defined(OS_MACOSX) + NSWindow* window_; + CALayer* background_layer_; + std::unique_ptr browser_compositor_; +#elif defined(USE_X11) + CefWindowX11* window_; + std::unique_ptr invisible_cursor_; +#endif + +#if defined(OS_MACOSX) + NSTextInputContext* text_input_context_osr_mac_; + + // Selected text on the renderer. + std::string selected_text_; + + // The current composition character range and its bounds. + gfx::Range composition_range_; + std::vector composition_bounds_; + + // The current caret bounds. + gfx::Rect caret_rect_; + + // The current first selection bounds. + gfx::Rect first_selection_rect_; #endif base::WeakPtrFactory weak_ptr_factory_; diff --git a/atom/browser/osr_window_mac.mm b/atom/browser/osr_window_mac.mm new file mode 100644 index 000000000000..0d806731eeff --- /dev/null +++ b/atom/browser/osr_window_mac.mm @@ -0,0 +1,112 @@ +#include "atom/browser/osr_window.h" + +#include +#include +#include +#include + +#import + +#include "base/compiler_specific.h" +#include "base/strings/utf_string_conversions.h" +#include "content/common/view_messages.h" +#include "ui/accelerated_widget_mac/accelerated_widget_mac.h" +#include "ui/events/latency_info.h" + +NSView* atom::OffScreenWindow::AcceleratedWidgetGetNSView() const { + return [window_ contentView]; +} + +void atom::OffScreenWindow::AcceleratedWidgetGetVSyncParameters( + base::TimeTicks* timebase, base::TimeDelta* interval) const { + *timebase = base::TimeTicks(); + *interval = base::TimeDelta(); +} + +void atom::OffScreenWindow::AcceleratedWidgetSwapCompleted() { +} + +void atom::OffScreenWindow::SetActive(bool active) { +} + +void atom::OffScreenWindow::ShowDefinitionForSelection() { +} + +bool atom::OffScreenWindow::SupportsSpeech() const { + return false; +} + +void atom::OffScreenWindow::SpeakSelection() { +} + +bool atom::OffScreenWindow::IsSpeaking() const { + return false; +} + +void atom::OffScreenWindow::StopSpeaking() { +} + +void atom::OffScreenWindow::SelectionChanged( + const base::string16& text, + size_t offset, + const gfx::Range& range) { + if (range.is_empty() || text.empty()) { + selected_text_.clear(); + } else { + size_t pos = range.GetMin() - offset; + size_t n = range.length(); + + DCHECK(pos + n <= text.length()) << "The text can not fully cover range."; + if (pos >= text.length()) { + DCHECK(false) << "The text can not cover range."; + return; + } + selected_text_ = base::UTF16ToUTF8(text.substr(pos, n)); + } + + RenderWidgetHostViewBase::SelectionChanged(text, offset, range); +} + +void atom::OffScreenWindow::CreatePlatformWidget() { + // Create a borderless non-visible 1x1 window. + window_ = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1) + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + + // Create a CALayer which is used by BrowserCompositorViewMac for rendering. + background_layer_ = [[[CALayer alloc] init] retain]; + [background_layer_ setBackgroundColor:CGColorGetConstantColor(kCGColorClear)]; + NSView* content_view = [window_ contentView]; + [content_view setLayer:background_layer_]; + [content_view setWantsLayer:YES]; + + browser_compositor_ = content::BrowserCompositorMac::Create(); + + compositor_.reset(browser_compositor_->compositor()); + compositor_->SetRootLayer(root_layer_.get()); + browser_compositor_->accelerated_widget_mac()->SetNSView(this); + browser_compositor_->compositor()->SetVisible(true); + + // CEF needs the browser compositor to remain responsive whereas normal + // rendering on OS X does not. This effectively reverts the changes from + // https://crbug.com/463988#c6 + compositor_->SetLocksWillTimeOut(true); + browser_compositor_->Unsuspend(); +} + +// void atom::OffScreenWindow::PlatformDestroyCompositorWidget() { +// DCHECK(window_); +// +// browser_compositor_->Destroy(); +// +// [window_ close]; +// window_ = nil; +// [background_layer_ release]; +// background_layer_ = nil; +// +// browser_compositor_.reset(); +// +// delete accelerated_widget_helper_; +// accelerated_widget_helper_ = nullptr; +// } diff --git a/default_app/default_app.js b/default_app/default_app.js index 9f28872735f6..7515d6cbf5ca 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -23,5 +23,9 @@ exports.load = (appUrl) => { console.log("asd") }) }) + + mainWindow.webContents.on('paint', (e, rect, w, h, data) => { + console.log('ELECTRON EVENT', rect, w, h, data) + }) }) } diff --git a/default_app/index.html b/default_app/index.html index edfb3e6ddc92..0c16fb1a91ee 100644 --- a/default_app/index.html +++ b/default_app/index.html @@ -129,6 +129,14 @@ e.preventDefault(); return false; }; + + document.onload = () => { + var a = false + setInterval(function () { + document.body.querySelector('.container').style.background = a ? 'red' : 'blue' + a = !a + }, 200) + }
diff --git a/default_app/main.js b/default_app/main.js index dd887e0def1e..a5359972bbca 100644 --- a/default_app/main.js +++ b/default_app/main.js @@ -331,5 +331,5 @@ if (option.file && !option.webdriver) { startRepl() } else { const indexPath = path.join(__dirname, '/index.html') - loadApplicationByUrl(`file://${indexPath}`) + loadApplicationByUrl(`https://vimeo.com/channels/staffpicks/174711575#t=7s`) } diff --git a/filenames.gypi b/filenames.gypi index b0c0ff0e727a..325b766030c7 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -207,6 +207,7 @@ 'atom/browser/osr_window.cc', 'atom/browser/osr_window.h', 'atom/browser/osr_window_win.cc', + 'atom/browser/osr_window_mac.mm', 'atom/browser/net/asar/asar_protocol_handler.cc', 'atom/browser/net/asar/asar_protocol_handler.h', 'atom/browser/net/asar/url_request_asar_job.cc',