Use the NativeEngine TextureData definition to avoid crashes unwrapping Napi::External<NativeEngine::TextureData> for jsi#805
Conversation
|
|
||
| struct NativeCanvas::Impl | ||
| { | ||
| std::unique_ptr<Babylon::TextureData> m_textureData; |
There was a problem hiding this comment.
For structs with public members, I think this should be TextureData instead of m_textureData.
There was a problem hiding this comment.
sounds good. i see this vary in different places. But am happy to start using capitalization throughout.
| { | ||
| static constexpr auto JS_CONSTRUCTOR_NAME = "NativeCanvas"; | ||
|
|
||
| struct NativeCanvas::Impl |
There was a problem hiding this comment.
| struct NativeCanvas::Impl | |
| struct NativeCanvas::Impl final |
There was a problem hiding this comment.
this should now be fixed.
| data->Width = m_width; | ||
| data->Height = m_height; | ||
| return Napi::External<TextureData>::New(info.Env(), data); | ||
| m_impl->m_textureData = std::make_unique<TextureData>(); |
There was a problem hiding this comment.
It looks like this gets deleted at the end of the function, but you are passing a pointer to the underlying data to the Napi::External. I think the default finalizer action for Napi::External is to delete the pointer, so it seems like it was correct the way it was, but maybe I'm missing something.
There was a problem hiding this comment.
It looks like Napi::External only deletes if you provide your own finalizer. This was likely leaking before.
There was a problem hiding this comment.
Hmm I wonder if we have leaks elsewhere…
There was a problem hiding this comment.
id bet we definitely have leaks elsewhere
There was a problem hiding this comment.
whatever was going on before, calling new just seems like a bad time for the TextureData when theres no subsequent delete call. To be fair though Cedric called out that this was leaking if i remember correctly.
There was a problem hiding this comment.
chatted with ryan offline. seems like a finalizer is what we should do here.
| if (!info[0].IsExternal() || | ||
| !info[1].IsExternal()) | ||
| { | ||
| return; | ||
| } | ||
|
|
There was a problem hiding this comment.
This is a bit weird. We just silently do nothing when the types are wrong? Is that the intended behavior?
There was a problem hiding this comment.
more was a check because the external wasn't unwrapping correctly for jsi when it was a unique struct definition. I'll undo this check.
There was a problem hiding this comment.
this should now be fixed.
|
|
||
| struct NativeCanvas::Impl final | ||
| { | ||
| std::unique_ptr<Babylon::TextureData> TextureData; |
There was a problem hiding this comment.
Do you need the unique_ptr?
There was a problem hiding this comment.
no we don't, good call out.
There was a problem hiding this comment.
this should now be fixed.
There was a problem hiding this comment.
see changes, this struct no longer exists.
| { | ||
| static constexpr auto JS_CONSTRUCTOR_NAME = "NativeCanvas"; | ||
|
|
||
| struct NativeCanvas::Impl final |
There was a problem hiding this comment.
I don't understand this. Why do you need an Impl class?
There was a problem hiding this comment.
I added an impl to have a data container so that i wouldn't have to declare the babylon/NativeEngine.h header in the the canvas header.
There was a problem hiding this comment.
tking another look it seems like there are two canvas headers and we already declare bgfx types in the internal canvas header. I'll remove this container and will just declare the texturedata as a property of the canvas struct for now.
There was a problem hiding this comment.
this should now be fixed. impl is removed and we just declare the property for the native canvas.
There was a problem hiding this comment.
based on other conversations, this datas lifecycle is going to be maintained by the external through a finalizer.
There was a problem hiding this comment.
that would make a lot more sense. create it for javascript and finalize with javascript.
There was a problem hiding this comment.
this should now be fixed
… data as property. remove no opt checks
| { | ||
| std::string fontName = info[0].As<Napi::String>().Utf8Value(); | ||
| auto& graphicsImpl{Babylon::GraphicsImpl::GetFromJavaScript(info.Env())}; | ||
| const auto cancellationSource = std::shared_ptr<arcana::cancellation_source>(); |
There was a problem hiding this comment.
this should now be fixed.
| std::vector<uint8_t> fontBuffer{}; | ||
| fontBuffer.resize(buffer.ByteLength()); | ||
| memcpy(fontBuffer.data(), (uint8_t*)buffer.Data(), buffer.ByteLength()); | ||
| arcana::make_task(graphicsImpl.BeforeRenderScheduler(), arcana::cancellation::none(), [fontName{ info[0].As<Napi::String>().Utf8Value() }, fontData{ std::move(fontBuffer) }]() { |
There was a problem hiding this comment.
If this is going to be async, then this function should return a promise right?
There was a problem hiding this comment.
if i understand correctly this function doesn't need to resolve before declaring text. Given returning a promise would require updating babylon.js, i'm inclined to say that returning a promise here is beyond the scope of this review and should be addressed when we finalize the public api contract for loading fonts (fontface, etc.)
There was a problem hiding this comment.
actually loadTTF isn't defined anywhere public. let me return a promise here.
There was a problem hiding this comment.
this should now be fixed
Co-authored-by: Gary Hsu <bghgary@users.noreply.github.com>
Defining a second TextureData definition for the Canvas seemed to resolve correctly in BabylonNative. But in BabylonReactNative the jsi wrapper unwrapping the external type threw exceptions. This change updates the Canvas polyfill to consume the TextureData struct defined by NativeEngine. It seems like this is what we should be doing to avoid getting into scenarios where the struct definition changes and we need to update multiple locations.
We also update loadTTF to only load a font once. Once a font is getting used if its updated on the js thread it can cause crashes for the graphics thread.
Note: this change requires having the Canvas polyfill reference internal headers for NativeEngine. It seems like this is already an expected practice. I'm not sure if polyfills are supposed to consume Core/Plugin definitions, but In this scenario we need to.