// TODO can determine which uniform buffers are used in which stage based on binding index (see 'Create' in GpuProgramsVk.cpp)
        // if (binding.GetShaderStageFlags() != VK_SHADER_STAGE_FRAGMENT_BIT) // *only* used in fragment shader
        static uint SerializeUniforms(CompiledShader compiled, BinaryWriter writer)
        {
            int totalConstants = 0;

            foreach (var constantBuffer in compiled.constantBuffers)
            {
                totalConstants += constantBuffer.usedConstants;
            }
            writer.Write((ushort)totalConstants);

            int size = 0;

            foreach (var constantBuffer in compiled.constantBuffers)
            {
                for (int i = 0; i < constantBuffer.usedConstants; i++)
                {
                    var constant = constantBuffer.constants[i];
                    UnityToBgfx.ConvertBuiltInUniform(constant.name, out string uniformName);
                    writer.Write((byte)uniformName.Length);
                    writer.Write(Encoding.UTF8.GetBytes(uniformName));

                    if (constant.dataType != UnityHelper.ShaderParamType.kShaderParamFloat)
                    {
                        throw new NotSupportedException("Only float types are supported for uniforms.");
                    }

                    // Data type
                    var type = UnityToBgfx.GetBgfxUniformDataType(constant);
                    writer.Write((byte)type); // TODO bx::write(_writer, uint8_t(un.type | fragmentBit));

                    //  array size
                    writer.Write((byte)constant.arraySize); // Should be 0 if not an array

                    // regIndex - bgfx puts all constants into a single buffer
                    writer.Write((ushort)(size + constant.idx)); // TODO

                    // regCount
                    var regCount = constant.arraySize;
                    if (type == BgfxHelper.UniformType.Mat3)
                    {
                        regCount *= 3;
                    }
                    else if (type == BgfxHelper.UniformType.Mat4)
                    {
                        regCount *= 4;
                    }
                    writer.Write((ushort)regCount);

                    // TODO this is only used in renderer_webgpu.cpp?
                    // texComponent
                    // texDimension
//                    writer.Write((byte)0);
//                    writer.Write((byte)0);
                }

                size += constantBuffer.size;
            }

            return((uint)size);
        }
        static uint SerializeUniforms(CompiledShaderData compiled, BinaryWriter writer, Stage stage)
        {
            if (compiled.constantBuffers.Count == 0 && compiled.samplers.Count == 0)
            {
                writer.Write((ushort)0);
                return(0);
            }

            uint fragmentBit   = stage == Stage.Fragment ? BgfxHelper.BGFX_UNIFORM_FRAGMENTBIT : 0u;
            var  uniformsCount = compiled.samplers.Count;

            foreach (var constantBuffer in compiled.constantBuffers)
            {
                uniformsCount += constantBuffer.constants.Count;
            }
            writer.Write((ushort)uniformsCount);

            uint constantBufferSize = 0;

            foreach (var constantBuffer in compiled.constantBuffers)
            {
                foreach (var constant in constantBuffer.constants)
                {
                    UnityToBgfx.ConvertBuiltInUniform(constant.name, out string uniformName);
                    writer.Write((byte)uniformName.Length);
                    writer.Write(Encoding.UTF8.GetBytes(uniformName));

                    if (constant.dataType != UnityHelper.ShaderParamType.kShaderParamFloat)
                    {
                        throw new NotSupportedException("Only float types are supported for uniforms.");
                    }

                    // data type
                    var type = UnityToBgfx.GetBgfxUniformDataType(constant);
                    writer.Write((byte)((uint)type | fragmentBit));

                    //  array size
                    var arraySize = Math.Max(constant.arraySize, 1); // Unity passes a 0 if not an array
                    writer.Write((byte)arraySize);

                    // regIndex - merge all constants into a single buffer
                    writer.Write((ushort)(constantBufferSize + constant.idx));

                    // regCount
                    var regCount = (ushort)arraySize;
                    if (type == BgfxHelper.UniformType.Mat3)
                    {
                        regCount *= 3;
                    }
                    else if (type == BgfxHelper.UniformType.Mat4)
                    {
                        regCount *= 4;
                    }
                    writer.Write(regCount);

                    // Unused
                    writer.Write((byte)0); // texComponent
                    writer.Write((byte)0); // texDimension
                }

                constantBufferSize += (uint)constantBuffer.size;
            }

            foreach (var sampler in compiled.samplers)
            {
                writer.Write((byte)sampler.name.Length);
                writer.Write(Encoding.UTF8.GetBytes(sampler.name));
                writer.Write((byte)((ushort)BgfxHelper.UniformType.Sampler | BgfxHelper.BGFX_UNIFORM_SAMPLERBIT | fragmentBit));

                // array size
                writer.Write((byte)0);

                // regIndex
                writer.Write((ushort)0);

                // regCount
                writer.Write((ushort)0);

                // texComponent
                writer.Write((byte)0);

                // texDimension
                writer.Write((byte)0);
            }

            return(constantBufferSize);
        }
        static void SerializeUniforms(CompiledShaderData compiled, BinaryWriter writer, Stage stage)
        {
            if (compiled.constantBuffers.Count == 0 && compiled.samplers.Count == 0)
            {
                writer.Write((ushort)0);
                return;
            }

            uint fragmentBit   = stage == Stage.Fragment ? BgfxHelper.BGFX_UNIFORM_FRAGMENTBIT : 0u;
            var  uniformsCount = compiled.samplers.Count;

            foreach (var constantBuffer in compiled.constantBuffers)
            {
                uniformsCount += constantBuffer.constants.Count;
            }
            writer.Write((ushort)uniformsCount);

            foreach (var constantBuffer in compiled.constantBuffers)
            {
                foreach (var constant in constantBuffer.constants)
                {
                    writer.Write((byte)constant.name.Length);
                    writer.Write(Encoding.UTF8.GetBytes(constant.name));

                    if (constant.dataType != UnityHelper.ShaderParamType.kShaderParamFloat)
                    {
                        throw new NotSupportedException("Only float types are supported for uniforms.");
                    }

                    // Data type
                    var type = UnityToBgfx.GetBgfxUniformDataType(constant);
                    writer.Write((byte)((uint)type | fragmentBit));

                    //  array size
                    int arraySize = Math.Max(constant.arraySize, 1); // Unity passes a 0 if not an array
                    writer.Write((byte)arraySize);

                    // regIndex is not used for GL
                    writer.Write((ushort)0);

                    // regCount
                    writer.Write((ushort)arraySize);

                    // Unused
                    writer.Write((byte)0); // texComponent
                    writer.Write((byte)0); // texDimension
                }
            }

            foreach (var sampler in compiled.samplers)
            {
                writer.Write((byte)sampler.name.Length);
                writer.Write(Encoding.UTF8.GetBytes(sampler.name));
                writer.Write((byte)((ushort)BgfxHelper.UniformType.Sampler | BgfxHelper.BGFX_UNIFORM_SAMPLERBIT | fragmentBit));

                // array size
                int arraySize = Math.Max(sampler.arraySize, 1);
                writer.Write((byte)arraySize);

                // regIndex
                writer.Write((ushort)0);

                // regCount
                writer.Write((ushort)arraySize);

                // texComponent
                writer.Write((byte)0);

                // texDimension
                writer.Write((byte)0);
            }
        }
        static uint SerializeUniforms(CompiledShaderData compiled, BinaryWriter writer, Stage stage)
        {
            if (compiled.constantBuffers.Count == 0 && compiled.samplers.Count == 0)
            {
                writer.Write((ushort)0);
                return(0);
            }

            uint fragmentBit   = stage == Stage.Fragment ? BgfxHelper.BGFX_UNIFORM_FRAGMENTBIT : 0u;
            var  uniformsCount = compiled.samplers.Count;

            foreach (var constantBuffer in compiled.constantBuffers)
            {
                uniformsCount += constantBuffer.constants.Count;
            }
            writer.Write((ushort)uniformsCount);

            uint globalConstantBufferSize = 0;

            foreach (var constantBuffer in compiled.constantBuffers)
            {
                uint constantBufferSize = 0;
                foreach (var constant in constantBuffer.constants)
                {
                    UnityToBgfx.ConvertBuiltInUniform(constant.name, out string uniformName);
                    writer.Write((byte)uniformName.Length);
                    writer.Write(Encoding.UTF8.GetBytes(uniformName));

                    if (constant.dataType != UnityHelper.ShaderParamType.kShaderParamFloat)
                    {
                        throw new NotSupportedException("Only float types are supported for uniforms.");
                    }

                    // Data type
                    uint parameterSize;
                    var  type = UnityToBgfx.GetBgfxUniformDataType(constant);
                    writer.Write((byte)((uint)type | fragmentBit));
                    if (type == BgfxHelper.UniformType.Vec4)
                    {
                        parameterSize = 16;
                    }
                    else if (type == BgfxHelper.UniformType.Mat4)
                    {
                        parameterSize = 64;
                    }
                    else
                    {
                        // bgfx.UniformType.Mat3. Size includes padding
                        parameterSize = 44;
                    }

                    // array size
                    writer.Write((byte)constant.arraySize); // Should be 0 if not an array

                    // regIndex - merge all constants into a single buffer
                    writer.Write((ushort)(globalConstantBufferSize + constant.idx));

                    // regCount
                    parameterSize = constant.arraySize > 0 ? parameterSize * (uint)constant.arraySize : parameterSize;
                    parameterSize = AlignUp(parameterSize, 16);
                    writer.Write((ushort)(parameterSize / 16));

                    // Unused
                    writer.Write((byte)0); // texComponent
                    writer.Write((byte)0); // texDimension

                    // Actual size of this constant buffer. All unused constants after the last used constant are removed
                    constantBufferSize = (uint)constant.idx + parameterSize;
                }

                globalConstantBufferSize += constantBufferSize;
            }

            foreach (var sampler in compiled.samplers)
            {
                writer.Write((byte)sampler.name.Length);
                writer.Write(Encoding.UTF8.GetBytes(sampler.name));
                writer.Write((byte)((ushort)BgfxHelper.UniformType.Sampler | BgfxHelper.BGFX_UNIFORM_SAMPLERBIT | fragmentBit));

                // array size
                writer.Write((byte)sampler.arraySize); // this will always be zero

                // regIndex
                writer.Write((ushort)sampler.register);

                // regCount
                writer.Write((ushort)1);

                // Unused
                writer.Write((byte)0); // texComponent
                writer.Write((byte)0); // texDimension
            }

            return(globalConstantBufferSize);
        }