Skip to content

Commit db312d3

Browse files
bkaradzicCopilot
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 b6ecf80 commit db312d3

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
@@ -2214,8 +2214,6 @@
22142214
{
22152215
"title": "Test updateTextureData",
22162216
"playgroundId": "#EVX1DH#80",
2217-
"excludeFromAutomaticTesting": true,
2218-
"reason": "Pixel comparison fails (more than 20% pixels differ)",
22192217
"referenceImage": "testUpdateTextureData.png"
22202218
},
22212219
{

Plugins/NativeEngine/Source/NativeEngine.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ namespace Babylon
723723
InstanceMethod("initializeTexture", &NativeEngine::InitializeTexture),
724724
InstanceMethod("loadTexture", &NativeEngine::LoadTexture),
725725
InstanceMethod("loadRawTexture", &NativeEngine::LoadRawTexture),
726+
InstanceMethod("updateTextureData", &NativeEngine::UpdateTextureData),
726727
InstanceMethod("loadRawTexture2DArray", &NativeEngine::LoadRawTexture2DArray),
727728
InstanceMethod("loadCubeTexture", &NativeEngine::LoadCubeTexture),
728729
InstanceMethod("loadCubeTextureWithMips", &NativeEngine::LoadCubeTextureWithMips),
@@ -1409,6 +1410,77 @@ namespace Babylon
14091410
#endif
14101411
}
14111412

1413+
void NativeEngine::UpdateTextureData(const Napi::CallbackInfo& info)
1414+
{
1415+
const auto texture{info[0].As<Napi::Pointer<Graphics::Texture>>().Get()};
1416+
const auto data{info[1].As<Napi::TypedArray>()};
1417+
const auto x{static_cast<uint16_t>(info[2].As<Napi::Number>().Uint32Value())};
1418+
const auto y{static_cast<uint16_t>(info[3].As<Napi::Number>().Uint32Value())};
1419+
const auto width{static_cast<uint16_t>(info[4].As<Napi::Number>().Uint32Value())};
1420+
const auto height{static_cast<uint16_t>(info[5].As<Napi::Number>().Uint32Value())};
1421+
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)};
1422+
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)};
1423+
const bool invertY{info.Length() > 8 && !info[8].IsUndefined() ? info[8].As<Napi::Boolean>().Value() : false};
1424+
1425+
if (texture == nullptr || !texture->IsValid())
1426+
{
1427+
throw Napi::Error::New(info.Env(), "updateTextureData called on an invalid texture");
1428+
}
1429+
1430+
// Validate the (JS-controlled) update rectangle against the mip-level extents before handing it to
1431+
// bgfx, so an out-of-range origin/size can't drive an out-of-bounds read of the source buffer below.
1432+
uint32_t mipWidth{static_cast<uint32_t>(texture->Width()) >> mip};
1433+
uint32_t mipHeight{static_cast<uint32_t>(texture->Height()) >> mip};
1434+
if (mipWidth == 0)
1435+
{
1436+
mipWidth = 1;
1437+
}
1438+
if (mipHeight == 0)
1439+
{
1440+
mipHeight = 1;
1441+
}
1442+
const uint16_t numLayers{texture->NumLayers() > 0 ? texture->NumLayers() : static_cast<uint16_t>(1)};
1443+
if (width == 0 || height == 0 ||
1444+
static_cast<uint32_t>(x) + width > mipWidth ||
1445+
static_cast<uint32_t>(y) + height > mipHeight ||
1446+
layer >= numLayers)
1447+
{
1448+
throw Napi::Error::New(info.Env(), "updateTextureData region is out of bounds");
1449+
}
1450+
1451+
// Size of the source rectangle in the texture's own format. bgfx is always linked (bimg is not, in
1452+
// builds without image loading), so size the upload with bgfx::calcTextureSize rather than bimg.
1453+
bgfx::TextureInfo textureInfo;
1454+
bgfx::calcTextureSize(textureInfo, width, height, 1, false, false, 1, texture->Format());
1455+
const uint32_t requiredSize{textureInfo.storageSize};
1456+
if (requiredSize == 0 || data.ByteLength() < requiredSize)
1457+
{
1458+
throw Napi::Error::New(info.Env(), "updateTextureData data size does not match width, height, and texture format");
1459+
}
1460+
1461+
const auto bytes{static_cast<uint8_t*>(data.ArrayBuffer().Data()) + data.ByteOffset()};
1462+
1463+
// Match the vertical orientation the base upload applies (PrepareImage flips the whole image when
1464+
// originBottomLeft ? invertY : !invertY). To land a sub-rectangle at the same place, flip it to the
1465+
// mirrored Y origin and reverse its rows so row 0 of the source lines up with the flipped base data.
1466+
const bool flip{bgfx::getCaps()->originBottomLeft ? invertY : !invertY};
1467+
const uint16_t targetY{flip ? static_cast<uint16_t>(mipHeight - y - height) : y};
1468+
const bgfx::Memory* mem{bgfx::alloc(requiredSize)};
1469+
if (flip)
1470+
{
1471+
const uint32_t rowBytes{requiredSize / height};
1472+
for (uint16_t row = 0; row < height; ++row)
1473+
{
1474+
std::memcpy(mem->data + static_cast<size_t>(row) * rowBytes, bytes + static_cast<size_t>(height - 1 - row) * rowBytes, rowBytes);
1475+
}
1476+
}
1477+
else
1478+
{
1479+
std::memcpy(mem->data, bytes, requiredSize);
1480+
}
1481+
texture->Update2D(layer, mip, x, targetY, width, height, mem);
1482+
}
1483+
14121484
void NativeEngine::LoadRawTexture2DArray(const Napi::CallbackInfo& info)
14131485
{
14141486
#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
@@ -93,6 +93,7 @@ namespace Babylon
9393
void LoadTexture(const Napi::CallbackInfo& info);
9494
void CopyTexture(NativeDataStream::Reader& data);
9595
void LoadRawTexture(const Napi::CallbackInfo& info);
96+
void UpdateTextureData(const Napi::CallbackInfo& info);
9697
void LoadRawTexture2DArray(const Napi::CallbackInfo& info);
9798
void LoadCubeTexture(const Napi::CallbackInfo& info);
9899
void LoadCubeTextureWithMips(const Napi::CallbackInfo& info);

0 commit comments

Comments
 (0)