Instancing Multiple Objects

Started by
11 comments, last by 21st Century Moose 12 years, 6 months ago
Hi guys, I followed this Efficiently Drawing Multiple Instances of Geometry article and got eveything working smoothly. I'm hoping my geometry buffer can contain multiple objects but I can't figure out how to do that.

I put two objects in my geometry buffer and can render either one but not both:


...
d3dDevice->SetStreamSourceFreq(0,(D3DSTREAMSOURCE_INDEXEDDATA|instanceCount));
...

// this draws the squares
d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,6,0,2);

// this draws the triangles
d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,6,6,3,0,1);


It renders instanceCount squares but not the triangles, if I comment out the draw squares line it will render instanceCount triangles. I can't get it to render both the squares and the triangles, I'll also want to render a different count of each object.

I'm trying to avoid making a vertex buffer for each and every object.

Thanks for helping.
Advertisement
Try setting the second argument in the second draw call to 0. That specific parameter is used if your index list and vertex list are not aligned how you need them to be when making the draw call if that makes sense.

Otherwise, I'm not sure exactly if this is the way it is or not, but it looks like its possible that your not resetting the data input stream before you call the second draw function. So what i mean is that as it is now, i think its the second draw call is starting where the first call left off. You could try to set the second and third arguments in the second call to 0, or you could just try to re-set the data input stream
@Endemoniada: Interesting idea. Never thought of using subsets this way. Maybe I experiment on this when I have some time this weekend.

The different count can only be achieved through resetting instanceCount in your first stream. Still needs two draw calls IMHO.

For mixing geometry and using one draw call only ? Hmmmm. Off the top of my head: How about tagging your instance data with a index to select the geometry and nullify the other one by scaling it to 0 in the vertex shader (look into bone indices and think of a zero bone matrix to get an idea). But whether this is worth the pain is a question of your setup and only profiling will tell. Why you wanna do this anyway ? Only for performance ? Or does e.g. draw order matter ?

@iedoc: Sorry, I can't quite follow, but it's late and likely just me. I thought the draw call defines everything that is needed (meaning: is independant of the previous draw calls). But yeah, since you experience an unexpected behaviour, try what he suggests and report back.

Slightly off topic: Jeez, that MSDN entry is strange, fortunately there's a community entry pointing out the problem. Never occurred to me. Doc rot ?
Hey guys, thanks for trying to help.

I don't mind having a draw call for each object, I don't think there is a way around that. I'm trying to have a single geometry buffer and not separate ones for each and every object.

iedoc, I tried what you suggested and unfortunately it didn't work.

When you guys say reset the stream do you mean call SetStreamSourceFreq() again ? I tried that too.

It's a good thing I found that MSDN page because I never would have figured out that the vertex declarations need to be combined.

I'll keep working on this.
Your right unbird, that was my bad, I guess I thought it was worth a suggestion, but I haven't used direct3d 9 for a while, and his problem made me think its possible the draw calls are not independent of each other... hehe

Endemoniada, just another suggestion, try doing this instead:

[color=#1C2837][font=CourierNew, monospace][size=2][color=#000000]d3dDevice[color=#666600]->[color=#660066]SetStreamSourceFreq[color=#666600]([color=#006666]0[color=#666600],([color=#000000]D3DSTREAMSOURCE_INDEXEDDATA[color=#666600]|[color=#000000]2[color=#666600]));[/font]
[color=#1C2837][font=CourierNew, monospace][size=2][color=#666600]
[/font]
[color=#1C2837][size=2][color=#666600][font="Arial"]Otherwise, could you post more of the code thats relevant? Like where you set your stream source and stuff like that[/font]
@Endemoniada: Ok, misread your intention and obviously went too far :-).

Now I remember: The parameters of DrawIndexedPrimitive are a bit tricky, you definitively want to look into this. I think you rather want to play around with the StartIndex parameter (and PrimitiveCount, of course).

@iedoc: That's what I meant. If you want to draw just two instances, that's the call you need.

But I agree: To make the discussion easier show us your buffer contents and some code.
Hi, I tried a lot of things and the second set of objects still isn't being drawn. Here is more code:



// the geometryBuffer has 9 vertices which define two objects (6 for objectA and 3 for objectB)
// to keep it simple the indexBuffer also has 9 vertices, corresponding 1:1 with the geometryBuffer

int instanceCount=4;

d3dDevice->SetStreamSourceFreq(0,(D3DSTREAMSOURCE_INDEXEDDATA|instanceCount));
d3dDevice->SetStreamSource(0,geometryBuffer,0,sizeof(Vertex));

d3dDevice->SetStreamSourceFreq(1,(D3DSTREAMSOURCE_INSTANCEDATA|1));
d3dDevice->SetStreamSource(1,instanceBuffer,0,sizeof(Instance));

// draw 4 instances of objectA

effect->Begin(&numPasses,0);
effect->BeginPass(0);

d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,6,0,2);

effect->EndPass();
effect->End();


// draw 4 instances of objectB - doesn't draw anything
// this will draw correctly if above DrawIndexedPrimitive() is commented out

// reseting doesn't help

// d3dDevice->SetStreamSourceFreq(0,(D3DSTREAMSOURCE_INDEXEDDATA|instanceCount));
// d3dDevice->SetStreamSource(0,geometryBuffer,0,sizeof(Vertex));

// d3dDevice->SetStreamSourceFreq(1,(D3DSTREAMSOURCE_INSTANCEDATA|1));
// d3dDevice->SetStreamSource(1,instanceBuffer,0,sizeof(Instance));

effect->Begin(&numPasses,0);
effect->BeginPass(0);

d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,3,6,1);

effect->EndPass();
effect->End();


d3dDevice->SetStreamSourceFreq(0,1);
d3dDevice->SetStreamSourceFreq(1,1);



It seems like it has to be reset to draw a new set of objects but that doesn't help (if I'm resetting it properly). I think it can be done somehow but maybe not ?

Thanks for helping.
I'm just going to throw out another suggestion, what do you have "instanceCount" set to? Try setting it to the number of triangles to draw instead of the number of objects like i thought in my last post

So:

[font="CourierNew, monospace"][color="#000000"]d3dDevice[color="#666600"]->[color="#660066"]SetStreamSourceFreq[color="#666600"]([color="#006666"]0[color="#666600"],([color="#000000"]D3DSTREAMSOURCE_INDEXEDDATA[color="#666600"]|3[color="#666600"]));[/font]
[font="CourierNew, monospace"] [/font]
[font="CourierNew, monospace"][color="#666600"]3 because 2 for the square, and 1 for the triangle[/font]
[font="CourierNew, monospace"] [/font]
[font="CourierNew, monospace"][color="#666600"]Wait... My bad, I need to look at this again[/font]
I was reading through that article since i didn't before. This instancing technique would be really useful for something such as a forest of trees.

Anyways, one thing I noticed is they only seem to make one draw call per instance group or whatever you want to call it. Try this: (Probably wrong, but worth i try i suppose)

[color="#1C2837"][color="#880000"]// the geometryBuffer has 9 vertices which define two objects (6 for objectA and 3 for objectB)
[color="#1C2837"][color="#880000"]// to keep it simple the indexBuffer also has 9 vertices, corresponding 1:1 with the geometryBuffer
[color="#1C2837"]
[color="#1C2837"] [color="#000088"]int[color="#000000"] instanceCount[color="#666600"]=[color="#006666"]4[color="#666600"];[color="#000000"]

d3dDevice[color="#666600"]->[color="#660066"]SetStreamSourceFreq[color="#666600"]([color="#006666"]0[color="#666600"],([color="#000000"]D3DSTREAMSOURCE_INDEXEDDATA[color="#666600"]|[color="#000000"]instanceCount[color="#666600"]));[color="#000000"]
d3dDevice[color="#666600"]->[color="#660066"]SetStreamSource[color="#666600"]([color="#006666"]0[color="#666600"],[color="#000000"]geometryBuffer[color="#666600"],[color="#006666"]0[color="#666600"],[color="#000088"]sizeof[color="#666600"]([color="#660066"]Vertex[color="#666600"]));[color="#000000"]

d3dDevice[color="#666600"]->[color="#660066"]SetStreamSourceFreq[color="#666600"]([color="#006666"]1[color="#666600"],([color="#000000"]D3DSTREAMSOURCE_INSTANCEDATA[color="#666600"]|[color="#006666"]1[color="#666600"]));[color="#000000"]
d3dDevice[color="#666600"]->[color="#660066"]SetStreamSource[color="#666600"]([color="#006666"]1[color="#666600"],[color="#000000"]instanceBuffer[color="#666600"],[color="#006666"]0[color="#666600"],[color="#000088"]sizeof[color="#666600"]([color="#660066"]Instance[color="#666600"])); [color="#880000"]// draw 4 instances of objectA[color="#000000"]

effect[color="#666600"]->[color="#660066"]Begin[color="#666600"](&[color="#000000"]numPasses[color="#666600"],[color="#006666"]0[color="#666600"]);[color="#000000"]
effect[color="#666600"]->[color="#660066"]BeginPass[color="#666600"]([color="#006666"]0[color="#666600"]);[color="#000000"]

d3dDevice[color="#666600"]->[color="#660066"]DrawIndexedPrimitive[color="#666600"]([color="#000000"]D3DPT_TRIANGLELIST[color="#666600"],[color="#006666"]0[color="#666600"],[color="#006666"]0[color="#666600"],[color="#006666"]6[color="#666600"],[color="#006666"]0[color="#666600"],[color="#006666"]2[color="#666600"]);[color="#000000"]

effect[color="#666600"]->[color="#660066"]EndPass[color="#666600"]();[color="#000000"]
effect[color="#666600"]->[color="#660066"]End[color="#666600"]();[color="#000000"]

[color="#000000"][color="#1C2837"]d3dDevice[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]SetStreamSourceFreq[color="#666600"][color="#1C2837"]([color="#006666"][color="#1C2837"]0[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]1[color="#666600"][color="#1C2837"]);[color="#000000"][color="#1C2837"]
d3dDevice[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]SetStreamSourceFreq[color="#666600"][color="#1C2837"]([color="#006666"][color="#1C2837"]1[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]1[color="#666600"][color="#1C2837"]);

[color="#880000"]// draw 4 instances of objectB - doesn't draw anything
[color="#1C2837"] [color="#880000"]// this will draw correctly if above DrawIndexedPrimitive() is commented out
[color="#1C2837"]
[color="#1C2837"][color="#880000"]// reseting doesn't help
[color="#1C2837"]
[color="#1C2837"] [color="#880000"]// d3dDevice->SetStreamSourceFreq(0,(D3DSTREAMSOURCE_INDEXEDDATA|instanceCount));
[color="#1C2837"] [color="#880000"]// d3dDevice->SetStreamSource(0,geometryBuffer,0,sizeof(Vertex));
[color="#1C2837"]
[color="#1C2837"] [color="#880000"]// d3dDevice->SetStreamSourceFreq(1,(D3DSTREAMSOURCE_INSTANCEDATA|1));
[color="#1C2837"] [color="#880000"]// d3dDevice->SetStreamSource(1,instanceBuffer,0,sizeof(Instance));[color="#000000"]

[color="#1C2837"][color="#000000"]d3dDevice[color="#666600"]->[color="#660066"]SetStreamSourceFreq[color="#666600"]([color="#006666"]0[color="#666600"],([color="#000000"]D3DSTREAMSOURCE_INDEXEDDATA[color="#666600"]|[color="#000000"]instanceCount[color="#666600"]));[color="#000000"]
d3dDevice[color="#666600"]->[color="#660066"]SetStreamSource[color="#666600"]([color="#006666"]0[color="#666600"],[color="#000000"]geometryBuffer[color="#666600"],[color="#006666"]0[color="#666600"],[color="#000088"]sizeof[color="#666600"]([color="#660066"]Vertex[color="#666600"]));[color="#000000"]

d3dDevice[color="#666600"]->[color="#660066"]SetStreamSourceFreq[color="#666600"]([color="#006666"]1[color="#666600"],([color="#000000"]D3DSTREAMSOURCE_INSTANCEDATA[color="#666600"]|[color="#006666"]1[color="#666600"]));[color="#000000"]
d3dDevice[color="#666600"]->[color="#660066"]SetStreamSource[color="#666600"]([color="#006666"]1[color="#666600"],[color="#000000"]instanceBuffer[color="#666600"],[color="#006666"]0[color="#666600"],[color="#000088"]sizeof[color="#666600"]([color="#660066"]Instance[color="#666600"]));
[color="#666600"]
[color="#1c2837"]effect[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]Begin[color="#666600"][color="#1C2837"](&[color="#000000"][color="#1C2837"]numPasses[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]0[color="#666600"][color="#1C2837"]);[color="#000000"][color="#1C2837"]
effect[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]BeginPass[color="#666600"][color="#1C2837"]([color="#006666"][color="#1C2837"]0[color="#666600"][color="#1C2837"]);[color="#000000"][color="#1C2837"]

