Improve off-screen rendering behavior (issue #1257).

- The |dirtyRects| argument to CefRenderHandler::OnPaint is now a single rectangle representing the union of all dirty rectangles since the last frame was generated. In most cases this rectangle will be smaller than the view (frame) size.
- cefclient: Use a ScopedGLContext class on all platforms to manage the current GL context and avoid GL state leaks.
- cefclient: Add debug checks in ClientOSRenderer for GL errors and fix some errors that were discovered.
- cefclient: Add a new `--show-update-rect` command-line flag that provides visualization of the update rectangle by drawing a red border around it for each frame.

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1945 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2014-12-02 11:23:57 +00:00
parent cbae1994ae
commit b12e172af0
15 changed files with 299 additions and 116 deletions

View File

@ -274,13 +274,21 @@ void CefRenderWidgetHostViewOSR::OnSwapCompositorFrame(
}
if (frame->delegated_frame_data) {
// 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();
gfx::Size frame_size = root_pass->output_rect.size();
gfx::Rect damage_rect = gfx::ToEnclosingRect(root_pass->damage_rect);
damage_rect.Intersect(gfx::Rect(frame_size));
delegated_frame_host_->SwapDelegatedFrame(
output_surface_id,
frame->delegated_frame_data.Pass(),
frame->metadata.device_scale_factor,
frame->metadata.latency_info);
GenerateFrame(true);
GenerateFrame(true, damage_rect);
return;
}
@ -700,7 +708,7 @@ void CefRenderWidgetHostViewOSR::Invalidate(
return;
}
GenerateFrame(true);
GenerateFrame(true, root_layer_->bounds());
}
void CefRenderWidgetHostViewOSR::SendKeyEvent(
@ -829,7 +837,9 @@ void CefRenderWidgetHostViewOSR::ResizeRootLayer() {
compositor_->SetScaleAndSize(CurrentDeviceScaleFactor(), size);
}
void CefRenderWidgetHostViewOSR::GenerateFrame(bool force_frame) {
void CefRenderWidgetHostViewOSR::GenerateFrame(
bool force_frame,
const gfx::Rect& damage_rect) {
if (force_frame && !frame_pending_)
frame_pending_ = true;
@ -837,6 +847,10 @@ void CefRenderWidgetHostViewOSR::GenerateFrame(bool force_frame) {
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;
@ -864,13 +878,17 @@ void CefRenderWidgetHostViewOSR::InternalGenerateFrame() {
if (!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.
scoped_ptr<cc::CopyOutputRequest> request =
cc::CopyOutputRequest::CreateRequest(base::Bind(
&CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceHasResult,
weak_ptr_factory_.GetWeakPtr()));
weak_ptr_factory_.GetWeakPtr(),
damage_rect));
const gfx::Rect& src_subrect_in_pixel =
content::ConvertRectToPixel(CurrentDeviceScaleFactor(),
@ -880,27 +898,30 @@ void CefRenderWidgetHostViewOSR::InternalGenerateFrame() {
}
void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceHasResult(
const gfx::Rect& damage_rect,
scoped_ptr<cc::CopyOutputResult> result) {
if (result->IsEmpty() || result->size().IsEmpty() || !render_widget_host_) {
OnFrameCaptureFailure();
OnFrameCaptureFailure(damage_rect);
return;
}
if (result->HasTexture()) {
PrepareTextureCopyOutputResult(result.Pass());
PrepareTextureCopyOutputResult(damage_rect, result.Pass());
return;
}
DCHECK(result->HasBitmap());
PrepareBitmapCopyOutputResult(result.Pass());
PrepareBitmapCopyOutputResult(damage_rect, result.Pass());
}
void CefRenderWidgetHostViewOSR::PrepareTextureCopyOutputResult(
const gfx::Rect& damage_rect,
scoped_ptr<cc::CopyOutputResult> result) {
DCHECK(result->HasTexture());
base::ScopedClosureRunner scoped_callback_runner(
base::Bind(&CefRenderWidgetHostViewOSR::OnFrameCaptureFailure,
weak_ptr_factory_.GetWeakPtr()));
weak_ptr_factory_.GetWeakPtr(),
damage_rect));
const gfx::Size& result_size = result->size();
SkIRect bitmap_size;
@ -950,6 +971,7 @@ void CefRenderWidgetHostViewOSR::PrepareTextureCopyOutputResult(
&CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinishedProxy,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&release_callback),
damage_rect,
base::Passed(&bitmap_),
base::Passed(&bitmap_pixels_lock)),
content::GLHelper::SCALER_QUALITY_FAST);
@ -959,6 +981,7 @@ void CefRenderWidgetHostViewOSR::PrepareTextureCopyOutputResult(
void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinishedProxy(
base::WeakPtr<CefRenderWidgetHostViewOSR> view,
scoped_ptr<cc::SingleReleaseCallback> release_callback,
const gfx::Rect& damage_rect,
scoped_ptr<SkBitmap> bitmap,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
bool result) {
@ -974,7 +997,7 @@ void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinishedProxy(
if (view) {
view->CopyFromCompositingSurfaceFinished(
bitmap.Pass(), bitmap_pixels_lock.Pass(), result);
damage_rect, bitmap.Pass(), bitmap_pixels_lock.Pass(), result);
} else {
bitmap_pixels_lock.reset();
bitmap.reset();
@ -982,6 +1005,7 @@ void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinishedProxy(
}
void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinished(
const gfx::Rect& damage_rect,
scoped_ptr<SkBitmap> bitmap,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
bool result) {
@ -990,14 +1014,15 @@ void CefRenderWidgetHostViewOSR::CopyFromCompositingSurfaceFinished(
bitmap_ = bitmap.Pass();
if (result) {
OnFrameCaptureSuccess(*bitmap_, bitmap_pixels_lock.Pass());
OnFrameCaptureSuccess(damage_rect, *bitmap_, bitmap_pixels_lock.Pass());
} else {
bitmap_pixels_lock.reset();
OnFrameCaptureFailure();
OnFrameCaptureFailure(damage_rect);
}
}
void CefRenderWidgetHostViewOSR::PrepareBitmapCopyOutputResult(
const gfx::Rect& damage_rect,
scoped_ptr<cc::CopyOutputResult> result) {
DCHECK(result->HasBitmap());
scoped_ptr<SkBitmap> source = result->TakeBitmap();
@ -1005,33 +1030,39 @@ void CefRenderWidgetHostViewOSR::PrepareBitmapCopyOutputResult(
if (source) {
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock(
new SkAutoLockPixels(*source));
OnFrameCaptureSuccess(*source, bitmap_pixels_lock.Pass());
OnFrameCaptureSuccess(damage_rect, *source, bitmap_pixels_lock.Pass());
} else {
OnFrameCaptureFailure();
OnFrameCaptureFailure(damage_rect);
}
}
void CefRenderWidgetHostViewOSR::OnFrameCaptureFailure() {
void CefRenderWidgetHostViewOSR::OnFrameCaptureFailure(
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);
OnFrameCaptureCompletion(force_frame);
}
void CefRenderWidgetHostViewOSR::OnFrameCaptureSuccess(
const gfx::Rect& damage_rect,
const SkBitmap& bitmap,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock) {
SkRect bounds;
bitmap.getBounds(&bounds);
gfx::Rect rect_in_bitmap(0, 0, bitmap.width(), bitmap.height());
rect_in_bitmap.Intersect(damage_rect);
CefRenderHandler::RectList rcList;
rcList.push_back(CefRect(0, 0, bounds.width(), bounds.height()));
rcList.push_back(CefRect(rect_in_bitmap.x(), rect_in_bitmap.y(),
rect_in_bitmap.width(), rect_in_bitmap.height()));
browser_impl_->GetClient()->GetRenderHandler()->OnPaint(
browser_impl_.get(),
IsPopupWidget() ? PET_POPUP : PET_VIEW,
rcList,
bitmap.getPixels(),
bounds.width(),
bounds.height());
bitmap.width(),
bitmap.height());
bitmap_pixels_lock.reset();
@ -1050,7 +1081,9 @@ void CefRenderWidgetHostViewOSR::OnFrameCaptureCompletion(bool force_frame) {
// Generate the pending frame now.
CEF_POST_TASK(CEF_UIT,
base::Bind(&CefRenderWidgetHostViewOSR::GenerateFrame,
weak_ptr_factory_.GetWeakPtr(), force_frame));
weak_ptr_factory_.GetWeakPtr(),
force_frame,
gfx::Rect()));
}
}

View File

@ -249,26 +249,32 @@ class CefRenderWidgetHostViewOSR
// Implementation based on RendererOverridesHandler::InnerSwapCompositorFrame
// and DelegatedFrameHost::CopyFromCompositingSurface.
void GenerateFrame(bool force_frame);
void GenerateFrame(bool force_frame, const gfx::Rect& damage_rect);
void InternalGenerateFrame();
void CopyFromCompositingSurfaceHasResult(
const gfx::Rect& damage_rect,
scoped_ptr<cc::CopyOutputResult> result);
void PrepareTextureCopyOutputResult(
const gfx::Rect& damage_rect,
scoped_ptr<cc::CopyOutputResult> result);
static void CopyFromCompositingSurfaceFinishedProxy(
base::WeakPtr<CefRenderWidgetHostViewOSR> view,
scoped_ptr<cc::SingleReleaseCallback> release_callback,
const gfx::Rect& damage_rect,
scoped_ptr<SkBitmap> bitmap,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
bool result);
void CopyFromCompositingSurfaceFinished(
const gfx::Rect& damage_rect,
scoped_ptr<SkBitmap> bitmap,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock,
bool result);
void PrepareBitmapCopyOutputResult(
const gfx::Rect& damage_rect,
scoped_ptr<cc::CopyOutputResult> result);
void OnFrameCaptureFailure();
void OnFrameCaptureFailure(const gfx::Rect& damage_rect);
void OnFrameCaptureSuccess(
const gfx::Rect& damage_rect,
const SkBitmap& bitmap,
scoped_ptr<SkAutoLockPixels> bitmap_pixels_lock);
void OnFrameCaptureCompletion(bool force_frame);
@ -323,6 +329,7 @@ class CefRenderWidgetHostViewOSR
bool frame_in_progress_;
int frame_retry_count_;
scoped_ptr<SkBitmap> bitmap_;
gfx::Rect pending_damage_rect_;
bool hold_resize_;
bool pending_resize_;

View File

@ -545,12 +545,15 @@ int main(int argc, char* argv[]) {
if (AppIsOffScreenRenderingEnabled()) {
CefRefPtr<CefCommandLine> cmd_line = AppGetCommandLine();
bool transparent =
const bool transparent =
cmd_line->HasSwitch(cefclient::kTransparentPaintingEnabled);
const bool show_update_rect =
cmd_line->HasSwitch(cefclient::kShowUpdateRect);
// Create the GTKGL surface.
CefRefPtr<OSRWindow> osr_window =
OSRWindow::Create(&g_main_browser_provider, transparent, vbox);
OSRWindow::Create(&g_main_browser_provider, transparent,
show_update_rect, vbox);
// Show the GTK window.
gtk_widget_show_all(GTK_WIDGET(window));

View File

@ -488,11 +488,14 @@ NSButton* MakeButton(NSRect* rect, NSString* title, NSView* parent) {
if (AppIsOffScreenRenderingEnabled()) {
CefRefPtr<CefCommandLine> cmd_line = AppGetCommandLine();
bool transparent =
const bool transparent =
cmd_line->HasSwitch(cefclient::kTransparentPaintingEnabled);
const bool show_update_rect =
cmd_line->HasSwitch(cefclient::kShowUpdateRect);
CefRefPtr<OSRWindow> osr_window =
OSRWindow::Create(&g_main_browser_provider, transparent, contentView,
OSRWindow::Create(&g_main_browser_provider, transparent,
show_update_rect, contentView,
CefRect(0, 0, kWindowWidth, kWindowHeight));
window_info.SetAsWindowless(osr_window->GetWindowHandle(), transparent);
g_handler->SetOSRHandler(osr_window->GetRenderHandler().get());

View File

@ -1096,12 +1096,14 @@ class ScopedGLContext {
// static
CefRefPtr<OSRWindow> OSRWindow::Create(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect,
ClientWindowHandle parentView) {
DCHECK(browser_provider);
if (!browser_provider)
return NULL;
return new OSRWindow(browser_provider, transparent, parentView);
return new OSRWindow(browser_provider, transparent, show_update_rect,
parentView);
}
// static
@ -1241,8 +1243,9 @@ void OSRWindow::ApplyPopupOffset(int& x, int& y) const {
OSRWindow::OSRWindow(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect,
ClientWindowHandle parentView)
: renderer_(transparent),
: renderer_(transparent, show_update_rect),
browser_provider_(browser_provider),
gl_enabled_(false),
painting_popup_(false),

View File

@ -24,6 +24,7 @@ class OSRWindow : public ClientHandler::RenderHandler {
// object.
static CefRefPtr<OSRWindow> Create(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect,
ClientWindowHandle parentView);
static CefRefPtr<OSRWindow> From(
@ -73,6 +74,7 @@ class OSRWindow : public ClientHandler::RenderHandler {
private:
OSRWindow(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect,
ClientWindowHandle parentView);
virtual ~OSRWindow();

View File

@ -42,7 +42,8 @@ class OSRBrowserProvider {
id endWheelMonitor_;
}
- (id)initWithFrame:(NSRect)frame andTransparency:(bool)transparency;
- (id)initWithFrame:(NSRect)frame andTransparency:(bool)transparency
andShowUpdateRect:(bool)show_update_rect;
- (NSPoint)getClickPointForEvent:(NSEvent*)event;
- (void)getKeyEvent:(CefKeyEvent&)keyEvent forEvent:(NSEvent*)event;
- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event;
@ -122,6 +123,7 @@ class OSRWindow {
public:
static CefRefPtr<OSRWindow> Create(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect,
CefWindowHandle parentView,
const CefRect& frame);
@ -134,6 +136,7 @@ class OSRWindow {
private:
OSRWindow(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect,
CefWindowHandle parentView,
const CefRect& frame);

View File

@ -20,11 +20,13 @@
#include "cefclient/osrenderer.h"
#include "cefclient/resource_util.h"
namespace {
// This method will return YES for OS X versions 10.7.3 and later, and NO
// otherwise.
// Used to prevent a crash when building with the 10.7 SDK and accessing the
// notification below. See: http://crbug.com/260595.
static BOOL SupportsBackingPropertiesChangedNotification() {
BOOL SupportsBackingPropertiesChangedNotification() {
// windowDidChangeBackingProperties: method has been added to the
// NSWindowDelegate protocol in 10.7.3, at the same time as the
// NSWindowDidChangeBackingPropertiesNotification notification was added.
@ -43,6 +45,25 @@ static BOOL SupportsBackingPropertiesChangedNotification() {
return methodDescription.name != NULL || methodDescription.types != NULL;
}
class ScopedGLContext {
public:
ScopedGLContext(ClientOpenGLView* view, bool swap_buffers)
: swap_buffers_(swap_buffers) {
context_ = [view openGLContext];
[context_ makeCurrentContext];
}
~ScopedGLContext() {
[NSOpenGLContext clearCurrentContext];
if (swap_buffers_)
[context_ flushBuffer];
}
private:
NSOpenGLContext* context_;
const bool swap_buffers_;
};
} // namespace
@interface ClientOpenGLView ()
- (void)resetDragDrop;
- (void)fillPasteboard;
@ -232,8 +253,7 @@ void ClientOSRHandler::OnPaint(CefRefPtr<CefBrowser> browser,
return;
}
NSOpenGLContext* context = [view_ openGLContext];
[context makeCurrentContext];
ScopedGLContext scoped_gl_context(view_, true);
view_->renderer_->OnPaint(browser, type, dirtyRects, buffer, width, height);
@ -248,7 +268,6 @@ void ClientOSRHandler::OnPaint(CefRefPtr<CefBrowser> browser,
}
view_->renderer_->Render();
[context flushBuffer];
}
void ClientOSRHandler::OnCursorChange(CefRefPtr<CefBrowser> browser,
@ -286,7 +305,8 @@ void ClientOSRHandler::SetLoading(bool isLoading) {
@synthesize was_last_mouse_down_on_view = was_last_mouse_down_on_view_;
- (id)initWithFrame:(NSRect)frame andTransparency:(bool)transparency {
- (id)initWithFrame:(NSRect)frame andTransparency:(bool)transparency
andShowUpdateRect:(bool)show_update_rect {
NSOpenGLPixelFormat * pixelFormat =
[[NSOpenGLPixelFormat alloc]
initWithAttributes:(NSOpenGLPixelFormatAttribute[]) {
@ -299,7 +319,7 @@ void ClientOSRHandler::SetLoading(bool isLoading) {
self = [super initWithFrame:frame pixelFormat:pixelFormat];
if (self) {
renderer_ = new ClientOSRenderer(transparency);
renderer_ = new ClientOSRenderer(transparency, show_update_rect);
rotating_ = false;
endWheelMonitor_ = nil;
@ -1191,18 +1211,23 @@ void ClientOSRHandler::SetLoading(bool isLoading) {
CefRefPtr<OSRWindow> OSRWindow::Create(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect,
CefWindowHandle parentView,
const CefRect& frame) {
return new OSRWindow(browser_provider, transparent, parentView, frame);
return new OSRWindow(browser_provider, transparent, show_update_rect,
parentView, frame);
}
OSRWindow::OSRWindow(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect,
CefWindowHandle parentView,
const CefRect& frame) {
NSRect window_rect = NSMakeRect(frame.x, frame.y, frame.width, frame.height);
ClientOpenGLView* view = [[ClientOpenGLView alloc] initWithFrame:window_rect
andTransparency:transparent];
ClientOpenGLView* view =
[[ClientOpenGLView alloc] initWithFrame:window_rect
andTransparency:transparent
andShowUpdateRect:show_update_rect];
this->view_ = view;
[parentView addSubview:view];
[view setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];

View File

@ -10,14 +10,42 @@
#include "include/wrapper/cef_closure_task.h"
#include "cefclient/resource.h"
namespace {
class ScopedGLContext {
public:
ScopedGLContext(HDC hdc, HGLRC hglrc, bool swap_buffers)
: hdc_(hdc),
swap_buffers_(swap_buffers) {
BOOL result = wglMakeCurrent(hdc, hglrc);
DCHECK(result);
}
~ScopedGLContext() {
BOOL result = wglMakeCurrent(NULL, NULL);
DCHECK(result);
if (swap_buffers_) {
result = SwapBuffers(hdc_);
DCHECK(result);
}
}
private:
const HDC hdc_;
const bool swap_buffers_;
};
} // namespace
// static
CefRefPtr<OSRWindow> OSRWindow::Create(OSRBrowserProvider* browser_provider,
bool transparent) {
CefRefPtr<OSRWindow> OSRWindow::Create(
OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect) {
DCHECK(browser_provider);
if (!browser_provider)
return NULL;
return new OSRWindow(browser_provider, transparent);
return new OSRWindow(browser_provider, transparent, show_update_rect);
}
// static
@ -135,7 +163,8 @@ void OSRWindow::OnPaint(CefRefPtr<CefBrowser> browser,
if (!hDC_)
EnableGL();
wglMakeCurrent(hDC_, hRC_);
{
ScopedGLContext scoped_gl_context(hDC_, hRC_, true);
renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
painting_popup_ = true;
@ -143,7 +172,7 @@ void OSRWindow::OnPaint(CefRefPtr<CefBrowser> browser,
painting_popup_ = false;
}
renderer_.Render();
SwapBuffers(hDC_);
}
}
void OSRWindow::OnCursorChange(CefRefPtr<CefBrowser> browser,
@ -249,8 +278,10 @@ CefBrowserHost::DragOperationsMask
#endif // defined(CEF_USE_ATL)
OSRWindow::OSRWindow(OSRBrowserProvider* browser_provider, bool transparent)
: renderer_(transparent),
OSRWindow::OSRWindow(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect)
: renderer_(transparent, show_update_rect),
browser_provider_(browser_provider),
hWnd_(NULL),
hDC_(NULL),
@ -275,9 +306,8 @@ void OSRWindow::Render() {
if (!hDC_)
EnableGL();
wglMakeCurrent(hDC_, hRC_);
ScopedGLContext scoped_gl_context(hDC_, hRC_, true);
renderer_.Render();
SwapBuffers(hDC_);
}
void OSRWindow::EnableGL() {
@ -303,8 +333,8 @@ void OSRWindow::EnableGL() {
// Create and enable the render context.
hRC_ = wglCreateContext(hDC_);
wglMakeCurrent(hDC_, hRC_);
ScopedGLContext scoped_gl_context(hDC_, hRC_, false);
renderer_.Initialize();
}
@ -314,11 +344,15 @@ void OSRWindow::DisableGL() {
if (!hDC_)
return;
{
ScopedGLContext scoped_gl_context(hDC_, hRC_, false);
renderer_.Cleanup();
}
if (IsWindow(hWnd_)) {
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC_);
// wglDeleteContext will make the context not current before deleting it.
BOOL result = wglDeleteContext(hRC_);
DCHECK(result);
ReleaseDC(hWnd_, hDC_);
}

View File

@ -28,7 +28,8 @@ class OSRWindow : public ClientHandler::RenderHandler
// Create a new OSRWindow instance. |browser_provider| must outlive this
// object.
static CefRefPtr<OSRWindow> Create(OSRBrowserProvider* browser_provider,
bool transparent);
bool transparent,
bool show_update_rect);
static CefRefPtr<OSRWindow> From(
CefRefPtr<ClientHandler::RenderHandler> renderHandler);
@ -100,7 +101,9 @@ class OSRWindow : public ClientHandler::RenderHandler
static int GetCefMouseModifiers(WPARAM wparam);
private:
OSRWindow(OSRBrowserProvider* browser_provider, bool transparent);
OSRWindow(OSRBrowserProvider* browser_provider,
bool transparent,
bool show_update_rect);
virtual ~OSRWindow();
void Render();

View File

@ -463,11 +463,14 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam,
if (AppIsOffScreenRenderingEnabled()) {
CefRefPtr<CefCommandLine> cmd_line = AppGetCommandLine();
bool transparent =
const bool transparent =
cmd_line->HasSwitch(cefclient::kTransparentPaintingEnabled);
const bool show_update_rect =
cmd_line->HasSwitch(cefclient::kShowUpdateRect);
CefRefPtr<OSRWindow> osr_window =
OSRWindow::Create(&g_main_browser_provider, transparent);
OSRWindow::Create(&g_main_browser_provider, transparent,
show_update_rect);
osr_window->CreateWidget(hWnd, rect, hInst, szOSRWindowClass);
info.SetAsWindowless(osr_window->hwnd(), transparent);
g_handler->SetOSRHandler(osr_window.get());

View File

@ -24,6 +24,7 @@ const char kUrl[] = "url";
const char kOffScreenRenderingEnabled[] = "off-screen-rendering-enabled";
const char kOffScreenFrameRate[] = "off-screen-frame-rate";
const char kTransparentPaintingEnabled[] = "transparent-painting-enabled";
const char kShowUpdateRect[] = "show-update-rect";
const char kMouseCursorChangeDisabled[] = "mouse-cursor-change-disabled";
} // namespace cefclient

View File

@ -16,6 +16,7 @@ extern const char kUrl[];
extern const char kOffScreenRenderingEnabled[];
extern const char kOffScreenFrameRate[];
extern const char kTransparentPaintingEnabled[];
extern const char kShowUpdateRect[];
extern const char kMouseCursorChangeDisabled[];
} // namespace cefclient

View File

@ -28,9 +28,21 @@
#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367
#endif
// DCHECK on gl errors.
#ifndef NDEBUG
#define VERIFY_NO_ERROR { \
int _gl_error = glGetError(); \
DCHECK(_gl_error == GL_NO_ERROR) << \
"glGetError returned " << _gl_error; \
}
#else
#define VERIFY_NO_ERROR
#endif
ClientOSRenderer::ClientOSRenderer(bool transparent)
ClientOSRenderer::ClientOSRenderer(bool transparent,
bool show_update_rect)
: transparent_(transparent),
show_update_rect_(show_update_rect),
initialized_(false),
texture_id_(0),
view_width_(0),
@ -47,21 +59,23 @@ void ClientOSRenderer::Initialize() {
if (initialized_)
return;
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); VERIFY_NO_ERROR;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); VERIFY_NO_ERROR;
// Necessary for non-power-of-2 textures to render correctly.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); VERIFY_NO_ERROR;
// Create the texture.
glGenTextures(1, &texture_id_);
DCHECK_NE(texture_id_, 0U);
glGenTextures(1, &texture_id_); VERIFY_NO_ERROR;
DCHECK_NE(texture_id_, 0U); VERIFY_NO_ERROR;
glBindTexture(GL_TEXTURE_2D, texture_id_);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D, texture_id_); VERIFY_NO_ERROR;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST); VERIFY_NO_ERROR;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST); VERIFY_NO_ERROR;
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); VERIFY_NO_ERROR;
initialized_ = true;
}
@ -87,19 +101,19 @@ void ClientOSRenderer::Render() {
{0.0f, 0.0f, -1.0f, 1.0f, 0.0f}
};
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); VERIFY_NO_ERROR;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW); VERIFY_NO_ERROR;
glLoadIdentity(); VERIFY_NO_ERROR;
// Match GL units to screen coordinates.
glViewport(0, 0, view_width_, view_height_);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 0, view_width_, view_height_, 0.1, 100.0);
glViewport(0, 0, view_width_, view_height_); VERIFY_NO_ERROR;
glMatrixMode(GL_PROJECTION); VERIFY_NO_ERROR;
glLoadIdentity(); VERIFY_NO_ERROR;
// Draw the background gradient.
glPushAttrib(GL_ALL_ATTRIB_BITS);
glPushAttrib(GL_ALL_ATTRIB_BITS); VERIFY_NO_ERROR;
// Don't check for errors until glEnd().
glBegin(GL_QUADS);
glColor4f(1.0, 0.0, 0.0, 1.0); // red
glVertex2f(-1.0, -1.0);
@ -107,38 +121,78 @@ void ClientOSRenderer::Render() {
glColor4f(0.0, 0.0, 1.0, 1.0); // blue
glVertex2f(1.0, 1.0);
glVertex2f(-1.0, 1.0);
glEnd();
glPopAttrib();
glEnd(); VERIFY_NO_ERROR;
glPopAttrib(); VERIFY_NO_ERROR;
// Rotate the view based on the mouse spin.
if (spin_x_ != 0)
glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f);
if (spin_y_ != 0)
glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f);
if (spin_x_ != 0) {
glRotatef(-spin_x_, 1.0f, 0.0f, 0.0f); VERIFY_NO_ERROR;
}
if (spin_y_ != 0) {
glRotatef(-spin_y_, 0.0f, 1.0f, 0.0f); VERIFY_NO_ERROR;
}
if (transparent_) {
// Alpha blending style. Texture values have premultiplied alpha.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); VERIFY_NO_ERROR;
// Enable alpha blending.
glEnable(GL_BLEND);
glEnable(GL_BLEND); VERIFY_NO_ERROR;
}
// Enable 2D textures.
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D); VERIFY_NO_ERROR;
// Draw the facets with the texture.
DCHECK_NE(texture_id_, 0U);
glBindTexture(GL_TEXTURE_2D, texture_id_);
glInterleavedArrays(GL_T2F_V3F, 0, vertices);
glDrawArrays(GL_QUADS, 0, 4);
DCHECK_NE(texture_id_, 0U); VERIFY_NO_ERROR;
glBindTexture(GL_TEXTURE_2D, texture_id_); VERIFY_NO_ERROR;
glInterleavedArrays(GL_T2F_V3F, 0, vertices); VERIFY_NO_ERROR;
glDrawArrays(GL_QUADS, 0, 4); VERIFY_NO_ERROR;
// Disable 2D textures.
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_2D); VERIFY_NO_ERROR;
if (transparent_) {
// Disable alpha blending.
glDisable(GL_BLEND);
glDisable(GL_BLEND); VERIFY_NO_ERROR;
}
// Draw a rectangle around the update region.
if (show_update_rect_ && !update_rect_.IsEmpty()) {
int left = update_rect_.x;
int right = update_rect_.x + update_rect_.width;
int top = update_rect_.y;
int bottom = update_rect_.y + update_rect_.height;
#if defined(OS_LINUX)
// Shrink the box so that top & right sides are drawn.
top += 1;
right -= 1;
#else
// Shrink the box so that left & bottom sides are drawn.
left += 1;
bottom -= 1;
#endif
glPushAttrib(GL_ALL_ATTRIB_BITS); VERIFY_NO_ERROR
glMatrixMode(GL_PROJECTION); VERIFY_NO_ERROR;
glPushMatrix(); VERIFY_NO_ERROR;
glLoadIdentity(); VERIFY_NO_ERROR;
glOrtho(0, view_width_, view_height_, 0, 0, 1); VERIFY_NO_ERROR;
glLineWidth(1); VERIFY_NO_ERROR;
glColor3f(1.0f, 0.0f, 0.0f); VERIFY_NO_ERROR;
// Don't check for errors until glEnd().
glBegin(GL_LINE_STRIP);
glVertex2i(left, top);
glVertex2i(right, top);
glVertex2i(right, bottom);
glVertex2i(left, bottom);
glVertex2i(left, top);
glEnd(); VERIFY_NO_ERROR;
glPopMatrix(); VERIFY_NO_ERROR;
glPopAttrib(); VERIFY_NO_ERROR;
}
}
@ -192,14 +246,14 @@ void ClientOSRenderer::OnPaint(CefRefPtr<CefBrowser> browser,
if (transparent_) {
// Enable alpha blending.
glEnable(GL_BLEND);
glEnable(GL_BLEND); VERIFY_NO_ERROR;
}
// Enable 2D textures.
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_2D); VERIFY_NO_ERROR;
DCHECK_NE(texture_id_, 0U);
glBindTexture(GL_TEXTURE_2D, texture_id_);
glBindTexture(GL_TEXTURE_2D, texture_id_); VERIFY_NO_ERROR;
if (type == PET_VIEW) {
int old_width = view_width_;
@ -208,24 +262,30 @@ void ClientOSRenderer::OnPaint(CefRefPtr<CefBrowser> browser,
view_width_ = width;
view_height_ = height;
glPixelStorei(GL_UNPACK_ROW_LENGTH, view_width_);
if (show_update_rect_)
update_rect_ = dirtyRects[0];
if (old_width != view_width_ || old_height != view_height_) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, view_width_); VERIFY_NO_ERROR;
if (old_width != view_width_ || old_height != view_height_ ||
(dirtyRects.size() == 1 &&
dirtyRects[0] == CefRect(0, 0, view_width_, view_height_))) {
// Update/resize the whole texture.
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, view_width_, view_height_, 0,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); VERIFY_NO_ERROR;
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); VERIFY_NO_ERROR;
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, view_width_, view_height_, 0,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, buffer); VERIFY_NO_ERROR;
} else {
// Update just the dirty rectangles.
CefRenderHandler::RectList::const_iterator i = dirtyRects.begin();
for (; i != dirtyRects.end(); ++i) {
const CefRect& rect = *i;
glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x);
glPixelStorei(GL_UNPACK_SKIP_ROWS, rect.y);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x); VERIFY_NO_ERROR;
glPixelStorei(GL_UNPACK_SKIP_ROWS, rect.y); VERIFY_NO_ERROR;
glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width,
rect.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
buffer);
buffer); VERIFY_NO_ERROR;
}
}
} else if (type == PET_POPUP && popup_rect_.width > 0 &&
@ -250,19 +310,19 @@ void ClientOSRenderer::OnPaint(CefRefPtr<CefBrowser> browser,
h -= y + h - view_height_;
// Update the popup rectangle.
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
glPixelStorei(GL_UNPACK_ROW_LENGTH, width); VERIFY_NO_ERROR;
glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels); VERIFY_NO_ERROR;
glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows); VERIFY_NO_ERROR;
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8_REV, buffer);
GL_UNSIGNED_INT_8_8_8_8_REV, buffer); VERIFY_NO_ERROR;
}
// Disable 2D textures.
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_2D); VERIFY_NO_ERROR;
if (transparent_) {
// Disable alpha blending.
glDisable(GL_BLEND);
glDisable(GL_BLEND); VERIFY_NO_ERROR;
}
}

View File

@ -11,8 +11,8 @@
class ClientOSRenderer {
public:
// The context must outlive this object.
explicit ClientOSRenderer(bool transparent);
ClientOSRenderer(bool transparent,
bool show_update_rect);
virtual ~ClientOSRenderer();
// Initialize the OpenGL environment.
@ -50,7 +50,8 @@ class ClientOSRenderer {
void ClearPopupRects();
private:
bool transparent_;
const bool transparent_;
const bool show_update_rect_;
bool initialized_;
unsigned int texture_id_;
int view_width_;
@ -59,6 +60,7 @@ class ClientOSRenderer {
CefRect original_popup_rect_;
float spin_x_;
float spin_y_;
CefRect update_rect_;
};
#endif // CEF_TESTS_CEFCLIENT_OSRENDERER_H_