I eventually got this working. As I said, my biggest mistake was apparently the fact that I wasn't handling the m_vertexBufferView properly per Tubby94's Reddit post that I mentioned in the comments.
The primary missing code to handle that appears at the end of Sample3DSceneRenderer.cpp:CreateDeviceDependentResources. So my CreateDeviceDependentResources function should have looked more like this
(notice the m_instanceBufferView code near the end):
... static const D3D12_INPUT_ELEMENT_DESC inputLayout[] = { // Vertex Buffer Data { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, /* { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, Old Microsoft stuff */ { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, // Instance Buffer Data { "INSTANCE_STUFF", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1 }, }; ... // Instance positions VertexPosition instancePositions[] = { XMFLOAT3(3.0f, 0.0f, 0.0f), XMFLOAT3(3.0f, 0.0f, 0.0f), XMFLOAT3(-3.0f, 0.0f, 0.0f) }; const UINT instanceBufferSize = sizeof(instancePositions); // Create the instance buffer resource in the GPU's default heap and copy instance data into it using the upload heap. // The upload resource must not be released until after the GPU has finished using it. Microsoft::WRL::ComPtr<ID3D12Resource> instanceBufferUpload; CD3DX12_RESOURCE_DESC instanceBufferDesc = CD3DX12_RESOURCE_DESC::Buffer(instanceBufferSize); DX::ThrowIfFailed(d3dDevice->CreateCommittedResource( &defaultHeapProperties, D3D12_HEAP_FLAG_NONE, &instanceBufferDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_instanceBuffer))); DX::ThrowIfFailed(d3dDevice->CreateCommittedResource( &uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &instanceBufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&instanceBufferUpload))); NAME_D3D12_OBJECT(m_instanceBuffer); // Upload the instance buffer to the GPU. { D3D12_SUBRESOURCE_DATA instanceData = {}; instanceData.pData = reinterpret_cast<BYTE*>(instancePositions); instanceData.RowPitch = instanceBufferSize; instanceData.SlicePitch = instanceData.RowPitch; UpdateSubresources(m_commandList.Get(), m_instanceBuffer.Get(), instanceBufferUpload.Get(), 0, 0, 1, &instanceData); CD3DX12_RESOURCE_BARRIER instanceBufferResourceBarrier = CD3DX12_RESOURCE_BARRIER::Transition(m_instanceBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); m_commandList->ResourceBarrier(1, &instanceBufferResourceBarrier); } ... // Create vertex/index buffer views. m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress(); m_vertexBufferView.StrideInBytes = sizeof(VertexPositionColor); m_vertexBufferView.SizeInBytes = sizeof(cubeVertices); m_instanceBufferView.BufferLocation = m_instanceBuffer->GetGPUVirtualAddress(); m_instanceBufferView.StrideInBytes = sizeof(VertexPosition); m_instanceBufferView.SizeInBytes = sizeof(someInstanceData); m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress(); m_indexBufferView.SizeInBytes = sizeof(cubeIndices); m_indexBufferView.Format = DXGI_FORMAT_R16_UINT; ... Then, in Sample3DSceneRenderer.cpp:Render I should have called IASetVertexBuffers on m_instanceBufferView as Tubby94 said:
... PIXBeginEvent(m_commandList.Get(), 0, L"Draw the cube"); { ... m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_commandList->IASetVertexBuffers(0, 1, &m_vertexBufferView); m_commandList->IASetVertexBuffers(1, 1, &m_instanceBufferView); m_commandList->IASetIndexBuffer(&m_indexBufferView); m_commandList->DrawIndexedInstanced(36, 3, 0, 0, 0); ... } ... The change to the ShaderStructures.h file is fairly intuitive but would look something like this:
namespace NameSpaceOfTheApp { // Used to send per-instance data to the vertex shader struct VertexPosition { DirectX::XMFLOAT3 pos; }; // Used to send per-vertex data to the vertex shader. struct VertexPositionColor { DirectX::XMFLOAT3 pos; DirectX::XMFLOAT3 color; }; // Constant buffer used to send MVP matrices to the vertex shader. struct ModelViewProjectionConstantBuffer { DirectX::XMFLOAT4X4 model; DirectX::XMFLOAT4X4 view; DirectX::XMFLOAT4X4 projection; }; } I don't have the exact code for this specific question anymore, but I have a walkthrough for a very similar problem posted here:
https://www.utilars.com/Blog/shawn-eary/2018/9/19/dx12Instancing
So the basic answer to the question follows from the comment above about me failing to perform the mechanism as Tubby94 suggested. Basically, I wasn't setting m_vertexBufferView up correctly. One thing in particular I failed to so was call IASetVertexBuffers on it in the Render function of Sample3DSceneRenderer.cpp