Skip to content

OpPtrAccessChain on PhysicalStorageBuffer pointer loaded from memory uses array stride 0 #3914

Description

@Appilat

Description of Bug

When using the Vulkan fragment shader debugger with Buffer Device Address (PhysicalStorageBuffer), indexing an array through a pointer that was loaded from memory (like a nested buffer pointer) always evaluates to element 0 regardless of the index.

In a shader where an inner buffer pointer is loaded from another buffer struct and indexed via OpPtrAccessChain (such as frameBuffer.instanceBuffer[i]), the debugger calculates a byte offset of 0. Every array element read evaluates to the data of element 0. When this struct contains descriptor, texture, or sampler IDs for bindless access, the debugger requests descriptors that the GPU never touched, spamming the console with:

Internal error: Binding Read-only Resource 0[x] did not exist in calculated descriptor access when performing ImageSampleImplicitLod operation.
Internal error: Binding Sampler 0[y] did not exist in calculated descriptor access when performing ImageSampleImplicitLod operation.

The pointer should be indexed using the type's ArrayStride, dereferencing the actual element 'i' at the correct byte offset. If the pointer is taken directly from push constants instead of loaded from memory it works correctly as expected. The draw call renders correctly on the GPU, and GetDescriptorAccess() contains the bindings the GPU actually accessed.

Steps to reproduce

StrideBug.zip

  1. Open the attached capture StrideBug.rdc in RenderDoc.
  2. In the Event Browser, expand Main Pass and select Event 153vkCmdDrawIndexedIndirectCount.
  3. Open the Texture Viewer for Color Attachment 191 and debug the fragment at any pixel coordinate.
  4. Step forward into SimpleShaders.slang to line 81 (the texture sample on material.albedoTextureId).
  5. Observe the calculated descriptor access errors in the Errors and Warnings tab, and notice in the variable watch that instance and material evaluate to element 0's values instead of the values for inputVertex.instanceID.

Environment

  • RenderDoc version: v1.47 (commit 54f92c2), self-built from source with Wayland support (also reproduced on v1.45)
  • Operating System: Arch Linux (Kernel 7.2.6-arch2-1, Wayland)
  • Graphics API: Vulkan 1.4 (NVIDIA GeForce GTX 1060, Driver 580.178.04)

Additional Information

I traced it to the spirv_debug.cpp file.
When a pointer is loaded from memory via Op::Load (ReadPointerValue), the destination ShaderVariable does not have its array stride populated via setArrayStride

When Op::PtrAccessChain subsequently indexes the pointer:
uint64_t byteOffset = element * debugger.GetPointerArrayStride(base);
debugger.GetPointerArrayStride(base) returns 0. ByteOffset evaluates to 0 for any element index, causing every read to fetch element 0.
This does not happen for root pointers coming directly from push constants. They have their array stride populated in cbufferCallback / MakeTypedPointer and evaluate the correct non-zero byte offset.

Quick local patch I made for myself:

// in spirv_debug.h (class Debugger)
uint32_t GetPointerTypeArrayStride(Id ptrType) const;

// in spirv_debug.cpp
uint32_t Debugger::GetPointerTypeArrayStride(Id ptrType) const
{
  const Decorations &dec = decorations[ptrType];
  return (dec.flags & Decorations::HasArrayStride) ? dec.arrayStride : 0;
}

void setArrayStride(ShaderVariable &var, uint32_t stride)
{
  var.value.u64v[9] = stride;
}

// In case Op::PtrAccessChain:
uint32_t stride = debugger.GetPointerArrayStride(base);
if(stride == 0 && chain.indexes.empty())
{
  // pointer was loaded from memory without stride info, recover it from the result type
  stride = debugger.GetPointerTypeArrayStride(chain.resultType);
  if(stride)
    setArrayStride(base, stride);
}
uint64_t byteOffset = (uint64_t)element * stride;

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugA crash, misbehaviour, or other problemUnresolvedWaiting for a fix or implementation

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions