Is ComputeShader.SetConstantBuffer working?

I can’t seem to find working examples of how to use ComputeShader.SetConstantBuffer. An attempt was made in this thread , but I’m afraid no one saw it because it was posted in a different forum.

Well, I can’t get it to work either. Testing in Unity 2020.3.

Am I doing it wrong?

In the example below I get “Result is: 0. Should be: 3”.

using UnityEngine;
using System.Runtime.InteropServices;

public class ConstantBufferTest : MonoBehaviour
{
    [SerializeField] ComputeShader _cs;

    struct OnceConstants
    {
        public int value1;
        public int value2;
    }

    void Start()
    {
        int executeKernel = _cs.FindKernel( "Execute" );

        // Create and set buffers.
        int onceSize = Marshal.SizeOf( typeof( OnceConstants ) );
        var onceBuffer = new ComputeBuffer( 1, onceSize, ComputeBufferType.Constant );
        var resultBuffer = new ComputeBuffer( 1, sizeof( int ) );
        _cs.SetBuffer( executeKernel, "_ResultBuffer", resultBuffer );
        _cs.SetConstantBuffer( "Once", onceBuffer, offset: 0, onceSize );

        // Upload constants.
        onceBuffer.SetData(
            new OnceConstants[]{
                new OnceConstants() {
                    value1 = 1,
                    value2 = 2,
                }
            }
        );

        // Execute shader.
        _cs.Dispatch( executeKernel, 1, 1, 1 );

        // Read back and log.
        int[] resultData = new int[ 1 ];
        resultBuffer.GetData( resultData );
        Debug.Log( "Result is: " + resultData[0] + ". Should be: " + ( 1 + 2 ) );

        // Clean up after the party.
        onceBuffer.Release();
        resultBuffer.Release();
    }
}

And the ComputeShader:

#include "UnityCG.cginc"

#pragma kernel Execute

RWStructuredBuffer<int> _ResultBuffer;

CBUFFER_START( Once )
    int value1;
    int value2;
CBUFFER_END

[numthreads(1,1,1)]
void Execute()
{
    _ResultBuffer[ 0 ] = value1 + value2;
    //_ResultBuffer[ 0 ] = 1 + 2; // This works
}

Your code looks fine and can confirm that it works as expected in 2021.1, so it must be a bug.

1 Like

Thank you so much for testing it. I just tested in 2021.2 and that does not work for me. Downloading 2021.1 now to test that as well.

Could you by any chance post the working compiled shader?

**** Platform Direct3D 11:
Compiled code for kernel Execute
keywords: <none>
binary blob size 228:
//
// Generated by Microsoft (R) D3D Shader Disassembler
//
//
// Input signature:
//
// Name                 Index   Mask Register SysValue  Format   Used
// -------------------- ----- ------ -------- -------- ------- ------
// no Input
//
// Output signature:
//
// Name                 Index   Mask Register SysValue  Format   Used
// -------------------- ----- ------ -------- -------- ------- ------
// no Output
      cs_5_0
      dcl_globalFlags refactoringAllowed
      dcl_constantbuffer CB0[1], immediateIndexed
      dcl_uav_structured u0, 4
      dcl_temps 1
      dcl_thread_group 1, 1, 1
   0: iadd r0.x, cb0[0].y, cb0[0].x
   1: store_structured u0.x, l(0), l(0), r0.x
   2: ret
// Approximately 0 instruction slots used

This is a true mystery.

Thank you for the compiled shader @grizzly . It is identical with the one I get in 2021.1.28f1. But it does not work.

I have updated my graphics driver (RTX2080). Updated Windows. Restarted. Reinstalled Unity. Clean project. No packages, no nothing. I am really out of ideas at this point.

EDIT:
I also tried using Visual Studio Code instead of Visual Studio Editor.

@grizzly I have now tested on two Windows 10 machines with Unity 2021.1.16f1 and 2021.1.28f1. None of them work.

What version of 2021.1 exactly worked for you?

That is strange. Tested here on an old Win 7 Dx11 1060 machine using Unity 2021.1.3f1. Previous versions have also worked for me, so I would suggest filing a bug :slight_smile:

Thanks @grizzly , much appreciated.

It does not work in 2021.1.3f1 for me. I really have no clue what could be the cause at this point.

Testing the script above in the newly published Unity 2021.2.17f1 on Windows, Editor runtime.

  • Target platform: “Windows, Mac, Linux”.
    Graphics API for “Windows, Mac, Linux”: DX11.
    Not working.

  • Target platform: “Windows, Mac, Linux”.
    Graphics API for “Windows, Mac, Linux”: Vulcan.
    Not working.

  • Target platform:“Android”.
    Graphics API for “Windows, Mac, Linux”: DX11 (the Editor).
    Graphics API for “Android”: OpenGLES3.
    Not working.

  • Target platform:“Android”.
    Graphics API for “Windows, Mac, Linux”: DX11 (the Editor).
    Graphics API for “Android”: Vulcan.
    Not working and sometimes crashing.

  • Target platform:“Android”.
    Graphics API for “Windows, Mac, Linux”: Vulcan (the Editor). Note: Oculus XR requires DX11.
    Graphics API for “Android”: Vulcan.
    Not working.

I made a bug report.
CASE 1416880

2 Likes

The issue persists in 2022.2.0a11

Same here, does not work with Unity 2021.2.10. Constant Buffer values are just 0.
Cbuffers work in a standalone application outside of Unity though.

Is there an alternative solution to set a block of data in a compute shader? In openGL for example you can use structs as uniforms and upload the struct data. Is this also possible with compute shaders in d3d11 or only possible with cbuffers?

Yep, same here. Constant buffers in compute shaders do not seem to work. Tried declaring them with cbuffer as well as the CBUFFER_START/CBUFFER_END macros, but their data is always zero. (Unity 2021.3.0f1, macOS)

Hey I think I am having the same with 2021.3.9. What’s going on?

you say it works with 2021.1 but not with 2021.2

between the two releases

Graphics.RenderMeshInstanced

has been introduced

My bet is on this new code, I have to check if it works using it.

Minimal working example.

Tested with Unity 2022.2.0b2, and RTX 2070 (drivers 516.94) with Windows 10 64-bit

using UnityEngine;
using System.Runtime.InteropServices;

public class ConstantBuffer : MonoBehaviour
{
    [SerializeField] ComputeShader _ComputeShader;

    struct Element
    {
        public int Index;
        public int Radius;
    }

    void Start()
    {
        if (_ComputeShader == null) return;
        ComputeBuffer constantBuffer = new ComputeBuffer(1, Marshal.SizeOf(typeof(Element)), ComputeBufferType.Constant);
        ComputeBuffer structuredBuffer = new ComputeBuffer(1, Marshal.SizeOf(typeof(System.Int32)), ComputeBufferType.Structured);
        _ComputeShader.SetConstantBuffer("_ConstantBuffer", constantBuffer, 0, Marshal.SizeOf(typeof(Element)));
        _ComputeShader.SetBuffer(0, "_StructuredBuffer", structuredBuffer);
        constantBuffer.SetData(new Element[]{new Element() {Index = 1, Radius = 2}});
        _ComputeShader.Dispatch(0, 1, 1, 1 );
        int[] result = new int[1];
        structuredBuffer.GetData( result );
        Debug.Log( "Result is: " + result[0] + ". Should be: " + ( 1 + 2 ));
        constantBuffer.Release();
        structuredBuffer.Release();
    }
}
#pragma kernel CSMain

RWStructuredBuffer<int> _StructuredBuffer;

cbuffer _ConstantBuffer
{
    int Index;
    int Radius;
};

[numthreads(1,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    _StructuredBuffer[0] = Index + Radius;
}

8503451--1132478--upload_2022-10-10_20-25-33.png

1 Like

Maybe it got fixed in 2022, as this exact code does not work on 2021.3.

1 Like

Update: I got this case to work in 2021LTS by calling SetConstantBuffer(“ConstantBuffer”,constantBuffer,0,sizeof(int)*4).
My compute buffer itself is declared with the same count and stride.
I think you need to bind a multiple of 16bytes and require padding for 2 ints, likely because of this : Packing rules for constant variables - Win32 apps | Microsoft Learn

Hope this helps people trying to make this works.

For the people at Unity: Rather than the fix workaround apparently in 2022.2.0a14, wouldn’t it be better to check that the size parameter is a multiple of 16 and give a warning or error if it isn’t and/or pad the data automatically (might not be desirable I’m sure you need have a lot of additional constraints on your side)

2 Likes

I just returned to the issue and it seems my original example works in 2021.3.37f1 if I change just Unity’s CBUFFER_START CBUFFER_END constant buffer declaration macro to plain cbuffer {} declaration, like in the example by @Przemyslaw_Zaworski .

I also noted that 16bit alignment is not necessary on the MacOS platform, if you are only targeting that. I didn’t test the performance difference though.

I’m not having much luck with declaring arrays of custom structs inside cbuffer. I get the error:

Shader error in 'ConstantBufferWithStructArrayTest': Unknown parameter type (0) for Elements at kernel Execute

Any ideas? Perhaps @aleksandrk ?

using UnityEngine;
using System.Runtime.InteropServices;
public class ConstantBufferWithStructArrayTest : MonoBehaviour
{
    [SerializeField] ComputeShader _cs;
    const int elementCapacity = 2;

    struct Element
    {
        public int value1;
        public int value2;
        public Vector2Int pad;
    }

    void Start()
    {
        int executeKernel = _cs.FindKernel( "Execute" );
        // Create and set buffers.
        int constantBufferSize = Marshal.SizeOf( typeof( Element ) ) * elementCapacity;
        var constantBuffer = new ComputeBuffer( 1, constantBufferSize, ComputeBufferType.Constant );
        var resultBuffer = new ComputeBuffer( 1, sizeof( int ) );
        _cs.SetBuffer( executeKernel, "_ResultBuffer", resultBuffer );
        _cs.SetConstantBuffer( "_ConstantBuffer", constantBuffer, offset: 0, constantBufferSize );
        // Upload constants.
        constantBuffer.SetData(
            new Element[ elementCapacity ]{
                new Element() {
                    value1 = 1,
                    value2 = 2,
                    pad = Vector2Int.zero
                },
                new Element() {
                    value1 = 3,
                    value2 = 4,
                    pad = Vector2Int.zero
                }
            }
        );
        // Execute shader.
        _cs.Dispatch( executeKernel, 1, 1, 1 );
        // Read back and log.
        int[] resultData = new int[ 1 ];
        resultBuffer.GetData( resultData );
        Debug.Log( "Result is: " + resultData[0] + ". Should be: " + ( 1 + 2 ) );
        // Clean up after the party.
        constantBuffer.Release();
        resultBuffer.Release();
    }
}
#include "UnityCG.cginc"
#pragma kernel Execute
RWStructuredBuffer<int> _ResultBuffer;

struct Element
{
    int value1;
    int value2;
    int2 pad;
};

cbuffer _ConstantBuffer
{
    Element Elements[2];
};

[numthreads(1,1,1)]
void Execute()
{
    Element element = Elements[ 0 ];
    _ResultBuffer[ 0 ] = element.value1 + element.value2;
}