Skip to content

Commit c9bd979

Browse files
committed
Merge branch 'native-update-texture-data' into native-combined-gl-fixes
2 parents c120e02 + db312d3 commit c9bd979

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
@@ -2198,8 +2198,6 @@
21982198
{
21992199
"title": "Test updateTextureData",
22002200
"playgroundId": "#EVX1DH#80",
2201-
"excludeFromAutomaticTesting": true,
2202-
"reason": "Pixel comparison fails (more than 20% pixels differ)",
22032201
"referenceImage": "testUpdateTextureData.png"
22042202
},
22052203
{

Plugins/NativeEngine/Source/NativeEngine.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,7 @@ namespace Babylon
959959
InstanceMethod("initializeTexture", &NativeEngine::InitializeTexture),
960960
InstanceMethod("loadTexture", &NativeEngine::LoadTexture),
961961
InstanceMethod("loadRawTexture", &NativeEngine::LoadRawTexture),
962+
InstanceMethod("updateTextureData", &NativeEngine::UpdateTextureData),
962963
InstanceMethod("loadRawTexture2DArray", &NativeEngine::LoadRawTexture2DArray),
963964
InstanceMethod("loadCubeTexture", &NativeEngine::LoadCubeTexture),
964965
InstanceMethod("loadCubeTextureWithMips", &NativeEngine::LoadCubeTextureWithMips),
@@ -1654,6 +1655,77 @@ namespace Babylon
16541655
#endif
16551656
}
16561657

1658+
void NativeEngine::UpdateTextureData(const Napi::CallbackInfo& info)
1659+
{
1660+
const auto texture{info[0].As<Napi::Pointer<Graphics::Texture>>().Get()};
1661+
const auto data{info[1].As<Napi::TypedArray>()};
1662+
const auto x{static_cast<uint16_t>(info[2].As<Napi::Number>().Uint32Value())};
1663+
const auto y{static_cast<uint16_t>(info[3].As<Napi::Number>().Uint32Value())};
1664+
const auto width{static_cast<uint16_t>(info[4].As<Napi::Number>().Uint32Value())};
1665+
const auto height{static_cast<uint16_t>(info[5].As<Napi::Number>().Uint32Value())};
1666+
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)};
1667+
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)};
1668+
const bool invertY{info.Length() > 8 && !info[8].IsUndefined() ? info[8].As<Napi::Boolean>().Value() : false};
1669+
1670+
if (texture == nullptr || !texture->IsValid())
1671+
{
1672+
throw Napi::Error::New(info.Env(), "updateTextureData called on an invalid texture");
1673+
}
1674+
1675+
// Validate the (JS-controlled) update rectangle against the mip-level extents before handing it to
1676+
// bgfx, so an out-of-range origin/size can't drive an out-of-bounds read of the source buffer below.
1677+
uint32_t mipWidth{static_cast<uint32_t>(texture->Width()) >> mip};
1678+
uint32_t mipHeight{static_cast<uint32_t>(texture->Height()) >> mip};
1679+
if (mipWidth == 0)
1680+
{
1681+
mipWidth = 1;
1682+
}
1683+
if (mipHeight == 0)
1684+
{
1685+
mipHeight = 1;
1686+
}
1687+
const uint16_t numLayers{texture->NumLayers() > 0 ? texture->NumLayers() : static_cast<uint16_t>(1)};
1688+
if (width == 0 || height == 0 ||
1689+
static_cast<uint32_t>(x) + width > mipWidth ||
1690+
static_cast<uint32_t>(y) + height > mipHeight ||
1691+
layer >= numLayers)
1692+
{
1693+
throw Napi::Error::New(info.Env(), "updateTextureData region is out of bounds");
1694+
}
1695+
1696+
// Size of the source rectangle in the texture's own format. bgfx is always linked (bimg is not, in
1697+
// builds without image loading), so size the upload with bgfx::calcTextureSize rather than bimg.
1698+
bgfx::TextureInfo textureInfo;
1699+
bgfx::calcTextureSize(textureInfo, width, height, 1, false, false, 1, texture->Format());
1700+
const uint32_t requiredSize{textureInfo.storageSize};
1701+
if (requiredSize == 0 || data.ByteLength() < requiredSize)
1702+
{
1703+
throw Napi::Error::New(info.Env(), "updateTextureData data size does not match width, height, and texture format");
1704+
}
1705+
1706+
const auto bytes{static_cast<uint8_t*>(data.ArrayBuffer().Data()) + data.ByteOffset()};
1707+
1708+
// Match the vertical orientation the base upload applies (PrepareImage flips the whole image when
1709+
// originBottomLeft ? invertY : !invertY). To land a sub-rectangle at the same place, flip it to the
1710+
// mirrored Y origin and reverse its rows so row 0 of the source lines up with the flipped base data.
1711+
const bool flip{bgfx::getCaps()->originBottomLeft ? invertY : !invertY};
1712+
const uint16_t targetY{flip ? static_cast<uint16_t>(mipHeight - y - height) : y};
1713+
const bgfx::Memory* mem{bgfx::alloc(requiredSize)};
1714+
if (flip)
1715+
{
1716+
const uint32_t rowBytes{requiredSize / height};
1717+
for (uint16_t row = 0; row < height; ++row)
1718+
{
1719+
std::memcpy(mem->data + static_cast<size_t>(row) * rowBytes, bytes + static_cast<size_t>(height - 1 - row) * rowBytes, rowBytes);
1720+
}
1721+
}
1722+
else
1723+
{
1724+
std::memcpy(mem->data, bytes, requiredSize);
1725+
}
1726+
texture->Update2D(layer, mip, x, targetY, width, height, mem);
1727+
}
1728+
16571729
void NativeEngine::LoadRawTexture2DArray(const Napi::CallbackInfo& info)
16581730
{
16591731
#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)