d3dDevice[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]DrawIndexedPrimitive[color="#666600"][color="#1C2837"]([color="#000000"][color="#1C2837"]D3DPT_TRIANGLELIST[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]0[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]0[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]3[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]6[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]1[color="#666600"][color="#1C2837"]);[color="#000000"][color="#1C2837"]

effect[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]EndPass[color="#666600"][color="#1C2837"]();[color="#000000"][color="#1C2837"]
effect[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]End[color="#666600"][color="#1C2837"]();[color="#000000"][color="#1C2837"]


d3dDevice[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]SetStreamSourceFreq[color="#666600"][color="#1C2837"]([color="#006666"][color="#1C2837"]0[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]1[color="#666600"][color="#1C2837"]);[color="#000000"][color="#1C2837"]
d3dDevice[color="#666600"][color="#1C2837"]->[color="#660066"][color="#1C2837"]SetStreamSourceFreq[color="#666600"][color="#1C2837"]([color="#006666"][color="#1C2837"]1[color="#666600"][color="#1C2837"],[color="#006666"][color="#1C2837"]1[color="#666600"][color="#1C2837"]);
[color="#666600"]
[color="#666600"]
[color="#1c2837"](I'm so sick of trying to add code on gamedev, I can't get the formatting to work at all, spend more time playing with the code and source tags than I do trying to answer the actual problem)
Ok, guys (and girls), long post ahead. I experimented and that's what I got. First my setup - I don't duplicate vertices, because, well, that's what indexing is all about ;-)

Vertex buffer:

0: {X:-0.5, Y:-0.5} quad vertices
1: {X:-0.5, Y: 0.5}
2: {X: 0.5, Y: 0.5}
3: {X: 0.5, Y:-0.5}
4: {X: 0 , Y: 0.7} "the" triangle vertices
5: {X: 0.7, Y:-0.7}
6: {X:-0.7, Y:-0.7}


Index Buffer:

0: 0 quads 1st triangle
1: 1
2: 2
3: 2 quads 2nd triangle
4: 3
5: 0
6: 4 "the" triangles triangle
7: 5
8: 6


Position offsets and color come from the instance stream, but I will concentrate on the geometry now:

First, drawing all geometry. Not your intention, just to check if the overall setup is fine.
Ignore that this is C#/SlimDX code, the parameters are the same:

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 7, 0, 3);

4f1f4b152086146.gif

Drawing just the quads:

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2); // (A)

8a3bd7152086148.gif

Drawing just the triangles:

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 4, 3, 6, 1); // (B)

4492cf152086151.gif

To elaborate:
In (A) we don't need more than the first four vertices, NumVertices = 4 instead of 7.
In (B) we select a subset for "the" triangle, namely vertcies 4,5,6 (so MinIndex = 4 and NumVertices = 3), the StartIndex is now 6.

An alternative I've found is this:

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 4, 0, 3, 6, 1);


BUT then the indices for "the" triangle have to be

...
6: 0
7: 1
8: 2

... since a BaseVertexIndex of 4 offsets them by 4.

You can also select all vertices:

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 7, 6, 1);


On my system, I can even do a:

device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 0, 6, 1);


I don't even get a fail. The driver obviously ignores the vertex range altogether. But neither do PIX (index out of range) nor the reference device (nothing gets drawn). Also the debug runtimes should complain if fully enabled.

("Should", because I currently have yet to discover why they sometimes don't. I just recently switched to Win7 and VS 2010 Express. Sometimes they show up in the VS output, sometimes in DebugView (using C#/SlimDX), but mostly they don't, even if I start my app NOT from VS. As said, dunno why yet, so PIX is my reference here).

Conclusion: It is possible - and I would have been surprised if it wasn't: as if we were the first to get instanced subsets on a 10-year-old API ;-)

For why you get your behavior on the other hand I can still only guess: I don't see you check the result codes, so maybe the device is in some erroneous state after the first (failing) call which lets your second call fail. Do check them, enable full debug runtimes, run it through PIX, use the reference device... and then get the numbers right ;-)

And, yeah, you don't need to reset the streams or the frequencies (I didn't in my setup).

You could try - if you haven't already - this:


d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,9,6,1); // full range


or


d3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,6,3,6,1); // subrange


if I got your numbers right. If you still can't figure out on your own, attach a minimal project and I'll take a look.

@iedoc: Using the "rich" editor ? Oh yeah, it's really bad. I disabled it pretty soon (can be done in your profile settings) and used tags again.

This topic is closed to new replies.

Advertisement