[2.2] Declaring and passing StructuredBuffer per particle-based Renderable. Topic is solved

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


Post Reply
przemir
Halfling
Posts: 68
Joined: Sun May 11, 2014 7:55 pm
Location: Poland
x 21

[2.2] Declaring and passing StructuredBuffer per particle-based Renderable.

Post by przemir »

Hi!

I am trying to make particle system and wanted to store their data (particle pos, colour, size etc.) in vertex shader.
From what I have seen in https://github.com/turanszkij/WickedEng ... cleVS.hlsl, particle data are stored in StructuredBuffer. How to create such buffer and associate with particle-based renderables In Ogre? Right now I don't want GPU particle simulation (maybe later), only pass particle data necessary for vertex shader.

I have already read viewtopic.php?t=94959 (although some links pointing to bitbucket are dead).

I already created ParticleSetRendarable with Ogre::BT_IMMUTABLE index buffer and Ogre::BT_DYNAMIC_PERSISTENT vertex buffer (I plan to change it to Ogre::BT_DEFAULT after it will be calculated via vertex shader). I also subclassed HlmsUnlit into HlmsParticle (https://spotcpp.com/creating-a-custom-h ... d-and-fog/ was really helpful here). Right now particles are rendered by filling vertex buffer each frame on CPU side.
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.2] Declaring and passing StructuredBuffer per particle-based Renderable.

Post by dark_sylinc »

In the case of Ogre 2.2, if you want to use StructuredBuffers you'll have to create an UavBufferPacked and then call UavBufferPacked::getAsTexBufferView.

In the case of Ogre 2.3, ReadOnlyBufferPacked were introduced which support structured buffers

As for one BufferPacked per Renderable, you'll have to create your own Renderable class (and a custom MovableObject, see Samples/2.0/ApiUsage/CustomRenderable sample) and a custom Hlms that downcasts to your custom class, accesses the per-Renderable ReadOnlyBufferPacked, and binds it.

Colibri shows one way to to 'tag' MovableObjects so that Hlms can identity your custom classes that need a special path and thus make sure it's
safe for downcasting.

Note that your custom MovableObject class should set the CustomParameter. The numbers 6372 & 6373 used in Colibri were completely arbitrary and will work as long as they are uniquely used for this purpose.

Cheers
przemir
Halfling
Posts: 68
Joined: Sun May 11, 2014 7:55 pm
Location: Poland
x 21

Re: [2.2] Declaring and passing StructuredBuffer per particle-based Renderable.

Post by przemir »

Thanks. I still got something wrong with binding buffer or filling it. I try to send particle data and replace just colour in vertex shader. But I don't see any particle (I expect they end up with (0.0, 0.0, 0.0, 0.0) colour, although I upload buffer with (0.0, 0.0, 1.0, 1.0)).

Fragments of generated VertexShader:

Code: Select all

...

struct Particle {
float3 pos;
float rot;
float4 colour;
float2 size;
float spriteNumber;
};

RWStructuredBuffer<Particle> particleDataList : register(u14);

struct VS_INPUT
{
	float4 vertex : POSITION;
	float4 colour : COLOR0;

	float2 uv0 : TEXCOORD0;
	uint drawId : DRAWID;
	
	uint vertexId : SV_VertexID;
};

struct PS_INPUT
{
	nointerpolation uint drawId	: TEXCOORD0;
	float4 colour	: TEXCOORD1;		
	float2 uv0	: TEXCOORD2;			
	float4 gl_Position : SV_Position;
};

PS_INPUT main( VS_INPUT input )
{
...
    outVs.colour = input.colour;
...

    uint particleIdx = inVs_vertexId / 4;
    uint vertexInQuad = inVs_vertexId % 4;

    Particle particle = particleDataList[particleIdx];
    outVs.colour = particle.colour;
    return outVs;
}
If I comment this line

Code: Select all

outVs.colour = particle.colour;
particles are shown with vertex buffer colour (I still have cpu calculation of vertex buffer).

Byte size of this struct:

Code: Select all

    const int ParticleSetRenderable::ParticleDataStructSize = sizeof(float) * 11u; // count number of floats inside 'Particle' struct
Initializing (the same place where indexBuffer and vertexBuffer are created):

Code: Select all

    mInstanceBuffer = vaoManager->createUavBuffer(maxParticles, ParticleDataStructSize, BB_FLAG_UAV|BB_FLAG_TEX, 0, false);
    mInstanceBufferAsTex = mInstanceBuffer->getAsTexBufferView( PFG_RGBA32_FLOAT );

    // use CPU copy to fill it and then send to GPU.
    mCpuInstanceBuffer = reinterpret_cast<float*>( OGRE_MALLOC_SIMD( maxParticles * ParticleDataStructSize, MEMCATEGORY_GENERAL ) );
Filling buffer:

