using createImageFromImageBitmap to import the canvas directly into a Flutter app results in flickering unless this is set to true (as the drawing buffer is otherwise cleared after each render, which may not line up with Flutter's ticker
233 lines
7.0 KiB
C++
233 lines
7.0 KiB
C++
#include "ThermionFlutterWebApi.h"
|
|
#include "ResourceBuffer.hpp"
|
|
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <future>
|
|
#include <iostream>
|
|
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#include <GL/gl.h>
|
|
#include <GL/glext.h>
|
|
#include <emscripten/emscripten.h>
|
|
#include <emscripten/html5.h>
|
|
#include <emscripten/threading.h>
|
|
#include <emscripten/val.h>
|
|
#include <emscripten/fetch.h>
|
|
|
|
class PendingCall
|
|
{
|
|
public:
|
|
PendingCall()
|
|
{
|
|
}
|
|
~PendingCall() {}
|
|
|
|
void Wait()
|
|
{
|
|
std::future<int32_t> accumulate_future = prom.get_future();
|
|
std::cout << "Loaded asset from Flutter of length " << accumulate_future.get() << std::endl;
|
|
}
|
|
|
|
void HandleResponse(void* data, int32_t length)
|
|
{
|
|
this->data = data;
|
|
this->length = length;
|
|
prom.set_value(length);
|
|
}
|
|
void* data = nullptr;
|
|
int32_t length = 0;
|
|
|
|
private:
|
|
std::mutex mutex_;
|
|
std::condition_variable cv_;
|
|
bool notified_ = false;
|
|
std::promise<int32_t> prom;
|
|
|
|
};
|
|
|
|
using emscripten::val;
|
|
|
|
extern "C"
|
|
{
|
|
|
|
//
|
|
// Since are using -sMAIN_MODULE with -sPTHREAD_POOL_SIZE=1, main will be called when the first worker is spawned
|
|
//
|
|
|
|
// EMSCRIPTEN_KEEPALIVE int main() {
|
|
// std::cout << "WEBAPI MAIN " << std::endl;
|
|
// return 0;
|
|
// }
|
|
|
|
EMSCRIPTEN_KEEPALIVE void thermion_filament_web_load_resource_callback(void* data, int32_t length, void* context) {
|
|
((PendingCall*)context)->HandleResponse(data, length);
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void thermion_filament_web_set(char* ptr, int32_t offset, int32_t val) {
|
|
memset(ptr+offset, val, 1);
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void thermion_filament_web_set_float(float* ptr, int32_t offset, float val) {
|
|
ptr[offset] = val;
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE float thermion_filament_web_get_float(float* ptr, int32_t offset) {
|
|
return ptr[offset];
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE double thermion_filament_web_get_double(double* ptr, int32_t offset) {
|
|
return ptr[offset];
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void thermion_filament_web_set_double(double* ptr, int32_t offset, double value) {
|
|
ptr[offset] = value;
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE int32_t thermion_filament_web_get_int32(int32_t* ptr, int32_t offset) {
|
|
return ptr[offset];
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void thermion_filament_web_set_int32(int32_t* ptr, int32_t offset, int32_t value) {
|
|
ptr[offset] = value;
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void thermion_filament_web_set_pointer(void** ptr, int32_t offset, void* val) {
|
|
ptr[offset] = val;
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void* thermion_filament_web_get_pointer(void** ptr, int32_t offset) {
|
|
return ptr[offset];
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE char thermion_filament_web_get(char* ptr, int32_t offset) {
|
|
return ptr[offset];
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void* thermion_filament_web_allocate(int32_t size) {
|
|
void* allocated = (void*)calloc(size, 1);
|
|
return allocated;
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE long thermion_filament_web_get_address(void** out) {
|
|
return (long)*out;
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_WEBGL_CONTEXT_HANDLE thermion_dart_web_create_gl_context() {
|
|
|
|
std::cout << "Creating WebGL context." << std::endl;
|
|
|
|
EmscriptenWebGLContextAttributes attr;
|
|
|
|
emscripten_webgl_init_context_attributes(&attr);
|
|
attr.alpha = EM_TRUE;
|
|
attr.depth = EM_TRUE;
|
|
attr.stencil = EM_FALSE;
|
|
attr.antialias = EM_FALSE;
|
|
attr.explicitSwapControl = EM_FALSE;
|
|
attr.preserveDrawingBuffer = EM_TRUE;
|
|
attr.proxyContextToMainThread = EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW;
|
|
attr.enableExtensionsByDefault = EM_TRUE;
|
|
attr.renderViaOffscreenBackBuffer = EM_FALSE;
|
|
attr.majorVersion = 2;
|
|
|
|
auto context = emscripten_webgl_create_context("#canvas", &attr);
|
|
|
|
std::cout << "Created WebGL context " << attr.majorVersion << "." << attr.minorVersion << std::endl;
|
|
|
|
auto success = emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)context);
|
|
if(success != EMSCRIPTEN_RESULT_SUCCESS) {
|
|
std::cout << "Failed to make WebGL context current"<< std::endl;
|
|
} else {
|
|
std::cout << "Made WebGL context current"<< std::endl;
|
|
// glClearColor(0.0, 0.0, 1.0, 1.0);
|
|
// glClear(GL_COLOR_BUFFER_BIT);
|
|
// emscripten_webgl_commit_frame();
|
|
}
|
|
return context;
|
|
}
|
|
|
|
int _lastResourceId = 0;
|
|
|
|
ResourceBuffer thermion_filament_web_load_resource(const char* path)
|
|
{
|
|
// ideally we should bounce the call to Flutter then wait for callback
|
|
// this isn't working for large assets though - seems like it's deadlocked
|
|
// will leave this here commented out so we can revisit later if needed
|
|
// auto pendingCall = new PendingCall();
|
|
// loadFlutterAsset(path, (void*)pendingCall);
|
|
// pendingCall->Wait();
|
|
// auto rb = ResourceBuffer { pendingCall->data, (int32_t) pendingCall->length, _lastResourceId } ;
|
|
_lastResourceId++;
|
|
// delete pendingCall;
|
|
// std::cout << "Deleted pending call" << std::endl;
|
|
|
|
// emscripten_fetch_attr_t attr;
|
|
// emscripten_fetch_attr_init(&attr);
|
|
// attr.onsuccess = [](emscripten_fetch_t* fetch) {
|
|
|
|
// };
|
|
// attr.onerror = [](emscripten_fetch_t* fetch) {
|
|
// std::cout << "Error" << std::endl;
|
|
// };
|
|
// attr.onprogress = [](emscripten_fetch_t* fetch) {
|
|
|
|
// };
|
|
// attr.onreadystatechange = [](emscripten_fetch_t* fetch) {
|
|
|
|
// };
|
|
// attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
|
|
|
// const char* headers[] = {"Accept-Encoding", "gzip, deflate", NULL};
|
|
// attr.requestHeaders = headers;
|
|
|
|
auto pathString = std::string(path);
|
|
// if(pathString.rfind("/",0) != 0) {
|
|
// pathString = std::string("/") + pathString;
|
|
// }
|
|
|
|
// std::cout << "Fetching from path " << pathString.c_str() << std::endl;
|
|
|
|
// auto request = emscripten_fetch(&attr, pathString.c_str());
|
|
// if(!request) {
|
|
// std::cout << "Request failed?" << std::endl;
|
|
// return ResourceBuffer { nullptr, 0, -1 } ;
|
|
// }
|
|
// auto data = malloc(request->numBytes);
|
|
// memcpy(data, request->data, request->numBytes);
|
|
// emscripten_fetch_close(request);
|
|
// return ResourceBuffer { data, (int32_t) request->numBytes, _lastResourceId } ;
|
|
void* data = nullptr;
|
|
int32_t numBytes = 0;
|
|
|
|
void** pBuffer = (void**)malloc(sizeof(void*));
|
|
int* pNum = (int*) malloc(sizeof(int*));
|
|
int* pError = (int*)malloc(sizeof(int*));
|
|
emscripten_wget_data(pathString.c_str(), pBuffer, pNum, pError);
|
|
data = *pBuffer;
|
|
numBytes = *pNum;
|
|
free(pBuffer);
|
|
free(pNum);
|
|
free(pError);
|
|
return ResourceBuffer { data, numBytes, _lastResourceId } ;
|
|
}
|
|
|
|
void thermion_filament_web_free_resource(ResourceBuffer rb) {
|
|
free((void*)rb.data);
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void thermion_filament_web_free(void* ptr) {
|
|
free(ptr);
|
|
}
|
|
|
|
EMSCRIPTEN_KEEPALIVE void* thermion_dart_web_get_resource_loader_wrapper() {
|
|
ResourceLoaderWrapper *rlw = (ResourceLoaderWrapper *)malloc(sizeof(ResourceLoaderWrapper));
|
|
rlw->loadResource = thermion_filament_web_load_resource;
|
|
rlw->loadFromOwner = nullptr;
|
|
rlw->freeResource = thermion_filament_web_free_resource;
|
|
rlw->freeFromOwner = nullptr;
|
|
rlw->loadToOut = nullptr;
|
|
rlw->owner = nullptr;
|
|
return rlw;
|
|
}
|
|
} |