Render queue texture clearing

Started by
14 comments, last by Juliean 11 years ago

Hello,

I've got a conceptual question concering a render queue as discussed in the "frostbite render architecture" thread in the forums. I've basically implemented such a queue, much like Hodgman descibed it there. I've just got some concerns about how clearing textures and/or the depth buffer is normally handled in such render queues. What would this be? Would this even be handled by the queue? If yes, what "type" would it be? Would it be a render state (like set vertex buffer, set index buffer, set texture...) ? Or would it be a DrawCall, therefore its own render instance? Should it rather be part of the Model, Material, Effect ? If it should better not be part of the render queue, how else is it "normally" handled? How do you handle it, for example if you need to clear the z-buffer for different shadow caster, or generally at the begin/end of a frame?

I'd really be glad to hear some of your opinions, I'm sure a lot of people have worked with such a render queue before...?

Advertisement

Hi,

I also implemented a render queue like discussed in "frostbite render architecture".

I have commands called ClearRenderTarget and ClearDepthStencilTarget that I put inside a "Stage" state group that is executed at the beginning of the corresponding stage.

I don't think Models, Materials or Effects have anything to do with clearing render targets that's why I have the Stage state group.

(I also have a command for updating buffers, etc in case you're wondering).

Thanks for your comment,

I don't think Models, Materials or Effects have anything to do with clearing render targets that's why I have the Stage state group.

Thats interesing though. I do also have stage groups, probably a lot like yours. As in one of Hodgmans posts, my model, material and effect all holds their own state group, which the model puts together and submits to the queue. Its somewhere on the second page I quess, some poorly formated code (forums fault, I quess).

Now that you say "models, materials and effects should not have anything to do" with clearing render targets - how do you set the stage group anyway? Since each of those entities have their own stage groups, command injection has to go through them, at some point, right? E.g. my model has a "SetVertexConstant"-method that adds a VertexConstantX-command to the stage. How do you handle this? Thats a question I was going to ask anyway at some point, because I was really unsure how this is normally done.

I do also have stage groups, probably a lot like yours. As in one of Hodgmans posts, my model, material and effect all holds their own state group, which the model puts together and submits to the queue.

In my implementation the models don't have access to the render queue, (in my opinion the model class should be independent on the Renderer), instead the model class has a method getStateGroups() an array containing the effect, material, mesh, model state groups.


for(Model model : models)
{
    queue.submit(model.getStageGroups(), model.getNumStateGroups();
}

Now that you say "models, materials and effects should not have anything to do" with clearing render targets - how do you set the stage group anyway? Since each of those entities have their own stage groups, command injection has to go through them, at some point, right? E.g. my model has a "SetVertexConstant"-method that adds a VertexConstantX-command to the stage. How do you handle this? Thats a question I was going to ask anyway at some point, because I was really unsure how this is normally done.

The Stage group is submitted individually before the models. How do you bind render targets/depth stencil buffers?

I don't have commands to set individual constants, instead I work with constant buffers, the model "SetVertexConstant"-method will change the value of the constant in a POD buffer then the model uses two commands, UpdateCBuffer (that fills a D3D buffer with the contents of the POD buffer containing the constants) and VSBindCBuffer0 (that binds the D3D buffer to the constant buffer slot 0 of the vertex stage)

In my implementation the models don't have access to the render queue, (in my opinion the model class should be independent on the Renderer), instead the model class has a method getStateGroups() an array containing the effect, material, mesh, model state groups.

My implementation isn't that different. I just gave the model a "Render"-method where I pass the render queue and let it submit its sort key, state groups and draw command.


void Model::Draw(render::IRenderer& render) const
{
	const render::StateGroup* pInstanceStates = &m_states;
	const render::StateGroup* pMaterialStates = &m_pMaterial->GetState();
	const render::StateGroup* pEffectStates = &m_pMaterial->GetEffect().GetState();
	const render::StateGroup* pMeshStates = &m_pMesh->GetState();
	const render::StateGroup* groupArr[4] = { pInstanceStates, pMaterialStates, pEffectStates, pMeshStates };

	render.Render(m_sortKey, &m_pMesh->GetDraw(), groupArr, 4); 
}

I do agree that the model probably should be completely independand of the renderer, but for now, it works OK (going to change that somewhen). Note that I've adapted Hodgmans method of having draw calls seperated from state groups, and create an "render instance" out of all this data...


The Stage group is submitted individually before the models. How do you bind render targets/depth stencil buffers?

Right now the bound render targets are determined by the effect only:


void Effect::RenderTarget(unsigned int index, const Texture* pTexture)
{
	const D3DTexture* pD3DTexture = nullptr;

	if(pTexture != nullptr)
		pD3DTexture = &pTexture->GetD3DTexture();

	switch(index)
	{
	case 0:
		m_states.Add<render::BindRenderTarget0>(*pD3DTexture);
		break;
	case 1:
		m_states.Add<render::BindRenderTarget1>(*pD3DTexture);
		break;
	case 2:
		m_states.Add<render::BindRenderTarget2>(*pD3DTexture);
		break;
	case 3:
		m_states.Add<render::BindRenderTarget2>(*pD3DTexture);
		break;
	}
}

I don't really know what else I'm going to need (probably per-material/per-instance/per-pass render targets too?), but as far as this is concerned, would you say this is ok? As far as clearing render targets goes, it obviously can't be per-effect, since that would clear the target every time I e.g. render another one of my lights, etc...

I don't have commands to set individual constants, instead I work with constant buffers, the model "SetVertexConstant"-method will change the value of the constant in a POD buffer then the model uses two commands, UpdateCBuffer (that fills a D3D buffer with the contents of the POD buffer containing the constants) and VSBindCBuffer0 (that binds the D3D buffer to the constant buffer slot 0 of the vertex stage)

I quess you are working with DirectX 11? does this concept of constant buffers also translate to DirectX9? In DX9 I've got up to 256 (I quess) constant registeres, and I figured the best direct way for now was to simply adress the register directly (when I'm going to implement shader permutations etc.. I'm going to think about another solution anyway)...

But as for the render target clearing, where'd you say this would fit here?

In our engine at work the submission is largely based around 'scenes' which are, effectively, a set of draw calls with a particular set of shaders in use (determined by flags on the scene).

The 'scene' description also has flags which allow you to declare if certain actions happen as they are executed;
- begin
- end
- clear

The first two being a chance for a scene to do something before rendering starts, the final one indicates if the buffers for a scene should be cleared or not (other parameters dictate clear modes, colours etc). This allows you have a number of scenes render to the same targets (targets are named and shared as required, although each scene has its own 'render target' setup structure) without clearing in between them.

Effective rendering;
for each scene in scene list
- if(scene:hasflag(begin)) -> call 'begin' function
- bind render targets
- if(scene:hasflag(clear)) -> do clear functionality
- render each visible model
- if(scene:hasflag(end)) -> call 'end' function

@phantom:

So is this how your entire rendering architecture works, or is it some part put on top of everything? It doesn't seem like a bad idea, but it appears to not being really compatible to my current rendering architecture, which works by submitting a render instance (state changes + sort key + draw call), but I'm not sure...

I do pretty much the same thing as Phantom, but call them 'stages' instead of 'scenes', like Tiago.
I put items (draw + state) into groups, and then put groups in / submit a group along with, a stage.
Some states can't be put into StateGroups (e.g. Render-targets/depth stencil) and are instead specified in the stage. The stage also says whether the targets should be cleared at the start and resolved / extra copies made at the end.

Ah, I think I get it. So if I was to adapt that design, I would put my models into stages, and submit them along with the stage/scene, right? How does this conceptually work? Is there some external RenderQueue::SetStageState-like function, that is called before the "models" are submitted? Like I said, you can imagine my render system be based somewhat along the lines of your second page posts + the render queue code thats somewhere in the middle of the third page in the "frostbite" thread (don't think it would be too helpfull if I posted my whole code).

IMO, you should create a new class, let's call it Stage along with a new StateGroup, and move the render target binding/clearing to it (now the Effects state group will only bind the hardware shaders and set default shader values).

In my renderer, models can have more than one RenderItem (draw call/state groups pair) each one is assigned to a Stage. Example: the RenderItem assigned to the Shadow Mapping stage doesn't need the material state group to be executed)

As you can read in this topic, the renderer has multiple buckets (lists of RenderItems) each bucket is associated with a Stage. Before rendering the renderer goes through the Scene (a list of model instances, etc) finds all the model instances and puts its RenderItems in the appropriate bucket.

Then Stages are executed according to the order defined in the renderer_config file: the bucket associated with each stage is sorted (if needed), the state group of that stage (containing the render target clearing/binding commands) is submitted and only then the RenderItems inside the bucket are submitted.

This topic is closed to new replies.

Advertisement