Why doesn't Unity provide a scissor test?

Why doesn’t Unity make scissor tests available?

I would like to do something like this:

GL.SetScissorRect(myScissorRect);
Graphics.DrawMeshNow(...);

I’ve tried the following code in a native plugin, in place of “GL.SetScissorRect” above:

glEnable(GL_SCISSOR_TEST);
glScissor(x, y, w, h);

But the above code had no effect.

My guess is that DrawMeshNow() either dispatches the work to another thread and blocks until done, or that DrawMeshNow() disables the scissor test. If anyone could shed some light on this situation, it would be appreciated.

I’m running on Windows 10, but have set the graphics driver to OpenGL Core.

Thanks

Unity - Scripting API: Camera.rect ?

Camera.rect controls the viewport, not clipping. It re-maps the edges of the screen to that rect, it doesn’t clip them off.

If you’re trying to suggest the method where you combine changing the viewport with a smaller orthographic projection matrix, then that solution is total overkill for such a simple problem, and it only works for 2D cameras. It also complicates things when you actually need the viewport rect for rendering to a specific part of the screen.

I know for a fact that scissor rects are trivial to implement in every version of OpenGL and D3D currently supported by Unity - which is why I was going to just implement it in a native plugin myself, except that Unity is doing something unexpected which stops it from working.

Actually I forgot that’s how camera.rect worked.

I suspect Unity is using scissor rects for the lighting system as it limits additive light rendering to square screen space bounds which sound a lot like what those are for. However that doesn’t explain it causing issues for DrawMeshNow since it acts as an unlit pass.

Looking into this further, it seems that glScissor() isn’t working because rendering happens on another thread.

Debug.Log(SystemInfo.graphicsMultiThreaded); // true

So aside from calling *glMakeCurrent(…) - which I’m almost 100% sure will interfere with Unity’s rendering - I have to find a way to call glScissor() on the rendering thread instead. To that end, GL.IssuePluginEvent() looks promising, except that the documentation doesn’t specify how it will interact with Graphics.DrawMeshNow() - are changes to the render-state made from a call to IssuePluginEvent() guaranteed to affect the rendering done by DrawMeshNow()? Based on the following response from Aras, it seems so:

“When multi-threaded rendering is used, then it’s similar to any other regular draw call – the information is pushed into the “rendering command buffer”, which the rendering thread picks up and does the actual work once it gets there.”

My guess is that IssuePluginEvent() and DrawMeshNow() will queue commands to the same buffer, and be ordered sequentially. The presence of IssuePluginEvent() in the CommandBuffer API also seems to suggest this. Confirmation would be appreciated though.

Thanks

I just tried using IssuePluginEvent() to call glEnable/glScissor right before Graphics.DrawMeshNow(), but the result was an access violation when Unity tried to actually draw the mesh. If I’m reading the crash dump correctly, the access violation seems to be coming from this:

GfxDeviceGLES::smile:rawBuffers(class GfxBuffer *,struct VertexStreamSource const *,int,struct DrawBuffersRange const *,int,class VertexDeclaration *,class ChannelAssigns const &),

The calls to glEnable/glScissor complete without crashing, and I don’t see how calls to those functions could cause an access violation =/

Edit1:
Simply calling GL.InvalidateState() causes Unity to crash as well.

Edit2:
Calling GL.InvalidateState() causes unity to freeze on MacOS.(IssuePluginEvent has the same behavior).

So at this point, GL.InvalidateState() appears to be the problem(or maybe where I’m calling it?)

Calling GL.InvalidateState() causes Unity to crash on windows, and freeze on MacOS. Using GL.IssuePluginEvent() to call an empty function causes the same behavior on both OSes. So does Unity implicitly call GL.InvalidateState() at some point after I’ve issued a plugin event? What else could cause InvalidateState to crash? Because the function simply being broken on all platforms without anyone noticing seems unlikely.

I’m calling these functions in OnPostRender().

Edit3:

Switching out my rendering code(GL.* and DrawMeshNow) for the following doesn’t crash, but doesn’t apply the scissor rect either. glGetError() says everything is fine.

CommandBuffer cb = new CommandBuffer();
cb.IssuePluginEvent(NativePlugin.GetRenderEventFunc(), 1); // enable scissor test and set rect
cb.DrawMesh(mesh, mtx, material, 0, 0);
cb.IssuePluginEvent(NativePlugin.GetRenderEventFunc(), 0); // disable scissor test
Graphics.ExecuteCommandBuffer(cb);

Well, I’ve given up on this for now.

@bgolus
I suppose it could be the lighting that’s interfering here. Everything I’m rendering with Graphics.DrawMeshNow() uses custom unlit shaders, but I suppose Graphics.DrawMeshNow() may assume it could be used between lit passes, and reset the scissor rect.

Scissor rectangles definitely should be made available though. On top of the reasons already stated above, Unity’s UI system forces you to use mask-textures to clip UI element contents(last I checked), which is a lot more expensive than simply applying a scissor rectangle.

The only other (ham-fisted) method I can think of would be to use a mutli-pass stencil approach; render a quad (or really any mesh) to write to the stencil, draw your element to reject any area not written to, then possibly render the stencil quad again to clear it if necessary. I suspect the Camera.rect and projection method might be faster, though certainly a lot more complicated.

As for your experience with native plugins, I unfortunately can’t give any insight there as I don’t have any experience using them, and certainly don’t have any knowledge of Unity’s internal code.

Your best option might to be use the feedback site to request the feature, but it’ll probably be years before they respond to it, if ever, unless you’re an enterprise subscriber in which case you wouldn’t need to use that site anyway.
https://feedback.unity3d.com/

Speed is important for me. I’m using a custom UI system. It supports controls clipping children, which is used quite a lot(list views, text for buttons, etc…). The camera.rect/ortho matrix is certainly faster than using a stencil buffer, most likely by several times.

I’ve done quite a bit of native multi-platform development myself, so I have some sympathy for the workload the Unity team must have to deal with, but IMO, there should be a fast-track for things that are the right combination of useful and *easy…*like access to scissor rectangles :wink: