Ogre-Next StereoRendering Issues

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Hi @Crystal Hammer,

ah that is good information. I already thought, I'm insane :)
Nice that, we have struggle on similiar topics. Maybe this will help @dark_sylinc in fixing some issues or assist further, what can be done.

I found out, that not only its because of using screen space reflection, but also using post processing compositors at all, because they are using also the Ogre/Copy/4xFP32 shaders.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Hi @dark_sylinc ,

just a ping, so that this important topic will not be forgotten.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5446
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1348

Re: Ogre-Next StereoRendering Issues

Post by dark_sylinc »

Lax wrote: Sun Jul 21, 2024 8:22 pm

What I do not understand:

  • There is no Pass Clear! Why?, just scene, shadow nodes, quad

The answer is quite simple: because PassClear never calls setRenderPassDescToCurrent.
You'll have to add a logging to CompositorPassClear::execute.

Lax wrote: Sun Jul 21, 2024 8:22 pm

The viewport sizes, seems to be correct (do not know if also for shadow nodes, as there are created not by code but in a compositor file separately

You have the following two that raise a red flag:

Code: Select all

21:15:16: Pass: NOWA_Pbs_Render_Scene_Pass_Scene_0 has viewport count: 1 size: 0 0 0.5 1
21:15:16: Pass: NOWA_Final_rtN_Pass_Quad_1 has viewport count: 1 size: 0.5 0 0.5 1

Assuming that these two passes render to the texture: If NOWA_Final_rtN_Pass_Quad_1 uses either clear or dont_care (either at load or store), it's going to overwrite what NOWA_Pbs_Render_Scene_Pass_Scene_0 already wrote, because load/store actions unfortunately ignore viewport bounds.

The same goes for the following two:

Code: Select all

21:15:16: Pass: NOWA_Pbs_Render_Scene_Pass_Scene_0 has viewport count: 1 size: 0 0 0.5 1
21:15:16: Pass: NOWA_Pbs_Render_Scene_Pass_Scene_1 has viewport count: 1 size: 0.5 0 0.5 1
Lax wrote: Sun Jul 21, 2024 8:22 pm
  • The mousepointer cannot be moved to the left side

Ummm... I don't know about that. If the cursor can't be moved it may be a bug in your code (i.e. allowed width is half of what it should be) or a rendering issue (the cursor is being overwritten, likely by wrong dont_care/clear usage).

I need that pass, because I'm using screen space reflections:

dont_care will overwrite the depth buffer too (you can specify to only the colour target to have dont_care).
Becareful that if you later still need the depth buffer, then dont_care will overwrite it.

I can't remember the specifics of the SSR pass (since it's quite a complex one) but it might be possible to just not attach a depth buffer into it (i.e. using RTVs or depth_pool 0).

I'm almost sure I have the same issue too now.
E.g. like so, for 3 way split: download/file.php?id=7373
But only since I added refractions, split rendering and started using load/store too.
(mentioned here viewtopic.php?p=556432#p556432)

Yeah it looks like the same issue. But it looks like simply you can't use load/dont care during split screen that way.

Strong Suggestion for everyone: Your use cases are getting complex. Instead of doing split screen directly on the final target, just render every player into its own TextureGpu.
Then use a custom pixel shader that composites all players into the final target and then apply shared UI on top.

User avatar
Crystal Hammer
Orc
Posts: 402
Joined: Sat Jun 23, 2007 5:16 pm
x 112

Re: Ogre-Next StereoRendering Issues

Post by Crystal Hammer »

Ah. Sounds like a lot of work, in my case. Surely for later, I'll mark refractions as not supported in splitscreen, as it works okay the old way for me.
And I got like a ton of questions again. Pity there is no sample.
So if we have 2 viewport vertical split like above, then we need to create 2 textures for render targets? What size, if screen is say 1920x1200, do I create two 960x1200? It's not power of two, is that okay for GPU?
This way needs creating compositor from cpp only am I right?
Will there be any issues putting them into screen together, or will I get issues with blurring or that half or 1 pixel offset etc? That combining shader will have floats for offset,size I assume.

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5446
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1348

Re: Ogre-Next StereoRendering Issues

Post by dark_sylinc »

Crystal Hammer wrote: Tue Sep 10, 2024 7:55 pm

Ah. Sounds like a lot of work, in my case. Surely for later, I'll mark refractions as not supported in splitscreen, as it works okay the old way for me.
And I got like a ton of questions again. Pity there is no sample.

You're not THAT crazy. Originally Refractions was made to reuse the depth buffer in read only mode. This is optimal in terms of performance (less copying around).

But it forces some inflexibilities on compositor setup, as your game shows. Complexities that could be solved if OgreNext showed how to do the same but with a duplicated depth buffer (at the cost of some perf of course).

Crystal Hammer wrote: Tue Sep 10, 2024 7:55 pm

So if we have 2 viewport vertical split like above, then we need to create 2 textures for render targets? What size, if screen is say 1920x1200, do I create two 960x1200? It's not power of two, is that okay for GPU?

1920x1200 isn't power of 2 either :lol:

Some GPUs may waste some VRAM in padding, but that's all.

Ultimately you always end up making sacrifices:

  1. The idea of dont_care and clear as LoadAction are meant to reduce BW but with split screen you need to dont_care/clear regions selectively, which isn't well supported by APIs and GPUs.

    • If you're curious, the reason is that behind the scenes dont_care/clear works in "blocks" (aka tiles) and there is no guarantee your subregion will coincide with the block size. And if it doesn't, the GPU would have to do some overhead.

  2. If you use multiple TextureGpus, you can make efficient use of dont_care/clear BUT you'll need an extra pass to stich everything together, which will result... in extra BW consumption.

So basically whatever you do you end up in an non-optimal path, so at that point why not make it easy for you instead of worrying about perf?

Crystal Hammer wrote: Tue Sep 10, 2024 7:55 pm

This way needs creating compositor from cpp only am I right?

I dunno, creativity is key.
Personally I mix C++ and compositor scripts as much as possible, as to have as little as C++ as possible.

Crystal Hammer wrote: Tue Sep 10, 2024 7:55 pm

Will there be any issues putting them into screen together, or will I get issues with blurring or that half or 1 pixel offset etc? That combining shader will have floats for offset,size I assume.

This was more common with D3D9 where you had to apply a 0.5 / resolution offset; otherwise you ended up sampling in the middle of a pixel, causing blurring. But not so much in D3D11/VK/GL.

However if you're that worried, use OGRE_Load2D instead of OGRE_Sample, which uses integers; and RenderDoc pixel shader debugging to double check.

User avatar
Crystal Hammer
Orc
Posts: 402
Joined: Sat Jun 23, 2007 5:16 pm
x 112

Re: Ogre-Next StereoRendering Issues

Post by Crystal Hammer »

Okay, thanks for clarifications. I understand more now.
I think I will code it all from cpp and drop .compositor then (or leave it for simpler, editor RTTs maybe). I didn't like having constants in .compositor, while I have them anyway i .h file, used by cpp.

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Thanks for taking an eye into the issue.

You have the following two that raise a red flag:

21:15:16: Pass: NOWA_Pbs_Render_Scene_Pass_Scene_0 has viewport count: 1 size: 0 0 0.5 1
21:15:16: Pass: NOWA_Final_rtN_Pass_Quad_1 has viewport count: 1 size: 0.5 0 0.5 1
Assuming that these two passes render to the texture: If NOWA_Final_rtN_Pass_Quad_1 uses either clear or dont_care (either at load or store), it's going to overwrite what NOWA_Pbs_Render_Scene_Pass_Scene_0 already wrote, because load/store actions unfortunately ignore viewport bounds.

The same goes for the following two:

21:15:16: Pass: NOWA_Pbs_Render_Scene_Pass_Scene_0 has viewport count: 1 size: 0 0 0.5 1
21:15:16: Pass: NOWA_Pbs_Render_Scene_Pass_Scene_1 has viewport count: 1 size: 0.5 0 0.5 1

For me that seems correct, as:

    • NOWA_Pbs_Render_Scene_Pass_Scene_0 -> 0 -> first viewport
    • NOWA_Final_rtN_Pass_Quad_1 -> 1 -> second viewport

Same here:

    • NOWA_Pbs_Render_Scene_Pass_Scene_0 -> 0 -> first viewport
    • NOWA_Pbs_Render_Scene_Pass_Scene_1 -> 1 -> second viewport

If I see this log:

Code: Select all

21:15:16: Pass: Shadow Node SCENE 7 has viewport count: 1 size: 0 0 1 0.285714
21:15:16: Pass: Shadow Node SCENE 8 has viewport count: 1 size: 0 0.285714 0.5 0.142857
21:15:16: Pass: Shadow Node SCENE 9 has viewport count: 1 size: 0.5 0.285714 0.5 0.142857
21:15:16: Pass: NOWA_Pbs_Render_Scene_Pass_Scene_0 has viewport count: 1 size: 0 0 0.5 1
21:15:16: Pass: NOWA_Final_rtN_Pass_Quad_0 has viewport count: 1 size: 0 0 0.5 1
21:15:16: Pass: Shadow Node SCENE 7 has viewport count: 1 size: 0 0 1 0.285714
21:15:16: Pass: Shadow Node SCENE 8 has viewport count: 1 size: 0 0.285714 0.5 0.142857
21:15:16: Pass: Shadow Node SCENE 9 has viewport count: 1 size: 0.5 0.285714 0.5 0.142857
21:15:16: Pass: NOWA_Pbs_Render_Scene_Pass_Scene_1 has viewport count: 1 size: 0.5 0 0.5 1
21:15:16: Pass: NOWA_Final_rtN_Pass_Quad_1 has viewport count: 1 size: 0.5 0 0.5 1
21:15:16: Pass: NOWA_Final_Render_Overlay_Pass_Scene_1 has viewport count: 1 size: 0 0 1 1

First every pass group is rendered for the first viewport "0 0 0.5 1"
Then every pass group is rendered for the second viewport "0.5 0 0.5 1"

Or I'm I totally wrong here?

And why is always the viewport count 1?

This is the log message I added to CompositorPass::setRenderPassDescToCurrent for debug purposes:

Code: Select all

    LogManager::getSingleton().logMessage(
LML_CRITICAL, "Pass: " + mDefinition->mProfilingId + " has viewport count: " +
                  Ogre::StringConverter::toString( numViewports ) +
                  " size: " +
                  Ogre::StringConverter::toString( vpSize[i] ) );

I also again checked, that all passes for splitscreen use this configuration:

Code: Select all

if (false == this->useSplitScreen)
{
				passScene->setAllLoadActions(Ogre::LoadAction::Clear);
				passScene->mStoreActionDepth = Ogre::StoreAction::DontCare;
				passScene->mStoreActionStencil = Ogre::StoreAction::DontCare;
}
else
{
				passScene->setAllLoadActions(Ogre::LoadAction::Load);
				passScene->mStoreActionDepth = Ogre::StoreAction::Store;
				passScene->mStoreActionStencil = Ogre::StoreAction::Store;
}

The same also for passClear and passQuat and still no success.

Could be the splitscreen feature eased for developers by setting automatically those passes to be splittscreen friendly?
That means, if splitscreen is used, do not use "clear" or "dont_care"? This could be added as parameter for splitscreen?

EDIT:
I'm sure its because of this quat pass:

Code: Select all

// Render Quad
{
				auto pass = targetDef->addPass(Ogre::PASS_QUAD);
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(pass);

			if (false == this->useSplitScreen)
			{
				passQuad->setAllLoadActions(Ogre::LoadAction::DontCare);
			}
			passQuad->mMaterialName = "Ogre/Copy/4xFP32";
			passQuad->addQuadTextureSource(0, "rtN");

			passQuad->mProfilingId = "NOWA_Final_rtN_Pass_Quad";

			this->applySplitScreenModifier(pass);
}

If I here manipulate the mExecutionMask, mViewportModifierMask, i get direct effects.

Code: Select all

if (pass->getType() == Ogre::CompositorPassType::PASS_QUAD)
{
	pass->mExecutionMask = 0x01;
	pass->mViewportModifierMask = 0x00;

pass->setAllLoadActions(Ogre::LoadAction::Load);
pass->setAllStoreActions(Ogre::StoreAction::StoreOrResolve);
}

If I set:
pass->mExecutionMask = 0x01;
This is the rendering:
Image

If I set:
pass->mExecutionMask = 0x02;
or
pass->mExecutionMask = 0xFF;
This is the rendering:
Image

Its not possible to merge both, so that on the left side picture 1 is rendered and at the right side picture 2. No matter what I do.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5446
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1348

Re: Ogre-Next StereoRendering Issues

Post by dark_sylinc »

The best way to see what's going on is to attempt to merge both, and open it in RenderDoc to see what is overwriting the contents of the other eye.

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Ok I created a Renderdoc capture:

https://www.lukas-kalinowski.com/Homepa ... creen2.rdc

I used the execution_mask, viewport_modifier_mask configuration like it worked for Ogre sky postprocessing:

Code: Select all

void WorkspaceBaseComponent::applySplitScreenModifier(Ogre::CompositorPassDef*& pass, bool isOverlay, bool isSky)
	{
		if (true == this->useSplitScreen)
		{
			pass->mProfilingId += "_" + Ogre::StringConverter::toString(this->cameraComponent->getEyeId());

		if (pass->getType() == Ogre::CompositorPassType::PASS_CLEAR)
		{
			pass->setAllLoadActions(Ogre::LoadAction::Clear);
			pass->setAllStoreActions(Ogre::StoreAction::Store);

			// Gets executed on the first eye
			pass->mExecutionMask = 0x01;
			// Don't be affected by the modifier, apply to the whole screen
			// Will be cleared once for all eyes
			pass->mViewportModifierMask = 0x00;
		}
		else if (pass->getType() == Ogre::CompositorPassType::PASS_SCENE && false == isOverlay)
		{
			// Gets executed in all eyes
			pass->mExecutionMask = 0xFF;
			// Be affected by the modifier, so we render just to a portion of the screen.
			// That means one part is rendered on first eye and the other on the second eye
			pass->mViewportModifierMask = 0xFF;

			pass->setAllLoadActions(Ogre::LoadAction::Clear);
			pass->setAllStoreActions(Ogre::StoreAction::Store);
		}
		else if (pass->getType() == Ogre::CompositorPassType::PASS_QUAD)
		{
			// Gets executed in all eyes
			pass->mExecutionMask = 0xFF;
			// Be affected by the modifier, so we render just to a portion of the screen.
			// That means one part is rendered on first eye and the other on the second eye
			pass->mViewportModifierMask = 0xFF;

			Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(pass);
			passQuad->mCameraName = this->cameraComponent->getCamera()->getName();

			pass->setAllLoadActions(Ogre::LoadAction::Load);
			pass->setAllStoreActions(Ogre::StoreAction::StoreOrResolve);
		}
		else if (pass->getType() == Ogre::CompositorPassType::PASS_SCENE && true == isOverlay)
		{
			// Gets executed in all eyes
			pass->mExecutionMask = 0xFF;
			// GUI gets executed on the first eye (left side)
			pass->mViewportModifierMask = 0x01;
		}
		else if (pass->getType() == Ogre::CompositorPassType::PASS_CUSTOM)
		{
			// Gets executed in all eyes
			pass->mExecutionMask = 0xFF;
			// GUI gets executed on the first eye (left side)
			pass->mViewportModifierMask = 0x01;
		}
	}
}

The Ogre::CompositorPassType::PASS_QUAD (which is in my case the passQuad->mMaterialName = "Ogre/Copy/4xFP32";) seems to make trouble.

I hope you can solve the situation here. I tried to read the capture in RenderDoc but I'm not able to understand what is going on.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Hi @dark_sylinc,

just a ping for this topic. I provided a renderdoc. I still cannot get it right.
I followed all your instructions.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Hi @dark_sylinc,

I'm still trying to get it right for my engine. I create a function, which is applied for any pass to set things right:

Code: Select all

void WorkspaceBaseComponent::applySplitScreenModifier(Ogre::CompositorPassDef*& pass, bool isOverlay, bool isSky)
{
	if (this->useSplitScreen)
	{
		pass->mProfilingId += "_" + Ogre::StringConverter::toString(this->cameraComponent->getEyeId());

	if (pass->getType() == Ogre::CompositorPassType::PASS_CLEAR)
	{
		pass->setAllLoadActions(Ogre::LoadAction::Clear);
		pass->setAllStoreActions(Ogre::StoreAction::Store);

		// Clear the entire render target once, not per viewport
		pass->mExecutionMask = this->cameraComponent->getEyeId() == 0 ? 0x01 : 0x02; // First eye only
		pass->mViewportModifierMask = 0x00; // Applies to the whole screen
	}
	else if (pass->getType() == Ogre::CompositorPassType::PASS_SCENE && !isOverlay)
	{
		pass->mExecutionMask = this->cameraComponent->getEyeId() == 0 ? 0x01 : 0x02;

		// Ensure pass affects only its viewport
		pass->mViewportModifierMask = this->cameraComponent->getEyeId() == 0 ? 0x01 : 0x02;

		// Prevent unintended overwrites by isolating viewport loads and stores
		pass->setAllLoadActions(Ogre::LoadAction::Load);
		pass->setAllStoreActions(Ogre::StoreAction::Store);
	}
	else if (pass->getType() == Ogre::CompositorPassType::PASS_QUAD && !isSky)
	{
		pass->mExecutionMask = this->cameraComponent->getEyeId() == 0 ? 0x01 : 0x02;

		// Ensure quad pass is scoped to its viewport
		pass->mViewportModifierMask = this->cameraComponent->getEyeId() == 0 ? 0x01 : 0x02;

		Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(pass);
		passQuad->mCameraName = this->cameraComponent->getCamera()->getName();

		pass->setAllLoadActions(Ogre::LoadAction::Load);
		pass->setAllStoreActions(Ogre::StoreAction::Store);
	}
	else if (pass->getType() == Ogre::CompositorPassType::PASS_QUAD && isSky)
	{

		pass->mProfilingId = "Sky";

		// Sky affects the entire screen for both eyes
		pass->mExecutionMask = 0xFF; // All cameras
		pass->mViewportModifierMask = 0xFF; // Full screen

		pass->setAllLoadActions(Ogre::LoadAction::Load);
		pass->setAllStoreActions(Ogre::StoreAction::StoreOrResolve);
	}
	else if (pass->getType() == Ogre::CompositorPassType::PASS_SCENE && isOverlay)
	{
		// Overlays (like GUI) render only on the left side
		pass->mExecutionMask = 0x01; // First eye
		pass->mViewportModifierMask = 0x01; // Left viewport
		pass->setAllLoadActions(Ogre::LoadAction::Load);
		pass->setAllStoreActions(Ogre::StoreAction::Store);
	}
	else if (pass->getType() == Ogre::CompositorPassType::PASS_CUSTOM)
	{
		// Custom passes (e.g., UI) render only on the first eye
		pass->mExecutionMask = 0x01;
		pass->mViewportModifierMask = 0x01;
		pass->setAllLoadActions(Ogre::LoadAction::Load);
		pass->setAllStoreActions(Ogre::StoreAction::Store);
	}
}
}

And this is the result:

Image

So the two cameras are visible, but why is there left and right an blank area? I'm really not able to understand that. I tried everything.

The rest I use nearly the same code like in the StereoRendering.cpp example.

I even changed compositor code for the Ogre tutorial sky, for testing:

Code: Select all

compositor_node StereoSkyRenderingNode
{
	in 0 rt_renderwindow

target rt_renderwindow
{
	pass clear
	{
		//Get executed on the first eye
		execution_mask			0x01
		//Don't be affected by the modifier, apply to the whole screen
		viewport_modifier_mask	0x00

		colour_value			0.2 0.4 0.6 1

		// Only write to the MSAA surface (don't resolve!)
		// because overlays due to a legacy problem break the pass
		// thus if we resolve now, we'll resolve twice
		store
		{
			colour	store
			depth	store
			stencil	store
		}

		profiling_id "Clear both eyes"
	}
	
	//Render opaque stuff
	pass render_scene
	{
		// Fixes the splitscreen
		//load
		//{
		//	all				clear
		//	clear_colour	0.2 0.4 0.6 1
		//}
		overlays	off
		rq_first	0
		rq_last		2
		
		//Get executed in all eyes
		execution_mask			0xff
		//Be affected by the modifier, so we render just to a portion of the screen.
		viewport_modifier_mask	0xff

		profiling_id "Opaque Objects"
	}
	
	//Render sky after opaque stuff (performance optimization)
	pass render_quad
	{
		quad_normals	camera_direction
		material SkyPostprocess

		profiling_id "Sky"
		
		//Get executed in all eyes
		execution_mask			0xff
		//Be affected by the modifier, so we render just to a portion of the screen.
		viewport_modifier_mask	0xff
	}
	
	//Render transparent stuff after sky
	pass render_scene
	{
		overlays	on
		rq_first	2
		
		//Get executed in all eyes
		execution_mask			0xff
		//Get executed on the first eye
		viewport_modifier_mask	0x01

		profiling_id "Transparents"
	}
}
}

workspace StereoSkyRenderingWorkspace
{
	connect_output StereoSkyRenderingNode 0
}

And the example does work. So still, it must have something todo with the quad pass with the material: Ogre/Copy/4xFP32:

Code: Select all

// Render Quad
{
				auto pass = targetDef->addPass(Ogre::PASS_QUAD);
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(pass);

			if (false == this->useSplitScreen)
			{
				passQuad->setAllLoadActions(Ogre::LoadAction::DontCare);
			}
			passQuad->mMaterialName = "Ogre/Copy/4xFP32";
			passQuad->addQuadTextureSource(0, "rtN");

			passQuad->mProfilingId = "NOWA_Final_rtN_Pass_Quad";

			this->applySplitScreenModifier(pass);
}

which I use in my engine for the final node. Which does nothing more than:

Code: Select all

Texture2D		myTexture : register(t0);
SamplerState	mySampler : register(s0);

float4 main( float2 uv : TEXCOORD0 ) : SV_Target
{
	return myTexture.Sample( mySampler, uv ).xyzw;
}

I need that quad_pass for reflections etc. So what am I still missing to get it right?

I'm at the moment refusing to use split screen texture approach, because I don't dare :(

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
Crystal Hammer
Orc
Posts: 402
Joined: Sat Jun 23, 2007 5:16 pm
x 112

Re: Ogre-Next StereoRendering Issues

Post by Crystal Hammer »

I think what happens was explained already here:

dark_sylinc wrote: Tue Sep 10, 2024 8:17 pm

Some GPUs may waste some VRAM in padding, but that's all.

Ultimately you always end up making sacrifices:

  1. The idea of dont_care and clear as LoadAction are meant to reduce BW but with split screen you need to dont_care/clear regions selectively, which isn't well supported by APIs and GPUs.

    • If you're curious, the reason is that behind the scenes dont_care/clear works in "blocks" (aka tiles) and there is no guarantee your subregion will coincide with the block size. And if it doesn't, the GPU would have to do some overhead.

  2. If you use multiple TextureGpus, you can make efficient use of dont_care/clear BUT you'll need an extra pass to stich everything together, which will result... in extra BW consumption.

and also from earlier:

dark_sylinc wrote: Tue Sep 10, 2024 5:15 pm

I'm almost sure I have the same issue too now.
E.g. like so, for 3 way split: download/file.php?id=7373
But only since I added refractions, split rendering and started using load/store too.
(mentioned here viewtopic.php?p=556432#p556432)

Yeah it looks like the same issue. But it looks like simply you can't use load/dont care during split screen that way.

Strong Suggestion for everyone: Your use cases are getting complex. Instead of doing split screen directly on the final target, just render every player into its own TextureGpu.
Then use a custom pixel shader that composites all players into the final target and then apply shared UI on top.

It was clear you can't do "advanced" split screen using this (mask approach):

Code: Select all

	pass->mExecutionMask = 0x01;
	pass->mViewportModifierMask = 0x00;

At least it is exactly so from my experience. I had a "simple" 1 node and target .compositor setup and the Mask approach worked.
When I moved to refractions "advanced" setup with separate nodes it stopped, and I saw exactly that offset like you have.
IDK what Ogre Next does, but there are things like combining passes etc. Or like said, it may be a GPU limit for regions or so.
So it may be working in the VR sample or other samples that have basic "simple" setups. Sure, it'd be great to have a sample that has everything at once, but not real to happen.

So, sorry if I'm being too bold, but I think you wrote too much code without testing, copying all effects into your pipeline, using the Mask apprach and you are now not willing to drop it and rewrite it using the (RTT) texture approach. Well IMO that code is worthless since it won't work. Like I said I'd drop it and begin writing from start, checking along the way in RenderDoc if you get what you code in cpp right. It was what I did, just that I had less effects, once it started working for me with refractions also in splitscreen. I then added SSAO and more effects later, also heavily relying on RenderDoc to tell me what's wrong.

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Ok, thanks for the explanation.

This sentence seems to be the keypoint:

So it may be working in the VR sample or other samples that have basic "simple" setups. Sure, it'd be great to have a sample that has everything at once, but not real to happen.

And that is what i still do not understand:

Ogre has the split feature prepared and genericly has access to all passes textures at one place. It gets the parameters how to behave for splitscreen and even the screen fractions viewport is set as parameter. So why is Ogre not able to render everything correctly, using this parameters. The Ogre samples are important, but useless. Because nobody will leave its app or game, or own editor with such simple scenarios.

Now we, as developer must build a complex solution around Ogre... That is frustrating.

And i have to admit: Using compositor approach, iam not the best at it. I still do not understand everything. So the task of a render engine should be to help the developer.

So again: i create rtv for hdr like depth or for e.g. distortion create a texture...

Do i need to create split textures (left eye, right eye) already at that early stage?

Or could i create everything for full resolution and at last before adding the workspace for each eye, create the split rtt for the left side, then do the same and create the right rtt und call the shader
to combine to final texture?

add left workspace, then add right workspace?

Because throwing away everything is not an option anymore. I have not that time and concentration anymore with a small child...

But the nice thing is: I have a level editor. So hdr etc ist not activated. I can just test for pbs workspace. But i prepare things for reflection and compositor effects like bloom etc. already. So i need that quad pass copy/4xfp... And use additional rtN.

Best regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Hi @Crystal Hammer,

I'm writing here, I cleared my head and trying again on this topic. So I get deeper and deeper...

Question 1:

  • You create 3 workspaces if splitscreen for 2 player:
    1) Left workspace
    2) Right workspace
    3) Combined workspace

I'm able to understand, which cameras are used for 1) and 2). But what camera should be assigned to the 3) workspace?

Imagine the following:

  • 1) follows the first player car
  • 2) follows the second player car
  • 3) -> What is the camera for the third workspace?

and you are creating the 1) and 2) with position 0 and the 3) with position -1.

Question 2:
How do you use mapOutputChannel?

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
Crystal Hammer
Orc
Posts: 402
Joined: Sat Jun 23, 2007 5:16 pm
x 112

Re: Ogre-Next StereoRendering Issues

Post by Crystal Hammer »

For the Combined workspace I am using a fixed camera, doesn't matter, I made it just to be there.
It won't be used, since there are no pass scene (that would need a camera) it in this workspace. Only pass quad with material and shader.

mapOutputChannel? I don't think I'm doing anything with it for split screen.
I just use it to map output from previous node as input for next.
void CompositorNodeDef::mapOutputChannel( size_t outChannel, IdString textureName )
So if a Node has outputs, I just mapOutputChannel to set them 0,1,etc to the texture that was used. And same target name rendered to that texture.
I then use outChannel numbers in connect:
void CompositorWorkspaceDef::connect( IdString outNode, uint32 outChannel, IdString inNode, uint32 inChannel )
when connecting ouputs and inputs between Nodes in one workspace.

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Ahhh thanks, now I understand, the final workspace just combines and just has a dummy camera.

Hm... I use mygui. So what, if i want to render gui over both splitscreens?
I think I could add that custom pass to the final combined workspace anyway?

Best regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
Crystal Hammer
Orc
Posts: 402
Joined: Sat Jun 23, 2007 5:16 pm
x 112

Re: Ogre-Next StereoRendering Issues

Post by Crystal Hammer »

Yes, I am adding MyGui pass once to the combine workspace in case of splitscreen.

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Hi folks,

I finally did it. Got my first version of split screen working! :D

I had to start the implementaion several times from a different direction and finally came up with an reaaalllllyyy easy solution!

And the best thing is: I just scratched the code of my whole compositor system and all graphics effects, I implemented in the last years, are now working out of the box for split screen!

I attached a video with a demonstration. The right camera, has a "Glass" Compositor Effect attached and those effect also is just rendered for the right split screen. I also tested HDR, which does work.

Splitscreen Test

If somebody is interested in my solution, feel free to ask.

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62

User avatar
Crystal Hammer
Orc
Posts: 402
Joined: Sat Jun 23, 2007 5:16 pm
x 112

Re: Ogre-Next StereoRendering Issues

Post by Crystal Hammer »

Congrats. I see it didn't take that long, so it wasn't that difficult.
So how is your solution working now, did you do anything special to have all effects, or did you rewrite them?
I guess your code will be available anyway like the rest.

Lax
Gnoll
Posts: 659
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 63

Re: Ogre-Next StereoRendering Issues

Post by Lax »

Thanks!

The only thing I did, is to exclude the RenderWindow Texture and replace from each split camera:

Code: Select all

void WorkspaceBaseComponent::addWorkspace(Ogre::CompositorWorkspaceDef* workspaceDef)
{
	if (true == this->externalChannels.empty())
	{
		this->externalChannels.resize(1);
		this->externalChannels[0] = Core::getSingletonPtr()->getOgreRenderWindow()->getTexture();
	}

if (false == this->customExternalChannels.empty() && true == this->involvedInSplitScreen)
{
	size_t priorCount = 0;
	// Note: Just eat the Core::getSingletonPtr()->getOgreRenderWindow()->getTexture(), because its surely used differently via customExternalChannels, but take all other existing channels, (e.g. cubeMap, terra etc.)
	if (this->externalChannels.size() > 0)
	{
		priorCount = this->externalChannels.size() - 1;
	}
	this->externalChannels.resize(priorCount + this->customExternalChannels.size());
	for (size_t i = 0; i < this->customExternalChannels.size(); i++)
	{
		if (i >= priorCount)
		{
			this->externalChannels[i] = this->customExternalChannels[i];
			// workspaceDef->connectExternal(priorCount + i, this->finalRenderingNodeName, priorCount + i);
		}
	}
}

int position = -1;

if (true == this->involvedInSplitScreen)
{
	position = 0;
}
this->workspace = WorkspaceModule::getInstance()->getCompositorManager()->addWorkspace(this->gameObjectPtr->getSceneManager(), this->externalChannels, this->cameraComponent->getCamera(), this->workspaceName, true, position);
}

And wrote a plugin component "SplitScreenComponent", which creates its splitscreen texture and attaches it instead of the RenderWindowTexture and then combine to the final workspace. Thats it...

Code: Select all

void SplitScreenComponent::setupSplitScreen(void)
{
	const auto& cameraCompPtr = NOWA::makeStrongPtr(this->gameObjectPtr->getComponent<CameraComponent>());
	if (nullptr == cameraCompPtr || this->gameObjectPtr->getId() == GameObjectController::MAIN_CAMERA_ID)
	{
		Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL, "[SplitScreenComponent] Error setting up split screen workspace, because the game object: " + this->gameObjectPtr->getName() + " is the main camera. Choose a different camera!");
		return;
	}

this->cameraComponent = cameraCompPtr.get();

WorkspaceModule::getInstance()->setSplitScreenScenarioActive(true);

this->cameraComponent->applySplitScreen(true, 1);

const auto& workspaceBaseCompPtr = NOWA::makeStrongPtr(this->gameObjectPtr->getComponent<WorkspaceBaseComponent>());
if (nullptr == workspaceBaseCompPtr || this->gameObjectPtr->getId() == GameObjectController::MAIN_CAMERA_ID)
{
	Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL, "[SplitScreenComponent] Error setting up split screen workspace, because the game object: " + this->gameObjectPtr->getName() + " is the main camera. Choose a different camera!");
	return;
}

this->workspaceBaseComponent = workspaceBaseCompPtr.get();

this->splitScreenTexture = this->createSplitScreenTexture("SplitScreenTexture_" + this->gameObjectPtr->getName());

this->externalChannels.resize(1);
this->externalChannels[0] = this->splitScreenTexture;

// Set the this external channels as custom external channels to create custom workspace
this->workspaceBaseComponent->setCustomExternalChannels(this->externalChannels);
this->workspaceBaseComponent->setInvolvedInSplitScreen(true);
this->workspaceBaseComponent->createWorkspace();

this->externalChannels.clear();
this->workspaceBaseComponent->setCustomExternalChannels(this->externalChannels);

Ogre::CompositorManager2::CompositorNodeDefMap nodeDefs = WorkspaceModule::getInstance()->getCompositorManager()->getNodeDefinitions();

// Iterate through Compositor Managers resources
auto& it = nodeDefs.begin();
auto& end = nodeDefs.end();

// Add all compositor resources to the view container
while (it != end)
{
	if (it->second->getNameStr() == this->workspaceBaseComponent->getRenderingNodeName() ||
		it->second->getNameStr() == this->workspaceBaseComponent->getFinalRenderingNodeName())
	{
		for (size_t i = 0; i < it->second->getNumTargetPasses(); i++)
		{
			for (size_t j = 0; j < it->second->getTargetPass(i)->getCompositorPasses().size(); j++)
			{
				const auto& pass = it->second->getTargetPass(i)->getCompositorPasses()[j];
				if (pass->getType() == Ogre::PASS_SCENE)
				{
					Ogre::CompositorPassSceneDef* passScene = static_cast<Ogre::CompositorPassSceneDef*>(pass);
					unsigned int finalRenderMask = AppStateManager::getSingletonPtr()->getGameObjectController()->getRenderCategoryId(this->cameraComponent->getExcludeRenderCategories());
					passScene->setVisibilityMask(finalRenderMask);
				}
			}
		}
	}

	++it;
}

