Skip to content

Commit 027dcd1

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 158e2ef commit 027dcd1

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
@@ -2212,8 +2212,6 @@
22122212
{
22132213
"title": "Test updateTextureData",
22142214
"playgroundId": "#EVX1DH#80",
2215-
"excludeFromAutomaticTesting": true,
2216-
"reason": "Pixel comparison fails (more than 20% pixels differ)",
22172215
"referenceImage": "testUpdateTextureData.png"
22182216
},
22192217
{

Plugins/NativeEngine/Source/NativeEngine.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,7 @@ namespace Babylon
714714
InstanceMethod("initializeTexture", &NativeEngine::InitializeTexture),
715715
InstanceMethod("loadTexture", &NativeEngine::LoadTexture),
716716
InstanceMethod("loadRawTexture", &NativeEngine::LoadRawTexture),
717+
InstanceMethod("updateTextureData", &NativeEngine::UpdateTextureData),
717718
InstanceMethod("loadRawTexture2DArray", &NativeEngine::LoadRawTexture2DArray),
718719
InstanceMethod("loadCubeTexture", &NativeEngine::LoadCubeTexture),
719720
InstanceMethod("loadCubeTextureWithMips", &NativeEngine::LoadCubeTextureWithMips),
@@ -1441,6 +1442,77 @@ namespace Babylon
14411442
#endif
14421443
}
14431444

1445+
void NativeEngine::UpdateTextureData(const Napi::CallbackInfo& info)
1446+
{
1447+
const auto texture{info[0].As<Napi::Pointer<Graphics::Texture>>().Get()};
1448+
const auto data{info[1].As<Napi::TypedArray>()};
1449+
const auto x{static_cast<uint16_t>(info[2].As<Napi::Number>().Uint32Value())};
1450+
const auto y{static_cast<uint16_t>(info[3].As<Napi::Number>().Uint32Value())};
1451+
const auto width{static_cast<uint16_t>(info[4].As<Napi::Number>().Uint32Value())};
1452+
const auto height{static_cast<uint16_t>(info[5].As<Napi::Number>().Uint32Value())};
1453+
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)};
1454+
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)};
1455+
const bool invertY{info.Length() > 8 && !info[8].IsUndefined() ? info[8].As<Napi::Boolean>().Value() : false};
1456+
1457+
if (texture == nullptr || !texture->IsValid())
1458+
{
1459+
throw Napi::Error::New(info.Env(), "updateTextureData called on an invalid texture");
1460+
}
1461+
1462+
// Validate the (JS-controlled) update rectangle against the mip-level extents before handing it to
1463+
// bgfx, so an out-of-range origin/size can't drive an out-of-bounds read of the source buffer below.
1464+
uint32_t mipWidth{static_cast<uint32_t>(texture->Width()) >> mip};
1465+
uint32_t mipHeight{static_cast<uint32_t>(texture->Height()) >> mip};
1466+
if (mipWidth == 0)
1467+
{
1468+
mipWidth = 1;
1469+
}
1470+
if (mipHeight == 0)
1471+
{
1472+
mipHeight = 1;
1473+
}
1474+
const uint16_t numLayers{texture->NumLayers() > 0 ? texture->NumLayers() : static_cast<uint16_t>(1)};
1475+
if (width == 0 || height == 0 ||
1476+
static_cast<uint32_t>(x) + width > mipWidth ||
1477+
static_cast<uint32_t>(y) + height > mipHeight ||
1478+
layer >= numLayers)
1479+
{
1480+
throw Napi::Error::New(info.Env(), "updateTextureData region is out of bounds");
1481+
}
1482+
1483+
// Size of the source rectangle in the texture's own format. bgfx is always linked (bimg is not, in
1484+
// builds without image loading), so size the upload with bgfx::calcTextureSize rather than bimg.
1485+
bgfx::TextureInfo textureInfo;
1486+
bgfx::calcTextureSize(textureInfo, width, height, 1, false, false, 1, texture->Format());
1487+
const uint32_t requiredSize{textureInfo.storageSize};
1488+
if (requiredSize == 0 || data.ByteLength() < requiredSize)
1489+
{
1490+
throw Napi::Error::New(info.Env(), "updateTextureData data size does not match width, height, and texture format");
1491+
}
1492+
1493+
const auto bytes{static_cast<uint8_t*>(data.ArrayBuffer().Data()) + data.ByteOffset()};
1494+
1495+
// Match the vertical orientation the base upload applies (PrepareImage flips the whole image when
1496+
// originBottomLeft ? invertY : !invertY). To land a sub-rectangle at the same place, flip it to the
1497+
// mirrored Y origin and reverse its rows so row 0 of the source lines up with the flipped base data.
1498+
const bool flip{bgfx::getCaps()->originBottomLeft ? invertY : !invertY};
1499+
const uint16_t targetY{flip ? static_cast<uint16_t>(mipHeight - y - height) : y};
1500+
const bgfx::Memory* mem{bgfx::alloc(requiredSize)};
1501+
if (flip)
1502+
{
1503+
const uint32_t rowBytes{requiredSize / height};
1504+
for (uint16_t row = 0; row < height; ++row)
1505+
{
1506+
std::memcpy(mem->data + static_cast<size_t>(row) * rowBytes, bytes + static_cast<size_t>(height - 1 - row) * rowBytes, rowBytes);
1507+
}
1508+
}
1509+
else
1510+
{
1511+
std::memcpy(mem->data, bytes, requiredSize);
1512+
}
1513+
texture->Update2D(layer, mip, x, targetY, width, height, mem);
1514+
}
1515+
14441516
void NativeEngine::LoadRawTexture2DArray(const Napi::CallbackInfo& info)
14451517
{
14461518
#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)