Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .github/workflows/build-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ on:
js-engine:
required: true
type: string
graphics-api:
required: false
type: string
default: OpenGL
enable-sanitizers:
required: false
type: boolean
Expand All @@ -38,7 +42,7 @@ jobs:
- name: Install packages
run: |
sudo apt-get update
sudo apt-get install -y libjavascriptcoregtk-4.1-dev libgl1-mesa-dev libcurl4-openssl-dev libwayland-dev clang ninja-build
sudo apt-get install -y libjavascriptcoregtk-4.1-dev libgl1-mesa-dev libcurl4-openssl-dev libwayland-dev clang ninja-build libvulkan-dev vulkan-validationlayers

- name: Build X11
run: |
Expand All @@ -47,6 +51,7 @@ jobs:
-D NAPI_JAVASCRIPT_ENGINE=${{ inputs.js-engine }} \
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
-D BX_CONFIG_DEBUG=ON \
-D GRAPHICS_API=${{ inputs.graphics-api }} \
-D OpenGL_GL_PREFERENCE=GLVND \
-D BABYLON_DEBUG_TRACE=ON \
-D ENABLE_SANITIZERS=${{ inputs.enable-sanitizers && 'ON' || 'OFF' }} .
Expand All @@ -71,21 +76,21 @@ jobs:
if: always()
uses: actions/upload-artifact@v6
with:
name: ${{ inputs.cc }}-${{ inputs.js-engine }}${{ inputs.enable-sanitizers && '-sanitizer' || '' }}-rendered-pictures
name: ${{ inputs.cc }}-${{ inputs.js-engine }}-${{ inputs.graphics-api }}${{ inputs.enable-sanitizers && '-sanitizer' || '' }}-rendered-pictures
path: build/Linux/Apps/Playground/Results

- name: Upload Error Pictures
if: failure()
uses: actions/upload-artifact@v6
with:
name: ${{ inputs.cc }}-${{ inputs.js-engine }}${{ inputs.enable-sanitizers && '-sanitizer' || '' }}-error-pictures
name: ${{ inputs.cc }}-${{ inputs.js-engine }}-${{ inputs.graphics-api }}${{ inputs.enable-sanitizers && '-sanitizer' || '' }}-error-pictures
path: build/Linux/Apps/Playground/Errors

- name: Upload Core Dumps
if: failure()
uses: actions/upload-artifact@v6
with:
name: ${{ inputs.cc }}-${{ inputs.js-engine }}${{ inputs.enable-sanitizers && '-sanitizer' || '' }}-core-dumps
name: ${{ inputs.cc }}-${{ inputs.js-engine }}-${{ inputs.graphics-api }}${{ inputs.enable-sanitizers && '-sanitizer' || '' }}-core-dumps
path: |
build/Linux/Apps/Playground/Playground
build/Linux/Apps/Playground/core.*
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/build-win32.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,14 @@ jobs:
)

- name: Validation Tests
if: ${{ inputs.graphics-api != 'D3D12' }}
if: ${{ inputs.graphics-api != 'D3D12' && inputs.graphics-api != 'Vulkan' }}
shell: cmd
run: |
cd build\${{ steps.vars.outputs.solution_name }}\Apps\Playground\RelWithDebInfo
Playground app:///Scripts/validation_native.js

- name: Upload Rendered Pictures
if: ${{ inputs.graphics-api != 'D3D12' && !cancelled() }}
if: ${{ inputs.graphics-api != 'D3D12' && inputs.graphics-api != 'Vulkan' && !cancelled() }}
uses: actions/upload-artifact@v6
with:
name: ${{ steps.vars.outputs.solution_name }}-${{ inputs.graphics-api }}${{ inputs.enable-sanitizers && '-sanitizer' || '' }}-rendered-pictures
Expand All @@ -130,7 +130,7 @@ jobs:
Copy-Item -Path "build\${{ steps.vars.outputs.solution_name }}\Apps\Playground\RelWithDebInfo\Playground.*" -Destination "$env:RUNNER_TEMP\Dumps\" -ErrorAction SilentlyContinue

- name: Unit Tests
if: ${{ inputs.graphics-api != 'D3D12' }}
if: ${{ inputs.graphics-api != 'D3D12' && inputs.graphics-api != 'Vulkan' }}
shell: cmd
run: |
cd build\${{ steps.vars.outputs.solution_name }}\Apps\UnitTests\RelWithDebInfo
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ jobs:
platform: x64
graphics-api: D3D12

Win32_x64_Vulkan:
uses: ./.github/workflows/build-win32.yml
with:
platform: x64
graphics-api: Vulkan

Win32_x64_D3D11_PrecompiledShaderTest:
uses: ./.github/workflows/build-win32-shader.yml

Expand Down Expand Up @@ -111,6 +117,14 @@ jobs:
cxx: g++
js-engine: JavaScriptCore

Ubuntu_Clang_JSC_Vulkan:
uses: ./.github/workflows/build-linux.yml
with:
cc: clang
cxx: clang++
js-engine: JavaScriptCore
graphics-api: Vulkan

# ── Android ───────────────────────────────────────────────────
Android_Ubuntu_JSC:
uses: ./.github/workflows/build-android.yml
Expand Down
2 changes: 1 addition & 1 deletion Apps/Playground/Scripts/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -578,4 +578,4 @@
"referenceImage": "two-vertex-buffers.png"
}
]
}
}
13 changes: 13 additions & 0 deletions Apps/UnitTests/Source/Utils.Vulkan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <gtest/gtest.h>
#include "Utils.h"

Babylon::Graphics::TextureT CreateTestTexture(Babylon::Graphics::DeviceT /*device*/, uint32_t /*width*/, uint32_t /*height*/, uint32_t /*arraySize*/)
{
// Vulkan external texture creation not yet implemented
return 0;
}

void DestroyTestTexture(Babylon::Graphics::TextureT /*texture*/)
{
// Vulkan external texture destruction not yet implemented
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <cstdint>

namespace Babylon::Graphics
{
using DeviceT = void*;
using TextureT = uint64_t;
using TextureFormatT = uint32_t;

struct PlatformInfo
{
DeviceT Device;
};
}
12 changes: 12 additions & 0 deletions Core/Graphics/Source/DeviceImpl_Vulkan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include <Babylon/Graphics/RendererType.h>
#include "DeviceImpl.h"

namespace Babylon::Graphics
{
const bgfx::RendererType::Enum DeviceImpl::s_bgfxRenderType = bgfx::RendererType::Vulkan;

PlatformInfo DeviceImpl::GetPlatformInfo() const
{
return {static_cast<DeviceT>(bgfx::getInternalData()->context)};
}
}
2 changes: 1 addition & 1 deletion Dependencies/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ if(BABYLON_NATIVE_DISABLE_WEBMIN)
else()
set(SPIRV_CROSS_ENABLE_WEBMIN ON)
endif()
if(NOT GRAPHICS_API STREQUAL "OpenGL")
if(NOT GRAPHICS_API STREQUAL "OpenGL" AND NOT GRAPHICS_API STREQUAL "Vulkan")
set(SPIRV_CROSS_ENABLE_GLSL OFF)
endif()
if(NOT GRAPHICS_API STREQUAL "Metal")
Expand Down
50 changes: 50 additions & 0 deletions Plugins/ExternalTexture/Source/ExternalTexture_Vulkan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <Babylon/Plugins/ExternalTexture.h>
#include <Babylon/Graphics/Device.h>
#include <Babylon/Graphics/DeviceContext.h>
#include <Babylon/Graphics/RendererType.h>
#include <Babylon/Graphics/Texture.h>
#include <napi/pointer.h>
#include <bx/bx.h>
#include <bgfx/bgfx.h>

#include "ExternalTexture_Base.h"

// Suppress unreachable code warnings from the shared header since
// GetInfo/Set throw unconditionally (Vulkan external textures not yet implemented).
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4702)
#endif

namespace Babylon::Plugins
{
class ExternalTexture::Impl final : public ImplBase
{
public:
// Implemented in ExternalTexture_Shared.h
Impl(Graphics::TextureT, std::optional<Graphics::TextureFormatT>);
void Update(Graphics::TextureT, std::optional<Graphics::TextureFormatT>, std::optional<uint16_t>);

Graphics::TextureT Get() const
{
throw std::runtime_error{"not implemented"};
}

private:
static void GetInfo(Graphics::TextureT, std::optional<Graphics::TextureFormatT>, Info&)
{
throw std::runtime_error{"not implemented"};
}

void Set(Graphics::TextureT)
{
throw std::runtime_error{"not implemented"};
}
};
}

#include "ExternalTexture_Shared.h"

#ifdef _MSC_VER
#pragma warning(pop)
#endif
61 changes: 53 additions & 8 deletions Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,24 +87,34 @@ namespace Babylon::ShaderCompilerCommon
}
}

void AppendSamplers(std::vector<uint8_t>& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector<spirv_cross::Resource>& samplers, std::map<std::string, uint8_t>& stages)
void AppendSamplers(std::vector<uint8_t>& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector<spirv_cross::Resource>& samplers, std::map<std::string, uint8_t>& stages, bool isFragment)
{
for (const spirv_cross::Resource& sampler : samplers)
{
AppendBytes(bytes, static_cast<uint8_t>(sampler.name.size()));
AppendBytes(bytes, sampler.name);
AppendBytes(bytes, static_cast<uint8_t>(bgfx::UniformType::Sampler | BGFX_UNIFORM_SAMPLERBIT));

// TODO : These values (num, regIndex, regCount) are only used by Vulkan and should be set for that API
// num, regIndex, regCount — only used by bgfx's Vulkan renderer.
// regIndex is the texture descriptor binding; bgfx computes the sampler
// binding at regIndex + kSpirvSamplerShift (16).
const auto samplerBinding = compiler.get_decoration(sampler.id, spv::DecorationBinding);
const uint16_t regIndex = static_cast<uint16_t>(samplerBinding >= 16 ? samplerBinding - 16 : samplerBinding);
AppendBytes(bytes, static_cast<uint8_t>(0));
AppendBytes(bytes, static_cast<uint16_t>(0));
AppendBytes(bytes, regIndex);
AppendBytes(bytes, static_cast<uint16_t>(0));

#if OPENGL
BX_UNUSED(compiler);
const auto stage{static_cast<uint8_t>(stages.size())};
stages[sampler.name] = stage;
BX_UNUSED(isFragment);
#elif VULKAN
// Stage index must match what bgfx computes: regIndex - reverseShift.
// For old binding model: reverseShift = (fragment ? 48 : 0) + 16
const uint8_t reverseShift = static_cast<uint8_t>(isFragment ? 64 : 16);
stages[sampler.name] = static_cast<uint8_t>(regIndex - reverseShift);
#else
BX_UNUSED(isFragment);
stages[sampler.name] = static_cast<uint8_t>(compiler.get_decoration(sampler.id, spv::DecorationBinding));
#endif
}
Expand Down Expand Up @@ -249,22 +259,57 @@ namespace Babylon::ShaderCompilerCommon

AppendBytes(vertexBytes, static_cast<uint16_t>(numUniforms));
AppendUniformBuffer(vertexBytes, uniformsInfo, false);
AppendSamplers(vertexBytes, compiler, samplers, bgfxShaderInfo.UniformStages);
AppendSamplers(vertexBytes, compiler, samplers, bgfxShaderInfo.UniformStages, false);

AppendBytes(vertexBytes, static_cast<uint32_t>(vertexShaderInfo.Bytes.size()));
AppendBytes(vertexBytes, vertexShaderInfo.Bytes);
AppendBytes(vertexBytes, static_cast<uint8_t>(0));

AppendBytes(vertexBytes, static_cast<uint8_t>(resources.stage_inputs.size()));

#if VULKAN
// bgfx's Vulkan renderer uses the attribute's index in the shader binary
// as the Vulkan location (via m_attrRemap[attr] = index). The SPIR-V uses
// Attrib::Enum values as Locations (e.g., TexCoord0=10). To make
// m_attrRemap[attr] == Location, we must write attributes at indices that
// match their Location, padding gaps with a dummy ID (0) that bgfx skips.
{
const auto& nameToAttrib = GetBgfxNameToAttribMap();
uint32_t maxLocation = 0;
for (const spirv_cross::Resource& stageInput : resources.stage_inputs)
{
const uint32_t loc = compiler.get_decoration(stageInput.id, spv::DecorationLocation);
if (loc > maxLocation) maxLocation = loc;
}
std::vector<uint16_t> attribIds(maxLocation + 1, 0);
for (const spirv_cross::Resource& stageInput : resources.stage_inputs)
{
const uint32_t loc = compiler.get_decoration(stageInput.id, spv::DecorationLocation);
auto it = nameToAttrib.find(stageInput.name);
bgfx::Attrib::Enum attrib = (it != nameToAttrib.end())
? it->second
: static_cast<bgfx::Attrib::Enum>(loc);
attribIds[loc] = bgfx::attribToId(attrib);

const std::string& originalName = vertexShaderInfo.AttributeRenaming[stageInput.name];
bgfxShaderInfo.VertexAttributeLocations[originalName] = static_cast<uint32_t>(attrib);
}
// Rewrite the count to include padding entries
vertexBytes.resize(vertexBytes.size() - 1);
AppendBytes(vertexBytes, static_cast<uint8_t>(attribIds.size()));
for (uint16_t id : attribIds)
{
AppendBytes(vertexBytes, id);
}
}
#else
for (const spirv_cross::Resource& stageInput : resources.stage_inputs)
{
const uint32_t location = compiler.get_decoration(stageInput.id, spv::DecorationLocation);
AppendBytes(vertexBytes, bgfx::attribToId(static_cast<bgfx::Attrib::Enum>(location)));

// Map from symbolName -> originalName to associate babylon.js shader attribute -> Babylon Native attribute location.
bgfxShaderInfo.VertexAttributeLocations[vertexShaderInfo.AttributeRenaming[stageInput.name]] = location;
}
#endif
AppendBytes(vertexBytes, static_cast<uint16_t>(uniformsInfo.ByteSize));
}

Expand All @@ -290,7 +335,7 @@ namespace Babylon::ShaderCompilerCommon

AppendBytes(fragmentBytes, static_cast<uint16_t>(numUniforms));
AppendUniformBuffer(fragmentBytes, uniformsInfo, true);
AppendSamplers(fragmentBytes, compiler, samplers, bgfxShaderInfo.UniformStages);
AppendSamplers(fragmentBytes, compiler, samplers, bgfxShaderInfo.UniformStages, true);

AppendBytes(fragmentBytes, static_cast<uint32_t>(fragmentShaderInfo.Bytes.size()));
AppendBytes(fragmentBytes, fragmentShaderInfo.Bytes);
Expand Down
32 changes: 31 additions & 1 deletion Plugins/ShaderCompiler/Source/ShaderCompilerCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,41 @@
#include <gsl/gsl>
#include <spirv_cross.hpp>
#include <spirv_parser.hpp>
#include <bgfx/bgfx.h>
#include <map>
#include <string>

namespace Babylon::ShaderCompilerCommon
{
// Mapping from bgfx attribute names to bgfx::Attrib::Enum.
// Used by both the shader traverser (to assign SPIR-V locations) and
// the shader binary builder (to write correct attrib IDs and set up
// VertexAttributeLocations).
inline const std::map<std::string, bgfx::Attrib::Enum>& GetBgfxNameToAttribMap()
{
static const std::map<std::string, bgfx::Attrib::Enum> map = {
{"a_position", bgfx::Attrib::Position},
{"a_normal", bgfx::Attrib::Normal},
{"a_tangent", bgfx::Attrib::Tangent},
{"a_texcoord0", bgfx::Attrib::TexCoord0},
{"a_texcoord1", bgfx::Attrib::TexCoord1},
{"a_texcoord2", bgfx::Attrib::TexCoord2},
{"a_texcoord3", bgfx::Attrib::TexCoord3},
{"a_texcoord4", bgfx::Attrib::TexCoord4},
{"a_texcoord5", bgfx::Attrib::TexCoord5},
{"a_texcoord6", bgfx::Attrib::TexCoord6},
{"a_texcoord7", bgfx::Attrib::TexCoord7},
{"a_color0", bgfx::Attrib::Color0},
{"a_indices", bgfx::Attrib::Indices},
{"a_weight", bgfx::Attrib::Weight},
{"i_data0", bgfx::Attrib::TexCoord4},
{"i_data1", bgfx::Attrib::TexCoord5},
{"i_data2", bgfx::Attrib::TexCoord6},
{"i_data3", bgfx::Attrib::TexCoord7},
{"i_data5", bgfx::Attrib::TexCoord3},
};
return map;
}
std::string ProcessShaderCoordinates(std::string_view source);

std::string ProcessSamplerFlip(std::string_view source);
Expand Down Expand Up @@ -61,7 +91,7 @@ namespace Babylon::ShaderCompilerCommon
};

void AppendUniformBuffer(std::vector<uint8_t>& bytes, const NonSamplerUniformsInfo& uniformBuffer, bool isFragment);
void AppendSamplers(std::vector<uint8_t>& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector<spirv_cross::Resource>& samplers, std::map<std::string, uint8_t>& stages);
void AppendSamplers(std::vector<uint8_t>& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector<spirv_cross::Resource>& samplers, std::map<std::string, uint8_t>& stages, bool isFragment);
NonSamplerUniformsInfo CollectNonSamplerUniforms(spirv_cross::Parser& parser, const spirv_cross::Compiler& compiler);

struct ShaderInfo
Expand Down
Loading
Loading