Code: Select all

void ParticleSetRenderable::updateToGPU2(const std::vector<Particle>& particles)
{
    float * RESTRICT_ALIAS instanceBuffer = reinterpret_cast<float*>( mCpuInstanceBuffer );
    const float *instanceBufferStart = instanceBuffer;

    int count = Geometry::min<int>(particles.size(), mMaxParticles);
    for (int i = 0; i < count; ++i) {
        const Particle& particle = particles[i];
        Ogre::ColourValue colour = particle.colour;

	// Just to check if vertex shader uses UAV buffer data, set each particle colour to blue.
        colour = Ogre::ColourValue(0.0f, 0.0f, 1.0f, 1.0f);

        *instanceBuffer++ = particle.pos.x;
        *instanceBuffer++ = particle.pos.y;
        *instanceBuffer++ = particle.pos.z;
        *instanceBuffer++ = particle.rot;

        *instanceBuffer++ = colour.r;
        *instanceBuffer++ = colour.g;
        *instanceBuffer++ = colour.b;
        *instanceBuffer++ = colour.a;

        *instanceBuffer++ = particle.size.x;
        *instanceBuffer++ = particle.size.y;
        *instanceBuffer++ = (float)particle.spriteNumber;
    }

    OGRE_ASSERT_LOW( (size_t)(instanceBuffer - instanceBufferStart) * sizeof(float) <=
                     mInstanceBuffer->getTotalSizeBytes() );

    //Fill the remaining bytes with 0
    memset( instanceBuffer, 0, mInstanceBuffer->getTotalSizeBytes() -
            (static_cast<size_t>(instanceBuffer - instanceBufferStart) * sizeof(float)) );
    mInstanceBuffer->upload( mCpuInstanceBuffer, 0u, mInstanceBuffer->getNumElements() );
}

Renderable binding (With ParticleDataTexSlot=14, MaxParticles = 1000):

Code: Select all

Ogre::uint32 HlmsParticle::fillBuffersFor(const Ogre::HlmsCache* cache, const Ogre::QueuedRenderable& queuedRenderable, bool casterPass, Ogre::uint32 lastCacheHash, Ogre::CommandBuffer* commandBuffer, bool isV1)
{

    const Ogre::Renderable::CustomParameterMap &customParams = queuedRenderable.renderable->getCustomParameters();
    if( customParams.find( RenderManager::ParticleRenderable ) != customParams.end() )
    {
        ParticleSetRenderable* particleSetRenderable = dynamic_cast<ParticleSetRenderable*>(queuedRenderable.renderable);
        Ogre::TexBufferPacked* particleDataTexBuffer = particleSetRenderable->getInstanceBufferAsTex();
        int totalSize = ParticleSetRenderable::ParticleDataStructSize * particleSetRenderable->getMaxParticles();
        *commandBuffer->addCommand<Ogre::CbShaderBuffer>() = Ogre::CbShaderBuffer(Ogre::VertexShader, ParticleDataTexSlot, particleDataTexBuffer, 0, totalSize);
        *commandBuffer->addCommand<Ogre::CbShaderBuffer>() = Ogre::CbShaderBuffer(Ogre::PixelShader, ParticleDataTexSlot, particleDataTexBuffer, 0, totalSize);
//        rebindTexBuffer( commandBuffer );
    }

    return Ogre::HlmsUnlit::fillBuffersFor(cache, queuedRenderable, casterPass, lastCacheHash, commandBuffer, isV1);
}
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.2] Declaring and passing StructuredBuffer per particle-based Renderable.

Post by dark_sylinc »

This code:

Code: Select all

RWStructuredBuffer<Particle> particleDataList : register(u14);
Should be:

Code: Select all

StructuredBuffer<Particle> particleDataList : register(t14);
If you really want to use RWStructuredBuffer (which doesn't seem so), then you have to bind mInstanceBuffer instead of mInstanceBufferAsTex
przemir
Halfling
Posts: 68
Joined: Sun May 11, 2014 7:55 pm
Location: Poland
x 21

Re: [2.2] Declaring and passing StructuredBuffer per particle-based Renderable.

Post by przemir »

It works! Thanks a lot for your help!

If I understand correctly, if I want to make particle simulation I should bind mInstanceBuffer and use RWStructuredBuffer in compute job (something like in OgreVctVoxelizer.cpp). And the same buffer should still be binded as tex to VertexShader (and used as StructuredBuffer there)?
User avatar
dark_sylinc
OGRE Team Member
OGRE Team Member
Posts: 5296
Joined: Sat Jul 21, 2007 4:55 pm
Location: Buenos Aires, Argentina
x 1278
Contact:

Re: [2.2] Declaring and passing StructuredBuffer per particle-based Renderable.

Post by dark_sylinc »

That's correct.

Great that it works! :D
Post Reply