Skip to content

Use the NativeEngine TextureData definition to avoid crashes unwrapping Napi::External<NativeEngine::TextureData> for jsi#805

Merged
chrisfromwork merged 9 commits intoBabylonJS:masterfrom
chrisfromwork:canvas
Jun 18, 2021
Merged

Use the NativeEngine TextureData definition to avoid crashes unwrapping Napi::External<NativeEngine::TextureData> for jsi#805
chrisfromwork merged 9 commits intoBabylonJS:masterfrom
chrisfromwork:canvas

Conversation

@chrisfromwork
Copy link
Copy Markdown
Contributor

@chrisfromwork chrisfromwork commented Jun 17, 2021

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.

Comment thread Polyfills/Canvas/Source/Canvas.cpp Outdated

struct NativeCanvas::Impl
{
std::unique_ptr<Babylon::TextureData> m_textureData;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For structs with public members, I think this should be TextureData instead of m_textureData.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good. i see this vary in different places. But am happy to start using capitalization throughout.

Comment thread Polyfills/Canvas/Source/Canvas.cpp Outdated
{
static constexpr auto JS_CONSTRUCTOR_NAME = "NativeCanvas";

struct NativeCanvas::Impl
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
struct NativeCanvas::Impl
struct NativeCanvas::Impl final

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should now be fixed.

Comment thread Polyfills/Canvas/Source/Canvas.cpp Outdated
data->Width = m_width;
data->Height = m_height;
return Napi::External<TextureData>::New(info.Env(), data);
m_impl->m_textureData = std::make_unique<TextureData>();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like Napi::External only deletes if you provide your own finalizer. This was likely leaking before.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I wonder if we have leaks elsewhere…

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id bet we definitely have leaks elsewhere

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chatted with ryan offline. seems like a finalizer is what we should do here.

@chrisfromwork chrisfromwork requested a review from ryantrem June 18, 2021 18:31
Comment on lines +1119 to +1124
if (!info[0].IsExternal() ||
!info[1].IsExternal())
{
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit weird. We just silently do nothing when the types are wrong? Is that the intended behavior?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should now be fixed.

Comment thread Polyfills/Canvas/Source/Canvas.cpp Outdated

struct NativeCanvas::Impl final
{
std::unique_ptr<Babylon::TextureData> TextureData;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need the unique_ptr?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no we don't, good call out.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should now be fixed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see changes, this struct no longer exists.

Comment thread Polyfills/Canvas/Source/Canvas.cpp Outdated
{
static constexpr auto JS_CONSTRUCTOR_NAME = "NativeCanvas";

struct NativeCanvas::Impl final
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this. Why do you need an Impl class?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should now be fixed. impl is removed and we just declare the property for the native canvas.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on other conversations, this datas lifecycle is going to be maintained by the external through a finalizer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would make a lot more sense. create it for javascript and finalize with javascript.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should now be fixed

@chrisfromwork chrisfromwork requested a review from bghgary June 18, 2021 19:00
Comment thread Polyfills/Canvas/Source/Canvas.cpp Outdated
{
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>();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused, remove?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) }]() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is going to be async, then this function should return a promise right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually loadTTF isn't defined anywhere public. let me return a promise here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should now be fixed

Comment thread Polyfills/Canvas/Source/Canvas.cpp Outdated
Co-authored-by: Gary Hsu <bghgary@users.noreply.github.com>
@chrisfromwork chrisfromwork enabled auto-merge (squash) June 18, 2021 22:41
@chrisfromwork chrisfromwork merged commit c00b503 into BabylonJS:master Jun 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants