From e7c58be8b6fda4e8c120c6828b0bacfc35b86b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Wed, 28 Oct 2026 21:08:38 -0700 Subject: [PATCH] Metal fixes, and instance data fallback on all renderers. (#4103) --- src/renderer_d3d11.cpp | 25 ++++++++++ src/renderer_d3d12.cpp | 25 ++++++++++ src/renderer_mtl.cpp | 105 ++++++++++++++++++++++++++++++---------- src/renderer_mtl.h | 10 ++++ src/renderer_vk.cpp | 37 ++++++++++++++ src/renderer_vk.h | 1 + src/renderer_webgpu.cpp | 43 +++++++++++++--- src/renderer_webgpu.h | 1 + 8 files changed, 215 insertions(+), 32 deletions(-) diff --git a/src/renderer_d3d11.cpp b/src/renderer_d3d11.cpp index e51f1fc85..97162d5ea 100644 --- a/src/renderer_d3d11.cpp +++ b/src/renderer_d3d11.cpp @@ -2932,6 +2932,31 @@ namespace bgfx { namespace d3d11 curr->AlignedByteOffset = ii*16; } + const D3D11_INPUT_ELEMENT_DESC fallback = { "TEXCOORD", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }; + + const uint32_t numFallback = 0 < _numStreams ? BGFX_CONFIG_MAX_INSTANCE_DATA_COUNT : 0; + + for (uint32_t ii = _numInstanceData; ii < numFallback && uint32_t(elem-vertexElements) < D3D11_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT; ++ii) + { + const uint32_t index = BGFX_CONFIG_INSTANCE_DATA_FIRST_TEXCOORD - ii; + + bool found = false; + for (const D3D11_INPUT_ELEMENT_DESC* curr = vertexElements; curr != elem && !found; ++curr) + { + found = true + && 0 == bx::strCmp(curr->SemanticName, "TEXCOORD") + && curr->SemanticIndex == index + ; + } + + if (!found) + { + bx::memCopy(elem, &fallback, sizeof(D3D11_INPUT_ELEMENT_DESC) ); + elem->SemanticIndex = index; + ++elem; + } + } + num = uint32_t(elem-vertexElements); DX_CHECK(m_device->CreateInputLayout(vertexElements , num diff --git a/src/renderer_d3d12.cpp b/src/renderer_d3d12.cpp index 5ab86d596..b7d66f7f0 100644 --- a/src/renderer_d3d12.cpp +++ b/src/renderer_d3d12.cpp @@ -3870,6 +3870,31 @@ namespace bgfx { namespace d3d12 curr->AlignedByteOffset = ii*16; } + const D3D12_INPUT_ELEMENT_DESC fallback = { "TEXCOORD", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }; + + const uint32_t numFallback = 0 < _numStreams ? BGFX_CONFIG_MAX_INSTANCE_DATA_COUNT : 0; + + for (uint32_t ii = _numInstanceData; ii < numFallback && uint32_t(elem-_vertexElements) < D3D12_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT; ++ii) + { + const uint32_t index = BGFX_CONFIG_INSTANCE_DATA_FIRST_TEXCOORD - ii; + + bool found = false; + for (const D3D12_INPUT_ELEMENT_DESC* curr = _vertexElements; curr != elem && !found; ++curr) + { + found = true + && 0 == bx::strCmp(curr->SemanticName, "TEXCOORD") + && curr->SemanticIndex == index + ; + } + + if (!found) + { + bx::memCopy(elem, &fallback, sizeof(D3D12_INPUT_ELEMENT_DESC) ); + elem->SemanticIndex = index; + ++elem; + } + } + return uint32_t(elem-_vertexElements); } diff --git a/src/renderer_mtl.cpp b/src/renderer_mtl.cpp index 3e34f5f2d..fd3e3f965 100644 --- a/src/renderer_mtl.cpp +++ b/src/renderer_mtl.cpp @@ -96,14 +96,14 @@ namespace bgfx { namespace mtl static const MTL::VertexFormat s_attribType[][4][2] = //type, count, normalized { - { // Uint8 - { MTL::VertexFormatChar2, MTL::VertexFormatChar2Normalized }, + { // Int8 + { MTL::VertexFormatChar, MTL::VertexFormatCharNormalized }, { MTL::VertexFormatChar2, MTL::VertexFormatChar2Normalized }, { MTL::VertexFormatChar3, MTL::VertexFormatChar3Normalized }, { MTL::VertexFormatChar4, MTL::VertexFormatChar4Normalized }, }, { // Uint8 - { MTL::VertexFormatUChar2, MTL::VertexFormatUChar2Normalized }, + { MTL::VertexFormatUChar, MTL::VertexFormatUCharNormalized }, { MTL::VertexFormatUChar2, MTL::VertexFormatUChar2Normalized }, { MTL::VertexFormatUChar3, MTL::VertexFormatUChar3Normalized }, { MTL::VertexFormatUChar4, MTL::VertexFormatUChar4Normalized }, @@ -115,19 +115,19 @@ namespace bgfx { namespace mtl { MTL::VertexFormatUInt1010102Normalized, MTL::VertexFormatUInt1010102Normalized }, }, { // Int16 - { MTL::VertexFormatShort2, MTL::VertexFormatShort2Normalized }, + { MTL::VertexFormatShort, MTL::VertexFormatShortNormalized }, { MTL::VertexFormatShort2, MTL::VertexFormatShort2Normalized }, { MTL::VertexFormatShort3, MTL::VertexFormatShort3Normalized }, { MTL::VertexFormatShort4, MTL::VertexFormatShort4Normalized }, }, - { // Int16 - { MTL::VertexFormatUShort2, MTL::VertexFormatUShort2Normalized }, + { // Uint16 + { MTL::VertexFormatUShort, MTL::VertexFormatUShortNormalized }, { MTL::VertexFormatUShort2, MTL::VertexFormatUShort2Normalized }, { MTL::VertexFormatUShort3, MTL::VertexFormatUShort3Normalized }, { MTL::VertexFormatUShort4, MTL::VertexFormatUShort4Normalized }, }, { // Half - { MTL::VertexFormatHalf2, MTL::VertexFormatHalf2 }, + { MTL::VertexFormatHalf, MTL::VertexFormatHalf }, { MTL::VertexFormatHalf2, MTL::VertexFormatHalf2 }, { MTL::VertexFormatHalf3, MTL::VertexFormatHalf3 }, { MTL::VertexFormatHalf4, MTL::VertexFormatHalf4 }, @@ -337,8 +337,8 @@ BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG("-Wunguarded-availability-new"); { MTL::PixelFormatRGBA16Uint, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTier2, { $R, $G, $B, $A }, false }, // RGBA16U { MTL::PixelFormatRGBA16Float, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTier2, { $R, $G, $B, $A }, true }, // RGBA16F { MTL::PixelFormatRGBA16Snorm, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTierNone, { $R, $G, $B, $A }, true }, // RGBA16S - { MTL::PixelFormatRGBA32Sint, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTier2, { $R, $G, $B, $A }, true }, // RGBA32I - { MTL::PixelFormatRGBA32Uint, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTier2, { $R, $G, $B, $A }, true }, // RGBA32U + { MTL::PixelFormatRGBA32Sint, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTier2, { $R, $G, $B, $A }, false }, // RGBA32I + { MTL::PixelFormatRGBA32Uint, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTier2, { $R, $G, $B, $A }, false }, // RGBA32U { MTL::PixelFormatRGBA32Float, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTier2, { $R, $G, $B, $A }, true }, // RGBA32F { MTL::PixelFormatB5G6R5Unorm, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTierNone, { $R, $G, $B, $A }, true }, // B5G6R5 { MTL::PixelFormatB5G6R5Unorm, MTL::PixelFormatInvalid, MTL::ReadWriteTextureTierNone, { $B, $G, $R, $A }, true }, // R5G6B5 @@ -3418,16 +3418,6 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames } } - MTL::VertexBufferLayoutDescriptor* vbld = vertexDesc->layouts()->object(1); - - if (usedFallbackAttrib - && 0 == vbld->stride() ) - { - vbld->setStride(4); - vbld->setStepFunction(MTL::VertexStepFunctionConstant); - vbld->setStepRate(0); - } - if (0 < _numInstanceData) { uint32_t numAttribs = 0; @@ -3450,6 +3440,30 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames vertexDesc->layouts()->object(stream+1)->setStepRate(1); } } + else + { + for (uint32_t ii = 0; ii < BX_COUNTOF(s_instanceDataName); ++ii) + { + const uint32_t loc = program.m_instanceData[ii]; + if (UINT32_MAX != loc) + { + vertexDesc->attributes()->object(loc)->setFormat(MTL::VertexFormatUChar4); + vertexDesc->attributes()->object(loc)->setBufferIndex(1); + vertexDesc->attributes()->object(loc)->setOffset(0); + usedFallbackAttrib = true; + } + } + } + + MTL::VertexBufferLayoutDescriptor* vbld = vertexDesc->layouts()->object(1); + + if (usedFallbackAttrib + && 0 == vbld->stride() ) + { + vbld->setStride(4); + vbld->setStepFunction(MTL::VertexStepFunctionConstant); + vbld->setStepRate(0); + } pd->setVertexDescriptor(vertexDesc); @@ -3457,7 +3471,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames MTL::RenderPipelineReflection* reflection = NULL; pso->m_rps = newRenderPipelineStateWithDescriptor(m_device, pd, MTL::PipelineOptionBufferTypeInfo, &reflection); - BGFX_FATAL(NULL != pso->m_rps, Fatal::InvalidShader, "Failed to create graphics PSO!"); + BGFX_FATAL(NULL != pso->m_rps, Fatal::InvalidShader, "Failed to create graphics PSO: %s", s_lastPipelineError); if (NULL != reflection) { @@ -4658,6 +4672,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames MTL_RELEASE_DEFERRED(m_ptrMsaa); MTL_RELEASE_DEFERRED(m_ptrStencil); + MTL_RELEASE_DEFERRED(m_ptrMsaaStencil); MTL_RELEASE_DEFERRED(m_ptrAlt); MTL_RELEASE_DEFERRED(m_ptrMsaaAlt); @@ -4811,12 +4826,41 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames ; } + MTL::Texture* TextureMtl::getMultisampleStencilView() + { + if (NULL == m_ptrMsaa + || 0 == (m_flags & BGFX_TEXTURE_MSAA_SAMPLE) ) + { + return NULL; + } + + if (NULL != m_ptrMsaaStencil) + { + return m_ptrMsaaStencil; + } + + MTL::PixelFormat format; + switch (m_ptrMsaa->pixelFormat() ) + { + case MTL::PixelFormatDepth32Float_Stencil8: format = MTL::PixelFormatX32_Stencil8; break; + case MTL::PixelFormatDepth24Unorm_Stencil8: format = MTL::PixelFormatX24_Stencil8; break; + case MTL::PixelFormatStencil8: return m_ptrMsaa; + default: return NULL; + } + + m_ptrMsaaStencil = m_ptrMsaa->newTextureView(format); + return m_ptrMsaaStencil; + } + MTL::Texture* TextureMtl::getSampledView(uint8_t _textureType, uint32_t _flags, uint16_t _firstLayer, uint16_t _numLayers, uint8_t _firstMip, uint8_t _numMips, bool _stencil) { if (MTL::TextureType2DMultisample == _textureType || MTL::TextureType2DMultisampleArray == _textureType) { - MTL::Texture* ptr = getMultisampleView(_flags); + MTL::Texture* ptr = _stencil + ? getMultisampleStencilView() + : getMultisampleView(_flags) + ; if (NULL != ptr) { @@ -4924,8 +4968,12 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames } const bool isCube = TextureCube == m_type; - const uint32_t rangeScale = isCube ? 6 : 1; - const uint32_t totalUnits = uint32_t(ptr->arrayLength() ); + const bool wants2D = MTL::TextureType2D == _textureType || MTL::TextureType2DArray == _textureType; + const bool wantsCube = MTL::TextureTypeCube == _textureType || MTL::TextureTypeCubeArray == _textureType; + + const bool facesAsLayers = isCube && wants2D; + const uint32_t rangeScale = isCube && !facesAsLayers ? 6 : 1; + const uint32_t totalUnits = uint32_t(ptr->arrayLength() ) * (facesAsLayers ? 6 : 1); const uint32_t totalLayers = totalUnits * rangeScale; const uint8_t firstMip = bx::min(_firstMip, uint8_t(m_numMips - 1) ); @@ -4937,7 +4985,14 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames MTL::TextureType type = ptr->textureType(); - if (isCube) + if (facesAsLayers) + { + type = 1 == numLayers && MTL::TextureType2D == _textureType + ? (MTL::TextureType)MTL::TextureType2D + : (MTL::TextureType)MTL::TextureType2DArray + ; + } + else if (isCube) { type = 1 == numUnits ? (MTL::TextureType)MTL::TextureTypeCube @@ -4952,8 +5007,6 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames const bool arrayOf2D = MTL::TextureType2D == type || MTL::TextureType2DArray == type; const bool arrayOfCube = MTL::TextureTypeCube == type || MTL::TextureTypeCubeArray == type; - const bool wants2D = MTL::TextureType2D == _textureType || MTL::TextureType2DArray == _textureType; - const bool wantsCube = MTL::TextureTypeCube == _textureType || MTL::TextureTypeCubeArray == _textureType; if ( (arrayOf2D && wants2D && (MTL::TextureType2DArray == _textureType || 1 == numLayers) ) || (arrayOfCube && wantsCube && (MTL::TextureTypeCubeArray == _textureType || 1 == numUnits ) ) ) diff --git a/src/renderer_mtl.h b/src/renderer_mtl.h index 670fa5d38..b80cec650 100644 --- a/src/renderer_mtl.h +++ b/src/renderer_mtl.h @@ -143,6 +143,8 @@ namespace bgfx { namespace mtl return lib; } + inline char s_lastPipelineError[1024] = ""; + inline MTL::RenderPipelineState* newRenderPipelineStateWithDescriptor(MTL::Device* _device, MTL::RenderPipelineDescriptor* _descriptor) { NS::Error* error = NULL; @@ -163,6 +165,11 @@ namespace bgfx { namespace mtl { NS::Error* error = NULL; MTL::RenderPipelineState* state = _device->newRenderPipelineState(_descriptor, _options, _reflection, &error); + if (NULL != error) + { + bx::strCopy(s_lastPipelineError, sizeof(s_lastPipelineError), error->localizedDescription()->utf8String() ); + } + BX_WARN(NULL == error , "newRenderPipelineStateWithDescriptor failed: %s" , error->localizedDescription()->utf8String() @@ -479,6 +486,7 @@ namespace bgfx { namespace mtl , m_ptrStencil(NULL) , m_ptrAlt(NULL) , m_ptrMsaaAlt(NULL) + , m_ptrMsaaStencil(NULL) , m_sampler(NULL) , m_videoDecoder(NULL) , m_flags(0) @@ -519,6 +527,7 @@ namespace bgfx { namespace mtl ); MTL::Texture* getMultisampleView(uint32_t _flags) const; + MTL::Texture* getMultisampleStencilView(); MTL::Texture* getSampledView(uint8_t _textureType, uint32_t _flags, uint16_t _firstLayer, uint16_t _numLayers, uint8_t _firstMip, uint8_t _numMips, bool _stencil); MTL::Texture* getTextureImage(uint8_t _mip, uint16_t _firstLayer = 0, uint16_t _numLayers = UINT16_MAX); @@ -532,6 +541,7 @@ namespace bgfx { namespace mtl MTL::Texture* m_ptrStencil; // for emulating packed depth/stencil formats - only for iOS8... MTL::Texture* m_ptrAlt; MTL::Texture* m_ptrMsaaAlt; + MTL::Texture* m_ptrMsaaStencil; stl::unordered_map m_ptrViews; MTL::SamplerState* m_sampler; VideoDecoderMtl* m_videoDecoder; diff --git a/src/renderer_vk.cpp b/src/renderer_vk.cpp index 7b15b64d2..7fef32452 100644 --- a/src/renderer_vk.cpp +++ b/src/renderer_vk.cpp @@ -2305,6 +2305,23 @@ VK_IMPORT_INSTANCE fmt = s_textureFormat[ii].m_fmtSrgb; } + if (0 != (support & BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN) ) + { + VkFormatProperties fp; + vkGetPhysicalDeviceFormatProperties(m_physicalDevice, depth ? s_textureFormat[ii].m_fmtDsv : s_textureFormat[ii].m_fmt, &fp); + + const VkFormatFeatureFlags mipFeatures = 0 + | VK_FORMAT_FEATURE_BLIT_SRC_BIT + | VK_FORMAT_FEATURE_BLIT_DST_BIT + | (depth ? 0 : VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) + ; + + if (mipFeatures != (fp.optimalTilingFeatures & mipFeatures) ) + { + support &= ~BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN; + } + } + g_caps.formats[ii] = support; } @@ -4135,10 +4152,24 @@ VK_IMPORT_DEVICE } } + const uint32_t firstInstanceLocation = _vertexInputState.vertexAttributeDescriptionCount; + if (0 < _numInstanceData) { fillInstanceBinding(_program.m_vsh, _vertexInputState, _numInstanceData); } + + const uint32_t numFallback = 0 < _numStream ? _program.m_vsh->m_numInstanceData : 0; + + for (uint32_t ii = _numInstanceData; ii < numFallback; ++ii) + { + VkVertexInputAttributeDescription* inputAttrib = const_cast(_vertexInputState.pVertexAttributeDescriptions + _vertexInputState.vertexAttributeDescriptionCount); + inputAttrib->location = firstInstanceLocation + ii; + inputAttrib->binding = 0; + inputAttrib->format = VK_FORMAT_R8G8B8A8_UNORM; + inputAttrib->offset = 0; + _vertexInputState.vertexAttributeDescriptionCount++; + } } VkResult getRenderPass(uint8_t _num, const VkFormat* _formats, const VkImageAspectFlags* _aspects, const bool* _resolve, VkSampleCountFlagBits _samples, uint16_t _clearFlags, uint8_t _depthFlags, ::VkRenderPass* _outRenderPass, uint32_t* _outHashKey) @@ -7000,6 +7031,8 @@ VK_DESTROY bx::read(&reader, m_numAttrs, &err); + m_numInstanceData = 0; + for (uint8_t ii = 0; ii < m_numAttrs; ++ii) { uint16_t id; @@ -7012,6 +7045,10 @@ VK_DESTROY m_attrMask[attr] = UINT16_MAX; m_attrRemap[attr] = ii; } + else + { + ++m_numInstanceData; + } } bx::HashMurmur2A murmur; diff --git a/src/renderer_vk.h b/src/renderer_vk.h index 9d30a10b9..2bb1c0b87 100644 --- a/src/renderer_vk.h +++ b/src/renderer_vk.h @@ -576,6 +576,7 @@ VK_DESTROY_FUNC(DescriptorSet); uint16_t m_size; uint8_t m_numPredefined; uint8_t m_numAttrs; + uint8_t m_numInstanceData; BindInfo m_bindInfo[BGFX_CONFIG_MAX_TEXTURE_SAMPLERS]; diff --git a/src/renderer_webgpu.cpp b/src/renderer_webgpu.cpp index aa17211ca..30ce14a0b 100644 --- a/src/renderer_webgpu.cpp +++ b/src/renderer_webgpu.cpp @@ -547,6 +547,14 @@ namespace bgfx { namespace wgpu return 0 <= idx && s_feature[idx].supported; } + static bool isMipGenFormat(TextureFormat::Enum _format) + { + return false + || TextureFormat::RGBA8 == _format + || TextureFormat::BGRA8 == _format + ; + } + struct TextureFormatCaps { TextureFormatCaps() @@ -735,7 +743,10 @@ namespace bgfx { namespace wgpu ; } - if (MipGen::isSupported(_fmt) ) + if (true + && storage + && isMipGenFormat(_fmt) + && MipGen::isSupported(_fmt) ) { caps |= BGFX_CAPS_FORMAT_TEXTURE_MIP_AUTOGEN; } @@ -3957,10 +3968,25 @@ WGPU_IMPORT } } + const uint32_t firstInstanceLocation = maxShaderLocation + (0 < numStreams); + const uint32_t numFallback = 0 < numStreams ? program.m_vsh->m_numInstanceData : 0; + + for (uint32_t ii = _numInstanceData; ii < numFallback; ++ii) + { + *elem = + { + .nextInChain = NULL, + .format = WGPUVertexFormat_Unorm8x4, + .offset = 0, + .shaderLocation = firstInstanceLocation+ii, + }; + + ++elem; + ++vertexBufferLayout[numStreams-1].attributeCount; + } + if (0 < _numInstanceData) { - maxShaderLocation += 0 < numStreams; - for (uint32_t ii = 0; ii < _numInstanceData; ++ii) { elem[ii] = @@ -3968,7 +3994,7 @@ WGPU_IMPORT .nextInChain = 0, .format = WGPUVertexFormat_Float32x4, .offset = ii*16ull, - .shaderLocation = maxShaderLocation+ii, + .shaderLocation = firstInstanceLocation+ii, }; } @@ -4962,6 +4988,8 @@ WGPU_IMPORT bx::read(&reader, m_numAttrs, &err); + m_numInstanceData = 0; + for (uint8_t ii = 0; ii < m_numAttrs; ++ii) { uint16_t id; @@ -4974,6 +5002,10 @@ WGPU_IMPORT m_attrMask[attr] = UINT16_MAX; m_attrRemap[attr] = ii; } + else + { + ++m_numInstanceData; + } } bx::HashMurmur3 murmur; @@ -5157,9 +5189,8 @@ WGPU_IMPORT static bool canGenerateMips(TextureFormat::Enum _format) { return true + && isMipGenFormat(_format) && 0 != (g_caps.formats[_format] & BGFX_CAPS_FORMAT_TEXTURE_IMAGE_WRITE) - && !bimg::isDepth(bimg::TextureFormat::Enum(_format) ) - && !bimg::isCompressed(bimg::TextureFormat::Enum(_format) ) ; } diff --git a/src/renderer_webgpu.h b/src/renderer_webgpu.h index eeb0eb275..0d0718941 100644 --- a/src/renderer_webgpu.h +++ b/src/renderer_webgpu.h @@ -547,6 +547,7 @@ namespace wgpu { uint16_t m_blockSize; uint8_t m_numPredefined; uint8_t m_numAttrs; + uint8_t m_numInstanceData; ShaderBinding m_shaderBinding[BGFX_CONFIG_MAX_TEXTURE_SAMPLERS];