Skip to content

Commit

Permalink
Support non-normalized, non-float vertex buffers and unaligned vertex…
Browse files Browse the repository at this point in the history
… buffers (#1382)

This is a replacement for #1376. The original thinking was that we will
promote the non-float attributes to float which was a complex and poor
performance solution. The better solution is to change the shader to
take non-float attributes. Fortunately, this shader processing is
already happening for WebGPU on the JS side. @Popov72 [factored out the
shader processor](BabylonJS/Babylon.js#15107) on
the JS side for WebGPU to also work with Babylon Native, which also
includes code that aligns the buffers to 4-byte boundaries. This native
PR ties everything together and removes all the promote to float logic
we had earlier to try to handle non-float attributes.

This change includes updating Babylon.js to 7.7.2. Using an older
version will result in a `console.error ` if non-normalized, non-float
vertex buffers are used.
  • Loading branch information
bghgary committed May 22, 2024
1 parent 4cf5677 commit 59798a7
Show file tree
Hide file tree
Showing 14 changed files with 220 additions and 382 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions Apps/Playground/Scripts/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,11 @@
"title": "CesiumMan from Khronos Sample Assets",
"playgroundId": "#4GWX8M",
"referenceImage": "CesiumMan.png"
},
{
"title": "Two vertex buffers pointing to one buffer",
"playgroundId": "#RZNHXT#7",
"referenceImage": "two-vertex-buffers.png"
}
]
}
86 changes: 43 additions & 43 deletions Apps/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions Apps/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
"getNightly": "node scripts/getNightly.js"
},
"dependencies": {
"babylonjs": "^7.6.2",
"babylonjs-gltf2interface": "^7.6.2",
"babylonjs-gui": "^7.6.2",
"babylonjs-loaders": "^7.6.2",
"babylonjs-materials": "^7.6.2",
"babylonjs": "^7.7.2",
"babylonjs-gltf2interface": "^7.7.2",
"babylonjs-gui": "^7.7.2",
"babylonjs-loaders": "^7.7.2",
"babylonjs-materials": "^7.7.2",
"chai": "^4.3.4",
"jsc-android": "^241213.1.0",
"mocha": "^9.2.2",
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ FetchContent_Declare(OpenXR-SDK
GIT_TAG 458984d7f59d1ae6dc1b597d94b02e4f7132eaba)
FetchContent_Declare(SPIRV-Cross
GIT_REPOSITORY https://github.com/BabylonJS/SPIRV-Cross.git
GIT_TAG 014d1ebe42c4177520fbcfd1e9a17238e532f1a6)
GIT_TAG 578a291759db6fe7c3f4735d3512c0526ad18efc)
# --------------------------------------------------

FetchContent_MakeAvailable(CMakeExtensions)
Expand Down
9 changes: 7 additions & 2 deletions Core/Graphics/Source/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace Babylon::Graphics
{
Texture::Texture(DeviceContext& deviceContext)
: m_deviceID{deviceContext.GetDeviceId()}
, m_deviceContext{deviceContext}
: m_deviceID{deviceContext.GetDeviceId()}
, m_deviceContext{deviceContext}
{
}

Expand Down Expand Up @@ -36,6 +36,11 @@ namespace Babylon::Graphics

// Always create with BGFX_TEXTURE_BLIT_DST to match web behavior.
m_handle = bgfx::createTexture2D(width, height, hasMips, numLayers, format, flags | BGFX_TEXTURE_BLIT_DST);
if (!bgfx::isValid(m_handle))
{
throw std::runtime_error{"Failed to create texture"};
}

m_ownsHandle = true;
m_width = width;
m_height = height;
Expand Down
55 changes: 26 additions & 29 deletions Plugins/NativeEngine/Source/IndexBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Babylon
{
IndexBuffer::IndexBuffer(Graphics::DeviceContext& deviceContext, const gsl::span<uint8_t> bytes, uint16_t flags, bool dynamic)
IndexBuffer::IndexBuffer(Graphics::DeviceContext& deviceContext, gsl::span<const uint8_t> bytes, uint16_t flags, bool dynamic)
: m_deviceContext{deviceContext}
, m_deviceID{deviceContext.GetDeviceId()}
, m_bytes{bytes.data(), bytes.data() + bytes.size()}
Expand Down Expand Up @@ -41,7 +41,7 @@ namespace Babylon
m_disposed = true;
}

void IndexBuffer::Update(const gsl::span<uint8_t> bytes, uint32_t startIndex)
void IndexBuffer::Update(gsl::span<const uint8_t> bytes, uint32_t startIndex)
{
if (!m_dynamic)
{
Expand All @@ -66,45 +66,42 @@ namespace Babylon
}
}

void IndexBuffer::Set(bgfx::Encoder* encoder, uint32_t firstIndex, uint32_t numIndices)
void IndexBuffer::Build()
{
if (!m_buildCalled)
if (!bgfx::isValid(m_handle))
{
m_buildCalled = true;
Build();
}
auto releaseFn = [](void*, void* userData) {
delete reinterpret_cast<decltype(m_bytes)*>(userData);
};

if (m_dynamic)
{
encoder->setIndexBuffer(m_dynamicHandle, firstIndex, numIndices);
}
else
{
encoder->setIndexBuffer(m_handle, firstIndex, numIndices);
auto* bytesPtr = new decltype(m_bytes){std::move(m_bytes)};
const bgfx::Memory* memory = bgfx::makeRef(bytesPtr->data(), static_cast<uint32_t>(bytesPtr->size()), releaseFn, bytesPtr);

if (m_dynamic)
{
m_dynamicHandle = bgfx::createDynamicIndexBuffer(memory, m_flags);
}
else
{
m_handle = bgfx::createIndexBuffer(memory, m_flags);
}

if (!bgfx::isValid(m_handle))
{
throw std::runtime_error{"Failed to create index buffer"};
}
}
}

void IndexBuffer::Build()
void IndexBuffer::Set(bgfx::Encoder* encoder, uint32_t firstIndex, uint32_t numIndices)
{
auto releaseFn = [](void*, void* userData) {
delete reinterpret_cast<decltype(m_bytes)*>(userData);
};

auto* bytesPtr = new decltype(m_bytes){std::move(m_bytes)};
const bgfx::Memory* memory = bgfx::makeRef(bytesPtr->data(), static_cast<uint32_t>(bytesPtr->size()), releaseFn, bytesPtr);

if (m_dynamic)
{
m_dynamicHandle = bgfx::createDynamicIndexBuffer(memory, m_flags);
encoder->setIndexBuffer(m_dynamicHandle, firstIndex, numIndices);
}
else
{
m_handle = bgfx::createIndexBuffer(memory, m_flags);
}

if (!bgfx::isValid(m_handle))
{
throw std::runtime_error{"Failed to create index buffer"};
encoder->setIndexBuffer(m_handle, firstIndex, numIndices);
}
}
}
9 changes: 4 additions & 5 deletions Plugins/NativeEngine/Source/IndexBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Babylon
class IndexBuffer final
{
public:
IndexBuffer(Graphics::DeviceContext& deviceContext, const gsl::span<uint8_t> bytes, uint16_t flags, bool dynamic);
IndexBuffer(Graphics::DeviceContext& deviceContext, gsl::span<const uint8_t> bytes, uint16_t flags, bool dynamic);
~IndexBuffer();

// No copy or move semantics
Expand All @@ -25,20 +25,19 @@ namespace Babylon

void Dispose();

void Update(const gsl::span<uint8_t> bytes, uint32_t startIndex);
void Update(gsl::span<const uint8_t> bytes, uint32_t startIndex);

void Build();

void Set(bgfx::Encoder* encoder, uint32_t firstIndex, uint32_t numIndices);

private:
void Build();

Graphics::DeviceContext& m_deviceContext;
const uintptr_t m_deviceID{};

std::vector<uint8_t> m_bytes{};
const uint16_t m_flags{};
const bool m_dynamic{};
bool m_buildCalled{};

union
{
Expand Down
33 changes: 31 additions & 2 deletions Plugins/NativeEngine/Source/NativeEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ namespace Babylon

std::function<std::pair<uint32_t, uint32_t>(uint32_t x, uint32_t y)> GetPixelMapper(bimg::Orientation::Enum orientation, uint32_t width, uint32_t height)
{
// clang-format off
switch (orientation)
{
// clang-format off
case bimg::Orientation::R0: return [](uint32_t x, uint32_t y) { return std::make_pair(x, y); };
case bimg::Orientation::R90: return [height](uint32_t x, uint32_t y) { return std::make_pair(height - y - 1, x); };
case bimg::Orientation::R180: return [width, height](uint32_t x, uint32_t y) { return std::make_pair(width - x - 1, height - y - 1); };
Expand All @@ -127,8 +127,8 @@ namespace Babylon
case bimg::Orientation::HFlipR270: return [](uint32_t x, uint32_t y) { return std::make_pair(y, x); };
case bimg::Orientation::VFlip: return [height](uint32_t x, uint32_t y) { return std::make_pair(x, height - y - 1); };
default: throw std::runtime_error{"Unexpected image orientation."};
// clang-format on
}
// clang-format on
}

using RGBA8ImageData = gsl::span<uint32_t>;
Expand Down Expand Up @@ -719,6 +719,12 @@ namespace Babylon
, m_boundFrameBuffer{&m_defaultFrameBuffer}
, m_boundFrameBufferNeedsRebinding{m_deviceContext, *m_cancellationSource, true}
{
// Set features supported by the NativeEngine from Babylon.js.
if (!info[0].IsUndefined())
{
auto jsInfo = info[0].As<Napi::Object>();
m_jsInfo.NonFloatVertexBuffers = jsInfo.Get("nonFloatVertexBuffers").As<Napi::Boolean>();
}
}

NativeEngine::~NativeEngine()
Expand Down Expand Up @@ -851,6 +857,29 @@ namespace Babylon
const bool normalized = info[7].As<Napi::Boolean>().Value();
const uint32_t divisor = info[8].As<Napi::Number>().Uint32Value();

if (!m_jsInfo.NonFloatVertexBuffers)
{
auto rendererType = bgfx::getCaps()->rendererType;

// clang-format off
bool nonFloatVertexBuffers = !normalized
&& (rendererType == bgfx::RendererType::Direct3D11 ||
rendererType == bgfx::RendererType::Direct3D12 ||
rendererType == bgfx::RendererType::Vulkan)
&& (type == bgfx::AttribType::Int8 ||
type == bgfx::AttribType::Uint8 ||
type == bgfx::AttribType::Uint10 ||
type == bgfx::AttribType::Int16 ||
type == bgfx::AttribType::Uint16);
// clang-format on

if (nonFloatVertexBuffers)
{
JsConsoleLogger::LogError(info.Env(), "Non-normalized, non-float vertex buffer is not supported for D3D11, D3D12, and Vulkan. Please update to a newer version of Babylon.js.");
return;
}
}

try
{
vertexArray->RecordVertexBuffer(vertexBuffer, location, byteOffset, byteStride, numElements, type, normalized, divisor);
Expand Down
6 changes: 6 additions & 0 deletions Plugins/NativeEngine/Source/NativeEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,5 +275,11 @@ namespace Babylon

// TODO: This should be changed to a non-owning ref once multi-update is available.
NativeDataStream* m_commandStream{};

// Information from the JS side used for backwards compatibility.
struct
{
bool NonFloatVertexBuffers{};
} m_jsInfo;
};
}
Loading

0 comments on commit 59798a7

Please sign in to comment.