cef/tests/cefclient/browser/osr_render_handler_win_d3d11.cc
reito 260dd0ca24 osr: Implement shared texture support (fixes #1006, fixes #2575)
Adds support for the OnAcceleratedPaint callback. Verified to work
on macOS and Windows. Linux support is present but not implemented
for cefclient, so it is not verified to work.

To test:
Run `cefclient --off-screen-rendering-enabled --shared-texture-enabled`
2024-04-23 13:03:56 -04:00

230 lines
6.1 KiB
C++

// Copyright 2018 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.
#include "tests/cefclient/browser/osr_render_handler_win_d3d11.h"
#include "include/base/cef_callback.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/shared/browser/util_win.h"
namespace client {
BrowserLayer::BrowserLayer(const std::shared_ptr<d3d11::Device>& device)
: d3d11::Layer(device, false /* flip */) {
frame_buffer_ = std::make_shared<d3d11::FrameBuffer>(device_);
}
void BrowserLayer::render(const std::shared_ptr<d3d11::Context>& ctx) {
// Use the base class method to draw our texture.
render_texture(ctx, frame_buffer_->texture());
}
void BrowserLayer::on_paint(void* share_handle) {
frame_buffer_->on_paint(share_handle);
}
std::pair<uint32_t, uint32_t> BrowserLayer::texture_size() const {
const auto texture = frame_buffer_->texture();
return std::make_pair(texture->width(), texture->height());
}
PopupLayer::PopupLayer(const std::shared_ptr<d3d11::Device>& device)
: BrowserLayer(device) {}
void PopupLayer::set_bounds(const CefRect& bounds) {
const auto comp = composition();
if (!comp) {
return;
}
const auto outer_width = comp->width();
const auto outer_height = comp->height();
if (outer_width == 0 || outer_height == 0) {
return;
}
original_bounds_ = bounds;
bounds_ = bounds;
// If x or y are negative, move them to 0.
if (bounds_.x < 0) {
bounds_.x = 0;
}
if (bounds_.y < 0) {
bounds_.y = 0;
}
// If popup goes outside the view, try to reposition origin
if (bounds_.x + bounds_.width > outer_width) {
bounds_.x = outer_width - bounds_.width;
}
if (bounds_.y + bounds_.height > outer_height) {
bounds_.y = outer_height - bounds_.height;
}
// If x or y became negative, move them to 0 again.
if (bounds_.x < 0) {
bounds_.x = 0;
}
if (bounds_.y < 0) {
bounds_.y = 0;
}
const auto x = bounds_.x / float(outer_width);
const auto y = bounds_.y / float(outer_height);
const auto w = bounds_.width / float(outer_width);
const auto h = bounds_.height / float(outer_height);
move(x, y, w, h);
}
OsrRenderHandlerWinD3D11::OsrRenderHandlerWinD3D11(
const OsrRendererSettings& settings,
HWND hwnd)
: OsrRenderHandlerWin(settings, hwnd) {}
bool OsrRenderHandlerWinD3D11::Initialize(CefRefPtr<CefBrowser> browser,
int width,
int height) {
CEF_REQUIRE_UI_THREAD();
// Create a D3D11 device instance.
device_ = d3d11::Device::create();
DCHECK(device_);
if (!device_) {
return false;
}
// Create a D3D11 swapchain for the window.
swap_chain_ = device_->create_swapchain(hwnd());
DCHECK(swap_chain_);
if (!swap_chain_) {
return false;
}
// Create the browser layer.
browser_layer_ = std::make_shared<BrowserLayer>(device_);
// Set up the composition.
composition_ = std::make_shared<d3d11::Composition>(device_, width, height);
composition_->add_layer(browser_layer_);
// Size to the whole composition.
browser_layer_->move(0.0f, 0.0f, 1.0f, 1.0f);
start_time_ = GetTimeNow();
SetBrowser(browser);
return true;
}
void OsrRenderHandlerWinD3D11::SetSpin(float spinX, float spinY) {
CEF_REQUIRE_UI_THREAD();
// Spin support is not implemented.
}
void OsrRenderHandlerWinD3D11::IncrementSpin(float spinDX, float spinDY) {
CEF_REQUIRE_UI_THREAD();
// Spin support is not implemented.
}
bool OsrRenderHandlerWinD3D11::IsOverPopupWidget(int x, int y) const {
CEF_REQUIRE_UI_THREAD();
return popup_layer_ && popup_layer_->contains(x, y);
}
int OsrRenderHandlerWinD3D11::GetPopupXOffset() const {
CEF_REQUIRE_UI_THREAD();
if (popup_layer_) {
return popup_layer_->xoffset();
}
return 0;
}
int OsrRenderHandlerWinD3D11::GetPopupYOffset() const {
CEF_REQUIRE_UI_THREAD();
if (popup_layer_) {
return popup_layer_->yoffset();
}
return 0;
}
void OsrRenderHandlerWinD3D11::OnPopupShow(CefRefPtr<CefBrowser> browser,
bool show) {
CEF_REQUIRE_UI_THREAD();
if (show) {
DCHECK(!popup_layer_);
// Create a new layer.
popup_layer_ = std::make_shared<PopupLayer>(device_);
composition_->add_layer(popup_layer_);
} else {
DCHECK(popup_layer_);
composition_->remove_layer(popup_layer_);
popup_layer_ = nullptr;
Render();
}
}
void OsrRenderHandlerWinD3D11::OnPopupSize(CefRefPtr<CefBrowser> browser,
const CefRect& rect) {
CEF_REQUIRE_UI_THREAD();
popup_layer_->set_bounds(rect);
}
void OsrRenderHandlerWinD3D11::OnPaint(
CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const void* buffer,
int width,
int height) {
// Not used with this implementation.
NOTREACHED();
}
void OsrRenderHandlerWinD3D11::OnAcceleratedPaint(
CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const CefAcceleratedPaintInfo& info) {
CEF_REQUIRE_UI_THREAD();
if (type == PET_POPUP) {
popup_layer_->on_paint(info.shared_texture_handle);
} else {
browser_layer_->on_paint(info.shared_texture_handle);
}
Render();
}
void OsrRenderHandlerWinD3D11::Render() {
// Update composition + layers based on time.
const auto t = (GetTimeNow() - start_time_) / 1000000.0;
composition_->tick(t);
auto ctx = device_->immedidate_context();
swap_chain_->bind(ctx);
const auto texture_size = browser_layer_->texture_size();
// Resize the composition and swap chain to match the texture if necessary.
composition_->resize(!send_begin_frame(), texture_size.first,
texture_size.second);
swap_chain_->resize(texture_size.first, texture_size.second);
// Clear the render target.
swap_chain_->clear(0.0f, 0.0f, 1.0f, 1.0f);
// Render the scene.
composition_->render(ctx);
// Present to window.
swap_chain_->present(send_begin_frame() ? 0 : 1);
}
} // namespace client