There's two separate parts to this question. First is how to record screenshots without hitting your framerate too much, and second is how to capture screenshots at all in the new render pipelines. The first part is way easier, so let's get that out of the way first.
Capturing screenshots/GIFs during gameplay
You said you wanted these screenshots for bug testing, and that it would be even better to have video. Luckily, there's already a plugin for exactly this! I found Moments Recorder and have used it in my own project already and the quality is great. Key features:
- Runs a constant buffer so hitting a button exports the last x seconds, which is ideal for catching hard-to-reproduce bugs
- Encoding runs in a separate thread, so it shouldn't affect your framerate much
- Customizable resolution/quality, so you can reduce resolution or thread priority if it's slowing things down
- Well-commented source code makes it easy to modify
My project was very poorly coded at the time and barely made 15fps, with randomized colors that are terrible for GIF encoding, but I saw no major additional slowdown when exporting a GIF. I should note the comments in the code for GIF encoding do say it could tank the framerate for high resolution, but if you find that's the case, then it gives plenty of options to play with to speed things up. Otherwise you can try to modify it to just export jpgs.
Here's an example of it in my editor:

The moments recorder uses System.Threading, but you could also look into the Unity Jobs system for multithreading your own solution, which could potentially be safer/faster/easier. I haven't tried it, and it's still in preview, so it may still be slower. Either way, it's a good example of how to separate out the capturing/saving code if you write your own solution.
Also, if you want to record video clips while in the editor, Unity has an official Asset Store plugin for that.
Capturing screenshots in LWRP
This is a huge pain in the ass and I've been looking through solutions for a while. I'm still learning this stuff so I may have missed an obvious or easier solution. Also, all my testing has been very haphazard so I may be mixing up solutions and results, and I've crashed Unity a few times. Follow at your own risk.
Option 1
You said ScreenCapture.CaptureScreenshot()
was taking 200ms, but have you tried CaptureScreenshotAsTexture()
? It seems like that should be a lot faster and you could split off the file conversion/writing into a separate thread. I tried testing this both by modifying the Moments Recorder.cs and in a separate simple script, but I'm not getting any screenshot output at all and no errors, so I'm not sure what's going on there. If that method is working for you, that could be the fastest/easiest solution.
Option 2:
This thread shows a way to capture an image from a camera in the new render pipelines, but the script linked doesn't show where/how it's called. Another post in the replies uses RenderPipeline.beginCameraRendering
, so I modified the Moments recorder to use this callback and replaced the OnRenderImage
method, but calling it at the beginning of camera rendering gives inconsistent/unhelpful results. It looks like it's moved out of experimental and a endCameraRendering
event is added in 2019.1, but I don't have time to upgrade and test it. That might be the ideal solution. To do that with Moments, change the OnRenderImage() method in Recorder.cs to the following:
void CaptureCamera(Camera _camera)
{
if (State != RecorderState.Recording)
{
return;
}
m_Time += Time.unscaledDeltaTime;
if (m_Time >= m_TimePerFrame)
{
// Limit the amount of frames stored in memory
if (m_Frames.Count >= m_MaxFrameCount)
m_RecycledRenderTexture = m_Frames.Dequeue();
m_Time -= m_TimePerFrame;
// Frame data
RenderTexture rt = m_RecycledRenderTexture;
m_RecycledRenderTexture = null;
if (rt == null)
{
rt = new RenderTexture(m_Width, m_Height, 0, RenderTextureFormat.ARGB32);
rt.wrapMode = TextureWrapMode.Clamp;
rt.filterMode = FilterMode.Bilinear;
rt.anisoLevel = 0;
}
_camera.targetTexture = rt;
_camera.Render();
_camera.targetTexture = null;
m_Frames.Enqueue(rt);
}
}
Then add this in Init()
cam = GetComponent<Camera>(); //don't forget to create the cam var somewhere
RenderPipeline.EndCameraRendering(GetComponent<Camera>());
RenderPipeline.endCameraRendering += CaptureCamera;
Option 3: works in 2018.3, easy but weird results
If you can't upgrade to 2019.x then this might work. I got this version working, but the resulting gif is washed out for some reason. Possibly shaders aren't being applied, not sure.
Modifying Recorder.cs in Moments, rename OnRenderImage
to Update()
or LateUpdate()
and get rid of all the Graphics.Blit
calls. Then at the end just throw this in:
Graphics.Blit(ScreenCapture.CaptureScreenshotAsTexture(), rt);
m_Frames.Enqueue(rt);