split D3D/GLES texture creation

This commit is contained in:
Nick Fisher
2024-11-04 17:17:24 +08:00
parent f3e96fe94a
commit bdcbd90ec6
26 changed files with 897 additions and 646 deletions

View File

@@ -5,7 +5,6 @@ import 'package:thermion_dart/src/viewer/src/shared_types/view.dart' as t;
import 'package:thermion_flutter/src/widgets/src/resize_observer.dart'; import 'package:thermion_flutter/src/widgets/src/resize_observer.dart';
import 'package:thermion_flutter/thermion_flutter.dart'; import 'package:thermion_flutter/thermion_flutter.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
import 'package:vector_math/vector_math_64.dart' hide Colors;
class ThermionTextureWidget extends StatefulWidget { class ThermionTextureWidget extends StatefulWidget {
/// ///
@@ -52,7 +51,10 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
void dispose() { void dispose() {
super.dispose(); super.dispose();
_views.remove(widget.view); _views.remove(widget.view);
_texture?.destroy(); if(_texture != null) {
ThermionFlutterPlatform.instance.destroyTexture(_texture!);
}
_states.remove(this); _states.remove(this);
} }
@@ -79,7 +81,10 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
"Target texture dimensions ${width}x${height} (pixel ratio : $dpr)"); "Target texture dimensions ${width}x${height} (pixel ratio : $dpr)");
_texture = await ThermionFlutterPlatform.instance _texture = await ThermionFlutterPlatform.instance
.createTexture(widget.view, width, height); .createTexture(width, height);
await ThermionFlutterPlatform.instance
.bind(widget.view, _texture!);
_logger.info( _logger.info(
"Actual texture dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)"); "Actual texture dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)");
@@ -109,7 +114,10 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
} }
await texture?.destroy(); if(texture != null) {
ThermionFlutterPlatform.instance.destroyTexture(texture);
}
_views.clear(); _views.clear();
}); });
}); });
@@ -124,7 +132,7 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
/// ///
/// Each instance of ThermionTextureWidget in the widget hierarchy must /// Each instance of ThermionTextureWidget in the widget hierarchy must
/// call[markFrameAvailable] on every frame to notify Flutter that the content /// call [markFrameAvailable] on every frame to notify Flutter that the content
/// of its backing texture has changed. /// of its backing texture has changed.
/// ///
/// Calling [requestFrame] on [ThermionViewer], however, will render all /// Calling [requestFrame] on [ThermionViewer], however, will render all
@@ -149,7 +157,9 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
await widget.viewer.requestFrame(); await widget.viewer.requestFrame();
lastRender = d.inMilliseconds; lastRender = d.inMilliseconds;
} }
await _texture?.markFrameAvailable(); if(_texture != null) {
await ThermionFlutterPlatform.instance.markTextureFrameAvailable(_texture!);
}
_rendering = false; _rendering = false;
} }
_requestFrame(); _requestFrame();
@@ -190,12 +200,7 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
_logger.info( _logger.info(
"Resizing texture to dimensions ${newWidth}x${newHeight} (pixel ratio : $dpr)"); "Resizing texture to dimensions ${newWidth}x${newHeight} (pixel ratio : $dpr)");
await _texture?.resize( await ThermionFlutterPlatform.instance.resizeTexture(_texture!, newWidth, newHeight);
newWidth,
newHeight,
0,
0,
);
_logger.info( _logger.info(
"Resized texture to dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)"); "Resized texture to dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)");

View File

@@ -3,4 +3,5 @@ library thermion_flutter;
export 'src/thermion_flutter_plugin.dart'; export 'src/thermion_flutter_plugin.dart';
export 'src/widgets/widgets.dart'; export 'src/widgets/widgets.dart';
export 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; export 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
export 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
export 'package:thermion_dart/thermion_dart.dart'; export 'package:thermion_dart/thermion_dart.dart';

View File

@@ -24,6 +24,11 @@ dependencies:
logging: ^1.2.0 logging: ^1.2.0
web: ^1.0.0 web: ^1.0.0
dependency_overrides:
thermion_flutter_ffi:
path: ../thermion_flutter_ffi
thermion_flutter_platform_interface:
path: ../thermion_flutter_platform_interface
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

View File

@@ -4,31 +4,16 @@ project(${PROJECT_NAME} LANGUAGES C CXX)
cmake_policy(VERSION 3.14...3.25) cmake_policy(VERSION 3.14...3.25)
# This value is used when generating builds using this plugin, so it must
# not be changed
set(PLUGIN_NAME "thermion_flutter_plugin") set(PLUGIN_NAME "thermion_flutter_plugin")
# Any new source files that you add to the plugin should be added here.
list(APPEND PLUGIN_SOURCES list(APPEND PLUGIN_SOURCES
"thermion_flutter_plugin.cpp" "thermion_flutter_plugin.cpp"
"thermion_flutter_plugin.h" "thermion_flutter_plugin.h"
"flutter_egl_texture.cpp"
) )
set(THERMION_EGL FALSE) add_subdirectory("rendering/egl")
set(WGL_USE_BACKING_WINDOW FALSE)
if(THERMION_EGL)
add_compile_definitions(THERMION_EGL)
list(APPEND PLUGIN_SOURCES "flutter_angle_texture.cpp" "egl_context.cpp" )
else()
if(WGL_USE_BACKING_WINDOW)
add_compile_definitions(WGL_USE_BACKING_WINDOW)
endif()
list(APPEND PLUGIN_SOURCES "wgl_context.cpp" "opengl_texture_buffer.cpp" "backing_window.cpp")
endif()
# Define the plugin library target. Its name must not be changed (see comment
# on PLUGIN_NAME above).
add_library(${PLUGIN_NAME} SHARED add_library(${PLUGIN_NAME} SHARED
"include/thermion_flutter/thermion_flutter_plugin_c_api.h" "include/thermion_flutter/thermion_flutter_plugin_c_api.h"
"thermion_flutter_plugin_c_api.cpp" "thermion_flutter_plugin_c_api.cpp"
@@ -45,37 +30,12 @@ target_compile_features(${PLUGIN_NAME} PUBLIC cxx_std_20)
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)
target_include_directories(${PLUGIN_NAME} INTERFACE target_include_directories(${PLUGIN_NAME} INTERFACE
"${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}"
) )
include_directories( include_directories(
"${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/include/filament" "${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/include/filament"
"${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/include" "${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/include"
)
if(THERMION_EGL)
list(APPEND GL_LIBS
EGL
GLESv2
)
set(ANGLE_OR_OPENGL_DIR angle)
add_library(EGL SHARED IMPORTED)
set_property(TARGET EGL PROPERTY IMPORTED_IMPLIB_DEBUG "${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/lib/windows/x86_64/mdd/libEGL.dll.lib")
set_property(TARGET EGL PROPERTY IMPORTED_IMPLIB_PROFILE "${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/lib/windows/x86_64/mt/angle/libEGL.dll.lib")
set_property(TARGET EGL PROPERTY IMPORTED_IMPLIB_RELEASE "${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/lib/windows/x86_64/mt/angle/libEGL.dll.lib")
add_library(GLESv2 SHARED IMPORTED)
set_property(TARGET GLESv2 PROPERTY IMPORTED_IMPLIB_DEBUG "${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/lib/windows/x86_64/mdd/libGLESv2.dll.lib")
set_property(TARGET GLESv2 PROPERTY IMPORTED_IMPLIB_PROFILE "${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/lib/windows/x86_64/mt/angle/libGLESv2.dll.lib")
set_property(TARGET GLESv2 PROPERTY IMPORTED_IMPLIB_RELEASE "${CMAKE_SOURCE_DIR}/../../../../thermion_dart/native/lib/windows/x86_64/mt/angle/libGLESv2.dll.lib")
else()
list(APPEND GL_LIBS
opengl32
dwmapi
comctl32
)
set(ANGLE_OR_OPENGL_DIR opengl)
endif()
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include
) )
@@ -83,24 +43,19 @@ target_link_libraries(${PLUGIN_NAME} PRIVATE
flutter flutter
flutter_wrapper_plugin flutter_wrapper_plugin
Shlwapi Shlwapi
${GL_LIBS} thermion_egl
) )
# List of absolute paths to libraries that should be bundled with the plugin # Copy thermion_egl library to the Flutter build directory
if(THERMION_EGL) add_custom_command(TARGET ${PLUGIN_NAME} POST_BUILD
set(thermion_flutter_bundled_libraries COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/lib/Debug/angle/libEGL.dll $<TARGET_FILE:thermion_egl>
${CMAKE_CURRENT_SOURCE_DIR}/lib/Debug/angle/libGLESv2.dll $<TARGET_FILE_DIR:${PLUGIN_NAME}>
${CMAKE_CURRENT_SOURCE_DIR}/lib/Debug/libc++.dll
${CMAKE_CURRENT_SOURCE_DIR}/lib/Debug/third_party_abseil-cpp_absl.dll
${CMAKE_CURRENT_SOURCE_DIR}/lib/Debug/third_party_zlib.dll
${CMAKE_CURRENT_SOURCE_DIR}/thermion_dart.dll
PARENT_SCOPE
) )
else()
set(thermion_flutter_bundled_libraries set(thermion_flutter_bundled_libraries
${runner_BINARY_DIR}/../../../native_assets/windows/thermion_dart.dll ${runner_BINARY_DIR}/../../../native_assets/windows/thermion_dart.dll
$<TARGET_FILE:thermion_egl>
PARENT_SCOPE PARENT_SCOPE
) )
endif()

View File

@@ -0,0 +1,53 @@
#pragma once
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <flutter/texture_registrar.h>
#include <flutter_texture_registrar.h>
#include "flutter_egl_texture.h"
namespace thermion::tflutter::windows
{
FlutterEGLTexture::FlutterEGLTexture(HANDLE d3dTexture2DHandle, uint32_t width, uint32_t height) : _width(width), _height(height)
{
_textureDescriptor = std::make_unique<FlutterDesktopGpuSurfaceDescriptor>();
_textureDescriptor->struct_size = sizeof(FlutterDesktopGpuSurfaceDescriptor);
_textureDescriptor->handle = d3dTexture2DHandle;
_textureDescriptor->width = _textureDescriptor->visible_width = width;
_textureDescriptor->height = _textureDescriptor->visible_height = height;
_textureDescriptor->release_context = nullptr;
_textureDescriptor->release_callback = [](void *release_context) {
};
_textureDescriptor->format = kFlutterDesktopPixelFormatBGRA8888;
_texture =
std::make_unique<::flutter::TextureVariant>(::flutter::GpuSurfaceTexture::GpuSurfaceTexture(
kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle,
[&](size_t width, size_t height)
{
if (width != this->_width || height != this->_height)
{
//this->_onResizeRequested(width, height);
}
return _textureDescriptor.get();
}));
}
::flutter::TextureVariant* FlutterEGLTexture::GetFlutterTexture() {
return _texture.get();
}
void FlutterEGLTexture::SetFlutterTextureId(int64_t textureId) {
_flutterTextureId = textureId;
}
int64_t FlutterEGLTexture::GetFlutterTextureId()
{
return _flutterTextureId;
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <d3d.h>
#include <d3d11.h>
#include <memory>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <flutter/texture_registrar.h>
#include <flutter_texture_registrar.h>
namespace thermion::tflutter::windows {
class FlutterEGLTexture {
public:
FlutterEGLTexture(HANDLE d3dTexture2DHandle, uint32_t width, uint32_t height);
::flutter::TextureVariant* GetFlutterTexture();
int64_t GetFlutterTextureId();
void SetFlutterTextureId(int64_t textureId);
private:
uint32_t _width;
uint32_t _height;
std::unique_ptr<FlutterDesktopGpuSurfaceDescriptor> _textureDescriptor = nullptr;
std::unique_ptr<::flutter::TextureVariant> _texture;
int64_t _flutterTextureId = -1;
};
}

View File

@@ -4,8 +4,6 @@ project(${PROJECT_NAME} LANGUAGES C CXX)
cmake_policy(VERSION 3.14...3.25) cmake_policy(VERSION 3.14...3.25)
add_compile_definitions(THERMION_EGL)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(BUILD_SHARED_LIBS TRUE) set(BUILD_SHARED_LIBS TRUE)
set(CMAKE_ENABLE_EXPORTS TRUE) set(CMAKE_ENABLE_EXPORTS TRUE)
@@ -41,22 +39,3 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
d3d11 d3d11
) )
# Test executable
add_executable(${PROJECT_NAME}_test
"main.cpp"
)
# Make sure the test depends on the library
add_dependencies(${PROJECT_NAME}_test ${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME}_test PRIVATE
${PROJECT_NAME}
)
# Copy ANGLE DLLs to the output directory
add_custom_command(TARGET ${PROJECT_NAME}_test POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"E:/angle/libEGL.dll"
"E:/angle/libGLESv2.dll"
"$<TARGET_FILE_DIR:${PROJECT_NAME}_test>"
)

View File

@@ -6,7 +6,7 @@
namespace thermion::windows::egl { namespace thermion::windows::egl {
FlutterEGLContext::FlutterEGLContext() { ThermionEGLContext::ThermionEGLContext() {
// D3D starts here // D3D starts here
IDXGIAdapter *adapter_ = nullptr; IDXGIAdapter *adapter_ = nullptr;
@@ -144,7 +144,7 @@ FlutterEGLContext::FlutterEGLContext() {
} }
} }
void FlutterEGLContext::CreateRenderingSurface( EGLTexture* ThermionEGLContext::CreateRenderingSurface(
uint32_t width, uint32_t height, uint32_t width, uint32_t height,
uint32_t left, uint32_t top uint32_t left, uint32_t top
) { ) {
@@ -152,17 +152,16 @@ void FlutterEGLContext::CreateRenderingSurface(
// glext::importGLESExtensionsEntryPoints(); // glext::importGLESExtensionsEntryPoints();
if(left != 0 || top != 0) { if(left != 0 || top != 0) {
// result->Error("ERROR", std::cout << "ERROR Rendering with EGL uses a Texture render target/Flutter widget and does not need a window offset." << std::endl;
// "Rendering with EGL uses a Texture render target/Flutter widget and does not need a window offset."); return nullptr;
return;
} }
if (_active.get()) { //if (_active && _active.get()) {
// result->Error("ERROR", // // result->Error("ERROR",
// "Texture already exists. You must call destroyTexture before " // // "Texture already exists. You must call destroyTexture before "
// "attempting to create a new one."); // // "attempting to create a new one.");
return; // return nullptr;
} //}
_active = std::make_unique<EGLTexture>( _active = std::make_unique<EGLTexture>(
width, height, width, height,
@@ -176,17 +175,25 @@ void FlutterEGLContext::CreateRenderingSurface(
// this->_channel->InvokeMethod("resize", std::move(val), nullptr); // this->_channel->InvokeMethod("resize", std::move(val), nullptr);
}); });
return _active.get();
} }
void FlutterEGLContext::RenderCallback() { void* ThermionEGLContext::GetSharedContext() {
if(_active.get()) {
((EGLTexture*)_active.get())->RenderCallback();
}
}
void* FlutterEGLContext::GetSharedContext() {
return (void*)_context; return (void*)_context;
} }
void ThermionEGLContext::ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top) {
}
void ThermionEGLContext::DestroyRenderingSurface() {
}
EGLTexture *ThermionEGLContext::GetActiveTexture() {
return _active.get();
}
} }

View File

@@ -7,12 +7,15 @@
namespace thermion::windows::egl { namespace thermion::windows::egl {
class FlutterEGLContext { class ThermionEGLContext {
public: public:
FlutterEGLContext(); ThermionEGLContext();
void* GetSharedContext(); void* GetSharedContext();
void RenderCallback(); EGLTexture * CreateRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top);
void CreateRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top); void DestroyRenderingSurface();
void ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top);
EGLTexture * GetActiveTexture();
private: private:
void* _context = nullptr; void* _context = nullptr;
@@ -20,7 +23,7 @@ private:
EGLDisplay _eglDisplay = NULL; EGLDisplay _eglDisplay = NULL;
ID3D11Device* _D3D11Device = nullptr; ID3D11Device* _D3D11Device = nullptr;
ID3D11DeviceContext* _D3D11DeviceContext = nullptr; ID3D11DeviceContext* _D3D11DeviceContext = nullptr;
std::unique_ptr<EGLTexture> _active; std::unique_ptr<EGLTexture> _active = nullptr;
}; };
} }

View File

@@ -59,21 +59,25 @@ static void logEglError(const char *name) noexcept {
std::cout << name << " failed with " << err << std::endl; std::cout << name << " failed with " << err << std::endl;
} }
void EGLTexture::RenderCallback() { void EGLTexture::Flush() {
glFinish(); // glFlush(); // Ensure GL commands are completed
_D3D11DeviceContext->Flush(); // _D3D11DeviceContext->Flush();
}
HANDLE EGLTexture::GetTextureHandle() {
return _d3dTexture2DHandle;
} }
EGLTexture::~EGLTexture() { EGLTexture::~EGLTexture() {
if (_eglDisplay != EGL_NO_DISPLAY && _eglSurface != EGL_NO_SURFACE) { // if (_eglDisplay != EGL_NO_DISPLAY && _eglSurface != EGL_NO_SURFACE) {
eglReleaseTexImage(_eglDisplay, _eglSurface, EGL_BACK_BUFFER); // eglReleaseTexImage(_eglDisplay, _eglSurface, EGL_BACK_BUFFER);
} // }
auto success = eglDestroySurface(this->_eglDisplay, this->_eglSurface); // auto success = eglDestroySurface(this->_eglDisplay, this->_eglSurface);
if(success != EGL_TRUE) { // if(success != EGL_TRUE) {
std::cout << "Failed to destroy EGL Surface" << std::endl; // std::cout << "Failed to destroy EGL Surface" << std::endl;
} // }
_d3dTexture2D->Release(); _d3dTexture2D->Release();
glDeleteTextures(1, &this->glTextureId); // glDeleteTextures(1, &this->glTextureId);
} }
EGLTexture::EGLTexture( EGLTexture::EGLTexture(
@@ -104,7 +108,7 @@ EGLTexture::EGLTexture(
auto hr = _D3D11Device->CreateTexture2D(&d3d11_texture2D_desc, nullptr, auto hr = _D3D11Device->CreateTexture2D(&d3d11_texture2D_desc, nullptr,
&_d3dTexture2D); &_d3dTexture2D);
if FAILED (hr) { if FAILED (hr) {
// result->Error("ERROR", "Failed to create D3D texture", nullptr); std::cout << "Failed to create D3D texture" << std::endl;
return; return;
; ;
} }
@@ -112,21 +116,39 @@ EGLTexture::EGLTexture(
hr = _d3dTexture2D.As(&resource); hr = _d3dTexture2D.As(&resource);
if FAILED (hr) { if FAILED (hr) {
// result->Error("ERROR", "Failed to create D3D texture", nullptr); std::cout << "Failed to create D3D texture" << std::endl;
return; return;
; ;
} }
hr = resource->GetSharedHandle(&_d3dTexture2DHandle); hr = resource->GetSharedHandle(&_d3dTexture2DHandle);
if FAILED (hr) { if FAILED (hr) {
// result->Error("ERROR", std::cout << "Failed to get shared handle to external D3D texture" << std::endl;
// "Failed to get shared handle to external D3D texture",
// nullptr);
return; return;
; ;
} }
_d3dTexture2D->AddRef(); _d3dTexture2D->AddRef();
std::cout << "Created external D3D texture" << std::endl; std::cout << "Created external D3D texture " << width << "x" << height << std::endl;
// Create render target view of the texture
ID3D11RenderTargetView* rtv = nullptr;
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
rtvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtvDesc.Texture2D.MipSlice = 0;
hr = _D3D11Device->CreateRenderTargetView(_d3dTexture2D.Get(), &rtvDesc, &rtv);
if (FAILED(hr)) {
std::cout << "Failed to create render target view" << std::endl;
return;
}
// Clear the texture to blue
float blueColor[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; // RGBA
_D3D11DeviceContext->ClearRenderTargetView(rtv, blueColor);
_D3D11DeviceContext->Flush();
std::cout << "Filled D3D texture blue" << std::endl;
EGLint pbufferAttribs[] = { EGLint pbufferAttribs[] = {
EGL_WIDTH, width, EGL_HEIGHT, height, EGL_WIDTH, width, EGL_HEIGHT, height,
@@ -144,25 +166,168 @@ EGLTexture::EGLTexture(
return; return;
} }
glGenTextures(1, &glTextureId); /******************
*
*
* THis is working
*
*
*/
// // Clear to purple
// glClearColor(0.5f, 0.0f, 0.5f, 1.0f);
// glClear(GL_COLOR_BUFFER_BIT);
if (glTextureId == 0) { // // Present the surface
std::cout // eglSwapBuffers(_eglDisplay, _eglSurface);
<< "Failed to generate OpenGL texture for ANGLE, OpenGL err was %d",
glGetError(); // // Synchronize
// glFlush();
if (!eglMakeCurrent(_eglDisplay, _eglSurface, _eglSurface, _eglContext)) {
logEglError("eglMakeCurrent");
return; return;
} }
glBindTexture(GL_TEXTURE_2D, glTextureId); // Create and setup shaders for rendering the texture
eglBindTexImage(_eglDisplay, _eglSurface, EGL_BACK_BUFFER); const char* vertexShaderSource = R"(#version 300 es
precision mediump float;
in vec2 position;
in vec2 texcoord;
out vec2 v_texcoord;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
v_texcoord = texcoord;
}
)";
const char* fragmentShaderSource = R"(#version 300 es
precision mediump float;
in vec2 v_texcoord;
uniform sampler2D u_texture;
out vec4 fragColor;
void main() {
fragColor = texture(u_texture, v_texcoord);
}
)";
// Create and compile vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);
// Check vertex shader compilation
GLint success;
GLchar infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog);
std::cout << "Vertex shader compilation failed:\n" << infoLog << std::endl;
}
// Create and compile fragment shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);
// Check fragment shader compilation
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog);
std::cout << "Fragment shader compilation failed:\n" << infoLog << std::endl;
}
// Create shader program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// Check program linking
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
std::cout << "Shader program linking failed:\n" << infoLog << std::endl;
}
// Create the source texture
GLuint sourceTexture;
glGenTextures(1, &sourceTexture);
glBindTexture(GL_TEXTURE_2D, sourceTexture);
// Fill texture with purple color
uint8_t purplePixels[] = {
128, 0, 128, 255 // Single purple pixel (RGBA)
};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, purplePixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// clearGlError // Create vertex buffer for fullscreen quad
GLenum const error = glGetError(); float vertices[] = {
if (error != GL_NO_ERROR) { // Position // Texcoords
std::cout << "Ignoring pending GL error " << error << std::endl; -1.0f, -1.0f, 0.0f, 0.0f, // Bottom left
1.0f, -1.0f, 1.0f, 0.0f, // Bottom right
-1.0f, 1.0f, 0.0f, 1.0f, // Top left
1.0f, 1.0f, 1.0f, 1.0f // Top right
};
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Setup vertex attributes
glUseProgram(shaderProgram);
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
GLint texAttrib = glGetAttribLocation(shaderProgram, "texcoord");
if (posAttrib < 0 || texAttrib < 0) {
std::cout << "Failed to get attribute locations. position: " << posAttrib
<< " texcoord: " << texAttrib << std::endl;
} }
glEnableVertexAttribArray(posAttrib);
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
// Set texture uniform
GLint texUniform = glGetUniformLocation(shaderProgram, "u_texture");
glUniform1i(texUniform, 0); // Use texture unit 0
// Clear and render
glViewport(0, 0, width, height);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw the fullscreen quad
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Present the result
eglSwapBuffers(_eglDisplay, _eglSurface);
glFlush();
// // Cleanup
// glDeleteBuffers(1, &vbo);
// glDeleteProgram(shaderProgram);
// glDeleteShader(vertexShader);
// glDeleteShader(fragmentShader);
// glDeleteTextures(1, &sourceTexture);
// Check for errors
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
std::cout << "GL error after rendering: " << error << std::endl;
}
std::cout << "FINISHED TEXTURE CREATION AND RENDERING" << std::endl;
_D3D11DeviceContext->Flush();
char const *version; char const *version;
version = (char const *)glGetString(GL_VERSION); version = (char const *)glGetString(GL_VERSION);
@@ -172,27 +337,127 @@ EGLTexture::EGLTexture(
glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor); glGetIntegerv(GL_MINOR_VERSION, &minor);
// _textureDescriptor = std::make_unique<FlutterDesktopGpuSurfaceDescriptor>(); std::cout << "FINISHED TEXTURE CREATION" << std::endl;
// _textureDescriptor->struct_size = sizeof(FlutterDesktopGpuSurfaceDescriptor);
// _textureDescriptor->handle = _d3dTexture2DHandle;
// _textureDescriptor->width = _textureDescriptor->visible_width = width;
// _textureDescriptor->height = _textureDescriptor->visible_height = height;
// _textureDescriptor->release_context = nullptr;
// _textureDescriptor->release_callback = [](void *release_context) {
// };
// _textureDescriptor->format = kFlutterDesktopPixelFormatBGRA8888;
// texture =
// std::make_unique<flutter::TextureVariant>(flutter::GpuSurfaceTexture(
// kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle,
// [&](size_t width, size_t height) {
// if(width != this->_width || height != this->_height) {
// this->_onResizeRequested(width, height);
// }
// return _textureDescriptor.get();
// }));
} }
void EGLTexture::FillBlueAndSaveToBMP(const char* filename) {
// Create render target view of the texture
ID3D11RenderTargetView* rtv = nullptr;
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
rtvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtvDesc.Texture2D.MipSlice = 0;
HRESULT hr = _D3D11Device->CreateRenderTargetView(_d3dTexture2D.Get(), &rtvDesc, &rtv);
if (FAILED(hr)) {
std::cout << "Failed to create render target view" << std::endl;
return;
}
// Clear the texture to blue
float blueColor[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; // RGBA
_D3D11DeviceContext->ClearRenderTargetView(rtv, blueColor);
// Create staging texture for CPU read access
D3D11_TEXTURE2D_DESC stagingDesc = {};
_d3dTexture2D->GetDesc(&stagingDesc);
stagingDesc.Usage = D3D11_USAGE_STAGING;
stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
stagingDesc.BindFlags = 0;
stagingDesc.MiscFlags = 0;
ID3D11Texture2D* stagingTexture = nullptr;
hr = _D3D11Device->CreateTexture2D(&stagingDesc, nullptr, &stagingTexture);
if (FAILED(hr)) {
rtv->Release();
std::cout << "Failed to create staging texture" << std::endl;
return;
}
// Copy to staging texture
_D3D11DeviceContext->CopyResource(stagingTexture, _d3dTexture2D.Get());
// Save to BMP
bool success = SaveTextureAsBMP(stagingTexture, filename);
// Cleanup
stagingTexture->Release();
rtv->Release();
if (success) {
std::cout << "Successfully saved texture to " << filename << std::endl;
}
}
bool EGLTexture::SaveTextureAsBMP(ID3D11Texture2D* texture, const char* filename) {
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
// Map texture to get pixel data
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT hr = _D3D11DeviceContext->Map(texture, 0, D3D11_MAP_READ, 0, &mappedResource);
if (FAILED(hr)) {
std::cout << "Failed to map texture" << std::endl;
return false;
}
// BMP file header
#pragma pack(push, 1)
struct BMPHeader {
uint16_t signature;
uint32_t fileSize;
uint32_t reserved;
uint32_t dataOffset;
uint32_t headerSize;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bitsPerPixel;
uint32_t compression;
uint32_t imageSize;
int32_t xPixelsPerMeter;
int32_t yPixelsPerMeter;
uint32_t totalColors;
uint32_t importantColors;
};
#pragma pack(pop)
// Create and fill header
BMPHeader header = {};
header.signature = 0x4D42; // 'BM'
header.fileSize = sizeof(BMPHeader) + desc.Width * desc.Height * 4;
header.dataOffset = sizeof(BMPHeader);
header.headerSize = 40;
header.width = desc.Width;
header.height = desc.Height;
header.planes = 1;
header.bitsPerPixel = 32;
header.compression = 0;
header.imageSize = desc.Width * desc.Height * 4;
header.xPixelsPerMeter = 2835; // 72 DPI
header.yPixelsPerMeter = 2835; // 72 DPI
// Write to file
FILE* file = nullptr;
fopen_s(&file, filename, "wb");
if (!file) {
_D3D11DeviceContext->Unmap(texture, 0);
return false;
}
fwrite(&header, sizeof(header), 1, file);
// Write pixel data (need to flip rows as BMP is bottom-up)
uint8_t* srcData = reinterpret_cast<uint8_t*>(mappedResource.pData);
for (int y = desc.Height - 1; y >= 0; y--) {
uint8_t* rowData = srcData + y * mappedResource.RowPitch;
fwrite(rowData, desc.Width * 4, 1, file);
}
fclose(file);
_D3D11DeviceContext->Unmap(texture, 0);
return true;
}
} // namespace thermion_flutter } // namespace thermion_flutter

View File

@@ -34,9 +34,14 @@ class EGLTexture {
); );
~EGLTexture(); ~EGLTexture();
void RenderCallback(); void Flush();
HANDLE GetTextureHandle();
GLuint glTextureId = 0; GLuint glTextureId = 0;
void FillBlueAndSaveToBMP(const char* filename);
bool SaveTextureAsBMP(ID3D11Texture2D* texture, const char* filename);
private: private:
bool _error = false; bool _error = false;
@@ -57,8 +62,6 @@ class EGLTexture {
EGLConfig _eglConfig = EGL_NO_CONFIG_KHR; EGLConfig _eglConfig = EGL_NO_CONFIG_KHR;
EGLSurface _eglSurface = EGL_NO_SURFACE; EGLSurface _eglSurface = EGL_NO_SURFACE;
// std::unique_ptr<FlutterDesktopGpuSurfaceDescriptor> _textureDescriptor = nullptr;
}; };
} }

View File

@@ -6,14 +6,14 @@
int main() { int main() {
std::cout << "Initializing EGL Context..." << std::endl; std::cout << "Initializing EGL Context..." << std::endl;
thermion::windows::egl::FlutterEGLContext context; thermion::windows::egl::ThermionEGLContext context;
// Create a rendering surface // Create a rendering surface
const uint32_t width = 800; const uint32_t width = 800;
const uint32_t height = 600; const uint32_t height = 600;
std::cout << "Creating rendering surface " << width << "x" << height << std::endl; std::cout << "Creating rendering surface " << width << "x" << height << std::endl;
context.CreateRenderingSurface(width, height, 0, 0); auto *texture = context.CreateRenderingSurface(width, height, 0, 0);
void* sharedContext = context.GetSharedContext(); void* sharedContext = context.GetSharedContext();
if (sharedContext) { if (sharedContext) {
@@ -23,13 +23,10 @@ int main() {
return 1; return 1;
} }
// Run a simple render loop // Fill with blue and save
std::cout << "Starting render loop..." << std::endl; texture->FillBlueAndSaveToBMP("output.bmp");
for (int i = 0; i < 10; i++) {
context.RenderCallback(); std::cout << "Saved blue texture to output.bmp" << std::endl;
std::cout << "Rendered frame " << i + 1 << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "EGL Context demo completed" << std::endl; std::cout << "EGL Context demo completed" << std::endl;
return 0; return 0;

View File

@@ -1,23 +0,0 @@
#pragma once
#include "flutter_texture_buffer.h"
namespace thermion_flutter {
class FlutterRenderContext {
public:
void CreateRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top);
void DestroyRenderingSurface();
void *GetSharedContext();
FlutterTextureBuffer GetActiveTexture() {
return _active->get();
}
protected:
FlutterRenderContext();
std::unique_ptr<FlutterTextureBuffer> _active = nullptr;
std::unique_ptr<FlutterTextureBuffer> _inactive = nullptr;
};
}
#endif

View File

@@ -1,25 +0,0 @@
#ifndef _FLUTTER_TEXTURE_BUFFER_H
#define _FLUTTER_TEXTURE_BUFFER_H
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <flutter/texture_registrar.h>
namespace thermion_flutter {
class FlutterTextureBuffer {
public:
flutter::TextureVariant GetTexture() {
return texture->get();
}
void RegisterFlutterTextureId(int64_t flutterTextureId);
int64_t GetFlutterTextureId();
private:
int64_t flutterTextureId = -1;
std::unique_ptr<flutter::TextureVariant> texture;
};
}
#endif

View File

@@ -27,15 +27,10 @@
#include <vector> #include <vector>
#include <thread> #include <thread>
#include "flutter_render_context.h" #include "flutter_egl_texture.h"
#include "rendering/egl/egl_context.h"
#if THERMION_EGL namespace thermion::tflutter::windows {
#include "egl_context.h"
#else
#include "wgl_context.h"
#endif
namespace thermion_flutter {
using namespace std::chrono_literals; using namespace std::chrono_literals;
@@ -133,10 +128,16 @@ static void _freeResource(ResourceBuffer rbf, void *const plugin) {
((ThermionFlutterPlugin *)plugin)->freeResource(rbf); ((ThermionFlutterPlugin *)plugin)->freeResource(rbf);
} }
static std::unique_ptr<FlutterEGLTexture> _texture;
void ThermionFlutterPlugin::CreateTexture( void ThermionFlutterPlugin::CreateTexture(
const flutter::MethodCall<flutter::EncodableValue> &methodCall, const flutter::MethodCall<flutter::EncodableValue> &methodCall,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) { std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (!_context) {
_context = std::make_unique<thermion::windows::egl::ThermionEGLContext>();
}
const auto *args = const auto *args =
std::get_if<flutter::EncodableList>(methodCall.arguments()); std::get_if<flutter::EncodableList>(methodCall.arguments());
@@ -146,14 +147,17 @@ void ThermionFlutterPlugin::CreateTexture(
int dTop = *(std::get_if<int>(&(args->at(3)))); int dTop = *(std::get_if<int>(&(args->at(3))));
auto width = (uint32_t)round(dWidth ); auto width = (uint32_t)round(dWidth );
auto height = (uint32_t)round(dHeight ); auto height = (uint32_t)round(dHeight );
auto left = (uint32_t)round(dLeft ); auto left = (uint32_t)round(dLeft);
auto top = (uint32_t)round(dTop ); auto top = (uint32_t)round(dTop);
_context->CreateRenderingSurface(width, height, std::move(result), left, top); thermion::windows::egl::EGLTexture* eglTexture = _context->CreateRenderingSurface(width, height, left, top);
auto texture = _context->GetActiveTexture();
auto flutterTextureId = _textureRegistrar->RegisterTexture(texture.GetFlutterTextureId()); _texture = std::make_unique<FlutterEGLTexture>(eglTexture->GetTextureHandle(), width, height);
texture.RegisterFlutterTextureId(flutterTextureId);
auto flutterTextureId = _textureRegistrar->RegisterTexture(_texture->GetFlutterTexture());
_texture->SetFlutterTextureId(flutterTextureId);
auto glTextureId = 0; // _texture.GetGLTextureId();
std::cout << "Registered Flutter texture ID " << flutterTextureId std::cout << "Registered Flutter texture ID " << flutterTextureId
<< std::endl; << std::endl;
@@ -169,7 +173,8 @@ void ThermionFlutterPlugin::DestroyTexture(
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) { std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
if (_context) { if (_context) {
_context->DestroyRenderingSurface(std::move(result)); _context->DestroyRenderingSurface();
result->Success(flutter::EncodableValue((int64_t)nullptr));
} }
else { else {
result->Error("NO_CONTEXT", "No rendering context is active"); result->Error("NO_CONTEXT", "No rendering context is active");
@@ -192,15 +197,11 @@ void ThermionFlutterPlugin::HandleMethodCall(
result->Success(flutter::EncodableValue((int64_t)wrapper)); result->Success(flutter::EncodableValue((int64_t)wrapper));
} else if(methodCall.method_name() == "getSharedContext") { } else if(methodCall.method_name() == "getSharedContext") {
if (!_context) { if (!_context) {
#ifdef THERMION_EGL _context = std::make_unique<thermion::windows::egl::ThermionEGLContext>();
_context = std::make_unique<FlutterEGLContext>(_pluginRegistrar, _textureRegistrar);
#else
_context = std::make_unique<WGLContext>(_pluginRegistrar, _textureRegistrar);
#endif
} }
result->Success(flutter::EncodableValue((int64_t)_context->GetSharedContext())); result->Success(flutter::EncodableValue((int64_t)_context->GetSharedContext()));
} else if (methodCall.method_name() == "resizeWindow") { } else if (methodCall.method_name() == "resizeWindow") {
#if WGL_USE_BACKING_WINDOW
const auto *args = const auto *args =
std::get_if<flutter::EncodableList>(methodCall.arguments()); std::get_if<flutter::EncodableList>(methodCall.arguments());
@@ -215,9 +216,6 @@ void ThermionFlutterPlugin::HandleMethodCall(
_context->ResizeRenderingSurface(width, height, left, top); _context->ResizeRenderingSurface(width, height, left, top);
result->Success(); result->Success();
#else
result->Error("ERROR", "resizeWindow is only available when using a backing window");
#endif
} else if (methodCall.method_name() == "createTexture") { } else if (methodCall.method_name() == "createTexture") {
CreateTexture(methodCall, std::move(result)); CreateTexture(methodCall, std::move(result));
} else if (methodCall.method_name() == "createWindow") { } else if (methodCall.method_name() == "createWindow") {
@@ -226,18 +224,17 @@ void ThermionFlutterPlugin::HandleMethodCall(
DestroyTexture(methodCall, std::move(result)); DestroyTexture(methodCall, std::move(result));
} else if (methodCall.method_name() == "markTextureFrameAvailable") { } else if (methodCall.method_name() == "markTextureFrameAvailable") {
if (_context) { if (_context) {
auto texture = context->GetActiveTexture();
auto flutterTextureId = texture.GetFlutterTextureId(); auto texture = _context->GetActiveTexture();
if(flutterTextureId == -1) { texture->Flush();
const auto* flutterTextureId = std::get_if<int64_t>(methodCall.arguments());
if(!flutterTextureId || *flutterTextureId == -1) {
std::cout << "Bad texture" << std::endl; std::cout << "Bad texture" << std::endl;
return; return;
} }
#ifdef THERMION_EGL std::cout << "Marking texture" << flutterTextureId << "available" << std::endl;
_context->RenderCallback(); _textureRegistrar->MarkTextureFrameAvailable(*flutterTextureId);
#endif
#if !WGL_USE_BACKING_WINDOW
_textureRegistrar->MarkTextureFrameAvailable(flutterTextureId);
#endif
} }
result->Success(flutter::EncodableValue((int64_t)nullptr)); result->Success(flutter::EncodableValue((int64_t)nullptr));
} else if (methodCall.method_name() == "getDriverPlatform") { } else if (methodCall.method_name() == "getDriverPlatform") {

View File

@@ -1,5 +1,4 @@
#ifndef FLUTTER_PLUGIN_FLUTTER_FILAMENT_PLUGIN_H_ #pragma once
#define FLUTTER_PLUGIN_FLUTTER_FILAMENT_PLUGIN_H_
#include <flutter/method_channel.h> #include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h> #include <flutter/plugin_registrar_windows.h>
@@ -15,18 +14,13 @@
#include "GL/GLu.h" #include "GL/GLu.h"
#include "ResourceBuffer.h" #include "ResourceBuffer.h"
#include "rendering/egl/egl_context.h"
#if THERMION_EGL namespace thermion::tflutter::windows {
#include "egl_context.h"
#else
#include "wgl_context.h"
#endif
namespace thermion_flutter { class ThermionFlutterPlugin : public ::flutter::Plugin {
class ThermionFlutterPlugin : public flutter::Plugin {
public: public:
static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); static void RegisterWithRegistrar(::flutter::PluginRegistrarWindows *registrar);
ThermionFlutterPlugin(flutter::TextureRegistrar *textureRegistrar, ThermionFlutterPlugin(flutter::TextureRegistrar *textureRegistrar,
flutter::PluginRegistrarWindows *registrar, flutter::PluginRegistrarWindows *registrar,
@@ -59,13 +53,9 @@ public:
void freeResource(ResourceBuffer rbuf); void freeResource(ResourceBuffer rbuf);
private: private:
#ifdef THERMION_EGL std::unique_ptr<thermion::windows::egl::ThermionEGLContext> _context = nullptr;
std::unique_ptr<FlutterEGLContext> _context = nullptr;
#else
std::unique_ptr<WGLContext> _context = nullptr;
#endif
}; };
} // namespace thermion_flutter } // namespace thermion_flutter
#endif // FLUTTER_PLUGIN_FLUTTER_FILAMENT_PLUGIN_H_

View File

@@ -6,7 +6,7 @@
void ThermionFlutterPluginCApiRegisterWithRegistrar( void ThermionFlutterPluginCApiRegisterWithRegistrar(
FlutterDesktopPluginRegistrarRef registrar) { FlutterDesktopPluginRegistrarRef registrar) {
thermion_flutter::ThermionFlutterPlugin::RegisterWithRegistrar( thermion::tflutter::windows::ThermionFlutterPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarManager::GetInstance() flutter::PluginRegistrarManager::GetInstance()
->GetRegistrar<flutter::PluginRegistrarWindows>(registrar)); ->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));
} }

View File

@@ -1,122 +1,123 @@
import 'package:logging/logging.dart'; // import 'package:logging/logging.dart';
import 'package:thermion_dart/thermion_dart.dart'; // import 'package:thermion_dart/thermion_dart.dart';
import 'thermion_flutter_method_channel_interface.dart'; // import 'thermion_flutter_method_channel_interface.dart';
class FlutterPlatformTexture extends MethodChannelFlutterTexture { // class FlutterPlatformTexture extends ThermionFlutterTexture {
final _logger = Logger("ThermionFlutterTexture"); // final _logger = Logger("ThermionFlutterTexture");
final ThermionViewer viewer; // final ThermionViewer viewer;
final View view; // final View view;
int flutterId = -1; // int flutterId = -1;
int _lastFlutterId = -1; // int _lastFlutterId = -1;
int _lastHardwareId = -1; // int _lastHardwareId = -1;
int hardwareId = -1; // int hardwareId = -1;
int width = -1; // int width = -1;
int height = -1; // int height = -1;
SwapChain? swapChain; // SwapChain? swapChain;
RenderTarget? _renderTarget; // RenderTarget? _renderTarget;
late bool destroySwapChainOnResize; // late bool destroySwapChainOnResize;
bool destroyed = false; // bool destroyed = false;
FlutterPlatformTexture( // FlutterPlatformTexture(
super.channel, this.viewer, this.view, this.swapChain) { // super.channel, this.viewer, this.view, this.swapChain) {
if (swapChain == null) { // if (swapChain == null) {
destroySwapChainOnResize = true; // destroySwapChainOnResize = true;
} else { // } else {
destroySwapChainOnResize = false; // destroySwapChainOnResize = false;
} // }
} // }
@override // @override
Future<void> resize( // Future<void> resize(
int newWidth, int newHeight, int newLeft, int newTop) async { // int newWidth, int newHeight, int newLeft, int newTop) async {
_logger.info( // _logger.info(
"Resizing texture to $newWidth x $newHeight (offset $newLeft, $newTop)"); // "Resizing texture to $newWidth x $newHeight (offset $newLeft, $newTop)");
if (newWidth == this.width && // if (newWidth == this.width &&
newHeight == this.height && // newHeight == this.height &&
newLeft == 0 && // newLeft == 0 &&
newTop == 0) { // newTop == 0) {
_logger.info("Existing texture matches requested dimensions"); // _logger.info("Existing texture matches requested dimensions");
return; // return;
} // }
this.width = newWidth; // this.width = newWidth;
this.height = newHeight; // this.height = newHeight;
var result = // var result =
await channel.invokeMethod("createTexture", [width, height, 0, 0]); // await channel.invokeMethod("createTexture", [width, height, 0, 0]);
if (result == null || (result[0] == -1)) { // if (result == null || (result[0] == -1)) {
throw Exception("Failed to create texture"); // throw Exception("Failed to create texture");
} // }
_lastFlutterId = flutterId; // _lastFlutterId = flutterId;
_lastHardwareId = hardwareId; // _lastHardwareId = hardwareId;
flutterId = result[0] as int; // flutterId = result[0] as int;
hardwareId = result[1] as int; // hardwareId = result[1] as int;
// var window = result[2] as int; // usually 0 for nullptr
_logger.info("Created texture ${flutterId} / ${hardwareId}"); // _logger.info("Created texture ${flutterId} / ${hardwareId}");
if (destroySwapChainOnResize) { // if (destroySwapChainOnResize) {
if (swapChain != null) { // if (swapChain != null) {
await viewer.destroySwapChain(swapChain!); // await viewer.destroySwapChain(swapChain!);
} // }
swapChain = await viewer.createSwapChain(result[2]); // swapChain = await viewer.createSwapChain(window);
await view.setRenderable(true, swapChain!); // await view.setRenderable(true, swapChain!);
} else if (hardwareId != _lastHardwareId) { // } else if (hardwareId != _lastHardwareId) {
if (_renderTarget != null) { // if (_renderTarget != null) {
await viewer.destroyRenderTarget(_renderTarget!); // await viewer.destroyRenderTarget(_renderTarget!);
} // }
_renderTarget = // _renderTarget =
await viewer.createRenderTarget(width, height, hardwareId); // await viewer.createRenderTarget(width, height, hardwareId);
await view.setRenderTarget(_renderTarget!); // await view.setRenderTarget(_renderTarget!);
await view.setRenderable(true, swapChain!); // await view.setRenderable(true, swapChain!);
if (_lastFlutterId != -1 && _lastHardwareId != -1) { // if (_lastFlutterId != -1 && _lastHardwareId != -1) {
await _destroyTexture(_lastFlutterId, _lastHardwareId); // await _destroyTexture(_lastFlutterId, _lastHardwareId);
_lastFlutterId = -1; // _lastFlutterId = -1;
_lastHardwareId = -1; // _lastHardwareId = -1;
} // }
} // }
} // }
Future<void> _destroyTexture( // Future<void> _destroyTexture(
int flutterTextureId, int hardwareTextureId) async { // int flutterTextureId, int hardwareTextureId) async {
try { // try {
await channel.invokeMethod( // await channel.invokeMethod(
"destroyTexture", [flutterTextureId, hardwareTextureId]); // "destroyTexture", [flutterTextureId, hardwareTextureId]);
_logger.info("Destroyed texture $flutterTextureId / $hardwareTextureId"); // _logger.info("Destroyed texture $flutterTextureId / $hardwareTextureId");
} catch (e) { // } catch (e) {
_logger.severe("Failed to destroy texture: $e"); // _logger.severe("Failed to destroy texture: $e");
} // }
} // }
bool destroying = false; // bool destroying = false;
Future destroy() async { // Future destroy() async {
if (destroyed || destroying) { // if (destroyed || destroying) {
return; // return;
} // }
destroying = true; // destroying = true;
await view.setRenderTarget(null); // await view.setRenderTarget(null);
if (_renderTarget != null) { // if (_renderTarget != null) {
await viewer.destroyRenderTarget(_renderTarget!); // await viewer.destroyRenderTarget(_renderTarget!);
_renderTarget = null; // _renderTarget = null;
} // }
if (destroySwapChainOnResize && swapChain != null) { // if (destroySwapChainOnResize && swapChain != null) {
await viewer.destroySwapChain(swapChain!); // await viewer.destroySwapChain(swapChain!);
swapChain = null; // swapChain = null;
} // }
await _destroyTexture(flutterId, hardwareId); // await _destroyTexture(flutterId, hardwareId);
flutterId = -1; // flutterId = -1;
hardwareId = -1; // hardwareId = -1;
destroying = false; // destroying = false;
destroyed = true; // destroyed = true;
} // }
Future markFrameAvailable() async { // Future markFrameAvailable() async {
await channel.invokeMethod("markTextureFrameAvailable", this.flutterId); // await channel.invokeMethod("markTextureFrameAvailable", this.flutterId);
} // }
} // }

View File

@@ -1,77 +0,0 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'dart:ffi';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:thermion_dart/src/viewer/src/ffi/thermion_viewer_ffi.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
import 'package:logging/logging.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
///
/// An abstract implementation of [ThermionFlutterPlatform] that uses
/// Flutter platform channels to create a rendering context,
/// resource loaders, and surface/render target(s).
///
abstract class ThermionFlutterMethodChannelInterface
extends ThermionFlutterPlatform {
final channel = const MethodChannel("dev.thermion.flutter/event");
final _logger = Logger("ThermionFlutterMethodChannelInterface");
ThermionViewerFFI? viewer;
Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async {
if (viewer != null) {
throw Exception(
"Only one ThermionViewer can be created at any given time; ensure you have called [dispose] on the previous instance before constructing a new instance.");
}
var resourceLoader = Pointer<Void>.fromAddress(
await channel.invokeMethod("getResourceLoaderWrapper"));
if (resourceLoader == nullptr) {
throw Exception("Failed to get resource loader");
}
var driverPlatform = await channel.invokeMethod("getDriverPlatform");
var driverPtr = driverPlatform == null
? nullptr
: Pointer<Void>.fromAddress(driverPlatform);
var sharedContext = await channel.invokeMethod("getSharedContext");
var sharedContextPtr = sharedContext == null
? nullptr
: Pointer<Void>.fromAddress(sharedContext);
viewer = ThermionViewerFFI(
resourceLoader: resourceLoader,
driver: driverPtr,
sharedContext: sharedContextPtr,
uberArchivePath: options?.uberarchivePath);
await viewer!.initialized;
viewer!.onDispose(() async {
this.viewer = null;
});
return viewer!;
}
}
abstract class MethodChannelFlutterTexture extends ThermionFlutterTexture {
final MethodChannel channel;
MethodChannelFlutterTexture(this.channel);
@override
int get flutterId;
@override
int get hardwareId;
@override
int get height;
@override
int get width;
}

View File

@@ -0,0 +1,132 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:thermion_dart/thermion_dart.dart' as t;
import 'package:thermion_flutter_ffi/src/thermion_flutter_method_channel_platform.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
import 'package:logging/logging.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart';
///
/// An abstract implementation of [ThermionFlutterPlatform] that uses
/// Flutter platform channels to create a rendering context,
/// resource loaders, and surface/render target(s).
///
class ThermionFlutterMethodChannelPlatform
extends ThermionFlutterPlatform {
final channel = const MethodChannel("dev.thermion.flutter/event");
final _logger = Logger("ThermionFlutterMethodChannelPlatform");
static SwapChain? _swapChain;
ThermionFlutterMethodChannelPlatform._();
static ThermionFlutterMethodChannelPlatform? instance;
static void registerWith() {
instance ??= ThermionFlutterMethodChannelPlatform._();
ThermionFlutterPlatform.instance = instance!;
}
ThermionViewerFFI? viewer;
Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async {
if (viewer != null) {
throw Exception(
"Only one ThermionViewer can be created at any given time; ensure you have called [dispose] on the previous instance before constructing a new instance.");
}
var resourceLoader = Pointer<Void>.fromAddress(
await channel.invokeMethod("getResourceLoaderWrapper"));
if (resourceLoader == nullptr) {
throw Exception("Failed to get resource loader");
}
var driverPlatform = await channel.invokeMethod("getDriverPlatform");
var driverPtr = driverPlatform == null
? nullptr
: Pointer<Void>.fromAddress(driverPlatform);
var sharedContext = await channel.invokeMethod("getSharedContext");
var sharedContextPtr = sharedContext == null
? nullptr
: Pointer<Void>.fromAddress(sharedContext);
viewer = ThermionViewerFFI(
resourceLoader: resourceLoader,
driver: driverPtr,
sharedContext: sharedContextPtr,
uberArchivePath: options?.uberarchivePath);
await viewer!.initialized;
viewer!.onDispose(() async {
_swapChain = null;
this.viewer = null;
});
if (_swapChain != null) {
throw Exception("Only a single swapchain can be created");
}
// this implementation renders directly into a texture/render target
// we still need to create a (headless) swapchain, but the actual dimensions
// don't matter
if (Platform.isMacOS || Platform.isIOS || Platform.isWindows) {
_swapChain = await viewer!.createHeadlessSwapChain(1, 1);
}
return viewer!;
}
Future<ThermionFlutterTexture?> createTexture(int width, int height) async {
var result =
await channel.invokeMethod("createTexture", [width, height, 0, 0]);
if (result == null || (result[0] == -1)) {
throw Exception("Failed to create texture");
}
final flutterId = result[0] as int;
final hardwareId = result[1] as int;
var window = result[2] as int; // usually 0 for nullptr
return ThermionFlutterTexture(flutterId:flutterId, hardwareId:hardwareId, height:height, width:width, window:window);
}
@override
Future bind(view, ThermionFlutterTexture texture) {
// TODO: implement bind
throw UnimplementedError();
}
@override
Future<ThermionFlutterWindow> createWindow(int width, int height, int offsetLeft, int offsetTop) {
// TODO: implement createWindow
throw UnimplementedError();
}
@override
Future destroyTexture(ThermionFlutterTexture texture) {
// TODO: implement destroyTexture
throw UnimplementedError();
}
@override
Future markTextureFrameAvailable(ThermionFlutterTexture texture) async {
await channel.invokeMethod("markTextureFrameAvailable", texture.flutterId);
}
@override
Future<ThermionFlutterTexture> resizeTexture(ThermionFlutterTexture texture, int width, int height) async {
return texture;
}
}

View File

@@ -1,71 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:thermion_dart/thermion_dart.dart' as t;
import 'package:thermion_flutter_ffi/src/thermion_flutter_method_channel_interface.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
import 'package:logging/logging.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart';
import 'platform_texture.dart';
///
/// An implementation of [ThermionFlutterPlatform] that uses
/// Flutter platform channels to create a rendering context,
/// resource loaders, and a texture that will be used as a render target
/// for a headless swapchain.
///
class ThermionFlutterTextureBackedPlatform
extends ThermionFlutterMethodChannelInterface {
final _logger = Logger("ThermionFlutterTextureBackedPlatform");
static SwapChain? _swapChain;
ThermionFlutterTextureBackedPlatform._();
static ThermionFlutterTextureBackedPlatform? instance;
static void registerWith() {
instance ??= ThermionFlutterTextureBackedPlatform._();
ThermionFlutterPlatform.instance = instance!;
}
@override
Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async {
var viewer = await super.createViewer(options: options);
if (_swapChain != null) {
throw Exception("Only a single swapchain can be created");
}
// this implementation renders directly into a texture/render target
// we still need to create a (headless) swapchain, but the actual dimensions
// don't matter
if (Platform.isMacOS || Platform.isIOS || Platform.isWindows) {
_swapChain = await viewer.createHeadlessSwapChain(1, 1);
}
viewer.onDispose(() async {
_swapChain = null;
});
return viewer;
}
// On desktop platforms, textures are always created
Future<ThermionFlutterTexture?> createTexture(
t.View view, int width, int height) async {
var texture = FlutterPlatformTexture(channel, viewer!, view,
(Platform.isMacOS || Platform.isIOS || Platform.isWindows) ? _swapChain : null);
await texture.resize(width, height, 0, 0);
return texture;
}
@override
Future<ThermionFlutterWindow> createWindow(
int width, int height, int offsetLeft, int offsetTop) {
// TODO: implement createWindow
throw UnimplementedError();
}
}

View File

@@ -1,135 +1,135 @@
import 'dart:async'; // import 'dart:async';
import 'package:flutter/services.dart'; // import 'package:flutter/services.dart';
import 'package:thermion_dart/thermion_dart.dart'; // import 'package:thermion_dart/thermion_dart.dart';
import 'package:thermion_flutter_ffi/src/thermion_flutter_method_channel_interface.dart'; // import 'package:thermion_flutter_ffi/src/thermion_flutter_method_channel_interface.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; // import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; // import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
import 'package:logging/logging.dart'; // import 'package:logging/logging.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart'; // import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart';
import 'platform_texture.dart'; // import 'platform_texture.dart';
/// // ///
/// A Windows-only implementation of [ThermionFlutterPlatform] that uses // /// A Windows-only implementation of [ThermionFlutterPlatform] that uses
/// a Flutter platform channel to create a rendering context, // /// a Flutter platform channel to create a rendering context,
/// resource loader and a native HWND that will be sit behind the running // /// resource loader and a native HWND that will be sit behind the running
/// Flutter application. // /// Flutter application.
/// // ///
class ThermionFlutterWindows // class ThermionFlutterWindows
extends ThermionFlutterMethodChannelInterface { // extends ThermionFlutterMethodChannelPlatform {
final _channel = const MethodChannel("dev.thermion.flutter/event"); // final _channel = const MethodChannel("dev.thermion.flutter/event");
final _logger = Logger("ThermionFlutterWindows"); // final _logger = Logger("ThermionFlutterWindows");
ThermionViewer? _viewer; // ThermionViewer? _viewer;
SwapChain? _swapChain; // SwapChain? _swapChain;
ThermionFlutterWindows._() {} // ThermionFlutterWindows._() {}
static void registerWith() { // static void registerWith() {
ThermionFlutterPlatform.instance = ThermionFlutterWindows._(); // ThermionFlutterPlatform.instance = ThermionFlutterWindows._();
} // }
@override // @override
Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async { // Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async {
if(_viewer != null) { // if(_viewer != null) {
throw Exception("Only one viewer should be instantiated over the life of the app"); // throw Exception("Only one viewer should be instantiated over the life of the app");
} // }
_viewer = await super.createViewer(options: options); // _viewer = await super.createViewer(options: options);
_viewer!.onDispose(() async { // _viewer!.onDispose(() async {
_viewer = null; // _viewer = null;
}); // });
return _viewer!; // return _viewer!;
} // }
/// // ///
/// Not supported on Windows. Throws an exception. // /// Not supported on Windows. Throws an exception.
/// // ///
@override // @override
Future<ThermionFlutterTexture?> createTexture(View view, int width, int height) async { // Future<ThermionFlutterTexture?> createTexture(View view, int width, int height) async {
var texture = FlutterPlatformTexture(channel, viewer!, view, null); // var texture = FlutterPlatformTexture(channel, viewer!, view, null);
await texture.resize(width, height, 0, 0); // await texture.resize(width, height, 0, 0);
return texture; // return texture;
} // }
@override // @override
Future<ThermionFlutterWindow> createWindow(int width, int height, int offsetLeft, int offsetTop) async { // Future<ThermionFlutterWindow> createWindow(int width, int height, int offsetLeft, int offsetTop) async {
var result = await _channel // var result = await _channel
.invokeMethod("createWindow", [width, height, offsetLeft, offsetLeft]); // .invokeMethod("createWindow", [width, height, offsetLeft, offsetLeft]);
if (result == null || result[2] == -1) { // if (result == null || result[2] == -1) {
throw Exception("Failed to create window"); // throw Exception("Failed to create window");
} // }
var window = // var window =
ThermionFlutterWindowImpl(result[2], _channel, viewer!); // ThermionFlutterWindowImpl(result[2], _channel, viewer!);
await window.resize(width, height, offsetLeft, offsetTop); // await window.resize(width, height, offsetLeft, offsetTop);
var view = await _viewer!.getViewAt(0); // var view = await _viewer!.getViewAt(0);
await view.updateViewport(width, height); // await view.updateViewport(width, height);
_swapChain = await _viewer!.createSwapChain(window.handle); // _swapChain = await _viewer!.createSwapChain(window.handle);
await view.setRenderable(true, _swapChain!); // await view.setRenderable(true, _swapChain!);
return window; // return window;
} // }
} // }
class ThermionFlutterWindowImpl extends ThermionFlutterWindow { // class ThermionFlutterWindowImpl extends ThermionFlutterWindow {
final ThermionViewer viewer; // final ThermionViewer viewer;
final int handle; // final int handle;
int height = 0; // int height = 0;
int width = 0; // int width = 0;
int offsetLeft = 0; // int offsetLeft = 0;
int offsetTop = 0; // int offsetTop = 0;
final MethodChannel _channel; // final MethodChannel _channel;
ThermionFlutterWindowImpl(this.handle, this._channel, this.viewer); // ThermionFlutterWindowImpl(this.handle, this._channel, this.viewer);
@override // @override
Future destroy() async { // Future destroy() async {
await _channel // await _channel
.invokeMethod("destroyWindow", this.handle); // .invokeMethod("destroyWindow", this.handle);
} // }
@override // @override
Future markFrameAvailable() { // Future markFrameAvailable() {
// TODO: implement markFrameAvailable // // TODO: implement markFrameAvailable
throw UnimplementedError(); // throw UnimplementedError();
} // }
bool _resizing = false; // bool _resizing = false;
/// // ///
/// Called by [ThermionWidget] to resize the window. Don't call this yourself. // /// Called by [ThermionWidget] to resize the window. Don't call this yourself.
/// // ///
@override // @override
Future resize( // Future resize(
int width, int height, int offsetLeft, int offsetTop) async { // int width, int height, int offsetLeft, int offsetTop) async {
if (_resizing) { // if (_resizing) {
throw Exception("Resize underway"); // throw Exception("Resize underway");
} // }
if (width == this.width && height == this.height && this.offsetLeft == offsetLeft && this.offsetTop == offsetTop) { // if (width == this.width && height == this.height && this.offsetLeft == offsetLeft && this.offsetTop == offsetTop) {
return; // return;
} // }
this.width = width; // this.width = width;
this.height = height; // this.height = height;
this.offsetLeft = offsetLeft; // this.offsetLeft = offsetLeft;
this.offsetTop = offsetTop; // this.offsetTop = offsetTop;
_resizing = true; // _resizing = true;
await _channel // await _channel
.invokeMethod("resizeWindow", [width, height, offsetLeft, offsetTop]); // .invokeMethod("resizeWindow", [width, height, offsetLeft, offsetTop]);
_resizing = false; // _resizing = false;
} // }
} // }

View File

@@ -1,3 +1,3 @@
library; library;
export 'src/thermion_flutter_windows.dart'; export 'src/thermion_flutter_windows.dart';
export 'src/thermion_flutter_texture_backed_platform.dart'; export 'src/thermion_flutter_method_channel_platform.dart';

View File

@@ -11,13 +11,13 @@ flutter:
implements: thermion_flutter_platform_interface implements: thermion_flutter_platform_interface
platforms: platforms:
ios: ios:
dartPluginClass: ThermionFlutterTextureBackedPlatform dartPluginClass: ThermionFlutterMethodChannelPlatform
android: android:
dartPluginClass: ThermionFlutterTextureBackedPlatform dartPluginClass: ThermionFlutterMethodChannelPlatform
macos: macos:
dartPluginClass: ThermionFlutterTextureBackedPlatform dartPluginClass: ThermionFlutterMethodChannelPlatform
windows: windows:
dartPluginClass: ThermionFlutterTextureBackedPlatform dartPluginClass: ThermionFlutterMethodChannelPlatform
dependencies: dependencies:
flutter: flutter:
@@ -26,7 +26,9 @@ dependencies:
thermion_flutter_platform_interface: ^0.2.1-dev.16 thermion_flutter_platform_interface: ^0.2.1-dev.16
thermion_dart: ^0.2.1-dev.0.0.16 thermion_dart: ^0.2.1-dev.0.0.16
logging: ^1.2.0 logging: ^1.2.0
dependency_overrides:
thermion_flutter_platform_interface:
path: ../thermion_flutter_platform_interface
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter

View File

@@ -39,7 +39,34 @@ abstract class ThermionFlutterPlatform extends PlatformInterface {
/// call this yourself. May not be supported on all platforms. /// call this yourself. May not be supported on all platforms.
/// ///
Future<ThermionFlutterTexture?> createTexture( Future<ThermionFlutterTexture?> createTexture(
t.View view, int width, int height); int width, int height);
///
///
///
///
Future<ThermionFlutterTexture?> resizeTexture(
ThermionFlutterTexture texture,
int width, int height);
///
///
///
Future destroyTexture(ThermionFlutterTexture texture);
///
///
///
Future markTextureFrameAvailable(ThermionFlutterTexture texture);
///
/// Binds a rendering surface to the given View.
///
/// This is internal; unless you are [thermion_*] package developer, don't
/// call this yourself. May not be supported on all platforms.
///
Future bind(
t.View view, ThermionFlutterTexture texture);
/// ///
/// Create a rendering window. /// Create a rendering window.

View File

@@ -1,17 +1,13 @@
abstract class ThermionFlutterTexture { class ThermionFlutterTexture {
int get width; final int width;
int get height; final int height;
int get flutterId; final int flutterId;
int get hardwareId; final int hardwareId;
/// final int window;
/// Destroy a texture and clean up the texture cache (if applicable).
///
Future destroy();
Future resize(int width, int height, int left, int top); ThermionFlutterTexture({required this.width, required this.height, required this.flutterId, required this.hardwareId, required this.window});
Future markFrameAvailable();
} }