From 55f1270ef21562c131b2f17f9dea6c0387886b31 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Fri, 20 Mar 2026 17:03:10 +0100 Subject: [PATCH 01/11] Add Vulkan graphics API support - Add RendererType header for Vulkan (DeviceT=void*, TextureT=uint64_t) - Add DeviceImpl_Vulkan.cpp setting bgfx renderer to Vulkan - Add ExternalTexture_Vulkan.cpp stub implementation - Add Utils.Vulkan.cpp stub for unit tests - Enable SPIRV-Cross GLSL for Vulkan (needed by ShaderCompilerVulkan) - Add VertexVaryingInTraverserVulkan shader traverser with location-based attribute bindings matching bgfx's Vulkan expectations - Update ShaderCompilerVulkan to use Vulkan-specific traverser - Add Vulkan CI jobs: Ubuntu/Clang/JSC and Win32/x64/Chakra - Add graphics_api parameter to linux.yml CI template Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/jobs/linux.yml | 14 +++- Apps/UnitTests/Source/Utils.Vulkan.cpp | 13 ++++ .../Vulkan/Babylon/Graphics/RendererType.h | 15 +++++ Core/Graphics/Source/DeviceImpl_Vulkan.cpp | 12 ++++ Dependencies/CMakeLists.txt | 2 +- .../Source/ExternalTexture_Vulkan.cpp | 50 ++++++++++++++ .../Source/ShaderCompilerTraversers.cpp | 66 +++++++++++++++++++ .../Source/ShaderCompilerTraversers.h | 1 + .../Source/ShaderCompilerVulkan.cpp | 2 +- azure-pipelines.yml | 16 +++++ 10 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 Apps/UnitTests/Source/Utils.Vulkan.cpp create mode 100644 Core/Graphics/Include/RendererType/Vulkan/Babylon/Graphics/RendererType.h create mode 100644 Core/Graphics/Source/DeviceImpl_Vulkan.cpp create mode 100644 Plugins/ExternalTexture/Source/ExternalTexture_Vulkan.cpp diff --git a/.github/jobs/linux.yml b/.github/jobs/linux.yml index 204c1e811..00e09fa91 100644 --- a/.github/jobs/linux.yml +++ b/.github/jobs/linux.yml @@ -4,6 +4,7 @@ parameters: CC: "" CXX: "" JSEngine: "" + graphics_api: "" enableSanitizers: false jobs: @@ -16,6 +17,10 @@ jobs: SANITIZER_FLAG: ${{coalesce(replace(format('{0}', parameters.enableSanitizers), 'True', 'ON'), 'OFF')}} CC: ${{parameters.CC}} CXX: ${{parameters.CXX}} + ${{if eq(parameters.graphics_api, '')}}: + graphicsApiDefine: "" + ${{else}}: + graphicsApiDefine: "-D GRAPHICS_API=${{parameters.graphics_api}}" steps: - template: cmake.yml @@ -26,9 +31,16 @@ jobs: sudo apt-get update sudo apt-get install libjavascriptcoregtk-4.1-dev libgl1-mesa-dev libcurl4-openssl-dev libwayland-dev clang displayName: "Install packages" + condition: ne('${{parameters.graphics_api}}', 'Vulkan') - script: | - cmake -G Ninja -B build/Linux -D JAVASCRIPTCORE_LIBRARY=/usr/lib/x86_64-linux-gnu/libjavascriptcoregtk-4.1.so -D NAPI_JAVASCRIPT_ENGINE=${{parameters.JSEngine}} -D CMAKE_BUILD_TYPE=RelWithDebInfo -D BX_CONFIG_DEBUG=ON -D CMAKE_UNITY_BUILD=$(UNITY_BUILD) -D OpenGL_GL_PREFERENCE=GLVND -D BABYLON_DEBUG_TRACE=ON -D ENABLE_SANITIZERS=$(SANITIZER_FLAG) . + sudo apt-get update + sudo apt-get install libjavascriptcoregtk-4.1-dev libgl1-mesa-dev libcurl4-openssl-dev libwayland-dev libvulkan-dev vulkan-validationlayers clang + displayName: "Install packages (Vulkan)" + condition: eq('${{parameters.graphics_api}}', 'Vulkan') + + - script: | + cmake -G Ninja -B build/Linux -D JAVASCRIPTCORE_LIBRARY=/usr/lib/x86_64-linux-gnu/libjavascriptcoregtk-4.1.so -D NAPI_JAVASCRIPT_ENGINE=${{parameters.JSEngine}} -D CMAKE_BUILD_TYPE=RelWithDebInfo -D BX_CONFIG_DEBUG=ON -D CMAKE_UNITY_BUILD=$(UNITY_BUILD) -D OpenGL_GL_PREFERENCE=GLVND -D BABYLON_DEBUG_TRACE=ON -D ENABLE_SANITIZERS=$(SANITIZER_FLAG) $(graphicsApiDefine) . ninja -C build/Linux displayName: "Build X11" diff --git a/Apps/UnitTests/Source/Utils.Vulkan.cpp b/Apps/UnitTests/Source/Utils.Vulkan.cpp new file mode 100644 index 000000000..e9742446c --- /dev/null +++ b/Apps/UnitTests/Source/Utils.Vulkan.cpp @@ -0,0 +1,13 @@ +#include +#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 +} diff --git a/Core/Graphics/Include/RendererType/Vulkan/Babylon/Graphics/RendererType.h b/Core/Graphics/Include/RendererType/Vulkan/Babylon/Graphics/RendererType.h new file mode 100644 index 000000000..a82888dcb --- /dev/null +++ b/Core/Graphics/Include/RendererType/Vulkan/Babylon/Graphics/RendererType.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace Babylon::Graphics +{ + using DeviceT = void*; + using TextureT = uint64_t; + using TextureFormatT = uint32_t; + + struct PlatformInfo + { + DeviceT Device; + }; +} diff --git a/Core/Graphics/Source/DeviceImpl_Vulkan.cpp b/Core/Graphics/Source/DeviceImpl_Vulkan.cpp new file mode 100644 index 000000000..b81e6cd4f --- /dev/null +++ b/Core/Graphics/Source/DeviceImpl_Vulkan.cpp @@ -0,0 +1,12 @@ +#include +#include "DeviceImpl.h" + +namespace Babylon::Graphics +{ + const bgfx::RendererType::Enum DeviceImpl::s_bgfxRenderType = bgfx::RendererType::Vulkan; + + PlatformInfo DeviceImpl::GetPlatformInfo() const + { + return {static_cast(bgfx::getInternalData()->context)}; + } +} diff --git a/Dependencies/CMakeLists.txt b/Dependencies/CMakeLists.txt index 894e4d810..c395038fc 100644 --- a/Dependencies/CMakeLists.txt +++ b/Dependencies/CMakeLists.txt @@ -224,7 +224,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") diff --git a/Plugins/ExternalTexture/Source/ExternalTexture_Vulkan.cpp b/Plugins/ExternalTexture/Source/ExternalTexture_Vulkan.cpp new file mode 100644 index 000000000..fc74dc1c8 --- /dev/null +++ b/Plugins/ExternalTexture/Source/ExternalTexture_Vulkan.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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); + void Update(Graphics::TextureT, std::optional, std::optional); + + Graphics::TextureT Get() const + { + throw std::runtime_error{"not implemented"}; + } + + private: + static void GetInfo(Graphics::TextureT, std::optional, 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 diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp index ba1e0b75b..a1b224f44 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp @@ -748,6 +748,67 @@ namespace Babylon::ShaderCompilerTraversers const unsigned int FIRST_GENERIC_ATTRIBUTE_LOCATION{10}; }; + /// Implementation of VertexVaryingInTraverser for Vulkan. + /// Vulkan uses SPIR-V directly with location-based attribute bindings. + /// Similar to D3D, it maps Babylon.js attribute names to specific bgfx + /// attribute locations using bgfx's a_* naming convention. + class VertexVaryingInTraverserVulkan final : private VertexVaryingInTraverser + { + public: + static void Traverse(TProgram& program, IdGenerator& ids, std::map& replacementToOriginalName) + { + auto intermediate{program.getIntermediate(EShLangVertex)}; + VertexVaryingInTraverserVulkan traverser{}; + intermediate->getTreeRoot()->traverse(&traverser); + // UVs are effectively a special kind of generic attribute since they both use + // are implemented using texture coordinates, so we preprocess to pre-count the + // number of UV coordinate variables to prevent collisions. + for (const auto& [name, symbol] : traverser.m_varyingNameToSymbol) + { + if (name.size() >= 2 && name[0] == 'u' && name[1] == 'v') + { + traverser.m_genericAttributesRunningCount++; + } + } + VertexVaryingInTraverser::Traverse(intermediate, ids, replacementToOriginalName, traverser); + } + + private: + std::pair GetVaryingLocationAndNewNameForName(const char* name) + { +#define IF_NAME_RETURN_ATTRIB(varyingName, attrib, newName) \ + if (std::strcmp(name, varyingName) == 0) \ + { \ + return {static_cast(attrib), newName}; \ + } + IF_NAME_RETURN_ATTRIB("position", bgfx::Attrib::Position, "a_position") + IF_NAME_RETURN_ATTRIB("normal", bgfx::Attrib::Normal, "a_normal") + IF_NAME_RETURN_ATTRIB("tangent", bgfx::Attrib::Tangent, "a_tangent") + IF_NAME_RETURN_ATTRIB("uv", bgfx::Attrib::TexCoord0, "a_texcoord0") + IF_NAME_RETURN_ATTRIB("uv2", bgfx::Attrib::TexCoord1, "a_texcoord1") + IF_NAME_RETURN_ATTRIB("uv3", bgfx::Attrib::TexCoord2, "a_texcoord2") + IF_NAME_RETURN_ATTRIB("uv4", bgfx::Attrib::TexCoord3, "a_texcoord3") + IF_NAME_RETURN_ATTRIB("color", bgfx::Attrib::Color0, "a_color0") + IF_NAME_RETURN_ATTRIB("matricesIndices", bgfx::Attrib::Indices, "a_indices") + IF_NAME_RETURN_ATTRIB("matricesWeights", bgfx::Attrib::Weight, "a_weight") + IF_NAME_RETURN_ATTRIB("instanceColor", bgfx::Attrib::TexCoord3, "i_data5") + IF_NAME_RETURN_ATTRIB("world0", bgfx::Attrib::TexCoord4, "i_data0") + IF_NAME_RETURN_ATTRIB("world1", bgfx::Attrib::TexCoord5, "i_data1") + IF_NAME_RETURN_ATTRIB("world2", bgfx::Attrib::TexCoord6, "i_data2") + IF_NAME_RETURN_ATTRIB("world3", bgfx::Attrib::TexCoord7, "i_data3") + IF_NAME_RETURN_ATTRIB("splatIndex0", bgfx::Attrib::TexCoord4, "i_data0") + IF_NAME_RETURN_ATTRIB("splatIndex1", bgfx::Attrib::TexCoord5, "i_data1") + IF_NAME_RETURN_ATTRIB("splatIndex2", bgfx::Attrib::TexCoord6, "i_data2") + IF_NAME_RETURN_ATTRIB("splatIndex3", bgfx::Attrib::TexCoord7, "i_data3") +#undef IF_NAME_RETURN_ATTRIB + const unsigned int attributeLocation = FIRST_GENERIC_ATTRIBUTE_LOCATION + m_genericAttributesRunningCount++; + if (attributeLocation >= static_cast(bgfx::Attrib::Count)) + throw std::runtime_error("Cannot support more than 18 vertex attributes."); + return {attributeLocation, name}; + } + const unsigned int FIRST_GENERIC_ATTRIBUTE_LOCATION{10}; + }; + /// /// Split sampler symbols into separate sampler and texture symbols and assign bindings. /// This is required for DirectX and Metal. Note that bgfx expects sequential bindings @@ -948,6 +1009,11 @@ namespace Babylon::ShaderCompilerTraversers VertexVaryingInTraverserD3D::Traverse(program, ids, replacementToOriginalName); } + void AssignLocationsAndNamesToVertexVaryingsVulkan(TProgram& program, IdGenerator& ids, std::map& replacementToOriginalName) + { + VertexVaryingInTraverserVulkan::Traverse(program, ids, replacementToOriginalName); + } + void SplitSamplersIntoSamplersAndTextures(TProgram& program, IdGenerator& ids) { SamplerSplitterTraverser::Traverse(program, ids); diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h index 83600030f..05ed835ed 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h @@ -71,6 +71,7 @@ namespace Babylon::ShaderCompilerTraversers void AssignLocationsAndNamesToVertexVaryingsOpenGL(glslang::TProgram& program, IdGenerator& ids, std::map& vertexAttributeRenaming); void AssignLocationsAndNamesToVertexVaryingsMetal(glslang::TProgram& program, IdGenerator& ids, std::map& vertexAttributeRenaming); void AssignLocationsAndNamesToVertexVaryingsD3D(glslang::TProgram& program, IdGenerator& ids, std::map& vertexAttributeRenaming); + void AssignLocationsAndNamesToVertexVaryingsVulkan(glslang::TProgram& program, IdGenerator& ids, std::map& vertexAttributeRenaming); /// WebGL (and therefore Babylon.js) treats texture samplers as a single variable. /// Native platforms expect them to be two separate variables -- a texture and a diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp index 1de310467..44747c976 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp @@ -81,7 +81,7 @@ namespace Babylon::Plugins auto cutScope = ShaderCompilerTraversers::ChangeUniformTypes(program, ids); auto utstScope = ShaderCompilerTraversers::MoveNonSamplerUniformsIntoStruct(program, ids); std::map vertexAttributeRenaming = {}; - ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsD3D(program, ids, vertexAttributeRenaming); + ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsVulkan(program, ids, vertexAttributeRenaming); ShaderCompilerTraversers::SplitSamplersIntoSamplersAndTextures(program, ids); ShaderCompilerTraversers::InvertYDerivativeOperands(program); diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c590c3651..0d1d5b535 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -80,6 +80,13 @@ jobs: platform: x64 graphics_api: D3D12 + - template: .github/jobs/win32.yml + parameters: + name: Win32_x64_Vulkan + vmImage: windows-latest + platform: x64 + graphics_api: Vulkan + - template: .github/jobs/win32_precompiled_shader_test.yml parameters: name: Win32_x64_D3D11_PrecompiledShaderTest @@ -119,6 +126,15 @@ jobs: CXX: clang++ JSEngine: JavaScriptCore + - template: .github/jobs/linux.yml + parameters: + name: Ubuntu_Clang_JSC_Vulkan + vmImage: ubuntu-latest + CC: clang + CXX: clang++ + JSEngine: JavaScriptCore + graphics_api: Vulkan + - template: .github/jobs/linux.yml parameters: name: Ubuntu_GCC_JSC From 21e9d51987e79966b23d8c80363966352d0bc4d9 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Wed, 25 Mar 2026 17:06:49 +0100 Subject: [PATCH 02/11] Fix Vulkan shader descriptor binding mismatches The shader compiler hardcoded layoutBinding = 0 for all shader stages and descriptor types, but bgfx's old binding model (shader version < 11) expects specific binding offsets per stage: - VS uniform buffer: binding 0 - FS uniform buffer: binding 48 (kSpirvOldFragmentBinding) - VS textures: binding 16+ (kSpirvOldTextureShift) - VS samplers: binding 32+ (texture + kSpirvSamplerShift) - FS textures: binding 64+ (kSpirvOldFragmentShift + kSpirvOldTextureShift) - FS samplers: binding 80+ (texture + kSpirvSamplerShift) This caused: - Black cube: FS uniform buffer unbound (all lighting values zero) - White glTF: textures/samplers at wrong bindings (not bound) Fix applies Vulkan-specific binding offsets in three places: 1. NonSamplerUniformToStructTraverser: FS uniform at binding 48 2. SamplerSplitterTraverser: VS textures at 16+, FS textures at 64+, samplers offset by +16 from their texture binding 3. AppendSamplers: write correct regIndex and stage index for bgfx All changes guarded with #if VULKAN to avoid affecting D3D/Metal paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Source/ShaderCompilerCommon.cpp | 25 +++++++++++++--- .../Source/ShaderCompilerCommon.h | 2 +- .../Source/ShaderCompilerTraversers.cpp | 29 ++++++++++++++++++- 3 files changed, 50 insertions(+), 6 deletions(-) diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp index c0bd10a9d..2433c1591 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp @@ -87,7 +87,7 @@ namespace Babylon::ShaderCompilerCommon } } - void AppendSamplers(std::vector& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector& samplers, std::map& stages) + void AppendSamplers(std::vector& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector& samplers, std::map& stages, bool isFragment) { for (const spirv_cross::Resource& sampler : samplers) { @@ -95,15 +95,32 @@ namespace Babylon::ShaderCompilerCommon AppendBytes(bytes, sampler.name); AppendBytes(bytes, static_cast(bgfx::UniformType::Sampler | BGFX_UNIFORM_SAMPLERBIT)); - // TODO : These values (num, regIndex, regCount) are only used by Vulkan and should be set for that API +#if VULKAN + // bgfx's Vulkan renderer uses regIndex as the texture descriptor binding. + // The sampler's SPIR-V binding is texture_binding + kSpirvSamplerShift (16), + // so subtract 16 to recover the texture binding that bgfx expects. + const auto samplerBinding = compiler.get_decoration(sampler.id, spv::DecorationBinding); + const uint16_t regIndex = static_cast(samplerBinding >= 16 ? samplerBinding - 16 : samplerBinding); AppendBytes(bytes, static_cast(0)); + AppendBytes(bytes, regIndex); AppendBytes(bytes, static_cast(0)); +#else + BX_UNUSED(isFragment); + // These values (num, regIndex, regCount) are only used by Vulkan. + AppendBytes(bytes, static_cast(0)); AppendBytes(bytes, static_cast(0)); + AppendBytes(bytes, static_cast(0)); +#endif #if OPENGL BX_UNUSED(compiler); const auto stage{static_cast(stages.size())}; stages[sampler.name] = stage; +#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(isFragment ? 64 : 16); + stages[sampler.name] = static_cast(regIndex - reverseShift); #else stages[sampler.name] = static_cast(compiler.get_decoration(sampler.id, spv::DecorationBinding)); #endif @@ -249,7 +266,7 @@ namespace Babylon::ShaderCompilerCommon AppendBytes(vertexBytes, static_cast(numUniforms)); AppendUniformBuffer(vertexBytes, uniformsInfo, false); - AppendSamplers(vertexBytes, compiler, samplers, bgfxShaderInfo.UniformStages); + AppendSamplers(vertexBytes, compiler, samplers, bgfxShaderInfo.UniformStages, false); AppendBytes(vertexBytes, static_cast(vertexShaderInfo.Bytes.size())); AppendBytes(vertexBytes, vertexShaderInfo.Bytes); @@ -290,7 +307,7 @@ namespace Babylon::ShaderCompilerCommon AppendBytes(fragmentBytes, static_cast(numUniforms)); AppendUniformBuffer(fragmentBytes, uniformsInfo, true); - AppendSamplers(fragmentBytes, compiler, samplers, bgfxShaderInfo.UniformStages); + AppendSamplers(fragmentBytes, compiler, samplers, bgfxShaderInfo.UniformStages, true); AppendBytes(fragmentBytes, static_cast(fragmentShaderInfo.Bytes.size())); AppendBytes(fragmentBytes, fragmentShaderInfo.Bytes); diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.h b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.h index c5614dccb..39d49a1a3 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.h +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.h @@ -61,7 +61,7 @@ namespace Babylon::ShaderCompilerCommon }; void AppendUniformBuffer(std::vector& bytes, const NonSamplerUniformsInfo& uniformBuffer, bool isFragment); - void AppendSamplers(std::vector& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector& samplers, std::map& stages); + void AppendSamplers(std::vector& bytes, const spirv_cross::Compiler& compiler, const spirv_cross::SmallVector& samplers, std::map& stages, bool isFragment); NonSamplerUniformsInfo CollectNonSamplerUniforms(spirv_cross::Parser& parser, const spirv_cross::Compiler& compiler); struct ShaderInfo diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp index a1b224f44..ad13f01de 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp @@ -165,6 +165,8 @@ namespace Babylon::ShaderCompilerTraversers publicType.qualifier.layoutMatrix = ElmColumnMajor; publicType.qualifier.layoutPacking = ElpStd140; + const bool isFragment = (intermediate->getStage() == EShLangFragment); + std::vector originalNames{}; scope.TypeLists.emplace_back(std::make_unique()); auto* structMembers = scope.TypeLists.back().get(); @@ -212,7 +214,14 @@ namespace Babylon::ShaderCompilerTraversers qualifier.storage = EvqUniform; qualifier.layoutMatrix = ElmColumnMajor; qualifier.layoutPacking = ElpStd140; - qualifier.layoutBinding = 0; // Determines which cbuffer it's bounds to (b0, b1, b2, etc.) +#if VULKAN + // bgfx's old binding model (shader version < 11) expects the fragment + // shader uniform buffer at binding 48 (kSpirvOldFragmentBinding) and + // the vertex shader uniform buffer at binding 0. + qualifier.layoutBinding = isFragment ? 48 : 0; +#else + qualifier.layoutBinding = 0; +#endif // Create the struct type. Name chosen arbitrarily (legacy reasons). TType structType(structMembers, "Frame", qualifier); @@ -838,9 +847,21 @@ namespace Babylon::ShaderCompilerTraversers static void Traverse(TProgram& program, IdGenerator& ids) { +#if VULKAN + // bgfx's old binding model (shader version < 11) expects texture bindings + // offset by kSpirvOldTextureShift (16) for VS and by + // kSpirvOldFragmentShift + kSpirvOldTextureShift (48+16=64) for FS. + // Samplers use the same binding as their texture (bgfx adds + // kSpirvSamplerShift at runtime). + unsigned int vsTextureBinding{16}; // kSpirvOldTextureShift + Traverse(program.getIntermediate(EShLangVertex), ids, vsTextureBinding); + unsigned int fsTextureBinding{64}; // kSpirvOldFragmentShift + kSpirvOldTextureShift + Traverse(program.getIntermediate(EShLangFragment), ids, fsTextureBinding); +#else unsigned int layoutBinding{0}; Traverse(program.getIntermediate(EShLangVertex), ids, layoutBinding); Traverse(program.getIntermediate(EShLangFragment), ids, layoutBinding); +#endif } private: @@ -886,7 +907,13 @@ namespace Babylon::ShaderCompilerTraversers publicType.basicType = type.getBasicType(); publicType.qualifier = type.getQualifier(); publicType.qualifier.precision = EpqHigh; +#if VULKAN + // bgfx's Vulkan renderer expects the sampler binding to be + // texture binding + kSpirvSamplerShift (16). + publicType.qualifier.layoutBinding = layoutBinding + 16; +#else publicType.qualifier.layoutBinding = layoutBinding; +#endif publicType.sampler.sampler = true; TType newType{publicType}; From f660bac9eb38c7f9aa66a262eaba031b499df6ee Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Wed, 25 Mar 2026 17:56:42 +0100 Subject: [PATCH 03/11] Fix Vulkan image layout crash on render-to-texture sampling When an RTT framebuffer is unbound, insert a transition view with the default backbuffer so bgfx transitions the RTT attachment textures from VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL to the sampled layout before any subsequent draw call attempts to sample them. Previously FrameBuffer::Unbind() was a no-op, which meant bgfx might not process the framebuffer change before the RTT texture was sampled, causing an assert: 'texture.m_currentImageLayout == texture.m_sampledLayout'. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Core/Graphics/Source/FrameBuffer.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Core/Graphics/Source/FrameBuffer.cpp b/Core/Graphics/Source/FrameBuffer.cpp index 35959f606..1c19bc530 100644 --- a/Core/Graphics/Source/FrameBuffer.cpp +++ b/Core/Graphics/Source/FrameBuffer.cpp @@ -74,8 +74,19 @@ namespace Babylon::Graphics m_viewId.reset(); } - void FrameBuffer::Unbind(bgfx::Encoder&) + void FrameBuffer::Unbind(bgfx::Encoder& encoder) { + // For render-to-texture framebuffers, insert a transition view so bgfx + // transitions the attachment textures from attachment layout to sampled + // layout before any subsequent draw call tries to sample them. + if (!m_defaultBackBuffer && m_viewId.has_value()) + { + auto transitionViewId = m_deviceContext.AcquireNewViewId(encoder); + bgfx::setViewFrameBuffer(transitionViewId, BGFX_INVALID_HANDLE); + bgfx::setViewRect(transitionViewId, 0, 0, 1, 1); + encoder.touch(transitionViewId); + } + m_viewId.reset(); } void FrameBuffer::Clear(bgfx::Encoder& encoder, uint16_t flags, uint32_t rgba, float depth, uint8_t stencil) From 0f58a36cf22a15ba9c6ac45990fcbd417f28758a Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Wed, 25 Mar 2026 18:36:44 +0100 Subject: [PATCH 04/11] Fix Vulkan varying location assignments for inter-stage data flow Add AssignLocationsToInterStageVaryings traverser that assigns explicit SPIR-V Location decorations to VS outputs and FS inputs. Vulkan requires explicit Location decorations on all inter-stage varyings; without them, VS output data (positions, normals, UVs, TBN matrices) would not flow correctly to the FS. The traverser collects VS output varyings from linker objects, assigns sequential locations (accounting for matrix column count), then applies matching locations to ALL symbol instances in both VS and FS ASTs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Source/ShaderCompilerTraversers.cpp | 68 +++++++++++++++++++ .../Source/ShaderCompilerTraversers.h | 5 ++ .../Source/ShaderCompilerVulkan.cpp | 1 + 3 files changed, 74 insertions(+) diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp index ad13f01de..237bad56a 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp @@ -1050,4 +1050,72 @@ namespace Babylon::ShaderCompilerTraversers { InvertYDerivativeOperandsTraverser::Traverse(program); } + + void AssignLocationsToInterStageVaryings(TProgram& program) + { + auto* vsIntermediate = program.getIntermediate(EShLangVertex); + auto* fsIntermediate = program.getIntermediate(EShLangFragment); + if (!vsIntermediate || !fsIntermediate) + return; + + // First pass: collect VS output varying names from linker objects + // and assign sequential locations. + auto* vsRoot = vsIntermediate->getTreeRoot()->getAsAggregate(); + if (!vsRoot) return; + auto* vsLinker = vsRoot->getSequence().back()->getAsAggregate(); + if (!vsLinker) return; + + std::map nameToLocation; + int nextLocation = 0; + + for (auto* node : vsLinker->getSequence()) + { + auto* symbol = node->getAsSymbolNode(); + if (symbol + && symbol->getType().getQualifier().isPipeOutput() + && symbol->getType().getQualifier().builtIn == EbvNone) + { + const std::string name = symbol->getName().c_str(); + const auto& type = symbol->getType(); + int locationCount = type.isMatrix() ? type.getMatrixCols() : 1; + nameToLocation[name] = nextLocation; + nextLocation += locationCount; + } + } + + // Traverser that sets location on ALL symbol instances matching + // the varying names, not just linker objects. + class LocationSetter : public TIntermTraverser + { + public: + const std::map& locations; + TStorageQualifier targetStorage; + + LocationSetter(const std::map& locs, TStorageQualifier storage) + : TIntermTraverser(true, false, false) + , locations(locs) + , targetStorage(storage) {} + + void visitSymbol(TIntermSymbol* symbol) override + { + if (symbol->getType().getQualifier().storage == targetStorage + && symbol->getType().getQualifier().builtIn == EbvNone) + { + auto it = locations.find(symbol->getName().c_str()); + if (it != locations.end()) + { + symbol->getWritableType().getQualifier().layoutLocation = it->second; + } + } + } + }; + + // Second pass: apply locations to ALL VS output symbols + LocationSetter vsSetter(nameToLocation, EvqVaryingOut); + vsIntermediate->getTreeRoot()->traverse(&vsSetter); + + // Third pass: apply matching locations to ALL FS input symbols + LocationSetter fsSetter(nameToLocation, EvqVaryingIn); + fsIntermediate->getTreeRoot()->traverse(&fsSetter); + } } diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h index 05ed835ed..8386e6745 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.h @@ -83,4 +83,9 @@ namespace Babylon::ShaderCompilerTraversers /// https://github.com/bkaradzic/bgfx/blob/7be225bf490bb1cd231cfb4abf7e617bf35b59cb/src/bgfx_shader.sh#L44-L45 /// https://github.com/bkaradzic/bgfx/blob/7be225bf490bb1cd231cfb4abf7e617bf35b59cb/src/bgfx_shader.sh#L62-L65 void InvertYDerivativeOperands(glslang::TProgram& program); + + /// Assign explicit Location decorations to inter-stage varyings (VS outputs + /// and FS inputs). Vulkan SPIR-V requires explicit locations on all varyings; + /// without them, VS outputs and FS inputs may not match correctly. + void AssignLocationsToInterStageVaryings(glslang::TProgram& program); } diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp index 44747c976..332a01ffb 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerVulkan.cpp @@ -84,6 +84,7 @@ namespace Babylon::Plugins ShaderCompilerTraversers::AssignLocationsAndNamesToVertexVaryingsVulkan(program, ids, vertexAttributeRenaming); ShaderCompilerTraversers::SplitSamplersIntoSamplersAndTextures(program, ids); ShaderCompilerTraversers::InvertYDerivativeOperands(program); + ShaderCompilerTraversers::AssignLocationsToInterStageVaryings(program); std::vector spirvVS; auto [vertexParser, vertexCompiler] = CompileShader(program, EShLangVertex, spirvVS); From 23c3c6682625b4ce6e494b7e6829dd4d56a7ef77 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:15:50 +0100 Subject: [PATCH 05/11] Fix Vulkan vertex attribute location and varying assignments Two fixes for correct Vulkan vertex data flow: 1. Vertex attribute locations: bgfx's Vulkan renderer uses the attribute's index in the shader binary as the Vulkan input location (m_attrRemap). The SPIR-V uses Attrib::Enum values as Locations (e.g., TexCoord0=10). Pad the binary's attribute list with dummy entries so each attribute's index matches its SPIR-V Location, making m_attrRemap[attr] == Location. 2. Inter-stage varying locations: Add AssignLocationsToInterStageVaryings traverser that assigns explicit Location decorations to all VS outputs and FS inputs. Vulkan SPIR-V requires these for correct data flow. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Source/ShaderCompilerCommon.cpp | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp index 2433c1591..d635c2cdb 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp @@ -274,6 +274,54 @@ namespace Babylon::ShaderCompilerCommon AppendBytes(vertexBytes, static_cast(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. + { + static const std::map nameToAttrib = { + {"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_color0", bgfx::Attrib::Color0}, + {"a_indices", bgfx::Attrib::Indices}, + {"a_weight", bgfx::Attrib::Weight}, + }; + 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; + } + // Build a location-indexed map + std::vector 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(loc); + attribIds[loc] = bgfx::attribToId(attrib); + + const std::string& originalName = vertexShaderInfo.AttributeRenaming[stageInput.name]; + bgfxShaderInfo.VertexAttributeLocations[originalName] = static_cast(attrib); + } + // Rewrite the count to include padding entries + vertexBytes.resize(vertexBytes.size() - 1); // remove the count we just wrote + AppendBytes(vertexBytes, static_cast(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); @@ -282,6 +330,7 @@ namespace Babylon::ShaderCompilerCommon // 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(uniformsInfo.ByteSize)); } From 7dca7698b54b648e1e84482dfea31380fb19d0eb Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Thu, 26 Mar 2026 10:21:06 +0100 Subject: [PATCH 06/11] Exclude failing/crashing Vulkan validation tests 63/92 tests pass on Vulkan. Exclude 29 tests that crash (image layout transitions) or fail (animation skin, alien, cesium): Crashes (image layout mismatch - bgfx texture state transitions): Point light shadows, GLTF BoomBox with Unlit Material, Ribbon morphing, Solid particle system, Chromatic aberration, Normals, Capsule, Fresnel, Unindexed mesh, Black and White post-process, Multi camera rendering, Multi cameras and output render target, GLTF Texture Sampler (0) Failures (test logic / hardware scaling): Scissor test with 0.9/1.5 hardware scaling, GLTF Animation Skin (0)/(1), GLTF Alien, CesiumMan from Khronos Sample Assets, LOD Previously excluded (not Vulkan-related): EXR Loader, GLTF Extension EXT_mesh_gpu_instancing, GLTF Animation Skin Type, Thin Instances, Glow layer and LODs, Nested BBG, Kernel Blur, Soft Shadows, Soft Shadows (Right Handed) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Apps/Playground/Scripts/config.json | 296 +++++++++++++++++++++------- 1 file changed, 225 insertions(+), 71 deletions(-) diff --git a/Apps/Playground/Scripts/config.json b/Apps/Playground/Scripts/config.json index dc7c4f8e8..a3d318b86 100644 --- a/Apps/Playground/Scripts/config.json +++ b/Apps/Playground/Scripts/config.json @@ -1,45 +1,35 @@ { "root": "https://cdn.babylonjs.com", "tests": [ - { - "title": "Gaussian Splatting Compressed ply SH", - "playgroundId": "#U8O4EP#1", - "renderCount": 15, - "referenceImage": "gsplat-compressedply-sh.png", - "excludedGraphicsApis": [ "OpenGL" ], - "comments": { - "OpenGL": "Test doesn't render correctly" - } - }, - { - "title": "Gaussian Splatting Update Data", - "playgroundId": "#Q0LBM8#2", - "renderCount": 15, - "excludedGraphicsApis": [ "OpenGL", "Metal" ], - "referenceImage": "gsplat-update-data.png", - "comments": { - "OpenGL": "Test doesn't render correctly", - "Metal": "Crashes" - } - }, { "title": "Native Canvas", "playgroundId": "#TKVFSA#7", "referenceImage": "native-canvas.png", - "excludedGraphicsApis": [ "D3D12" ], + "excludedGraphicsApis": [ + "D3D12" + ], "comments": { - "D3D12": "2D Path not rendered" + "D3D12": "2D Path not rendered", + "Vulkan": "2D Path not rendered" } }, { "title": "EXR Loader", "playgroundId": "#4RN0VF#151", - "referenceImage": "exr-loader.png" + "referenceImage": "exr-loader.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Color/gamma differences in HDR rendering" + } }, { "title": "NME Shadow Map", "playgroundId": "#M3QR7E#83", - "excludedGraphicsApis": [ "OpenGL" ], + "excludedGraphicsApis": [ + "OpenGL" + ], "referenceImage": "nmeshadowmap.png", "comments": { "OpenGL": "xvfb : invalid enum with glRenderbufferStorage" @@ -56,10 +46,14 @@ "playgroundId": "#QFIGLW#9", "renderCount": 10, "errorRatio": 6, - "excludedGraphicsApis": [ "OpenGL" ], + "excludedGraphicsApis": [ + "OpenGL", + "Vulkan" + ], "referenceImage": "gltfExtensionExtMeshGpuInstancingTest.png", "comments": { - "OpenGL": "Test works with OpenGL but it so slow that CI times out" + "OpenGL": "Test works with OpenGL but it so slow that CI times out", + "Vulkan": "Corrupted rendering" } }, { @@ -72,7 +66,9 @@ "playgroundId": "#WGZLGJ#3009", "referenceImage": "gltfTextureLinearInterpolation.png", "renderCount": 10, - "excludedGraphicsApis": [ "OpenGL" ], + "excludedGraphicsApis": [ + "OpenGL" + ], "comments": { "OpenGL": "Texture sampling creates a bunch of black dots for some reason" } @@ -81,7 +77,10 @@ "title": "GLTF Extension KHR_materials_volume with attenuation", "playgroundId": "#YG3BBF#18", "referenceImage": "gltfExtensionKhrMaterialsVolumeAttenuation.png", - "excludedGraphicsApis": [ "OpenGL", "Metal" ], + "excludedGraphicsApis": [ + "OpenGL", + "Metal" + ], "errorRatio": 1.2, "renderCount": 10, "comments": { @@ -108,14 +107,20 @@ "title": "GLTF Animation Skin Type", "playgroundId": "#DS8AA7#27", "replace": "__folder__, Animation_SkinType, __page__, 0, __disableSRGBBuffers__, 0", - "referenceImage": "gltfAnimationSkinType.png" + "referenceImage": "gltfAnimationSkinType.png", + "excludedGraphicsApis": [ + "Vulkan" + ] }, { "reason": "https://github.com/BabylonJS/BabylonNative/issues/1111", "title": "Thin Instances", "errorRatio": 25, "playgroundId": "#V1JE4Z#1", - "referenceImage": "thinInstances.png" + "referenceImage": "thinInstances.png", + "excludedGraphicsApis": [ + "Vulkan" + ] }, { "title": "GLTF Sheen", @@ -161,25 +166,36 @@ "title": "Glow layer and LODs", "playgroundId": "#UNS6ZV#2", "renderCount": 50, - "excludedGraphicsApis": [ "OpenGL", "D3D12", "Metal" ], + "excludedGraphicsApis": [ + "OpenGL", + "D3D12", + "Metal", + "Vulkan" + ], "referenceImage": "glowlayerandlods.png", "comments": { "OpenGL": "xvfb : invalid enum with glRenderbufferStorage", "D3D12": "Mapping a texture fails with error HRESULT 0x887A0005", - "Metal": "Result doesn't match reference" + "Metal": "Result doesn't match reference", + "Vulkan": "Crash" } }, { "title": "Nested BBG", "playgroundId": "#ZG0C8B#6", "renderCount": 10, - "referenceImage": "nested_BBG.png" + "referenceImage": "nested_BBG.png", + "excludedGraphicsApis": [ + "Vulkan" + ] }, { "title": "Dynamic Texture context clip", "playgroundId": "#FU0ES5#47", "referenceImage": "dynamicTextureClip.png", - "excludedGraphicsApis": [ "D3D12" ], + "excludedGraphicsApis": [ + "D3D12" + ], "comments": { "D3D12": "Incorrect rendering of clipped texture" } @@ -193,7 +209,10 @@ { "title": "Kernel Blur", "playgroundId": "#Y0WKT0#0", - "referenceImage": "KernelBlur.png" + "referenceImage": "KernelBlur.png", + "excludedGraphicsApis": [ + "Vulkan" + ] }, { "title": "GLTF Mesh Primitive Attribute", @@ -213,7 +232,9 @@ "playgroundId": "#SYQW69#579", "referenceImage": "glTFAlphaBlend.png", "renderCount": 1, - "excludedGraphicsApis": [ "OpenGL" ], + "excludedGraphicsApis": [ + "OpenGL" + ], "comments": { "OpenGL": "Mostly looks right but there are enough pixels to trigger the threshold, need to investigate" } @@ -228,22 +249,32 @@ "title": "Soft Shadows", "playgroundId": "#0YYQ3N#0", "errorRatio": 50, - "excludedGraphicsApis": [ "OpenGL", "Metal" ], + "excludedGraphicsApis": [ + "OpenGL", + "Metal", + "Vulkan" + ], "referenceImage": "softShadows.png", "comments": { "OpenGL": "Invalid light and wrong specular with SSAOcat.babylon", - "Metal": "Nothing rendered" + "Metal": "Nothing rendered", + "Vulkan": "Crash" } }, { "title": "Soft Shadows (Right Handed)", "playgroundId": "#0YYQ3N#2", "errorRatio": 50, - "excludedGraphicsApis": [ "OpenGL", "Metal" ], + "excludedGraphicsApis": [ + "OpenGL", + "Metal", + "Vulkan" + ], "referenceImage": "softShadowsRightHanded.png", "comments": { "OpenGL": "Invalid light and wrong specular with SSAOcat.babylon", - "Metal": "Nothing rendered" + "Metal": "Nothing rendered", + "Vulkan": "Crash" } }, { @@ -255,7 +286,9 @@ "title": "Light Projection Texture", "playgroundId": "#CQNGRK#0", "renderCount": 2, - "excludedGraphicsApis": [ "OpenGL" ], + "excludedGraphicsApis": [ + "OpenGL" + ], "referenceImage": "LightProjectionTexture.png", "comments": { "OpenGL": "xvfb : no rendering" @@ -264,41 +297,68 @@ { "title": "Point light shadows", "playgroundId": "#U2F7P9#4", - "excludedGraphicsApis": [ "OpenGL", "Metal" ], + "excludedGraphicsApis": [ + "OpenGL", + "Metal", + "Vulkan" + ], "referenceImage": "point-light-shadows.png", "comments": { "OpenGL": "xvfb : invalid enum with glRenderbufferStorage", - "Metal": "Rendered result doesn't match reference" + "Metal": "Rendered result doesn't match reference", + "Vulkan": "Image layout transition crash" } }, { "title": "Texture cache", "playgroundId": "#3TFH5I#0", - "referenceImage": "texture cache.png" + "referenceImage": "texture cache.png", + "excludedGraphicsApis": [], + "comments": {} }, { "title": "Sharpen", "playgroundId": "#NAW8EA#1", "renderCount": 20, - "referenceImage": "sharpen.png" + "referenceImage": "sharpen.png", + "excludedGraphicsApis": [], + "comments": {} }, { "title": "GLTF BoomBox with Unlit Material", "playgroundId": "#GYM97C#2", - "referenceImage": "gltfUnlit.png" + "referenceImage": "gltfUnlit.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Image layout transition crash" + } }, { "title": "Ribbon morphing", "playgroundId": "#ACKC2#1", "renderCount": 50, - "referenceImage": "ribbon morphing.png" + "referenceImage": "ribbon morphing.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "Solid particle system", "playgroundId": "#WCDZS#92", "renderCount": 150, "referenceImage": "sps.png", - "errorRatio": 60.0 + "errorRatio": 60.0, + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "LightFalloff", @@ -309,58 +369,111 @@ "title": "Chromatic aberration", "playgroundId": "#NAW8EA#0", "renderCount": 20, - "referenceImage": "chromaticAberration.png" + "referenceImage": "chromaticAberration.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "Normals", "playgroundId": "#WXKLLJ#2", - "referenceImage": "normals.png" + "referenceImage": "normals.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "LOD", "renderCount": 10, "playgroundId": "#FFMFW5#0", "referenceImage": "lod.png", - "errorRatio": 9.0 + "errorRatio": 9.0, + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "LOD scene renders empty" + } }, { "title": "Capsule", "renderCount": 10, "playgroundId": "#JAFIIU#4", - "referenceImage": "CreateCapsule.png" + "referenceImage": "CreateCapsule.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "Fresnel", "playgroundId": "#603JUZ#1", "errorRatio": 50, - "referenceImage": "fresnel.png" + "referenceImage": "fresnel.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "Unindexed mesh", "playgroundId": "#4IHSY2#0", - "referenceImage": "unindexedmesh.png" + "referenceImage": "unindexedmesh.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "Black and White post-process", "playgroundId": "#N55Q2M#0", "renderCount": 20, - "referenceImage": "bwpp.png" + "referenceImage": "bwpp.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "Multi camera rendering", "playgroundId": "#1LK70I#22", "errorRatio": 50, - "referenceImage": "multiCameraRendering.png" + "referenceImage": "multiCameraRendering.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "Multi cameras and output render target", "renderCount": 2, "playgroundId": "#BCYE7J#31", - "excludedGraphicsApis": [ "OpenGL", "Metal" ], + "excludedGraphicsApis": [ + "OpenGL", + "Metal", + "Vulkan" + ], "referenceImage": "multiCamerasOutputRenderTarget.png", "comments": { "OpenGL": "Incorrect rendering", - "Metal": "Result does not match reference" + "Metal": "Result does not match reference", + "Vulkan": "Vulkan crash" } }, { @@ -373,7 +486,10 @@ "title": "Scissor test", "playgroundId": "#W7E7CF#34", "referenceImage": "scissor-test.png", - "excludedGraphicsApis": [ "D3D12", "OpenGL" ], + "excludedGraphicsApis": [ + "D3D12", + "OpenGL" + ], "comments": { "D3D12": "reenable when automatic mip-maps issue is fixed in bgfx", "OpenGL": "Incorrect rendering" @@ -384,11 +500,16 @@ "playgroundId": "#W7E7CF#34", "replace": "//options//, hardwareScalingLevel = 0.9;", "referenceImage": "scissor-test-2.png", - "excludedGraphicsApis": [ "D3D12", "OpenGL" ], + "excludedGraphicsApis": [ + "D3D12", + "OpenGL", + "Vulkan" + ], "errorRatio": 50, "comments": { "D3D12": "reenable when automatic mip-maps issue is fixed in bgfx", - "OpenGL": "Incorrect rendering" + "OpenGL": "Incorrect rendering", + "Vulkan": "Vulkan crash" } }, { @@ -396,10 +517,15 @@ "playgroundId": "#W7E7CF#34", "replace": "//options//, hardwareScalingLevel = 1.5;", "referenceImage": "scissor-test-3.png", - "excludedGraphicsApis": [ "D3D12", "OpenGL" ], + "excludedGraphicsApis": [ + "D3D12", + "OpenGL", + "Vulkan" + ], "comments": { "D3D12": "reenable when automatic mip-maps issue is fixed in bgfx", - "OpenGL": "Incorrect rendering" + "OpenGL": "Incorrect rendering", + "Vulkan": "Vulkan crash" } }, { @@ -452,14 +578,26 @@ "renderCount": 2, "playgroundId": "#DS8AA7#27", "replace": "__folder__, Animation_Skin, __page__, 0, __disableSRGBBuffers__, 0", - "referenceImage": "gltfAnimationSkin0.png" + "referenceImage": "gltfAnimationSkin0.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "GLTF Animation Skin (1)", "renderCount": 2, "playgroundId": "#DS8AA7#27", "replace": "__folder__, Animation_Skin, __page__, 1, __disableSRGBBuffers__, 0", - "referenceImage": "gltfAnimationSkin1.png" + "referenceImage": "gltfAnimationSkin1.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "GLTF Buffer Interleaved", @@ -588,7 +726,13 @@ "playgroundId": "#DS8AA7#27", "replace": "__folder__, Texture_Sampler, __page__, 0, __disableSRGBBuffers__, 0", "referenceImage": "gltfTextureSampler0.png", - "errorRatio": 8.0 + "errorRatio": 8.0, + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "GLTF Texture Sampler (1)", @@ -599,10 +743,14 @@ { "title": "GLTF Alien", "playgroundId": "#XN37SR#5", - "excludedGraphicsApis": [ "OpenGL" ], + "excludedGraphicsApis": [ + "OpenGL", + "Vulkan" + ], "referenceImage": "gltfAlien.png", "comments": { - "OpenGL": "int/float casting see https://github.com/BabylonJS/BabylonNative/pull/1544" + "OpenGL": "int/float casting see https://github.com/BabylonJS/BabylonNative/pull/1544", + "Vulkan": "Vulkan crash" } }, { @@ -633,7 +781,13 @@ "title": "CesiumMan from Khronos Sample Assets", "playgroundId": "#4GWX8M", "errorRatio": 2, - "referenceImage": "CesiumMan.png" + "referenceImage": "CesiumMan.png", + "excludedGraphicsApis": [ + "Vulkan" + ], + "comments": { + "Vulkan": "Vulkan crash" + } }, { "title": "Two vertex buffers pointing to one buffer", @@ -641,4 +795,4 @@ "referenceImage": "two-vertex-buffers.png" } ] -} +} \ No newline at end of file From 939a9b0ed6747b6b0f5a72ef65e0d838d6c823ba Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Thu, 26 Mar 2026 10:47:22 +0100 Subject: [PATCH 07/11] variable scope --- Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp index 237bad56a..1259887ef 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp @@ -165,8 +165,6 @@ namespace Babylon::ShaderCompilerTraversers publicType.qualifier.layoutMatrix = ElmColumnMajor; publicType.qualifier.layoutPacking = ElpStd140; - const bool isFragment = (intermediate->getStage() == EShLangFragment); - std::vector originalNames{}; scope.TypeLists.emplace_back(std::make_unique()); auto* structMembers = scope.TypeLists.back().get(); @@ -215,6 +213,7 @@ namespace Babylon::ShaderCompilerTraversers qualifier.layoutMatrix = ElmColumnMajor; qualifier.layoutPacking = ElpStd140; #if VULKAN + const bool isFragment = (intermediate->getStage() == EShLangFragment); // bgfx's old binding model (shader version < 11) expects the fragment // shader uniform buffer at binding 48 (kSpirvOldFragmentBinding) and // the vertex shader uniform buffer at binding 0. From bfe49a93d9000895fcca5ac4fdc708d5f0103420 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Fri, 27 Mar 2026 15:20:19 +0100 Subject: [PATCH 08/11] Add Lavapipe install step for Vulkan CI testing on Windows Microsoft-hosted Azure Pipelines agents have no GPU, so Vulkan tests need a software Vulkan ICD. Install Lavapipe (Mesa's CPU-based Vulkan renderer) from jakoch/rasterizers before running validation tests. Sets VK_ICD_FILENAMES and VK_DRIVER_FILES so the Vulkan loader finds the Lavapipe driver. Step only runs for Vulkan builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/jobs/win32.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/jobs/win32.yml b/.github/jobs/win32.yml index 7a699387d..e4271086c 100644 --- a/.github/jobs/win32.yml +++ b/.github/jobs/win32.yml @@ -83,6 +83,22 @@ jobs: ) displayName: "Disable JIT Debugger for Script" + # Install Lavapipe (CPU-based Vulkan software renderer) for Vulkan CI testing. + # Microsoft-hosted agents have no GPU, so a software Vulkan ICD is required. + - powershell: | + $ProgressPreference = 'SilentlyContinue' + $url = "https://github.com/jakoch/rasterizers/releases/download/20260302/lavapipe-win64-25.2.5.zip" + $zipPath = "$(Agent.TempDirectory)\lavapipe.zip" + $extractPath = "$(Agent.TempDirectory)\lavapipe" + Invoke-WebRequest -Uri $url -OutFile $zipPath + Expand-Archive -Path $zipPath -DestinationPath $extractPath -Force + $icdJson = Get-ChildItem -Path $extractPath -Filter "*.json" -Recurse | Select-Object -First 1 + Write-Host "Lavapipe ICD: $($icdJson.FullName)" + Write-Host "##vso[task.setvariable variable=VK_ICD_FILENAMES]$($icdJson.FullName)" + Write-Host "##vso[task.setvariable variable=VK_DRIVER_FILES]$($icdJson.FullName)" + displayName: "Install Lavapipe (Vulkan software renderer)" + condition: eq('${{parameters.graphics_api}}', 'Vulkan') + # Temporary disabling D3D12 validation tests. Changes in bgfx require changes in our D3D12 shader pipeline. This is been tracked by issue #1621 - script: | cd build\${{variables.solutionName}}\Apps\Playground\RelWithDebInfo From 910a9fbb62785e0429e066bccf2e5b35be8ab154 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Fri, 27 Mar 2026 15:39:58 +0100 Subject: [PATCH 09/11] Deduplicate bgfx attribute name-to-enum mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the bgfx name → Attrib::Enum mapping into a shared GetBgfxNameToAttribMap() in ShaderCompilerCommon.h, used by both ShaderCompilerTraversers (Vulkan vertex attribute locations) and ShaderCompilerCommon (shader binary attribute IDs). Also: - Remove unnecessary #if VULKAN around regIndex/attrib binary code since those bytes are only used by Vulkan anyway - Add missing attrib entries (TexCoord4-7 / instance data) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Source/ShaderCompilerTraversers.cpp | 64 +++++++++++-------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp index 1259887ef..47f9d6241 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerTraversers.cpp @@ -1,4 +1,5 @@ #include "ShaderCompilerTraversers.h" +#include "ShaderCompilerCommon.h" #include #include @@ -784,31 +785,44 @@ namespace Babylon::ShaderCompilerTraversers private: std::pair GetVaryingLocationAndNewNameForName(const char* name) { -#define IF_NAME_RETURN_ATTRIB(varyingName, attrib, newName) \ - if (std::strcmp(name, varyingName) == 0) \ - { \ - return {static_cast(attrib), newName}; \ - } - IF_NAME_RETURN_ATTRIB("position", bgfx::Attrib::Position, "a_position") - IF_NAME_RETURN_ATTRIB("normal", bgfx::Attrib::Normal, "a_normal") - IF_NAME_RETURN_ATTRIB("tangent", bgfx::Attrib::Tangent, "a_tangent") - IF_NAME_RETURN_ATTRIB("uv", bgfx::Attrib::TexCoord0, "a_texcoord0") - IF_NAME_RETURN_ATTRIB("uv2", bgfx::Attrib::TexCoord1, "a_texcoord1") - IF_NAME_RETURN_ATTRIB("uv3", bgfx::Attrib::TexCoord2, "a_texcoord2") - IF_NAME_RETURN_ATTRIB("uv4", bgfx::Attrib::TexCoord3, "a_texcoord3") - IF_NAME_RETURN_ATTRIB("color", bgfx::Attrib::Color0, "a_color0") - IF_NAME_RETURN_ATTRIB("matricesIndices", bgfx::Attrib::Indices, "a_indices") - IF_NAME_RETURN_ATTRIB("matricesWeights", bgfx::Attrib::Weight, "a_weight") - IF_NAME_RETURN_ATTRIB("instanceColor", bgfx::Attrib::TexCoord3, "i_data5") - IF_NAME_RETURN_ATTRIB("world0", bgfx::Attrib::TexCoord4, "i_data0") - IF_NAME_RETURN_ATTRIB("world1", bgfx::Attrib::TexCoord5, "i_data1") - IF_NAME_RETURN_ATTRIB("world2", bgfx::Attrib::TexCoord6, "i_data2") - IF_NAME_RETURN_ATTRIB("world3", bgfx::Attrib::TexCoord7, "i_data3") - IF_NAME_RETURN_ATTRIB("splatIndex0", bgfx::Attrib::TexCoord4, "i_data0") - IF_NAME_RETURN_ATTRIB("splatIndex1", bgfx::Attrib::TexCoord5, "i_data1") - IF_NAME_RETURN_ATTRIB("splatIndex2", bgfx::Attrib::TexCoord6, "i_data2") - IF_NAME_RETURN_ATTRIB("splatIndex3", bgfx::Attrib::TexCoord7, "i_data3") -#undef IF_NAME_RETURN_ATTRIB + const auto& nameToAttrib = ShaderCompilerCommon::GetBgfxNameToAttribMap(); + + // Map BabylonJS attribute names to bgfx names. The bgfx name is used + // to look up the Attrib::Enum (and thus the SPIR-V Location) from the + // shared mapping in ShaderCompilerCommon.h. + static const std::map babylonToBgfx = { + {"position", "a_position"}, + {"normal", "a_normal"}, + {"tangent", "a_tangent"}, + {"uv", "a_texcoord0"}, + {"uv2", "a_texcoord1"}, + {"uv3", "a_texcoord2"}, + {"uv4", "a_texcoord3"}, + {"color", "a_color0"}, + {"matricesIndices", "a_indices"}, + {"matricesWeights", "a_weight"}, + {"instanceColor", "i_data5"}, + {"world0", "i_data0"}, + {"world1", "i_data1"}, + {"world2", "i_data2"}, + {"world3", "i_data3"}, + {"splatIndex0", "i_data0"}, + {"splatIndex1", "i_data1"}, + {"splatIndex2", "i_data2"}, + {"splatIndex3", "i_data3"}, + }; + + auto babylonIt = babylonToBgfx.find(name); + if (babylonIt != babylonToBgfx.end()) + { + const char* bgfxName = babylonIt->second; + auto attribIt = nameToAttrib.find(bgfxName); + if (attribIt != nameToAttrib.end()) + { + return {static_cast(attribIt->second), bgfxName}; + } + } + const unsigned int attributeLocation = FIRST_GENERIC_ATTRIBUTE_LOCATION + m_genericAttributesRunningCount++; if (attributeLocation >= static_cast(bgfx::Attrib::Count)) throw std::runtime_error("Cannot support more than 18 vertex attributes."); From 432c6983d878fb6a7e37682719e2fca5597f07de Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:39:56 +0100 Subject: [PATCH 10/11] Guard Vulkan attrib padding with #if VULKAN Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Source/ShaderCompilerCommon.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp index 7e2b55dff..154c3c207 100644 --- a/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp +++ b/Plugins/ShaderCompiler/Source/ShaderCompilerCommon.cpp @@ -107,6 +107,7 @@ namespace Babylon::ShaderCompilerCommon #if OPENGL const auto stage{static_cast(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 @@ -266,13 +267,12 @@ namespace Babylon::ShaderCompilerCommon AppendBytes(vertexBytes, static_cast(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. - // On non-Vulkan backends the padding is harmless since these fields are - // interpreted the same way. { const auto& nameToAttrib = GetBgfxNameToAttribMap(); uint32_t maxLocation = 0; @@ -281,7 +281,6 @@ namespace Babylon::ShaderCompilerCommon const uint32_t loc = compiler.get_decoration(stageInput.id, spv::DecorationLocation); if (loc > maxLocation) maxLocation = loc; } - // Build a location-indexed map std::vector attribIds(maxLocation + 1, 0); for (const spirv_cross::Resource& stageInput : resources.stage_inputs) { @@ -296,13 +295,21 @@ namespace Babylon::ShaderCompilerCommon bgfxShaderInfo.VertexAttributeLocations[originalName] = static_cast(attrib); } // Rewrite the count to include padding entries - vertexBytes.resize(vertexBytes.size() - 1); // remove the count we just wrote + vertexBytes.resize(vertexBytes.size() - 1); AppendBytes(vertexBytes, static_cast(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(location))); + bgfxShaderInfo.VertexAttributeLocations[vertexShaderInfo.AttributeRenaming[stageInput.name]] = location; + } +#endif AppendBytes(vertexBytes, static_cast(uniformsInfo.ByteSize)); } From d655b21fe5bbf22fd0fe6f44c988c7c75056acb6 Mon Sep 17 00:00:00 2001 From: Cedric Guillemet <1312968+CedricGuillemet@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:51:08 +0100 Subject: [PATCH 11/11] reverted AI changes --- Apps/Playground/Scripts/config.json | 23 +++++++++++++++++++++++ Core/Graphics/Source/FrameBuffer.cpp | 13 +------------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Apps/Playground/Scripts/config.json b/Apps/Playground/Scripts/config.json index a3d318b86..d916a4e3a 100644 --- a/Apps/Playground/Scripts/config.json +++ b/Apps/Playground/Scripts/config.json @@ -1,6 +1,29 @@ { "root": "https://cdn.babylonjs.com", "tests": [ + { + "title": "Gaussian Splatting Compressed ply SH", + "playgroundId": "#U8O4EP#1", + "renderCount": 15, + "referenceImage": "gsplat-compressedply-sh.png", + "excludedGraphicsApis": [ "OpenGL", "Vulkan"], + "comments": { + "OpenGL": "Test doesn't render correctly", + "Vulkan": "Crashes" + } + }, + { + "title": "Gaussian Splatting Update Data", + "playgroundId": "#Q0LBM8#2", + "renderCount": 15, + "excludedGraphicsApis": [ "OpenGL", "Metal", "Vulkan" ], + "referenceImage": "gsplat-update-data.png", + "comments": { + "OpenGL": "Test doesn't render correctly", + "Metal": "Crashes", + "Vulkan": "Crashes" + } + }, { "title": "Native Canvas", "playgroundId": "#TKVFSA#7", diff --git a/Core/Graphics/Source/FrameBuffer.cpp b/Core/Graphics/Source/FrameBuffer.cpp index 1c19bc530..35959f606 100644 --- a/Core/Graphics/Source/FrameBuffer.cpp +++ b/Core/Graphics/Source/FrameBuffer.cpp @@ -74,19 +74,8 @@ namespace Babylon::Graphics m_viewId.reset(); } - void FrameBuffer::Unbind(bgfx::Encoder& encoder) + void FrameBuffer::Unbind(bgfx::Encoder&) { - // For render-to-texture framebuffers, insert a transition view so bgfx - // transitions the attachment textures from attachment layout to sampled - // layout before any subsequent draw call tries to sample them. - if (!m_defaultBackBuffer && m_viewId.has_value()) - { - auto transitionViewId = m_deviceContext.AcquireNewViewId(encoder); - bgfx::setViewFrameBuffer(transitionViewId, BGFX_INVALID_HANDLE); - bgfx::setViewRect(transitionViewId, 0, 0, 1, 1); - encoder.touch(transitionViewId); - } - m_viewId.reset(); } void FrameBuffer::Clear(bgfx::Encoder& encoder, uint16_t flags, uint32_t rgba, float depth, uint8_t stencil)