MRT, some primitives draw to one target and not the other

Hi, this is my first post, but I’ve found many an answer here over the years as other users have asked questions I already had.

This is a mostly theoretical question, as there are a variety of obstacles to me giving a thorough account of what I’m currently working on (application complexity, the code being work-related, etc), so I’m more looking for education and a hint at direction.

  • I’m drawing a large number of quads (as pairs of triangles) in one draw call to multiple render targets. It is intended that they write useful information to the 1st and 5th targets. It doesn’t matter that they’ll also happen to write information to targets 2-4.
  • All of these quads draw successfully to the 5th render target (whether this is true for targets 2-4 is less obvious, but not important in my application), but many of them don’t draw to the 1st render target.
  • My outputs are named out_0, out_1, out_2, out_3, out_4, and for debugging purposes I’ve tried out_0 = vec4(1.0) as well as out_0 = out_4, and this has no effect on which quads fail to draw to the out_0 render target, even though they all successfully draw to the out_4 render target.
  • At no point does the application set up different stenciling or blending rules for different render targets (glEnablei and glDisablei appear nowhere in the application).
  • At no point does “discard” appear in the fragment shader.

So the theoretical question, then, is:
In one draw call with multiple render targets, under what conditions can all triangles draw successfully to one render target, but only some triangles draw successfully to another render target?

Thanks so much for your time,
Jibb

EDIT:
I’m targeting Windows, OpenGL 4.5, testing on an NVIDIA GeForce GTX 960M

Does the issue occur if the fragment shader is trivial (i.e. writes constant values to each output and does nothing else)? If that works, then it looks like something is producing undefined behaviour, for which there are too many possible reasons to list.

Does calling glMemoryBarrier(GL_ALL_BARRIER_BITS) after the draw command have any effect?

In one draw call with multiple render targets, under what conditions can all triangles draw successfully to one render target, but only some triangles draw successfully to another render target?

Under no conditions.

All of the buffers specified by glDrawBuffers will receive data from any fragments which are not discarded. Period. And since discarding prevents writing to any of the buffers, writes are all or nothing.

[QUOTE=GClements;1293322]Does the issue occur if the fragment shader is trivial (i.e. writes constant values to each output and does nothing else)? If that works, then it looks like something is producing undefined behaviour, for which there are too many possible reasons to list.

Does calling glMemoryBarrier(GL_ALL_BARRIER_BITS) after the draw command have any effect?[/QUOTE]
Hi GClements, the shader is indeed trivial, as you described. I’ve tried scattering glMemoryBarrier(GL_ALL_BARRIER_BITS) about, including after the draw command, to no avail.

[QUOTE=Alfonse Reinheart;1293326]Under no conditions.

All of the buffers specified by glDrawBuffers will receive data from any fragments which are not discarded. Period. And since discarding prevents writing to any of the buffers, writes are all or nothing.[/QUOTE]
Hi Alfonse Reinheart, I thought as much. Thanks for confirming.

I’ll look elsewhere for where I may have broken things.

After some more work, the theoretical problem has changed a little.

The vertex shader has some logic that decides whether or not a quad should be visible and, if not, collapses itself so it won’t be drawn. It does this based on information in a Shader Storage Buffer (the Draw call is immediately preceded by a glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT)). If I remove the collapsing logic, the quads all draw to both render targets. If I have a colour passed from the vertex shader to the fragment shader, white normally, black if the vertex would’ve been collapsed, I again see that the first target draws a bunch of quads black that I would normally expect to draw white (corresponding with the quads that were incorrectly absent, previously), and the other render target displays the quads white as expected.

This behaviour looks an awful lot like the vertex shader is being run multiple times for different render targets, and that it’s getting different results for each one.

So it seems the problem isn’t, “Some triangles don’t draw to all render targets”, but “Some triangles behave differently for different render targets.”

I think ultimately this doesn’t change the answer. It’s still, “There’s no way this should happen, there must be something else causing undefined behaviour,” right? I mean, no matter what weird stuff I’m doing, I shouldn’t have separate fragment shader invocations for the same fragment for each render target. Even if the GPU for some reason chose to do that behind the scenes, the result should be the same as if the vertex shader were invoked once for each vertex, and the fragment shader once for each fragment (regardless of how many render targets), right?

Thanks again for your time.

Right. It sounds like the driver is trying to overcome hardware limits by using multiple passes but isn’t getting the same inputs each time. Does the shader program do anything which would interfere with such an approach?

[QUOTE=JibbSmart;1293320]So the theoretical question, then, is:
In one draw call with multiple render targets, under what conditions can all triangles draw successfully to one render target, but only some triangles draw successfully to another render target?

[/QUOTE]

I remember being here before. Potentially useful: selectively write to one target with multi-render tagets.

To the above, yes, without layered rendering it’s rasterized to all of them, but you may be able to use blending to make it visible only on a subset of the buffers.

[QUOTE=Dark Photon;1293340]I remember being here before. Potentially useful: selectively write to one target with multi-render tagets.

To the above, yes, without layered rendering it’s rasterized to all of them, but you may be able to use blending to make it visible only on a subset of the buffers.[/QUOTE]Hi Dark Photon, thanks for the answer. That does answer the theoretical question I posed, and when we need to skip rendering to some targets for some primitives within a draw call, blending with a 0 alpha is the solution we use, too :slight_smile:

It doesn’t, but… well, I solved the problem. In years of OpenGL programming, I know that it’s always my fault if something I write doesn’t work. I’d convinced myself this was the exception – that it was a driver bug or something. But it wasn’t.

Basically, we have a variety of applications using the same engine we’ve written over the years. I had this entity working in one application, needed it in another, and then encountered this weird behaviour that prompted my original post. What I’d forgotten is that this other application intentionally draws to one target in one pass, and then the others in another pass, in a way that the entities don’t normally have to think about. It’s because of this that I was encountering different results in one render target than the others – I thought they were all drawn to together, but they were actually drawn to in separate passes.

Thanks again for your time. Sorry it was such a waste, and turned out to be far less interesting than originally thought.