Skip to content

feat: accelerated paint #509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions java/org/cef/CefBrowserSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,29 @@ public class CefBrowserSettings {
*/
public int windowless_frame_rate = 0;

/**
* Set to true to enable shared texture rendering. When enabled, the browser
* will render to a shared texture that can be accessed by the host application
* for hardware-accelerated compositing. This is supported on Windows via D3D11,
* macOS via Metal/OpenGL, and Linux via native buffers.
*/
public boolean shared_texture_enabled = false;

/**
* Set to true to enable external begin frame scheduling. When enabled, the
* client must call CefBrowserHost::SendExternalBeginFrame to trigger frame
* rendering at the specified frame rate.
*/
public boolean external_begin_frame_enabled = false;

public CefBrowserSettings() {}

@Override
public CefBrowserSettings clone() {
CefBrowserSettings tmp = new CefBrowserSettings();
tmp.windowless_frame_rate = windowless_frame_rate;
tmp.shared_texture_enabled = shared_texture_enabled;
tmp.external_begin_frame_enabled = external_begin_frame_enabled;
return tmp;
}
}
19 changes: 19 additions & 0 deletions java/org/cef/CefClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.cef.callback.CefMenuModel;
import org.cef.callback.CefPrintDialogCallback;
import org.cef.callback.CefPrintJobCallback;
import org.cef.handler.CefAcceleratedPaintInfo;
import org.cef.handler.CefClientHandler;
import org.cef.handler.CefContextMenuHandler;
import org.cef.handler.CefDialogHandler;
Expand Down Expand Up @@ -768,6 +769,15 @@ public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
realHandler.onPaint(browser, popup, dirtyRects, buffer, width, height);
}

@Override
public void onAcceleratedPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, CefAcceleratedPaintInfo info) {
if (browser == null) return;

CefRenderHandler realHandler = browser.getRenderHandler();
if (realHandler != null)
realHandler.onAcceleratedPaint(browser, popup, dirtyRects, info);
}

@Override
public void addOnPaintListener(Consumer<CefPaintEvent> listener) {}

Expand All @@ -777,6 +787,15 @@ public void setOnPaintListener(Consumer<CefPaintEvent> listener) {}
@Override
public void removeOnPaintListener(Consumer<CefPaintEvent> listener) {}

@Override
public void addOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener) {}

@Override
public void setOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener) {}

@Override
public void removeOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener) {}

@Override
public boolean startDragging(CefBrowser browser, CefDragData dragData, int mask, int x, int y) {
if (browser == null) return false;
Expand Down
48 changes: 48 additions & 0 deletions java/org/cef/browser/CefAcceleratedPaintEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

package org.cef.browser;

import org.cef.handler.CefAcceleratedPaintInfo;

import java.awt.*;

public class CefAcceleratedPaintEvent {
private final CefBrowser browser;
private final boolean popup;
private final Rectangle[] dirtyRects;
private final CefAcceleratedPaintInfo acceleratedPaintInfo;

public CefAcceleratedPaintEvent(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
CefAcceleratedPaintInfo acceleratedPaintInfo) {
this.browser = browser;
this.popup = popup;
this.dirtyRects = dirtyRects;
this.acceleratedPaintInfo = acceleratedPaintInfo;
}

public CefBrowser getBrowser() {
return browser;
}

public boolean getPopup() {
return popup;
}

public Rectangle[] getDirtyRects() {
return dirtyRects;
}

public CefAcceleratedPaintInfo getAcceleratedPaintInfo() {
return acceleratedPaintInfo;
}

public int getWidth() {
return acceleratedPaintInfo.width;
}

public int getHeight() {
return acceleratedPaintInfo.height;
}
}
9 changes: 9 additions & 0 deletions java/org/cef/browser/CefBrowser.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,15 @@ public void runFileDialog(FileDialogMode mode, String title, String defaultFileP
*/
public void setWindowlessFrameRate(int frameRate);

/**
* Send an external begin frame to trigger frame rendering when external begin frame
* scheduling is enabled. This method should be called at the desired frame rate when
* CefBrowserSettings.external_begin_frame_enabled is set to true.
*
* @throws UnsupportedOperationException if not supported
*/
public void sendExternalBeginFrame();

/**
* Returns the maximum rate in frames per second (fps) that {@code CefRenderHandler::onPaint}
* will be called for a windowless browser. The actual fps may be lower if the browser cannot
Expand Down
54 changes: 54 additions & 0 deletions java/org/cef/browser/CefBrowserOsr.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.cef.CefClient;
import org.cef.OS;
import org.cef.callback.CefDragData;
import org.cef.handler.CefAcceleratedPaintInfo;
import org.cef.handler.CefRenderHandler;
import org.cef.handler.CefScreenInfo;

Expand Down Expand Up @@ -90,6 +91,9 @@ class CefBrowserOsr extends CefBrowser_N implements CefRenderHandler {
private CopyOnWriteArrayList<Consumer<CefPaintEvent>> onPaintListeners =
new CopyOnWriteArrayList<>();

private CopyOnWriteArrayList<Consumer<CefAcceleratedPaintEvent>> onAcceleratedPaintListeners =
new CopyOnWriteArrayList<>();

CefBrowserOsr(CefClient client, String url, boolean transparent, CefRequestContext context,
CefBrowserSettings settings) {
this(client, url, transparent, context, null, null, settings);
Expand Down Expand Up @@ -378,6 +382,22 @@ public void removeOnPaintListener(Consumer<CefPaintEvent> listener) {
onPaintListeners.remove(listener);
}

@Override
public void addOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener) {
onAcceleratedPaintListeners.add(listener);
}

@Override
public void setOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener) {
onAcceleratedPaintListeners.clear();
onAcceleratedPaintListeners.add(listener);
}

@Override
public void removeOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener) {
onAcceleratedPaintListeners.remove(listener);
}

@Override
public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
ByteBuffer buffer, int width, int height) {
Expand Down Expand Up @@ -409,6 +429,40 @@ public void run() {
}
}

@Override
public void onAcceleratedPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, CefAcceleratedPaintInfo info) {
// If window is closing, canvas_ or opengl context could be null
final GLContext context = canvas_ != null ? canvas_.getContext() : null;

if (context == null) {
return;
}

// This result can occur due to GLContext re-initialization when changing displays.
if (context.makeCurrent() == GLContext.CONTEXT_NOT_CURRENT) {
return;
}

// TODO: Implement renderer handling for accelerated paint
// On Windows, convert the D3D11 shared texture handle using ImportMemoryWin32HandleEXT
// through OpenGL's EXT_external_objects_win32. LWJGL supports it, but I am not familiar with JOGL's API.
// renderer_.onAcceleratedPaint(canvas_.getGL().getGL2(), popup, dirtyRects, info);

context.release();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
canvas_.display();
}
});
if (!onAcceleratedPaintListeners.isEmpty()) {
CefAcceleratedPaintEvent paintEvent =
new CefAcceleratedPaintEvent(browser, popup, dirtyRects, info);
for (Consumer<CefAcceleratedPaintEvent> l : onAcceleratedPaintListeners) {
l.accept(paintEvent);
}
}
}

@Override
public boolean onCursorChange(CefBrowser browser, final int cursorType) {
SwingUtilities.invokeLater(new Runnable() {
Expand Down
9 changes: 9 additions & 0 deletions java/org/cef/browser/CefBrowser_N.java
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,14 @@ public void setWindowlessFrameRate(int frameRate) {
}
}

public void sendExternalBeginFrame() {
try {
N_SendExternalBeginFrame();
} catch (UnsatisfiedLinkError ule) {
ule.printStackTrace();
}
}

public CompletableFuture<Integer> getWindowlessFrameRate() {
final CompletableFuture<Integer> future = new CompletableFuture<>();
try {
Expand Down Expand Up @@ -882,5 +890,6 @@ private final native void N_DragTargetDragEnter(
private final native void N_SetParent(long windowHandle, Component canvas);
private final native void N_NotifyMoveOrResizeStarted();
private final native void N_SetWindowlessFrameRate(int frameRate);
private final native void N_SendExternalBeginFrame();
private final native void N_GetWindowlessFrameRate(IntCallback frameRateCallback);
}
43 changes: 43 additions & 0 deletions java/org/cef/handler/CefAcceleratedPaintInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.

package org.cef.handler;

/**
* Structure representing shared texture info for accelerated painting.
*/
public class CefAcceleratedPaintInfo {
/**
* Shared texture handle. The meaning depends on the platform:
* - Windows: HANDLE to a texture that can be opened with D3D11 OpenSharedResource
* - macOS: IOSurface pointer that can be opened with Metal or OpenGL
* - Linux: Contains several planes, each with an fd to the underlying system native buffer
*/
public long shared_texture_handle = 0;

/**
* Format of the shared texture.
*/
public int format = 0;

/**
* Size information for the shared texture.
*/
public int width = 0;
public int height = 0;

public CefAcceleratedPaintInfo() {}

public CefAcceleratedPaintInfo(long shared_texture_handle, int format, int width, int height) {
this.shared_texture_handle = shared_texture_handle;
this.format = format;
this.width = width;
this.height = height;
}

@Override
public CefAcceleratedPaintInfo clone() {
return new CefAcceleratedPaintInfo(shared_texture_handle, format, width, height);
}
}
30 changes: 30 additions & 0 deletions java/org/cef/handler/CefRenderHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package org.cef.handler;

import org.cef.browser.CefAcceleratedPaintEvent;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefPaintEvent;
import org.cef.callback.CefDragData;
Expand Down Expand Up @@ -67,6 +68,35 @@ public interface CefRenderHandler {
public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
ByteBuffer buffer, int width, int height);

/**
* Called when an element has been rendered to the shared texture handle.
* This method is only called when CefWindowInfo::shared_texture_enabled is set to true.
* @param browser The browser generating the event.
* @param popup True if painting a popup window.
* @param dirtyRects Array of dirty regions.
* @param info Contains the shared handle and texture information.
*/
public void onAcceleratedPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
CefAcceleratedPaintInfo info);

/**
* Add provided listener for accelerated paint events.
* @param listener Code that gets executed after a frame was rendered with accelerated painting.
*/
public void addOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener);

/**
* Remove existing accelerated paint listeners and replace with provided listener.
* @param listener Code that gets executed after a frame was rendered with accelerated painting.
*/
public void setOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener);

/**
* Remove provided accelerated paint listener.
* @param listener Code that gets executed after a frame was rendered with accelerated painting.
*/
public void removeOnAcceleratedPaintListener(Consumer<CefAcceleratedPaintEvent> listener);

/**
* Add provided listener.
* @param listener Code that gets executed after a frame was rendered.
Expand Down
4 changes: 4 additions & 0 deletions java/org/cef/handler/CefRenderHandlerAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public void onPopupSize(CefBrowser browser, Rectangle size) {}
public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
ByteBuffer buffer, int width, int height) {}

@Override
public void onAcceleratedPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects,
CefAcceleratedPaintInfo info) {}

@Override
public boolean onCursorChange(CefBrowser browser, int cursorType) {
return false;
Expand Down
14 changes: 13 additions & 1 deletion native/CefBrowser_N.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ KeyboardCode KeyboardCodeFromXKeysym(unsigned int keysym) {
return VKEY_OEM_7;
case XK_ISO_Level5_Shift:
return VKEY_OEM_8;
case XK_Shift_L:
case XK_Shift_L:
case XK_Shift_R:
return VKEY_SHIFT;
case XK_Control_L:
Expand Down Expand Up @@ -1010,6 +1010,10 @@ void create(std::shared_ptr<JNIObjectsForCreate> objs,
objs->jbrowserSettings != nullptr) { // Dev-tools settings are null
GetJNIFieldInt(env, cefBrowserSettings, objs->jbrowserSettings,
"windowless_frame_rate", &settings.windowless_frame_rate);
GetJNIFieldBoolean(env, cefBrowserSettings, objs->jbrowserSettings,
"shared_texture_enabled", &windowInfo.shared_texture_enabled);
GetJNIFieldBoolean(env, cefBrowserSettings, objs->jbrowserSettings,
"external_begin_frame_enabled", &windowInfo.external_begin_frame_enabled);
}

CefRefPtr<CefBrowser> browserObj;
Expand Down Expand Up @@ -2170,6 +2174,14 @@ Java_org_cef_browser_CefBrowser_1N_N_1SetWindowlessFrameRate(JNIEnv* env,
host->SetWindowlessFrameRate(frameRate);
}

JNIEXPORT void JNICALL
Java_org_cef_browser_CefBrowser_1N_N_1SendExternalBeginFrame(JNIEnv* env,
jobject jbrowser) {
CefRefPtr<CefBrowser> browser = JNI_GET_BROWSER_OR_RETURN(env, jbrowser);
CefRefPtr<CefBrowserHost> host = browser->GetHost();
host->SendExternalBeginFrame();
}

void getWindowlessFrameRate(CefRefPtr<CefBrowserHost> host,
CefRefPtr<IntCallback> callback) {
callback->onComplete((jint)host->GetWindowlessFrameRate());
Expand Down
9 changes: 9 additions & 0 deletions native/CefBrowser_N.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading