Skip to content

Commit 358aae6

Browse files
bkaradzicCopilot
authored andcommitted
NativeEngine: implement updateTextureData (re-enables Test updateTextureData)
updateTextureData previously threw "not implemented" on Native. Implement it so sub-rectangle texture updates work. - Add NativeEngine::UpdateTextureData: upload the requested sub-rectangle via bgfx::updateTexture2D (Texture::Update2D). Validates the JS-controlled rect against the mip extents, sizes the copy with bgfx::calcTextureSize (no bimg dependency, so it also works in no-image-loading builds), and mirrors the vertical flip the base texture upload applies so the sub-rect lines up on top-left-origin backends (e.g. D3D11). - Re-enable the "Test updateTextureData" validation test. Pairs with the Babylon.js change (engine.name = "Native" so name-gated WebGL _gl access skips Native, plus the updateTextureData override). CI stays red until a babylonjs npm with that change is published and the dependency bumped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b26e738 commit 358aae6

3 files changed

Lines changed: 73 additions & 2 deletions

File tree

Apps/Playground/Scripts/config.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,8 +2196,6 @@
21962196
{
21972197
"title": "Test updateTextureData",
21982198
"playgroundId": "#EVX1DH#80",
2199-
"excludeFromAutomaticTesting": true,
2200-
"reason": "Pixel comparison fails (more than 20% pixels differ)",
22012199
"referenceImage": "testUpdateTextureData.png"
22022200
},
22032201
{

Plugins/NativeEngine/Source/NativeEngine.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,7 @@ namespace Babylon
950950
InstanceMethod("initializeTexture", &NativeEngine::InitializeTexture),
951951
InstanceMethod("loadTexture", &NativeEngine::LoadTexture),
952952
InstanceMethod("loadRawTexture", &NativeEngine::LoadRawTexture),
953+
InstanceMethod("updateTextureData", &NativeEngine::UpdateTextureData),
953954
InstanceMethod("loadRawTexture2DArray", &NativeEngine::LoadRawTexture2DArray),
954955
InstanceMethod("loadCubeTexture", &NativeEngine::LoadCubeTexture),
955956
InstanceMethod("loadCubeTextureWithMips", &NativeEngine::LoadCubeTextureWithMips),
@@ -1686,6 +1687,77 @@ namespace Babylon
16861687
#endif
16871688
}
16881689

1690+
void NativeEngine::UpdateTextureData(const Napi::CallbackInfo& info)
1691+
{
1692+
const auto texture{info[0].As<Napi::Pointer<Graphics::Texture>>().Get()};
1693+
const auto data{info[1].As<Napi::TypedArray>()};
1694+
const auto x{static_cast<uint16_t>(info[2].As<Napi::Number>().Uint32Value())};
1695+
const auto y{static_cast<uint16_t>(info[3].As<Napi::Number>().Uint32Value())};
1696+
const auto width{static_cast<uint16_t>(info[4].As<Napi::Number>().Uint32Value())};
1697+
const auto height{static_cast<uint16_t>(info[5].As<Napi::Number>().Uint32Value())};
1698+
const uint16_t layer{info.Length() > 6 && !info[6].IsUndefined() ? static_cast<uint16_t>(info[6].As<Napi::Number>().Uint32Value()) : static_cast<uint16_t>(0)};
1699+
const uint8_t mip{info.Length() > 7 && !info[7].IsUndefined() ? static_cast<uint8_t>(info[7].As<Napi::Number>().Uint32Value()) : static_cast<uint8_t>(0)};
1700+
const bool invertY{info.Length() > 8 && !info[8].IsUndefined() ? info[8].As<Napi::Boolean>().Value() : false};
1701+
1702+
if (texture == nullptr || !texture->IsValid())
1703+
{
1704+
throw Napi::Error::New(info.Env(), "updateTextureData called on an invalid texture");
1705+
}
1706+
1707+
// Validate the (JS-controlled) update rectangle against the mip-level extents before handing it to
1708+
// bgfx, so an out-of-range origin/size can't drive an out-of-bounds read of the source buffer below.
1709+
uint32_t mipWidth{static_cast<uint32_t>(texture->Width()) >> mip};
1710+
uint32_t mipHeight{static_cast<uint32_t>(texture->Height()) >> mip};
1711+
if (mipWidth == 0)
1712+
{
1713+
mipWidth = 1;
1714+
}
1715+
if (mipHeight == 0)
1716+
{
1717+
mipHeight = 1;
1718+
}
1719+
const uint16_t numLayers{texture->NumLayers() > 0 ? texture->NumLayers() : static_cast<uint16_t>(1)};
1720+
if (width == 0 || height == 0 ||
1721+
static_cast<uint32_t>(x) + width > mipWidth ||
1722+
static_cast<uint32_t>(y) + height > mipHeight ||
1723+
layer >= numLayers)
1724+
{
1725+
throw Napi::Error::New(info.Env(), "updateTextureData region is out of bounds");
1726+
}
1727+
1728+
// Size of the source rectangle in the texture's own format. bgfx is always linked (bimg is not, in
1729+
// builds without image loading), so size the upload with bgfx::calcTextureSize rather than bimg.
1730+
bgfx::TextureInfo textureInfo;
1731+
bgfx::calcTextureSize(textureInfo, width, height, 1, false, false, 1, texture->Format());
1732+
const uint32_t requiredSize{textureInfo.storageSize};
1733+
if (requiredSize == 0 || data.ByteLength() < requiredSize)
1734+
{
1735+
throw Napi::Error::New(info.Env(), "updateTextureData data size does not match width, height, and texture format");
1736+
}
1737+
1738+
const auto bytes{static_cast<uint8_t*>(data.ArrayBuffer().Data()) + data.ByteOffset()};
1739+
1740+
// Match the vertical orientation the base upload applies (PrepareImage flips the whole image when
1741+
// originBottomLeft ? invertY : !invertY). To land a sub-rectangle at the same place, flip it to the
1742+
// mirrored Y origin and reverse its rows so row 0 of the source lines up with the flipped base data.
1743+
const bool flip{bgfx::getCaps()->originBottomLeft ? invertY : !invertY};
1744+
const uint16_t targetY{flip ? static_cast<uint16_t>(mipHeight - y - height) : y};
1745+
const bgfx::Memory* mem{bgfx::alloc(requiredSize)};
1746+
if (flip)
1747+
{
1748+
const uint32_t rowBytes{requiredSize / height};
1749+
for (uint16_t row = 0; row < height; ++row)
1750+
{
1751+
std::memcpy(mem->data + static_cast<size_t>(row) * rowBytes, bytes + static_cast<size_t>(height - 1 - row) * rowBytes, rowBytes);
1752+
}
1753+
}
1754+
else
1755+
{
1756+
std::memcpy(mem->data, bytes, requiredSize);
1757+
}
1758+
texture->Update2D(layer, mip, x, targetY, width, height, mem);
1759+
}
1760+
16891761
void NativeEngine::LoadRawTexture2DArray(const Napi::CallbackInfo& info)
16901762
{
16911763
#ifndef BABYLON_NATIVE_PLUGIN_NATIVEENGINE_LOAD_IMAGES

Plugins/NativeEngine/Source/NativeEngine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ namespace Babylon
101101
void LoadTexture(const Napi::CallbackInfo& info);
102102
void CopyTexture(NativeDataStream::Reader& data);
103103
void LoadRawTexture(const Napi::CallbackInfo& info);
104+
void UpdateTextureData(const Napi::CallbackInfo& info);
104105
void LoadRawTexture2DArray(const Napi::CallbackInfo& info);
105106
void LoadCubeTexture(const Napi::CallbackInfo& info);
106107
void LoadCubeTextureWithMips(const Napi::CallbackInfo& info);

0 commit comments

Comments
 (0)