{"record":{"id":"26c6e17456e27475","repo":"3b1b/manim","slug":"an-array-in-a-block-holds-array-element-size-flo","errorCode":null,"errorMessage":"An array in a block holds {ARRAY_ELEMENT_SIZE} floats to an element, so {name} cannot hold {count} of {size}","messagePattern":"An array in a block holds (.+?) floats to an element, so (.+?) cannot hold (.+?) of (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"manimlib/renderer/uniform_block.py","lineNumber":62,"sourceCode":"\n    The rules reproduced are that a member is aligned to its own size, rounded up to four\n    floats for anything wider than two, and that the block as a whole is rounded up likewise.\n    What that alignment skips over is declared as a field rather than left as a gap, numpy not\n    carrying the contents of a gap over when copying, which would leave whatever the memory\n    held to be compared against and sent.\n    \"\"\"\n    names: list[str] = []\n    formats: list[Any] = []\n\n    def add(name: str, shape: tuple[int, ...]) -> None:\n        names.append(name)\n        formats.append(np.float32 if shape == (1,) else (np.float32, shape))\n\n    size_so_far = 0\n    for name, size, *rest in members:\n        count = rest[0] if rest else 1\n        if count > 1 and size != ARRAY_ELEMENT_SIZE:\n            raise ValueError(\n                f\"An array in a block holds {ARRAY_ELEMENT_SIZE} floats to an element, so \"\n                f\"{name} cannot hold {count} of {size}\"\n            )\n        if size not in BLOCK_MEMBER_TYPES:\n            raise ValueError(f\"No room in a block for {name}, of {size} floats\")\n        skipped = -size_so_far % (size if size <= 2 else 4)\n        if skipped:\n            add(f\"_pad{len(names)}\", (skipped,))\n        add(name, (size,) if count == 1 else (count, size))\n        size_so_far += skipped + count * size\n    if -size_so_far % 4:\n        add(f\"_pad{len(names)}\", (-size_so_far % 4,))\n    # Left to pack the fields itself, numpy places them back to back, which is where the\n    # padding above has been chosen to put them\n    return np.dtype({\"names\": names, \"formats\": formats})\n\n\ndef uniform_block_code(dtype: np.dtype) -> str:","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/3b1b/manim/blob/dee01804d47b9f94402d71472674710dcac125b8/manimlib/renderer/uniform_block.py#L44-L80","documentation":"uniform_block builds a packed numpy dtype plus a WGSL block from a member list. GLSL/WGSL std140-style rules require each element of an array in a uniform block to be a vec4 (ARRAY_ELEMENT_SIZE = 4 floats, uniform_block.py:34). A member declared with count > 1 but a per-element size other than 4 floats (e.g. three vec3s) cannot be laid out, so it raises ValueError at block-construction time.","triggerScenarios":"Adding a uniform-block member like ('body_positions', 3, 3) — an array of 3-float elements — in a custom shader's UniformBlock declaration; any ('name', size, count) with count > 1 and size != 4.","commonSituations":"Writing custom shaders that pass an array of vec3s (positions, colors per instance); porting GLSL uniform blocks to this renderer; upgrading manim versions where the block-building API gained this check.","solutions":["Pad each element to 4 floats: declare the member as size 4 and store positions as vec4s (w unused)","Alternatively split into separate non-array members of size 3, which are padded automatically by the existing _pad logic","Keep arrays in blocks to vec4 (or mat4) elements only"],"exampleFix":"# before\nUniformBlock(... members=[('body_positions', 3, 3)])\n# after\nUniformBlock(... members=[('body_positions', 4, 3)])  # vec4 elements, w unused","handlingStrategy":"validation","validationCode":"ARRAY_ELEMENT_SIZE = 4\nfor name, size, *rest in members:\n    count = rest[0] if rest else 1\n    if count > 1:\n        assert size == ARRAY_ELEMENT_SIZE, f\"{name}: array elements must be {ARRAY_ELEMENT_SIZE} floats\"","typeGuard":"def valid_block_members(members) -> bool:\n    return all(\n        (rest[0] if rest else 1) == 1 or size == 4\n        for _, size, *rest in members\n    )","tryCatchPattern":null,"preventionTips":["Use only vec4 (or mat4) elements inside uniform-block arrays","Pad vec3 data to vec4 in the buffer upload (numpy structured dtype already does this when declared size 4)"],"tags":["opengl","uniforms","shader","wgsl","layout"],"backgroundTag":null,"analyzedSha":"dee01804d47b9f94402d71472674710dcac125b8","analyzedAt":"2026-08-14T19:53:44.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}