[2.1]Problem with custom HLMS

Problems building or running the engine, queries about how to use features etc.
Post Reply
Lanboost
Gnoblar
Posts: 4
Joined: Sun Jun 08, 2014 2:02 pm

[2.1]Problem with custom HLMS

Post by Lanboost »

Hi,

been reading the "guide" at: viewtopic.php?f=25&t=83763&p=519279#p519340
and viewtopic.php?t=85410

When I took the code from the first, I converted the opengl -> direct as that is the only thing my ogre has for now and removed the texture buffer input etc.

So what it is supposed to do, the bare minimum is send the mvp matrix so I can do the simplist vertex shader. But either Im missunderstanding it totally or it does something wrong cause it always seems like my "mvp" forces the object rendered to be put "exactly" where the camera is (debugged both manually and with RenderingDoc)

Because of RenderingDoc it seems to both put the mvp in the constant buffer and read to correct one, its just that the "mvp" itself seems to not be correct?

I only post the "minimum" code here, for the HlmsBufferManager its the same one as in the first thread. I load it like him too.

Vertex shader

Code: Select all

cbuffer ModelViewProjectionCB : register( b0 )
{
    matrix projection[1024];
};


struct VS_INPUT
{
	float4 vertex : POSITION;
	float3 normal : NORMAL;
	//float2 uv0 : TEXCOORD;
	uint drawId : DRAWID;
};

struct PS_INPUT
{
	float4 gl_Position : SV_Position;
    float3 normal : NORMAL;
	//float2 uv0 : TEXCOORD;
};

float4 main( VS_INPUT input ):SV_Position
{
	PS_INPUT outVs;
    
	outVs.gl_Position	= mul(input.vertex,projection[input.drawId]);
    //mul(input.vertex,projection).xyz;
    outVs.normal = input.normal;
    //outVs.uv0 = input.uv0;
	//return outVs;
    return outVs.gl_Position;
}
Pixel shader

Code: Select all

//////////////
// TYPEDEFS //
//////////////
struct PS_INPUT
{
	//float4 gl_Position : SV_Position;
    //float3 normal : NORMAL;
	//float2 uv0 : TEXCOORD;
};
float4 main() : SV_Target
{
    return float4(1,1,1,1);
}
HlmsBlock.h (Same as his HlmsGround.h)

Code: Select all

//////////////////////////////////////////////
//			HlmsGround.h					//
//											//
//											//
//////////////////////////////////////////////

#ifndef __HlmsGround
	#define __HlmsGround

	// Forward declarations
	namespace Ogre
	{
		class VaoManager ;
		class ConstBufferPacked ;
	}

	class OgreInitializer ;

	#include "HlmsBufferManager.h"

	#include <OgreHlmsDatablock.h>
	#include <OgreMatrix4.h>

	// This is a simple datablock. I am not sure what to put in there for now, but in this simple example it is not needed
	class HlmsBlockDatablock : public Ogre::HlmsDatablock
	{
		private :

			// Attributes

		public :

			// Constructor, destructor
			HlmsBlockDatablock (Ogre::IdString name, Ogre::Hlms* creator, const Ogre::HlmsMacroblock* macroblock, const Ogre::HlmsBlendblock* blendblock, const Ogre::HlmsParamVec& params)
			:	HlmsDatablock(name, creator, macroblock, blendblock, params)
			{
				// Nothing to do
			}

			~HlmsBlockDatablock ()
			{
				// Nothing to do
			}
	} ;
	
	// Here is the real class, inheriting from the BufferManager
	class HlmsBlock : public HlmsBufferManager
	{
		private :
		
			// Attributes
			// The buffers used for the passes (one for every pass, we can't map the same accross different frames)
			unsigned int _currentPassBuffer ;
			std::vector<Ogre::ConstBufferPacked*> _passBuffers ;

			// We want to give the WorldMat. This one will be calculated only once, so keep it there
			Ogre::Matrix4 _passViewProj ;

			// Sampler block we will use for the texture
			const Ogre::HlmsSamplerblock* _samplerBlock ;
		
		public :
		
			// Constructor, destructor
			HlmsBlock(Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders, Ogre::VaoManager* vaoManager) ;
			~HlmsBlock() ;

			// HLMS functions
			virtual const Ogre::HlmsCache* createShaderCacheEntry (Ogre::uint32 renderableHash, const Ogre::HlmsCache& passCache, Ogre::uint32 finalHash, const Ogre::QueuedRenderable& queuedRenderable) ;
			virtual Ogre::HlmsCache preparePassHash (const Ogre::CompositorShadowNode* shadowNode, bool casterPass, bool dualParaboloid, Ogre::SceneManager* sceneManager) ;
			virtual Ogre::uint32 fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::uint32 lastTextureHash) ;
			virtual Ogre::uint32 fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer) ;
			virtual void frameEnded () ;

			virtual HlmsBlockDatablock* createDatablockImpl (Ogre::IdString datablockName, const Ogre::HlmsMacroblock* macroblock, const Ogre::HlmsBlendblock* blendblock, const Ogre::HlmsParamVec& paramVec) ;
	} ;
	
#endif
HlmsBlock.cpp (Edited his HlmsGround.cpp)

Code: Select all

#include "pch.h"

//////////////////////////////////////////////
//			HlmsGround.cpp					//
//											//
//											//
//////////////////////////////////////////////

/// Defines ----------------------------------

// Buffer : Light direction + light's colour
// I put the total pass' size here, in values
#define GROUND_HLMS_PASS_BUFFER_SIZE 8

/// Includes ---------------------------------

// Locals
#include "material/HlmsBlock.h"

// Natives
#include <cstddef>

#include <iostream>

#include <OgreHlmsCommon.h>

#include <OgreLight.h>
#include <OgreCamera.h>
#include <OgreRenderable.h>
#include <OgreRenderQueue.h>
#include <OgreTextureManager.h>

#include <CommandBuffer/OgreCbShaderBuffer.h>
#include <CommandBuffer/OgreCbTexture.h>
#include <CommandBuffer/OgreCommandBuffer.h>

#include <Vao/OgreConstBufferPacked.h>
#include <Vao/OgreVaoManager.h>

/// Constructor, destructor ------------------

// Here you can see we give the name we want it to have, and its type
HlmsBlock::HlmsBlock(Ogre::Archive* dataFolder, Ogre::ArchiveVec* libraryFolders,  Ogre::VaoManager* vaoManager)
:	HlmsBufferManager (dataFolder, libraryFolders, vaoManager, "Block", Ogre::HLMS_USER0),
	_currentPassBuffer (0),
	_passBuffers (),
	_passViewProj (Ogre::Matrix4::IDENTITY),
	_samplerBlock (NULL)
{
	// We register this HLMS class with the Ogre::HLMS_USER0 id. For another one, you could use HLMS_USER1, and so on

	// Initiailize our sampler block for the texture
	// We create a reference sampler block, to ask Ogre about the one used in intern. This is done in order to cache things and avoid having multiple blocks doing the same thing
	// That's less API overhead as we won't switch for nothing
	Ogre::HlmsSamplerblock samplerBlockRef ;
	// You can change every parameter here. This is defining the sampler block you want from Ogre
	// Let's put WRAP on U and V, it will repeat the texture (you can MIRROR, CLAMP...)
	samplerBlockRef.mU = Ogre::TAM_WRAP ;
	samplerBlockRef.mV = Ogre::TAM_WRAP ;

	// Then, let's ask Ogre about its internal buffer. We will keep it and use it !
	Ogre::HlmsManager* hlmsMan = Ogre::Root::getSingleton().getHlmsManager() ;
	_samplerBlock = hlmsMan->getSamplerblock(samplerBlockRef) ;
}

HlmsBlock::~HlmsBlock()
{
	// We have to delete our pass buffer
	for (unsigned int i = 0 ; i < _passBuffers.size() ; i++)
		_vaoManager->destroyConstBuffer(_passBuffers[i]) ;
}

/// HLMS functions ---------------------------

const Ogre::HlmsCache* HlmsBlock::createShaderCacheEntry (Ogre::uint32 renderableHash, const Ogre::HlmsCache& passCache, Ogre::uint32 finalHash, const Ogre::QueuedRenderable& queuedRenderable)
{
	// First the parent one
	const Ogre::HlmsCache* retVal = HlmsBufferManager::createShaderCacheEntry(renderableHash, passCache, finalHash, queuedRenderable) ;

	// Here is where you can put the default values for the shaders.
	// So we set the samplerId for our texture (to 0, let's be fair)
	//retVal->pso.pixelShader->getDefaultParameters()->setNamedConstant("baseTex", 0) ;
	
	// We have to switch the values in the program
	
	mRenderSystem->_setPipelineStateObject(&retVal->pso) ;

	// Doing it only with the pixel shader as it is the only one needing it
	Ogre::GpuProgramParametersSharedPtr psParams = retVal->pso.pixelShader->getDefaultParameters() ;
	mRenderSystem->bindGpuProgramParameters(Ogre::GPT_FRAGMENT_PROGRAM, psParams, Ogre::GPV_ALL) ;
	
	// Would be this with the vertex shader
	Ogre::GpuProgramParametersSharedPtr vsParams = retVal->pso.vertexShader->getDefaultParameters() ;
	mRenderSystem->bindGpuProgramParameters(Ogre::GPT_VERTEX_PROGRAM, vsParams, Ogre::GPV_ALL);

	// Done
	return retVal ;
}

Ogre::HlmsCache HlmsBlock::preparePassHash (const Ogre::CompositorShadowNode* shadowNode, bool casterPass, bool dualParaboloid, Ogre::SceneManager* sceneManager)
{
	// Let's call the parent again
	Ogre::HlmsCache retVal = Hlms::preparePassHash (shadowNode, casterPass, dualParaboloid, sceneManager) ;

	// Let's prepare our viewProj matrix (before the pass)
	// You can get the camera from wherever you want, but we will take the current active camera here
	Ogre::Camera* cam = sceneManager->getCameraInProgress() ;

	Ogre::Matrix4 tempview = cam->getViewMatrix(true);

	Ogre::Matrix4 projection = cam->getProjectionMatrixWithRSDepth() ;

	Ogre::RenderTarget* rt = sceneManager->getCurrentViewport()->getTarget() ;

	Ogre::Matrix4 identityProjMat;

	mRenderSystem->_convertProjectionMatrix(Ogre::Matrix4::IDENTITY,
		identityProjMat, true);

	// Maybe we need to flip it (RTT at least)
	if (rt->requiresTextureFlipping())
	{
		projection[1][0] = -projection[1][0] ;
		projection[1][1] = -projection[1][1] ;
		projection[1][2] = -projection[1][2] ;
		projection[1][3] = -projection[1][3] ;
	}

	Ogre::Matrix4 viewProj = projection * tempview;
	_passViewProj = viewProj;

	return retVal ;
}

Ogre::uint32 HlmsBlock::fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::uint32 lastTextureHash)
{
	// I didn't get any call for this one, but at least the console will tell us if it is so
	std::cout << "fillBuffersFor Tex" << std::endl ;

	return 0 ;
}

Ogre::uint32 HlmsBlock::fillBuffersFor (const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer)
{
	//We dont have any pass params, skip that and go directly to instance buffers

	// We have now to fill the datas for an instance (and not a pass)
	// The size of what we write to keep track of where we are
	// We write only a mat4, so 16 values
	Ogre::uint32 valuesToWrite = 16 ;

	// Let's check if the buffer is big enough
	bool bufferTooSmall = (_bufferCurrentIndex - _bufferStartIndex + valuesToWrite) > _bufferSize ;

	if (bufferTooSmall)
	{
		// We have to ask for a new buffer, from the HlmsBufferManager parent
		mapNextConstBuffer(commandBuffer) ;
	}
    
    //Special check for "unsupported" features that this material doesnt allow
	bool useIdentityProjection = queuedRenderable.renderable->getUseIdentityProjection();
	if (useIdentityProjection) {
		cout << "useIdentityProjection" << endl;
		throw std::runtime_error("asd");
	}
    #if OGRE_DOUBLE_PRECISION
	cout << "Double prec used, need to throw" << endl;
	throw std::runtime_error("asd");
    #endif
    
    
    
	// Fill the buffer then.
	// Get the World mat
	const Ogre::Matrix4& worldMat = queuedRenderable.movableObject->_getParentNodeFullTransform() ;

	// We can then compute the final matrix
	Ogre::Matrix4 mvpStandard = _passViewProj * worldMat ;
	// Here I guess I had to transpose because it is OpenGL. Not sure though, as I saw transpose in the PBS one was done every time
	Ogre::Matrix4 mvpMat = mvpStandard.transpose() ;
	// We can push the matrix into our buffer
	memcpy(_bufferCurrentIndex, &mvpMat, 16 * sizeof(float)) ;
	// We have to update the offset for the next Item calling this function before its draw
	_bufferCurrentIndex += valuesToWrite ;
	// This drawId is important (see Dark_Sylinc's answer)
	// Keep in mind that only the first one has to be correct, as Ogre's auto instancing will increment the base value automatically.
	Ogre::uint32 drawId = ((_bufferCurrentIndex - _bufferStartIndex) / valuesToWrite) - 1 ;
	return drawId ;
}

void HlmsBlock::frameEnded ()
{
	// We have to call this one, it is resetting the constBuffer vars for us here
	HlmsBufferManager::frameEnded() ;

	// And as we added a pass component, we have to reset it ourself
	_currentPassBuffer = 0 ;
	_bufferStartIndex = 0;
	_bufferCurrentIndex = 0;
	_bufferSize = 0;
}

HlmsBlockDatablock* HlmsBlock::createDatablockImpl (Ogre::IdString datablockName, const Ogre::HlmsMacroblock* macroblock, const Ogre::HlmsBlendblock* blendblock, const Ogre::HlmsParamVec& paramVec)
{
	// Only return the datablock
	return OGRE_NEW HlmsBlockDatablock (datablockName, this, macroblock, blendblock, paramVec) ;
}

As Ive tried to undo the transpose of the matrix, debugging each stage of the matrix multiplication etc Im now out of clues. Anyone can see the "probably" easy error?

Note: Ive added the cases so I ensure Im not running double precision ogre, and Im not, neither does my rendable want the "identiyprojectionmatrix".

Best regards /Lan
Post Reply