diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp
index c2341e7de37abd2fd03195b519666dbab5f6eda8..72cdfe5704b27bc9444f7173700bceeea571b6e0 100644
--- a/applications/reconstruct/src/main.cpp
+++ b/applications/reconstruct/src/main.cpp
@@ -14,6 +14,7 @@
 #include <ftl/rgbd/streamer.hpp>
 #include <ftl/slave.hpp>
 #include <ftl/rgbd/group.hpp>
+#include <ftl/threads.hpp>
 
 #include "ilw.hpp"
 #include <ftl/render/splat_render.hpp>
@@ -138,6 +139,8 @@ static void run(ftl::Configurable *root) {
 			// TODO: Release frameset here...
 			//cudaSafeCall(cudaStreamSynchronize(scene->getIntegrationStream()));
 
+			UNIQUE_LOCK(scene_A.mtx, lk);
+
 			// Send all frames to GPU, block until done?
 			scene_A.upload(Channel::Colour + Channel::Depth);  // TODO: (Nick) Add scene stream.
 			align->process(scene_A);
diff --git a/components/renderers/cpp/src/splat_render.cpp b/components/renderers/cpp/src/splat_render.cpp
index 9d84f2f821e52d05ba158f5f055f2b5b641737f9..9bae08e43687ab8f1a2a05d75e718fd0f406e117 100644
--- a/components/renderers/cpp/src/splat_render.cpp
+++ b/components/renderers/cpp/src/splat_render.cpp
@@ -20,9 +20,10 @@ Splatter::~Splatter() {
 }
 
 bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cudaStream_t stream) {
+	SHARED_LOCK(scene_->mtx, lk);
 	if (!src->isReady()) return false;
 
-	LOG(INFO) << "Render ready2";
+	if (scene_->frames.size() > 0) LOG(INFO) << "Render ready2 " << (unsigned int)scene_->frames[0].getChannels();
 
 	const auto &camera = src->parameters();
 
@@ -82,16 +83,18 @@ bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cuda
 	out.get<GpuMat>(Channel::Depth).setTo(cv::Scalar(1000.0f), cvstream);
 	out.get<GpuMat>(Channel::Colour).setTo(cv::Scalar(0,0,0), cvstream);
 
-	LOG(INFO) << "Render ready";
+	LOG(INFO) << "Render ready: " << camera.width << "," << camera.height;
 
 	// Render each camera into virtual view
 	for (size_t i=0; i<scene_->frames.size(); ++i) {
 		auto &f = scene_->frames[i];
 		auto *s = scene_->sources[i];
 
-		if (!f.hasChannel(Channel::Depth) || f.isCPU(Channel::Depth)) {
-			LOG(ERROR) << "Missing required Depth channel";
-			return false;
+		if (f.empty(Channel::Depth + Channel::Colour)) {
+			LOG(ERROR) << "Missing required channel";
+			continue;
+		} else {
+			LOG(INFO) << "Channels found";
 		}
 
 		// Needs to create points channel first?
@@ -123,6 +126,7 @@ bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cuda
 		//ftl::cuda::mls_render_depth(depth1_, depth3_, params, scene_->cameraCount(), stream);
 
 		if (src->getChannel() == Channel::Depth) {
+			LOG(INFO) << "Rendering depth";
 			//ftl::cuda::int_to_float(depth1_, depth2_, 1.0f / 1000.0f, stream);
 			if (value("splatting",  false)) {
 				//ftl::cuda::splat_points(depth1_, colour1_, normal1_, depth2_, colour2_, params, stream);
@@ -149,6 +153,7 @@ bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cuda
 			//	src->writeFrames(colour1_, depth2_, stream);
 			//}
 		} else if (src->getChannel() == Channel::Right) {
+			LOG(INFO) << "Rendering right";
 			// Adjust pose to right eye position
 			Eigen::Affine3f transform(Eigen::Translation3f(camera.baseline,0.0f,0.0f));
 			Eigen::Matrix4f matrix =  src->getPose().cast<float>() * transform.matrix();
@@ -160,13 +165,14 @@ bool Splatter::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, cuda
 			//src->writeFrames(ts, colour1_, colour2_, stream);
 			//src->write(scene_.timestamp, output_, stream);
 		} else {
+			LOG(INFO) << "No second rendering";
 			if (value("splatting",  false)) {
 				//ftl::cuda::splat_points(depth1_, colour1_, normal1_, depth2_, colour2_, params, stream);
 				//src->writeFrames(ts, colour1_, depth2_, stream);
 				//src->write(scene_.timestamp, out, stream);
 			} else {
 				//ftl::cuda::int_to_float(depth1_, depth2_, 1.0f / 1000.0f, stream);
-				temp_.get<GpuMat>(Channel::Depth).convertTo(out.get<GpuMat>(Channel::Depth), CV_32F, 1.0f / 1000.0f, cvstream);
+				//temp_.get<GpuMat>(Channel::Depth).convertTo(out.get<GpuMat>(Channel::Depth), CV_32F, 1.0f / 1000.0f, cvstream);
 				//src->writeFrames(ts, colour1_, depth2_, stream);
 				//src->write(scene_.timestamp, output_, stream);
 			}
diff --git a/components/rgbd-sources/include/ftl/rgbd/frame.hpp b/components/rgbd-sources/include/ftl/rgbd/frame.hpp
index 89523fc9bb1d09f1ed3ca003f234ffb5df2534ec..26da81422395c9b09cc8ab7ad8f38d1b9818663c 100644
--- a/components/rgbd-sources/include/ftl/rgbd/frame.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/frame.hpp
@@ -88,6 +88,13 @@ public:
 	 */
 	void reset();
 
+	bool empty(ftl::rgbd::Channels c);
+
+	inline bool empty(ftl::rgbd::Channel c) {
+		auto &m = _get(c);
+		return !hasChannel(c) || (m.host.empty() && m.gpu.empty());
+	}
+
 	/**
 	 * Is there valid data in channel (either host or gpu).
 	 */
@@ -95,6 +102,8 @@ public:
 		return channels_.has(channel);
 	}
 
+	inline ftl::rgbd::Channels getChannels() const { return channels_; }
+
 	/**
 	 * Is the channel data currently located on GPU. This also returns false if
 	 * the channel does not exist.
diff --git a/components/rgbd-sources/src/frame.cpp b/components/rgbd-sources/src/frame.cpp
index ea207404b7213d147062bee328e5a48b3be24cff..5a10e1ff125c58ed6645d58fe231825cdcb9e1a8 100644
--- a/components/rgbd-sources/src/frame.cpp
+++ b/components/rgbd-sources/src/frame.cpp
@@ -39,24 +39,36 @@ void Frame::upload(Channels c, cv::cuda::Stream stream) {
 	}
 }
 
+bool Frame::empty(ftl::rgbd::Channels channels) {
+	for (auto c : channels) {
+		LOG(INFO) << "Check empty for " << (int)c;
+		if (empty(c)) return true;
+	}
+	return false;
+}
+
 void Frame::swapTo(ftl::rgbd::Channels channels, Frame &f) {
+	f.reset();
+
 	// For all channels in this frame object
 	for (auto c : channels_) {
 		// Should we swap this channel?
 		if (channels.has(c)) {
 			// Does 'f' have this channel?
-			if (!f.hasChannel(c)) {
+			//if (!f.hasChannel(c)) {
 				// No, so create it first
 				// FIXME: Allocate the memory as well?
 				if (isCPU(c)) f.create<cv::Mat>(c);
 				else f.create<cv::cuda::GpuMat>(c);
-			}
+			//}
 
 			auto &m1 = _get(c);
 			auto &m2 = f._get(c);
 
 			cv::swap(m1.host, m2.host);
 			cv::cuda::swap(m1.gpu, m2.gpu);
+
+			LOG(INFO) << "Swapping channel: " << static_cast<int>(c);
 		}
 	}
 }
diff --git a/components/rgbd-sources/src/virtual.cpp b/components/rgbd-sources/src/virtual.cpp
index c38a3da528b532fd10b105acc1f696da24419228..54c9754b949f3bb7316136516c6dd7f3678fa875 100644
--- a/components/rgbd-sources/src/virtual.cpp
+++ b/components/rgbd-sources/src/virtual.cpp
@@ -6,8 +6,8 @@ using ftl::rgbd::Channel;
 
 class VirtualImpl : public ftl::rgbd::detail::Source {
 	public:
-	explicit VirtualImpl(ftl::rgbd::Source *host) : ftl::rgbd::detail::Source(host) {
-
+	explicit VirtualImpl(ftl::rgbd::Source *host, const ftl::rgbd::Camera &params) : ftl::rgbd::detail::Source(host) {
+		params_ = params;
 	}
 
 	~VirtualImpl() {
@@ -52,7 +52,8 @@ class VirtualImpl : public ftl::rgbd::detail::Source {
 };
 
 VirtualSource::VirtualSource(ftl::config::json_t &cfg) : Source(cfg) {
-	impl_ = new VirtualImpl(this);
+	auto params = params_;
+	impl_ = new VirtualImpl(this, params);
 }
 
 VirtualSource::~VirtualSource() {