Background Scroll performance

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


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

Background Scroll performance

Post by Lax »

Hi,

I have a workspace with up to 9 background images, which are place on the top of each other for background scrolling. I have 9 hlsl and 9 glsl shaders. This is a hlsl:

Code: Select all

Texture2D backgroundMap		: register(t0);
SamplerState samplerState	: register(s0);
float speedX;
float speedY;

float4 main(float2 uv0 : TEXCOORD0) : SV_Target
{
	// Apply scroll
	uv0.x += speedX;
	uv0.y += speedY;
	return backgroundMap.Sample( samplerState, uv0 );
}

#version ogre_glsl_ver_330

vulkan( layout( ogre_s0 ) uniform sampler2D backgroundMap;
vulkan( layout( ogre_s0 ) uniform sampler samplerState );

vulkan_layout( location = 0 ) uniform float speedX;
vulkan_layout( location = 0 ) uniform float speedY;

vulkan_layout( location = 0 ) out vec4 fragColour;

vulkan_layout( location = 0 )
in block
{
	vec2 uv0;
} inPs;

void main()
{
	// Apply scroll
	inPs.uv0.x += speedX;
	inPs.uv0.y += speedY;
	fragColour = texture( vkSampler2D( backgroundMap, samplerState ), inPs.uv0 )
}

Then I have 9 background.material files, which uses the shader and the texture. This is one of them:

Code: Select all

// GLSL shaders
fragment_program NOWABackgroundPostprocess_ps_GLSL glsl
{
	source NOWABackgroundPostprocess_ps.glsl
	default_params
	{
		param_named backgroundMap int 0
	}
}

// GLSL (Vulkan) shaders
fragment_program NOWABackgroundPostprocess_ps_VK glslvk
{
	source NOWABackgroundPostprocess_ps.glsl
	default_params
	{
		param_named backgroundMap int 0
	}
}

// HLSL shaders
fragment_program NOWABackgroundPostprocess_ps_HLSL hlsl
{
	source NOWABackgroundPostprocess_ps.hlsl
	entry_point main
	target ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3
}

// Metal shaders
fragment_program NOWABackgroundPostprocess_ps_Metal metal
{
	source NOWABackgroundPostprocess_ps.metal
	// Because of this one, propably metal will not work, because background is just one texture and this vs is created for cube and not adapted (to complicated).
	// shader_reflection_pair_hint NOWASkyPostprocess_vs_Metal
}

fragment_program NOWABackgroundPostprocess_ps unified
{
	delegate NOWABackgroundPostprocess_ps_HLSL
	delegate NOWABackgroundPostprocess_ps_GLSL
	delegate NOWABackgroundPostprocess_ps_VK
	// Causes could not find best technique crash
	// delegate NOWABackgroundPostprocess_ps_Metal
}

// Material definition
material NOWABackgroundPostprocess
{
	technique
	{
		
	pass
	{
		// https://forums.ogre3d.org/viewtopic.php?t=23280
		// https://forums.ogre3d.org/viewtopic.php?f=2&t=82855&p=515294&hilit=material+scroll+texture#p515294
		// scene_blend modulate
		scene_blend alpha_blend
		// scene_blend add
		//lighting off
		depth_check on
		depth_write off
		

		// 
		cull_hardware none

		vertex_program_ref Ogre/Compositor/Quad_vs
		{
		}

		fragment_program_ref NOWABackgroundPostprocess_ps
		{
			// Scroll parameters
			// scroll * speed must be 1
			// param_named_auto scroll time_0_x 20
			param_named speedX float 0
			param_named speedY float 0
		}

		texture_unit backgroundMap
		{
			texture Sky.png
			alpha_op_ex add src_manual src_texture 0.1
			filtering			none
		}
	}
}
}

In C++ code I use this materials:

Code: Select all

void WorkspaceBackgroundComponent::internalCreateCompositorNode(void)
{
	if (!this->compositorManager->hasNodeDefinition(this->renderingNodeName))
	{
		Ogre::CompositorNodeDef* compositorNodeDefinition = this->compositorManager->addNodeDefinition(this->renderingNodeName);

	unsigned short numTexturesDefinitions = 0;
	if (false == this->useHdr->getBool())
	{
		numTexturesDefinitions = 2;
	}
	else
	{
		numTexturesDefinitions = 3;
	}
	compositorNodeDefinition->setNumLocalTextureDefinitions(numTexturesDefinitions);

	Ogre::TextureDefinitionBase::TextureDefinition* texDef = compositorNodeDefinition->addTextureDefinition("rt0");

	if (this->superSampling->getReal() <= 0.0f)
	{
		this->superSampling->setValue(1.0f);
	}

	texDef->width = 0; // target_width
	texDef->height = 0; // target_height

	texDef->widthFactor = this->superSampling->getReal();
	texDef->heightFactor = this->superSampling->getReal();
	texDef->textureFlags = Ogre::TextureFlags::RenderToTexture | Ogre::TextureFlags::MsaaExplicitResolve;

	// For SSAO necessary?
	// texDef->format = Ogre::PFG_RGBA8_UNORM_SRGB;
	texDef->format = Ogre::PFG_RGBA16_FLOAT;

	if (true == this->useDistortion->getBool())
	{
		// texDef->depthBufferFormat = Ogre::PFG_D32_FLOAT;
		// texDef->depthBufferId = 2;
		// texDef->preferDepthTexture = true;

		Ogre::TextureDefinitionBase::TextureDefinition* distortionTexDef = compositorNodeDefinition->addTextureDefinition("rt_distortion");
		distortionTexDef->width = 0.0f; // target_width
		distortionTexDef->height = 0.0f; // target_height
		distortionTexDef->format = Ogre::PFG_RGBA8_UNORM_SRGB;
		distortionTexDef->depthBufferFormat = Ogre::PFG_D32_FLOAT;
		distortionTexDef->preferDepthTexture = true;
		// Attention depth_pool?
		distortionTexDef->depthBufferId = 2;
		distortionTexDef->textureFlags = Ogre::TextureFlags::RenderToTexture;
	}

	Ogre::RenderTargetViewDef* rtv = compositorNodeDefinition->addRenderTextureView("rt0");
	Ogre::RenderTargetViewEntry attachment;
	attachment.textureName = "rt0";
	rtv->colourAttachments.push_back(attachment);
	rtv->depthBufferId = Ogre::DepthBuffer::POOL_DEFAULT;

	texDef = compositorNodeDefinition->addTextureDefinition("rt1");
	
	texDef->width = 0; // target_width
	texDef->height = 0; // target_height
	
	texDef->widthFactor = this->superSampling->getReal();
	texDef->heightFactor = this->superSampling->getReal();
	texDef->format = Ogre::PFG_RGBA16_FLOAT;
	// texDef->msaa = this->msaaLevel;
	texDef->textureFlags = Ogre::TextureFlags::RenderToTexture | Ogre::TextureFlags::MsaaExplicitResolve;

	rtv = compositorNodeDefinition->addRenderTextureView("rt1");
	attachment.textureName = "rt1";
	rtv->colourAttachments.push_back(attachment);
	rtv->depthBufferId = Ogre::DepthBuffer::POOL_DEFAULT;

	if (true == this->useHdr->getBool())
	{
		texDef = compositorNodeDefinition->addTextureDefinition("oldLumRt");
		texDef->width = 0.0f;
		texDef->height = 0.0f;
		texDef->format = Ogre::PFG_RGBA16_FLOAT;
		texDef->textureFlags = Ogre::TextureFlags::RenderToTexture;
		texDef->depthBufferId = Ogre::DepthBuffer::POOL_DEFAULT;

		rtv = compositorNodeDefinition->addRenderTextureView("oldLumRt");
		attachment.textureName = "oldLumRt";
		rtv->colourAttachments.push_back(attachment);
		rtv->depthBufferId = Ogre::DepthBuffer::POOL_NO_DEPTH;
	}

	if (true == this->useDistortion->getBool())
	{
		rtv = compositorNodeDefinition->addRenderTextureView("rt_distortion");
		Ogre::RenderTargetViewEntry attachment;
		attachment.textureName = "rt_distortion";
		rtv->colourAttachments.push_back(attachment);
		// rtv->depthBufferId = 2;
	}

	unsigned short numTargetPass = 1;
	if (true == this->useHdr->getBool())
	{
		numTargetPass++;
	}
	if (true == this->useDistortion->getBool())
	{
		numTargetPass++;
	}
	compositorNodeDefinition->setNumTargetPass(numTargetPass);

	{
		Ogre::CompositorTargetDef* targetDef = compositorNodeDefinition->addTargetPass("rt0");

		{
			// if (true == this->canUseSplitscreen)
			{
				// Clear Pass
				{
					Ogre::CompositorPassClearDef* passClear;
					auto pass = targetDef->addPass(Ogre::PASS_CLEAR);
					passClear = static_cast<Ogre::CompositorPassClearDef*>(pass);

					passClear->mClearColour[0] = Ogre::ColourValue(0.2f, 0.4f, 0.6f);
					// passClear->setAllStoreActions(Ogre::StoreAction::Store);
					passClear->mStoreActionColour[0] = Ogre::StoreAction::Store;
					passClear->mStoreActionDepth = Ogre::StoreAction::Store;
					passClear->mStoreActionStencil = Ogre::StoreAction::Store;
					passClear->mProfilingId = "NOWA_Background_Split_Pass_Clear";
				}
			}

			// Render Scene
			{
				Ogre::CompositorPassSceneDef* passScene;
				auto pass = targetDef->addPass(Ogre::PASS_SCENE);
				passScene = static_cast<Ogre::CompositorPassSceneDef*>(pass);

				Ogre::ColourValue color(0.2f, 0.4f, 0.6f);
				// passScene->setAllClearColours(color);
				passScene->setAllLoadActions(Ogre::LoadAction::Clear);
				passScene->mClearColour[0] = color;

				// passScene->setAllStoreActions(Ogre::StoreAction::StoreOrResolve);
				passScene->mStoreActionColour[0] = Ogre::StoreAction::StoreOrResolve; // Ogre::StoreAction::StoreAndMultisampleResolve; causes a crash, why?
				passScene->mStoreActionDepth = Ogre::StoreAction::DontCare;
				passScene->mStoreActionStencil = Ogre::StoreAction::DontCare;
				passScene->mShadowNode = WorkspaceModule::getInstance()->shadowNodeName;

				passScene->mFirstRQ = 0;
				passScene->mLastRQ = 2;

				if (true == this->usePlanarReflection->getBool())
				{
					// Used to identify this pass wants planar reflections
					passScene->mIdentifier = 25001;
				}

				passScene->mProfilingId = "NOWA_Background_Before_Background_Pass_Scene";

				// passScene->setAllStoreActions(Ogre::StoreAction::StoreOrResolve);
				passScene->mStoreActionColour[0] = Ogre::StoreAction::StoreOrResolve;
				

				if (true == this->canUseReflection)
				{
					//Our materials in this pass will be using this cubemap,
					//so we need to expose it to the pass.
					//Note: Even if it "just works" without exposing, the reason for
					//exposing is to ensure compatibility with Vulkan & D3D12.

					passScene->mExposedTextures.emplace_back(Ogre::IdString("rt1"));
				}

				passScene->mIncludeOverlays = false;
			}

			//https://forums.ogre3d.org/viewtopic.php?t=93636
			//https://forums.ogre3d.org/viewtopic.php?t=94748

			const Ogre::CompositorPassQuadDef::FrustumCorners corners = Ogre::CompositorPassQuadDef::NO_CORNERS;

			// Background 1 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess";
				passQuad->mFrustumCorners = corners;

				passQuad->mProfilingId = "NOWA_Background_1_Background_Pass_Quad";
			}

			// Background 2 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess2";
				passQuad->mFrustumCorners = corners;
				passQuad->mProfilingId = "NOWA_Background_2_Background_Pass_Quad";
			}

			// Background 3 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess3";
				passQuad->mFrustumCorners = corners;
				passQuad->mProfilingId = "NOWA_Background_3_Background_Pass_Quad";
			}

			// Background 4 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess4";
				passQuad->mFrustumCorners = corners;
				passQuad->mProfilingId = "NOWA_Background_4_Background_Pass_Quad";
			}

			// Background 5 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess5";
				passQuad->mFrustumCorners = corners;
				passQuad->mProfilingId = "NOWA_Background_5_Background_Pass_Quad";
			}

			// Background 6 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess6";
				passQuad->mFrustumCorners = corners;
				passQuad->mProfilingId = "NOWA_Background_6_Background_Pass_Quad";
			}

			// Background 7 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess7";
				passQuad->mFrustumCorners = corners;
				passQuad->mProfilingId = "NOWA_Background_7_Background_Pass_Quad";
			}

			// Background 8 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess8";
				passQuad->mFrustumCorners = corners;
				passQuad->mProfilingId = "NOWA_Background_8_Background_Pass_Quad";
			}

			// Background 9 quad
			{
				Ogre::CompositorPassQuadDef* passQuad = static_cast<Ogre::CompositorPassQuadDef*>(targetDef->addPass(Ogre::PASS_QUAD));
				passQuad->mMaterialName = "NOWABackgroundPostprocess9";
				passQuad->mFrustumCorners = corners;
				passQuad->mProfilingId = "NOWA_Background_9_Background_Pass_Quad";
			}

			// Render Scene
			{
				Ogre::CompositorPassSceneDef* passScene;
				auto pass = targetDef->addPass(Ogre::PASS_SCENE);
				passScene = static_cast<Ogre::CompositorPassSceneDef*>(pass);
				passScene->mIncludeOverlays = false;
				passScene->mShadowNode = WorkspaceModule::getInstance()->shadowNodeName;
				passScene->mShadowNodeRecalculation = Ogre::ShadowNodeRecalculation::SHADOW_NODE_REUSE;
				passScene->mFirstRQ = 2;

				passScene->mProfilingId = "NOWA_Background_After_Background_Pass_Scene";
			}
		}

		if (true == this->useHdr->getBool())
		{
			Ogre::CompositorTargetDef* targetDef = compositorNodeDefinition->addTargetPass("oldLumRt");

			{
				Ogre::CompositorPassClearDef* passClear;
				auto pass = targetDef->addPass(Ogre::PASS_CLEAR);
				passClear = static_cast<Ogre::CompositorPassClearDef*>(pass);

				passClear->mNumInitialPasses = 1;
				passClear->mClearColour[0] = Ogre::ColourValue(0.01f, 0.01f, 0.01f, 1.0f);
				passClear->mProfilingId = "NOWA_Background_Hdr_Pass_Clear";
			}
		}

		if (true == this->useDistortion->getBool())
		{
			Ogre::CompositorTargetDef* targetDef = compositorNodeDefinition->addTargetPass("rt_distortion");

			// Render Scene for distortion
			{
				Ogre::CompositorPassSceneDef* passScene;
				auto pass = targetDef->addPass(Ogre::PASS_SCENE);
				passScene = static_cast<Ogre::CompositorPassSceneDef*>(pass);

				passScene->mProfilingId = "NOWA_Background_Distortion_Pass_Scene";

				passScene->setAllLoadActions(Ogre::LoadAction::Clear);
				passScene->mClearColour[0] = Ogre::ColourValue(0.5f, 0.5f, 0.0f, 0.0f);

				passScene->mUpdateLodLists = false;

				passScene->mIncludeOverlays = false;
				passScene->mFirstRQ = 16;
				passScene->mLastRQ = 17;
			}

			this->createDistortionNode();
		}

		// Output channels
		unsigned short outputChannel = 0;

		if (false == this->useHdr->getBool())
		{
			outputChannel = 2;
		}
		else
		{
			outputChannel = 3;
		}

		if (true == this->useDistortion->getBool())
		{
			outputChannel++;
		}

		compositorNodeDefinition->setNumOutputChannels(outputChannel);

		outputChannel = 0;

		compositorNodeDefinition->mapOutputChannel(outputChannel++, "rt0");
		compositorNodeDefinition->mapOutputChannel(outputChannel++, "rt1");

		if (true == this->useHdr->getBool())
		{
			compositorNodeDefinition->mapOutputChannel(outputChannel++, "oldLumRt");
		}
		if (true == this->useDistortion->getBool())
		{
			compositorNodeDefinition->mapOutputChannel(outputChannel++, "rt_distortion");
		}

		this->createFinalRenderNode();

		// For VR: disable this line and use NOWAPbsRenderingNodeVR
		// this->changeViewportRect(0, this->viewportRect->getVector4());

		if (true == this->externalChannels.empty())
		{
			this->externalChannels.resize(1);
			this->externalChannels[0] = Core::getSingletonPtr()->getOgreRenderWindow()->getTexture();
		}
	}

	if (true == this->usePlanarReflection->getBool())
	{
		if (false == this->compositorManager->hasNodeDefinition(this->planarReflectionReflectiveRenderingNode))
		{
			Ogre::CompositorNodeDef* compositorNodeDefinition = compositorManager->addNodeDefinition(this->planarReflectionReflectiveRenderingNode);

			compositorNodeDefinition->addTextureSourceName("rt_renderwindow", 0, Ogre::TextureDefinitionBase::TEXTURE_INPUT);

			unsigned short numTargetPass = 1;
			compositorNodeDefinition->setNumTargetPass(numTargetPass);

			{
				Ogre::CompositorTargetDef* targetDef = compositorNodeDefinition->addTargetPass("rt_renderwindow");

				{
					// Render Scene
					{
						Ogre::CompositorPassSceneDef* passScene;
						auto pass = targetDef->addPass(Ogre::PASS_SCENE);
						passScene = static_cast<Ogre::CompositorPassSceneDef*>(pass);
						passScene->mProfilingId = "NOWA_Background_PlanarReflection_Pass_Scene";

						Ogre::ColourValue color(0.2f, 0.4f, 0.6f);
						// passScene->setAllClearColours(color);
						passScene->setAllLoadActions(Ogre::LoadAction::Clear);
						passScene->mClearColour[0] = color;

						// passScene->setAllStoreActions(Ogre::StoreAction::StoreOrResolve);
						passScene->mStoreActionColour[0] = Ogre::StoreAction::StoreOrResolve; // Ogre::StoreAction::StoreAndMultisampleResolve; causes a crash, why?
						passScene->mStoreActionDepth = Ogre::StoreAction::DontCare;
						passScene->mStoreActionStencil = Ogre::StoreAction::DontCare;

						passScene->mFirstRQ = 0;
						passScene->mLastRQ = 2;

						passScene->mIncludeOverlays = false;

						// passScene->mVisibilityMask = 0xfffffffe;

						//https://forums.ogre3d.org/viewtopic.php?t=93636
						//https://forums.ogre3d.org/viewtopic.php?t=94748
						passScene->mShadowNode = WorkspaceModule::getInstance()->shadowNodeName;
					}

					// Add background image passes
					for (size_t i = 0; i < 9; i++)
					{
						Ogre::String strMaterialName = "NOWABackgroundPostprocess";
						if (i > 0)
						{
							strMaterialName = "NOWABackgroundPostprocess" + Ogre::StringConverter::toString(i + 1);
						}
						
						Ogre::CompositorPassQuadDef* passQuad;
						auto pass = targetDef->addPass(Ogre::PASS_QUAD);
						passQuad = static_cast<Ogre::CompositorPassQuadDef*>(pass);

						passQuad->mMaterialName = strMaterialName;
						passQuad->mProfilingId = "NOWA_Background_Background_" + Ogre::StringConverter::toString(i) + "_Pass_Scene";
					}

					// Render Scene
					{
						Ogre::CompositorPassSceneDef* passScene;
						auto pass = targetDef->addPass(Ogre::PASS_SCENE);
						passScene = static_cast<Ogre::CompositorPassSceneDef*>(pass);

						passScene->mIncludeOverlays = false;
						passScene->mShadowNode = WorkspaceModule::getInstance()->shadowNodeName;
						passScene->mShadowNodeRecalculation = Ogre::ShadowNodeRecalculation::SHADOW_NODE_REUSE;
						passScene->mFirstRQ = 2;
						passScene->mProfilingId = "NOWA_Background_Before_Background_Pass_Scene";
					}

					// Generate Mipmaps
					{
						Ogre::CompositorPassMipmapDef* passMipmap;
						passMipmap = static_cast<Ogre::CompositorPassMipmapDef*>(targetDef->addPass(Ogre::PASS_MIPMAP));

						passMipmap->mMipmapGenerationMethod = Ogre::CompositorPassMipmapDef::ComputeHQ;

						passMipmap->mProfilingId = "NOWA_Background_Pass_Mipmap";

						// ATTENTION: What here with splitting??
					}
				}
			}
		}
	}
}

for (size_t i = 0; i < 9; i++)
{
	Ogre::String strMaterialName = "NOWABackgroundPostprocess";
	if (i > 0)
	{
		strMaterialName = "NOWABackgroundPostprocess" + Ogre::StringConverter::toString(i + 1);
	}
	this->materialBackgroundPtr[i] = Ogre::MaterialManager::getSingletonPtr()->getByName(strMaterialName);
	
	if (true == this->materialBackgroundPtr[i].isNull())
	{
		Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL, "[WorkspaceBackgroundComponent] Could not set: " + this->workspaceName + " because the material: '" + strMaterialName + "' does not exist!");
		throw Ogre::Exception(Ogre::Exception::ERR_ITEM_NOT_FOUND, "Could not create: " + this->workspaceName + " because the material: '" + strMaterialName + "' does not exist!", "NOWA");
	}

	Ogre::Material* material = this->materialBackgroundPtr[i].getPointer();
	this->passBackground[i] = material->getTechnique(0)->getPass(0);
}
}

I control the position via:

Code: Select all

void WorkspaceBackgroundComponent::setBackgroundScrollSpeedX(unsigned short index, Ogre::Real backgroundScrollSpeedX)
{
	// Threadsafe from the outside
	if (nullptr != this->passBackground[index])
	{
		this->passBackground[index]->getFragmentProgramParameters()->setNamedConstant("speedX", backgroundScrollSpeedX);
	}
}

void WorkspaceBackgroundComponent::setBackgroundScrollSpeedY(unsigned short index, Ogre::Real backgroundScrollSpeedY)
{
	// Threadsafe from the outside
	if (nullptr != this->passBackground[index])
	{
		this->passBackground[index]->getFragmentProgramParameters()->setNamedConstant("speedY", backgroundScrollSpeedY);
	}
}

So the issue is: This approach is totally slow. What can that be?

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

paroj
OGRE Team Member
OGRE Team Member
Posts: 2180
Joined: Sun Mar 30, 2014 2:51 pm
x 1168

Re: Background Scroll performance

Post by paroj »

overdraw. put everything into one shader

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

Re: Background Scroll performance

Post by Lax »

Hi,

ok I reimplemented everything. I have now:

Code: Select all

Ogre::Real layerEnabled[9];
Ogre::Real speedsX[9];
Ogre::Real speedsY[9];

In workspace node just one QUAD instead of 9:

Code: Select all

Ogre::String strMaterialName = "NOWABackgroundScroll";

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

passQuad->mMaterialName = strMaterialName;
passQuad->mProfilingId = "NOWA_Background_Background_Pass_Scene";

I set data:

Code: Select all

void WorkspaceBackgroundComponent::changeBackground(unsigned short index, const Ogre::String& backgroundTextureName)
{
	if (index >= 9)
	{
		Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL,
			"[WorkspaceBackgroundComponent] Invalid layer index: " + Ogre::StringConverter::toString(index));
		return;
	}

this->layerEnabled[index] = 1.0f;

Ogre::String strMaterialName = "NOWABackgroundScroll";
this->materialBackgroundPtr = Ogre::MaterialManager::getSingletonPtr()->getByName(strMaterialName);

if (this->materialBackgroundPtr.isNull())
{
	Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL,
		"[WorkspaceBackgroundComponent] Could not set: " + this->workspaceName +
		" because the material: '" + strMaterialName + "' does not exist!");
	throw Ogre::Exception(Ogre::Exception::ERR_ITEM_NOT_FOUND,
		"Could not create: " + this->workspaceName +
		" because the material: '" + strMaterialName + "' does not exist!", "NOWA");
}

Ogre::Material* material = this->materialBackgroundPtr.getPointer();
this->passBackground = material->getTechnique(0)->getPass(0);

// Update layer enabled flags
this->passBackground->getFragmentProgramParameters()->setNamedConstant("layerEnabled", this->layerEnabled, 9, 1);

// Change background texture
Ogre::TextureUnitState* tex = this->passBackground->getTextureUnitState(index);
tex->setNumMipmaps(0);
tex->setTextureName(backgroundTextureName);

// Set proper texture addressing for scrolling
// tex->setTextureAddressingMode(Ogre::TextureUnitState::TAM_WRAP);

if (this->hardwareGammaEnabled->getBool())
{
	tex->setGamma(8.0);
}
tex->setHardwareGammaEnabled(this->hardwareGammaEnabled->getBool());

this->materialBackgroundPtr->compile();
}

void WorkspaceBackgroundComponent::setBackgroundScrollSpeedX(unsigned short index, Ogre::Real speedX)
{
	if (index >= 9)
	{
		Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL,
			"[WorkspaceBackgroundComponent] Invalid layer index for X speed: " + Ogre::StringConverter::toString(index));
		return;
	}

this->speedsX[index] = speedX;

// Update shader parameters
if (this->passBackground)
{
	this->passBackground->getFragmentProgramParameters()->setNamedConstant("speedsX", this->speedsX, 9, 1);
}
}

void WorkspaceBackgroundComponent::setBackgroundScrollSpeedY(unsigned short index, Ogre::Real speedY)
{
	if (index >= 9)
	{
		Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL,
			"[WorkspaceBackgroundComponent] Invalid layer index for Y speed: " + Ogre::StringConverter::toString(index));
		return;
	}

this->speedsY[index] = speedY;

// Update shader parameters
if (this->passBackground)
{
	this->passBackground->getFragmentProgramParameters()->setNamedConstant("speedsY", this->speedsY, 9, 1);
}
}

I have this material file:

Code: Select all


// GLSL shaders
fragment_program NOWABackgroundScroll_ps_GLSL glsl
{
	source NOWABackgroundScroll_ps.glsl
	default_params
	{
		param_named backgroundMap int 0
	}
}

// GLSL (Vulkan) shaders
fragment_program NOWABackgroundScroll_ps_VK glslvk
{
	source NOWABackgroundScroll_ps.glsl
	default_params
	{
		param_named backgroundMap int 0
	}
}

// HLSL shaders
fragment_program NOWABackgroundScroll_ps_HLSL hlsl
{
	source NOWABackgroundScroll_ps.hlsl
	entry_point main
	target ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3
}

// Metal shaders
fragment_program NOWABackgroundScroll_ps_Metal metal
{
	source NOWABackgroundScroll_ps.metal
	// Because of this one, propably metal will not work, because background is just one texture and this vs is created for cube and not adapted (to complicated).
	// shader_reflection_pair_hint NOWASkyPostprocess_vs_Metal
}

fragment_program NOWABackgroundScroll_ps unified
{
	delegate NOWABackgroundScroll_ps_HLSL
	delegate NOWABackgroundScroll_ps_GLSL
	delegate NOWABackgroundScroll_ps_VK
	// Causes could not find best technique crash
	// delegate NOWABackgroundScroll_ps_Metal
}

material NOWABackgroundScroll
{
    technique
    {
        pass
        {
            // Use alpha blending instead of replace
            scene_blend alpha_blend
			// scene_blend src_alpha one_minus_src_alpha
			// scene_blend one one_minus_src_alpha
            depth_check on
            depth_write off
            
// Ensure proper alpha handling alpha_to_coverage off transparent_sorting on vertex_program_ref Ogre/Compositor/Quad_vs { } fragment_program_ref NOWABackgroundScroll_ps { // Initialize speeds param_named speedsX float9 0 0 0 0 0 0 0 0 0 param_named speedsY float9 0 0 0 0 0 0 0 0 0 // Disable all layers by default (changed from enabled) param_named layerEnabled float9 0 0 0 0 0 0 0 0 0 } // All 9 texture units with proper filtering texture_unit backgroundMap0 { texture Sky1.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap1 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap2 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap3 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap4 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap5 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap6 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap7 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap8 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } } } }

And this hlsl shader:

Code: Select all

// Individual texture declarations (not an array)
Texture2D backgroundMap0 : register(t0);
Texture2D backgroundMap1 : register(t1);
Texture2D backgroundMap2 : register(t2);
Texture2D backgroundMap3 : register(t3);
Texture2D backgroundMap4 : register(t4);
Texture2D backgroundMap5 : register(t5);
Texture2D backgroundMap6 : register(t6);
Texture2D backgroundMap7 : register(t7);
Texture2D backgroundMap8 : register(t8);

SamplerState samplerState : register(s0);

float speedsX[9];
float speedsY[9];
float layerEnabled[9];

float4 main(float2 uv0 : TEXCOORD0) : SV_Target
{
    float4 result = float4(0, 0, 0, 0);
    float2 uv;
    float4 currentLayer;

// Layer 0
// if (layerEnabled[0] > 0.5)
{
    uv = uv0 + float2(speedsX[0], speedsY[0]);
    currentLayer = backgroundMap0.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

// Layer 1
// if (layerEnabled[1] > 0.5)
{
    uv = uv0 + float2(speedsX[1], speedsY[1]);
    currentLayer = backgroundMap1.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

// Layer 2
// if (layerEnabled[2] > 0.5)
{
    uv = uv0 + float2(speedsX[2], speedsY[2]);
    currentLayer = backgroundMap2.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

// Layer 3
// if (layerEnabled[3] > 0.5)
{
    uv = uv0 + float2(speedsX[3], speedsY[3]);
    currentLayer = backgroundMap3.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

// Layer 4
// if (layerEnabled[4] > 0.5)
{
    uv = uv0 + float2(speedsX[4], speedsY[4]);
    currentLayer = backgroundMap4.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

// Layer 5
// if (layerEnabled[5] > 0.5)
{
    uv = uv0 + float2(speedsX[5], speedsY[5]);
    currentLayer = backgroundMap5.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

// Layer 6
// if (layerEnabled[6] > 0.5)
{
    uv = uv0 + float2(speedsX[6], speedsY[6]);
    currentLayer = backgroundMap6.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

// Layer 7
// if (layerEnabled[7] > 0.5)
{
    uv = uv0 + float2(speedsX[7], speedsY[7]);
    currentLayer = backgroundMap7.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

// Layer 8
// if (layerEnabled[8] > 0.5)
{
    uv = uv0 + float2(speedsX[8], speedsY[8]);
    currentLayer = backgroundMap8.Sample(samplerState, uv);
    result.rgb = lerp(result.rgb, currentLayer.rgb, currentLayer.a);
    result.a = max(result.a, currentLayer.a);
}

return result;
}

So: I test it with 2 textures, both are visible, if i commend out the layerEnabled, because, else I see just one texture.

But the big issue is: For testing I want to scroll both textures in x-direction. But just the first texture is always scrolled, the second one remains where it is. I have no idea why. With my old approach, that did work.

Has anybody an idea 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

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

Re: Background Scroll performance

Post by Crystal Hammer »

Isn't this maybe an issue with having

Code: Select all

float speedsX[9];
float speedsY[9];
float layerEnabled[9];

float and [9] in shader?
I'd use float4 only and then .x .y .z .w for each
or maybe try float [12] to be 4 multiples.

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

Re: Background Scroll performance

Post by Lax »

Hi,

hm, but I have to satisfy for maximum of 9 Textures x and y scrolling (18 values).
So If I would use float4 it would not suit 18 values.

Maybe the issue is different and its not possible to have just one pass and 9 texture units and control just each of them?

In the past I had 9 shaders, each for one texture and that did work.

This is how I set the parameters from C++ to material for x-scroll:

this->passBackground->getFragmentProgramParameters()->setNamedConstant("speedsX", this->speedsX, 9, 1);

And this are the variables in the material:

// Initialize speeds
param_named speedsX float9 0 0 0 0 0 0 0 0 0
param_named speedsY float9 0 0 0 0 0 0 0 0 0

And then used in the shader:

float speedsX[9];
float speedsY[9];

If I left of a variable, I get a crash, so those variables seems to be bound correctly. But only the first texture is scrolled, the second one is not scrolled, even I debugged it and the speeds array has correct values on each index.

I even found in an Ogre Compositor Effect such constellation:

Code: Select all

fragment_program ESM/GaussianLogFilterH_ps unified
{
	delegate ESM/GaussianLogFilterH_ps_GLSL
	delegate ESM/GaussianLogFilterH_ps_VK
	delegate ESM/GaussianLogFilterH_ps_HLSL
	delegate ESM/GaussianLogFilterH_ps_Metal

default_params
{
	param_named weights float9 0.013960189 0.022308320 0.033488754 0.047226712 0.062565230 0.077863686 0.091031872 0.099978946 0.10315263
}
}

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: 680
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 65

Re: Background Scroll performance

Post by Lax »

Finally got it working with the help of ai (v0).

@Crystal Hammer: You were right to use multiples for float4, thanks!

For anyone, which may also want to have 9 textures parallax scrolling, this is the code:

Code: Select all

Texture2D backgroundMap0 : register(t0);
Texture2D backgroundMap1 : register(t1);
Texture2D backgroundMap2 : register(t2);
Texture2D backgroundMap3 : register(t3);
Texture2D backgroundMap4 : register(t4);
Texture2D backgroundMap5 : register(t5);
Texture2D backgroundMap6 : register(t6);
Texture2D backgroundMap7 : register(t7);
Texture2D backgroundMap8 : register(t8);
SamplerState samplerState : register(s0);

struct PS_INPUT
{
    float2 uv0 : TEXCOORD0;
};

float getArrayValue(float4 array[3], int idx)
{
    return array[idx / 4][idx % 4];
}

float4 main(
    PS_INPUT inPs,
    uniform float4 speedsX[3],
    uniform float4 speedsY[3],
    uniform float4 layerEnabled[3],
    float4 gl_FragCoord : SV_Position
) : SV_Target
{
    float4 result = float4(0, 0, 0, 0);
    
Texture2D maps[9] = { backgroundMap0, backgroundMap1, backgroundMap2, backgroundMap3, backgroundMap4, backgroundMap5, backgroundMap6, backgroundMap7, backgroundMap8 }; float2 uv0 = inPs.uv0; for (int i = 0; i < 9; ++i) { float enabled = getArrayValue(layerEnabled, i); if (enabled > 0.5f) { float2 scroll = float2( getArrayValue(speedsX, i), getArrayValue(speedsY, i) ); float2 uv = uv0 + scroll; float4 layer = maps[i].Sample(samplerState, uv); result.rgb = lerp(result.rgb, layer.rgb, layer.a); result.a = max(result.a, layer.a); } } return result; } // GLSL shaders fragment_program NOWABackgroundScroll_ps_GLSL glsl { source NOWABackgroundScroll_ps.glsl default_params { param_named backgroundMap int 0 } } // GLSL (Vulkan) shaders fragment_program NOWABackgroundScroll_ps_VK glslvk { source NOWABackgroundScroll_ps.glsl default_params { param_named backgroundMap int 0 } } // HLSL shaders fragment_program NOWABackgroundScroll_ps_HLSL hlsl { source NOWABackgroundScroll_ps.hlsl entry_point main target ps_5_0 ps_4_0 ps_4_0_level_9_1 ps_4_0_level_9_3 } // Metal shaders fragment_program NOWABackgroundScroll_ps_Metal metal { source NOWABackgroundScroll_ps.metal // Because of this one, propably metal will not work, because background is just one texture and this vs is created for cube and not adapted (to complicated). // shader_reflection_pair_hint NOWASkyPostprocess_vs_Metal } fragment_program NOWABackgroundScroll_ps unified { delegate NOWABackgroundScroll_ps_HLSL delegate NOWABackgroundScroll_ps_GLSL delegate NOWABackgroundScroll_ps_VK // Causes could not find best technique crash // delegate NOWABackgroundScroll_ps_Metal } material NOWABackgroundScroll { technique { pass { // Use alpha blending instead of replace scene_blend alpha_blend // scene_blend src_alpha one_minus_src_alpha // scene_blend one one_minus_src_alpha depth_check on depth_write off
// Ensure proper alpha handling alpha_to_coverage off transparent_sorting on vertex_program_ref Ogre/Compositor/Quad_vs { } fragment_program_ref NOWABackgroundScroll_ps { // Initialize speeds param_named speedsX float9 0 0 0 0 0 0 0 0 0 param_named speedsY float9 0 0 0 0 0 0 0 0 0 // Disable all layers by default (changed from enabled) param_named layerEnabled float9 0 0 0 0 0 0 0 0 0 } // All 9 texture units with proper filtering texture_unit backgroundMap0 { texture Sky1.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap1 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap2 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap3 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap4 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap5 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap6 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap7 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } texture_unit backgroundMap8 { texture alpha.png filtering none alpha_op_ex add src_manual src_texture 0.1 tex_address_mode wrap } } } } Ogre::Real layerEnabled[9]; Ogre::Real speedsX[9]; Ogre::Real speedsY[9]; this->passBackground->getFragmentProgramParameters()->setNamedConstant("speedsX", this->speedsX, 9, 1); this->passBackground->getFragmentProgramParameters()->setNamedConstant("speedsY", this->speedsY, 9, 1);

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