auto splitScreenComponents = AppStateManager::getSingletonPtr()->getGameObjectController()->getGameObjectComponents<SplitScreenComponent>();

bool isLastComponent = false;


if (splitScreenComponents.size() > 0)
{
	isLastComponent = this == splitScreenComponents[splitScreenComponents.size() - 1].get();
}

if (true == isLastComponent)
{
	Ogre::CompositorManager2* compositorManager = WorkspaceModule::getInstance()->getCompositorManager();

	Ogre::String finalRenderingNodeName = "FinalSplitScreenCombineNode_" + Ogre::StringConverter::toString(this->gameObjectPtr->getId());
	Ogre::CompositorNodeDef* finalNodeDef = compositorManager->addNodeDefinition(finalRenderingNodeName);

	// Add render window as input
	finalNodeDef->addTextureSourceName("rt_renderwindow", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT);

	// Add split textures as inputs
	for (size_t i = 0; i < splitScreenComponents.size(); i++)
	{
		finalNodeDef->addTextureSourceName(splitScreenComponents[i]->getSplitScreenTexture()->getNameStr(), i + 1, Ogre::TextureDefinitionBase::TEXTURE_INPUT);
	}

	finalNodeDef->setNumTargetPass(1);
	Ogre::CompositorTargetDef* targetDef = finalNodeDef->addTargetPass("rt_renderwindow");

	targetDef->setNumPasses(2);

	// Quad pass to combine split textures
	{
		auto* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
		passQuad->setAllLoadActions(Ogre::LoadAction::DontCare);
		passQuad->mMaterialName = "SplitScreen_2h";
		passQuad->mProfilingId = "QuadPass_SplitScreen_2h";

		for (size_t i = 0; i < splitScreenComponents.size(); i++)
		{
			passQuad->addQuadTextureSource(i, splitScreenComponents[i]->getSplitScreenTexture()->getNameStr());
		}
	}

	// MyGUI pass
	{
		auto pass = targetDef->addPass(Ogre::PASS_CUSTOM, "MYGUI");
		pass->mProfilingId = "Split_MyGUI_Pass_" + Ogre::StringConverter::toString(this->gameObjectPtr->getId());
	}

	Ogre::String finalWorkspaceName = "finalCombinedSplitScreenWorkspace_" + Ogre::StringConverter::toString(this->gameObjectPtr->getId());
	Ogre::CompositorWorkspaceDef* workspaceDef = compositorManager->addWorkspaceDefinition(finalWorkspaceName);

	workspaceDef->connectExternal(0, finalRenderingNodeName, 0);

	// Connect all split textures
	for (size_t i = 0; i < splitScreenComponents.size(); i++)
	{
		workspaceDef->connectExternal(i + 1, finalRenderingNodeName, i + 1);
	}

	Ogre::CompositorChannelVec finalExternalChannels;

	// Add render window as the first channel
	finalExternalChannels.push_back(Core::getSingletonPtr()->getOgreRenderWindow()->getTexture());

	// Add split RTTs
	for (size_t i = 0; i < splitScreenComponents.size(); i++)
	{
		finalExternalChannels.push_back(splitScreenComponents[i]->getSplitScreenTexture());
	}

	// Is not used, just a dummy, also the finalRenderingNodeName is just a node without a scene, which just combines the textures in a shader
	this->tempCamera = this->gameObjectPtr->getSceneManager()->createCamera("FinalSplitScreenDummyCamera_" + Ogre::StringConverter::toString(this->gameObjectPtr->getId()));
	NOWA::Core::getSingletonPtr()->setMenuSettingsForCamera(tempCamera);
	this->tempCamera->setFOVy(Ogre::Degree(90.0f));
	this->tempCamera->setNearClipDistance(0.1f);
	this->tempCamera->setFarClipDistance(500.0f);
	this->tempCamera->setQueryFlags(0 << 0);
	this->tempCamera->setPosition(this->tempCamera->getParentSceneNode()->convertLocalToWorldPositionUpdated(Ogre::Vector3(0.0f, 1.0f, -2.0f)));

	AppStateManager::getSingletonPtr()->getCameraManager()->addCamera(this->tempCamera, false);

	this->finalCombinedWorkspace = compositorManager->addWorkspace(this->gameObjectPtr->getSceneManager(), finalExternalChannels, this->tempCamera, finalWorkspaceName, true, -1);

	Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL, "[WorkspaceSplitComponent] Creating final combined workspace: " + finalWorkspaceName);

	WorkspaceModule::getInstance()->setPrimaryWorkspace2(this->gameObjectPtr->getSceneManager(), this->tempCamera, this->finalCombinedWorkspace);
}
}

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62