Skip to content
Closed
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
2 changes: 2 additions & 0 deletions engine/src/flutter/shell/platform/embedder/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ template("embedder_source_set") {
"embedder_engine.h",
"embedder_external_texture_resolver.cc",
"embedder_external_texture_resolver.h",
"embedder_external_texture_software.cc",
"embedder_external_texture_software.h",
"embedder_external_view.cc",
"embedder_external_view.h",
"embedder_external_view_embedder.cc",
Expand Down
36 changes: 36 additions & 0 deletions engine/src/flutter/shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2314,6 +2314,42 @@ FlutterEngineResult FlutterEngineInitialize(size_t version,
}
}
#endif

// Software-mode external texture callback (no GL/Metal required).
// Allows Texture widget to render plugin-provided pixel buffers when
// Flutter falls back to software rasterization.
flutter::EmbedderExternalTextureSoftware::ExternalTextureCallback
external_texture_software_callback;
if (config->type == kSoftware) {
const FlutterSoftwareRendererConfig* software_config = &config->software;
if (SAFE_ACCESS(software_config, external_texture_frame_callback,
nullptr) != nullptr) {
auto sw_user_data =
SAFE_ACCESS(software_config, external_texture_user_data, user_data);
// If embedder did not set the override field (or set it to nullptr),
// fall back to the main engine user_data — every callback reaches
// back into the engine via that pointer.
if (!sw_user_data) {
sw_user_data = user_data;
}
external_texture_software_callback =
[ptr = software_config->external_texture_frame_callback,
sw_user_data](int64_t texture_identifier, size_t width,
size_t height)
-> std::unique_ptr<FlutterSoftwarePixelBuffer> {
std::unique_ptr<FlutterSoftwarePixelBuffer> pixel_buffer =
std::make_unique<FlutterSoftwarePixelBuffer>();
if (!ptr(sw_user_data, texture_identifier, width, height,
pixel_buffer.get())) {
return nullptr;
}
return pixel_buffer;
};
external_texture_resolver = std::make_unique<ExternalTextureResolver>(
external_texture_software_callback);
}
}

