Coherent GT rendering integration with OGRE 2.2 (howto RenderPassDescriptor)

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


User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75

Coherent GT rendering integration with OGRE 2.2 (howto RenderPassDescriptor)

Post by TaaTT4 »

Hi, finally I've started the migration to OGRE 2.2! I choose to begin with Coherent GT (the library I use for UI) so I'll be able to show debug messages/info from the start.

Here below, how was the workflow in OGRE 2.1:

Code: Select all

// Create RTT texture
m_texture = TextureManager::getSingleton().createManual(m_nameInternal, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D,
		viewInfo->Width, viewInfo->Height, 0, PF_BYTE_RGBA, TU_RENDERTARGET);

Code: Select all

// Assign depth/stencil buffer to RTT texture and bind them to Coherent GT view
const auto renderTarget = m_texture->getBuffer()->getRenderTarget();

Root::getSingleton().getRenderSystem()->setDepthBufferFor(renderTarget, true);

auto renderTargetNative = NativeRenderTarget{};
renderTargetNative.DepthStencilTexture = static_cast<D3D11DepthBuffer*>(renderTarget->getDepthBuffer())->getDepthStencilView(0);

renderTarget->getCustomAttribute("ID3D11RenderTargetView", &renderTargetNative.Texture);

m_viewRenderer = WebPageManager::getSingleton().renderer()->CreateViewRenderer(m_view, renderTargetNative, m_texture->getWidth(),
		m_texture->getHeight(), 1);

Code: Select all

// Set RTT texture to overlay datablock
datablockUnlit->setTexture(0, 0, m_texture);

Code: Select all

// Between SceneManager::updateSceneGraph and Root::_updateAllRenderTargets ...
// Render UI to RTT texture
m_viewRenderer()->Paint();
With OGRE 2.2, I changed the texture creation in this way:

Code: Select all

m_texture = Root::getSingleton().getRenderSystem()->getTextureGpuManager()->createTexture(m_nameInternal, GpuPageOutStrategy::Discard,
		TextureFlags::RenderToTexture, TextureTypes::Type2D);
(and I removed arrayIndex from datablock setTexture method).

So far so good till now, but I'm a bit lost about how to convert the second block of code. Since there's a ID3D11RenderTargetView object in the field, I guess I have to deal with a RenderPassDescriptor. Am I right?
About RenderPassDescriptor, is not really clear to me (since there's no example... :roll:) how it interacts with compositor chain/rendering process (especially in my scenario... :lol:). Reading the documentation, I've understood that I have to call RenderSystem::beginRenderPassDescriptor, maybe RenderSystem::executeRenderPassDescriptorDelayedActions, but not RenderSystem::endRenderPassDescriptor (due to optimizations)... right?. And when this calls must be made? Is it OK between SceneManager::updateSceneGraph and Root::_updateAllRenderTargets?

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

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

Re: Coherent GT rendering integration with OGRE 2.2 (howto RenderPassDescriptor)

Post by dark_sylinc »

Hi!

I'm not sure if you've seen What's new in Ogre 2.2.

I'd advise you to enclose your Coherent GT rendering inside a custom CompositorPass, i.e. copy/paste OgreCompositorPassQuad.cpp, rename it to OgreCompositorPassCoherentGT and call Coherent GT in execute().

Why? Because the pass will create an adecuate RenderPassDescriptor from where you can grab the ID3D11RenderTargetView you need:

Code: Select all

ID3D11RenderTargetView *colourRtv;
ID3D11DepthStencilView *depthRtv;
mRenderPassDesc->getCustomAttribute( "ID3D11RenderTargetView",  &colourRtv, 0 );
mRenderPassDesc->getCustomAttribute( "ID3D11DepthStencilView",  &depthRtv, 0 );
Now, to explain your actual questions:

1. Each compositor pass creates a RenderPassDescriptor. It may have to recreate it if the texture changes.

2. RenderPassDescriptor contains info like which colour textures to render to (e.g. MRT / GBuffer for deferred rendering), which slice index, and which depth buffers to use.

3. The compositor calls setRenderPassDescToCurrent(), which itself calls renderSystem->beginRenderPassDescriptor.

After we're done, we're supposed to call endRenderPassDescriptor. However we don't do so as an optimization, because it is very likely the next pass will use an equivalent RenderPassDescriptor

When the next pass calls beginRenderPassDescriptor, two things may happen:
  1. The descriptors are incompatible. Thus RenderSystem automatically performs endRenderPassDescriptor and opens a new render pass
  2. The descriptors are very similar. Hence we do nothing or perform some minor changes (e.g. like changing the viewport)
What's executeRenderPassDescriptorDelayedActions? Originally there was beginRenderPassDescriptor and endRenderPassDescriptor.
But due to some legacy code (Overlays, but there may be something more), this was causing performance problems with Metal, because we were issuing copy commands inside rendering; and we can't do that: we can only issue copy commands outside begin/end pass descriptors.

So beginRenderPassDescriptor sets the current render pass so that Ogre knows the new settings (e.g. RenderTarget resolution, viewport size), but some API calls are delayed until executeRenderPassDescriptorDelayedActions gets called.

At the end of everything, since there are no more passes, someone ends up calling endRenderPassDescriptor and closes the whole frame, but it can be called more than once. We don't in order to perform some optimizations.
User avatar
TaaTT4
OGRE Contributor
OGRE Contributor
Posts: 267
Joined: Wed Apr 23, 2014 3:49 pm
Location: Bologna, Italy
x 75

Re: Coherent GT rendering integration with OGRE 2.2 (howto RenderPassDescriptor)

Post by TaaTT4 »

dark_sylinc wrote: I'm not sure if you've seen What's new in Ogre 2.2.
Seen and studied it sir! :wink:
Speaking of this, what is RenderPassColourTarget::allLayers? Because I've seen that in documentation you set it to true while in the code is defaulted to false.
dark_sylinc wrote: I'd advise you to enclose your Coherent GT rendering inside a custom CompositorPass, i.e. copy/paste OgreCompositorPassQuad.cpp, rename it to OgreCompositorPassCoherentGT and call Coherent GT in execute().

Why? Because the pass will create an adecuate RenderPassDescriptor
This is not required right? I mean, I could just "extract" the RenderPassDescriptor creation part. Calling CompositorPass::execute by hand seems like an hack to me and integrate CompositorPassCoherentGt in my compositor chain would require an effort I wouldn't want to do at the moment.
dark_sylinc wrote: 1. Each compositor pass creates a RenderPassDescriptor. It may have to recreate it if the texture changes.

2. RenderPassDescriptor contains info like which colour textures to render to (e.g. MRT / GBuffer for deferred rendering), which slice index, and which depth buffers to use.

3. The compositor calls setRenderPassDescToCurrent(), which itself calls renderSystem->beginRenderPassDescriptor.

After we're done, we're supposed to call endRenderPassDescriptor. However we don't do so as an optimization, because it is very likely the next pass will use an equivalent RenderPassDescriptor

When the next pass calls beginRenderPassDescriptor, two things may happen:
  1. The descriptors are incompatible. Thus RenderSystem automatically performs endRenderPassDescriptor and opens a new render pass
  2. The descriptors are very similar. Hence we do nothing or perform some minor changes (e.g. like changing the viewport)
What's executeRenderPassDescriptorDelayedActions? Originally there was beginRenderPassDescriptor and endRenderPassDescriptor.
But due to some legacy code (Overlays, but there may be something more), this was causing performance problems with Metal, because we were issuing copy commands inside rendering; and we can't do that: we can only issue copy commands outside begin/end pass descriptors.

So beginRenderPassDescriptor sets the current render pass so that Ogre knows the new settings (e.g. RenderTarget resolution, viewport size), but some API calls are delayed until executeRenderPassDescriptorDelayedActions gets called.

At the end of everything, since there are no more passes, someone ends up calling endRenderPassDescriptor and closes the whole frame, but it can be called more than once. We don't in order to perform some optimizations.
Thank you very much! It's much more clear now.
I know it's an advanced topic that very few people will have to deal with, but maybe you should consider to integrate the "What's new" section with the description above or at least put a reference to this post.

Guess I've found a bug in CompositorPass::setupRenderPassDesc. If in a compositor script I create a texture in this way (with a non shareable depth/stencil buffer)

Code: Select all

texture foo target_width target_height PFG_RGBA8_UNORM depth_pool 65534
the CompositorPass::setupRenderPassDesc method ends to create two different textures for RenderPassDescriptor::mDepth and RenderPassDescriptor::mStencil. Shouldn't the same texture be shared by both instead?


[EDIT]
I forgot to ask another thing. :D Under certain conditions (mouse or its wheel movement), Coherent GT requires to access to ID3D11Resource* of the RTT texture. In OGRE 2.1, I retrieved that pointer in this way:

Code: Select all

static_cast<D3D11Texture*>(m_texture.get())->getTextureResource()
In OGRE 2.2, I suppose I can do the same thing using D3D11TextureGpu::getDisplayTextureName, correct?

Senior programmer at 505 Games; former senior engine programmer at Sandbox Games
Worked on: Racecraft EsportRacecraft Coin-Op, Victory: The Age of Racing

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

Re: Coherent GT rendering integration with OGRE 2.2 (howto RenderPassDescriptor)

Post by dark_sylinc »

TaaTT4 wrote: Fri Sep 27, 2019 12:19 pm Speaking of this, what is RenderPassColourTarget::allLayers? Because I've seen that in documentation you set it to true while in the code is defaulted to false.
That value only matters when rendering to 2D Array textures:

Code: Select all

/// When true, slice will be ignored, and
/// all slices will be attached instead.
bool        allLayers;
Not all rendersystems support it though. This is a very specific and advanced setting.
TaaTT4 wrote: Fri Sep 27, 2019 12:19 pm Why? Because the pass will create an adecuate RenderPassDescriptor
(...)
This is not required right? I mean, I could just "extract" the RenderPassDescriptor creation part. Calling CompositorPass::execute by hand seems like an hack to me and integrate CompositorPassCoherentGt in my compositor chain would require an effort I wouldn't want to do at the moment.
Oh I just meant creating a custom pass (that you'd integrate into your compositor chain) should be the easiest way (I never said anything about calling CompositorPass::execute by hand!). But if you think otherwise that's ok.

Overall setting up a RenderPassDescriptor with hardcoded values is very easy, but setting up a RenderPassDescriptor that handles all the possible combinations can be quite hard (i.e. what CompositorPass::setupRenderPassDesc does)
TaaTT4 wrote: Fri Sep 27, 2019 12:19 pm Guess I've found a bug in CompositorPass::setupRenderPassDesc. If in a compositor script I create a texture in this way (with a non shareable depth/stencil buffer)

Code: Select all

texture foo target_width target_height PFG_RGBA8_UNORM depth_pool 65534
the CompositorPass::setupRenderPassDesc method ends to create two different textures for RenderPassDescriptor::mDepth and RenderPassDescriptor::mStencil. Shouldn't the same texture be shared by both instead?
I don't know, probably you're right. Right now my mind is confused because D3D11 only has shared buffers (i.e. depth buffer and stencil combined) but AMD GPUs internally have them separate; OpenGL wants us to specify depth & stencil separately even if they're combined; and Metal iOS wants depth and stencil to always be separate. It's a mess.

However 65534 (POOL_NON_SHAREABLE) is a special value meant for depth-only textures; so that you can render to multiple depth textures of the same resolution and format (i.e. shadow mapping) and access them directly.

If you want a colour RTT to have its own depth buffer that no one else touches, then just assign it to an unused pool ID. You have 65533 different values to choose from!

It doesn't make much sense to have a colour RTT with POOL_NON_SHAREABLE pool ID because there's no straightforward way to read from the depth buffer.
If you use e.g. pool ID 11234, you can later define a depth buffer that is also in that same pool and same settings, so that you can bind the depth buffer as a texture for reading.
TaaTT4 wrote: Fri Sep 27, 2019 12:19 pm I forgot to ask another thing. :D Under certain conditions (mouse or its wheel movement), Coherent GT requires to access to ID3D11Resource* of the RTT texture. In OGRE 2.1, I retrieved that pointer in this way:

Code: Select all

static_cast<D3D11Texture*>(m_texture.get())->getTextureResource()
In OGRE 2.2, I suppose I can do the same thing using D3D11TextureGpu::getDisplayTextureName, correct?
Yes, but beware of getDisplayTextureName and getFinalTextureName. These are more relevant for texture streaming though (i.e. AutomaticBatching flag is set):
  • getDisplayTextureName returns the texture used for displaying (which can be a dummy if the real one isn't ready yet; i.e. texture hasn't yet finished loading from file)
  • getFinalTextureName always returns the internal texture resource (or nullptr not resident)
Also you can use getCustomAttribute( "ID3D11Resource", &finalTextureName ); to grab this data without having to cast the TextureGpu pointer to D3D11TextureGpu*

Cheers