auto custom_task_runners = SAFE_ACCESS(args, custom_task_runners, nullptr);
auto thread_config_callback = [&custom_task_runners](
const fml::Thread::ThreadConfig& config) {
Expand Down
42 changes: 42 additions & 0 deletions engine/src/flutter/shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,36 @@ typedef bool (*TextureFrameCallback)(void* /* user data */,
size_t /* width */,
size_t /* height */,
FlutterOpenGLTexture* /* texture out */);

// A CPU pixel buffer used to deliver an external texture frame in software
// rendering mode. Mirrors the generic shape of FlutterDesktopPixelBuffer but
// is defined here in the embedder layer to keep the renderer-config API
// platform-agnostic.
typedef struct {
/// Pointer to the pixel data. Must be RGBA8888 with row-major layout and
/// |width| * 4 bytes per row.
const uint8_t* buffer;
/// Width of the pixel buffer in pixels.
size_t width;
/// Height of the pixel buffer in pixels.
size_t height;
/// Optional callback invoked when the engine has consumed |buffer|. The
/// embedder must keep |buffer| valid until this callback fires (or until
/// the texture is unregistered).
void (*release_callback)(void* release_context);
/// Opaque data passed to |release_callback|.
void* release_context;
} FlutterSoftwarePixelBuffer;

// Software-mode external texture callback. Mirrors |TextureFrameCallback|
// but delivers a CPU pixel buffer instead of a GL texture.
typedef bool (*SoftwareTextureFrameCallback)(
void* /* user data */,
int64_t /* texture identifier */,
size_t /* width */,
size_t /* height */,
FlutterSoftwarePixelBuffer* /* pixel buffer out */);

typedef void (*VsyncCallback)(void* /* user data */, intptr_t /* baton */);
typedef void (*OnPreEngineRestartCallback)(void* /* user data */);

Expand Down Expand Up @@ -920,6 +950,18 @@ typedef struct {
/// format. The buffer is owned by the Flutter engine and must be copied in
/// this callback if needed.
SoftwareSurfacePresentCallback surface_present_callback;

/// Optional callback invoked by the engine to retrieve a pixel buffer for
/// an external texture rendered in software mode. When set, allows the
/// |Texture| widget to render content provided by embedders/plugins even
/// when the engine is using the software rasterizer (e.g. when ANGLE/D3D11
/// is unavailable on Windows).
///
/// This field is read using SAFE_ACCESS so older embedders that do not
/// provide it continue to work.
SoftwareTextureFrameCallback external_texture_frame_callback;
/// User data passed back to |external_texture_frame_callback|.
void* external_texture_user_data;
} FlutterSoftwareRendererConfig;

typedef struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ EmbedderExternalTextureResolver::EmbedderExternalTextureResolver(
: metal_callback_(std::move(metal_callback)) {}
#endif

EmbedderExternalTextureResolver::EmbedderExternalTextureResolver(
EmbedderExternalTextureSoftware::ExternalTextureCallback sw_callback)
: software_callback_(std::move(sw_callback)) {}

std::unique_ptr<Texture>
EmbedderExternalTextureResolver::ResolveExternalTexture(int64_t texture_id) {
#ifdef SHELL_ENABLE_GL
Expand All @@ -37,6 +41,11 @@ EmbedderExternalTextureResolver::ResolveExternalTexture(int64_t texture_id) {
}
#endif

if (software_callback_) {
return std::make_unique<EmbedderExternalTextureSoftware>(texture_id,
software_callback_);
}

return nullptr;
}

Expand All @@ -53,6 +62,10 @@ bool EmbedderExternalTextureResolver::SupportsExternalTextures() {
}
#endif

if (software_callback_) {
return true;
}

return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "flutter/shell/platform/embedder/embedder_external_texture_metal.h"
#endif

#include "flutter/shell/platform/embedder/embedder_external_texture_software.h"

namespace flutter {
class EmbedderExternalTextureResolver {
public:
Expand All @@ -34,6 +36,9 @@ class EmbedderExternalTextureResolver {
EmbedderExternalTextureMetal::ExternalTextureCallback metal_callback);
#endif

explicit EmbedderExternalTextureResolver(
EmbedderExternalTextureSoftware::ExternalTextureCallback sw_callback);

std::unique_ptr<Texture> ResolveExternalTexture(int64_t texture_id);

bool SupportsExternalTextures();
Expand All @@ -47,6 +52,8 @@ class EmbedderExternalTextureResolver {
EmbedderExternalTextureMetal::ExternalTextureCallback metal_callback_;
#endif

EmbedderExternalTextureSoftware::ExternalTextureCallback software_callback_;

FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalTextureResolver);
};
} // namespace flutter
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2013 The Flutter 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 "flutter/shell/platform/embedder/embedder_external_texture_software.h"

#include "flutter/fml/logging.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkPixmap.h"
#include "third_party/skia/include/core/SkSize.h"

namespace flutter {

EmbedderExternalTextureSoftware::EmbedderExternalTextureSoftware(
int64_t texture_identifier,
const ExternalTextureCallback& callback)
: Texture(texture_identifier), external_texture_callback_(callback) {
FML_DCHECK(external_texture_callback_);
}

EmbedderExternalTextureSoftware::~EmbedderExternalTextureSoftware() = default;

// |flutter::Texture|
void EmbedderExternalTextureSoftware::Paint(PaintContext& context,
const SkRect& bounds,
bool freeze,
const DlImageSampling sampling) {
if (last_image_ == nullptr) {
last_image_ =
ResolveTexture(Id(), //
SkISize::Make(bounds.width(), bounds.height()) //
);
}

DlCanvas* canvas = context.canvas;
const DlPaint* paint = context.paint;

if (last_image_) {
SkRect image_bounds = SkRect::Make(last_image_->bounds());
if (bounds != image_bounds) {
canvas->DrawImageRect(last_image_, image_bounds, bounds, sampling, paint);
} else {
canvas->DrawImage(last_image_, SkPoint{bounds.x(), bounds.y()}, sampling,
paint);
}
}
}

sk_sp<DlImage> EmbedderExternalTextureSoftware::ResolveTexture(
int64_t texture_id,
const SkISize& size) {
std::unique_ptr<FlutterSoftwarePixelBuffer> pixel_buffer =
external_texture_callback_(texture_id, size.width(), size.height());

if (!pixel_buffer || !pixel_buffer->buffer || pixel_buffer->width == 0 ||
pixel_buffer->height == 0) {
return nullptr;
}

SkImageInfo info = SkImageInfo::Make(
static_cast<int>(pixel_buffer->width),
static_cast<int>(pixel_buffer->height), kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
SkPixmap pixmap(info, pixel_buffer->buffer,
pixel_buffer->width * 4 /* row bytes (RGBA) */);
Comment thread
guide-nhant marked this conversation as resolved.

// Copy the pixels into a Skia-owned SkImage so we can release the source
// buffer immediately. RasterFromPixmapCopy allocates and copies; for very
// high frame rates we could alternatively use RasterFromPixmap with a
// release proc to avoid the copy, but that requires more careful lifetime
// management with the embedder's pixel buffer ownership model.
sk_sp<SkImage> image = SkImages::RasterFromPixmapCopy(pixmap);

if (pixel_buffer->release_callback) {
pixel_buffer->release_callback(pixel_buffer->release_context);
}

if (!image) {
FML_LOG(ERROR) << "Could not create software external texture image.";
return nullptr;
}

return DlImage::Make(std::move(image));
}

// |flutter::Texture|
void EmbedderExternalTextureSoftware::OnGrContextCreated() {}

// |flutter::Texture|
void EmbedderExternalTextureSoftware::OnGrContextDestroyed() {}

// |flutter::Texture|
void EmbedderExternalTextureSoftware::MarkNewFrameAvailable() {
last_image_ = nullptr;
}

// |flutter::Texture|
void EmbedderExternalTextureSoftware::OnTextureUnregistered() {}

} // namespace flutter
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_SOFTWARE_H_
#define FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_SOFTWARE_H_

#include "flutter/common/graphics/texture.h"
#include "flutter/fml/macros.h"
#include "flutter/shell/platform/embedder/embedder.h"
#include "third_party/skia/include/core/SkSize.h"

namespace flutter {

// External texture implementation for software rendering mode.
// Wraps a CPU pixel buffer as a Skia raster image — no GL required.
// Mirrors EmbedderExternalTextureGL but bypasses the OpenGL pipeline so
// embedders can render external textures while Flutter is in software mode.
class EmbedderExternalTextureSoftware : public flutter::Texture {
public:
using ExternalTextureCallback = std::function<
std::unique_ptr<FlutterSoftwarePixelBuffer>(int64_t, size_t, size_t)>;

EmbedderExternalTextureSoftware(int64_t texture_identifier,
const ExternalTextureCallback& callback);

~EmbedderExternalTextureSoftware() override;

private:
const ExternalTextureCallback& external_texture_callback_;
sk_sp<DlImage> last_image_;

sk_sp<DlImage> ResolveTexture(int64_t texture_id, const SkISize& size);

// |flutter::Texture|
void Paint(PaintContext& context,
const SkRect& bounds,
bool freeze,
const DlImageSampling sampling) override;

// |flutter::Texture|
void OnGrContextCreated() override;

// |flutter::Texture|
void OnGrContextDestroyed() override;

// |flutter::Texture|
void MarkNewFrameAvailable() override;

// |flutter::Texture|
void OnTextureUnregistered() override;

FML_DISALLOW_COPY_AND_ASSIGN(EmbedderExternalTextureSoftware);
};

} // namespace flutter

#endif // FLUTTER_SHELL_PLATFORM_EMBEDDER_EMBEDDER_EXTERNAL_TEXTURE_SOFTWARE_H_
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ FlutterRendererConfig GetSoftwareRendererConfig() {
FML_UNREACHABLE();
return false;
};
// Allow the Texture widget to render plugin-provided pixel buffers while
// Flutter is in software mode. Without this, |Texture| would render black
// because the embedder API has no way to ask back for the pixel data.
config.software.external_texture_frame_callback =
[](void* user_data, int64_t texture_id, size_t width, size_t height,
FlutterSoftwarePixelBuffer* out) -> bool {
auto host = static_cast<FlutterWindowsEngine*>(user_data);
if (!host->texture_registrar()) {
return false;
}
return host->texture_registrar()->PopulateTextureSoftware(
texture_id, width, height, out);
};
return config;
}

Expand Down
Loading