RenderSystem Resources
Basically, the IRenderSystem interface offers function to create resources. In this section, the different resources that can be created using that interface are described.
RenderTargets
A RenderTarget is a surface that is the destination of render commands. RenderTargets can store color or depth information.
To activate a render target you need to invoke the methods SetColorBuffer and SetDepthBuffer of the CommandBuffer.
For example, the following example create a color and depth buffer and binds them.
mShadowMapRT = mRenderSystem->CreateRenderTarget(
1024, 1024, SurfaceFormat_R32F, 1, RenderTargetFlags_Resolvable);
mDepthStencil = mRenderSystem->CreateRenderTarget(
1024, 1024, SurfaceFormat_DepthStencil, 1, 0);
// Binding
commands->SetColorBuffer(mShadowMapRT);
commands->SetDepthBuffer(mDepthStencil);
// Clear
commands->Clear(true, 0xffffffff, true, 1.0f, false, 0);
RenderTargets can not be used as textures. To create a RenderTarget that can be used as a texture the RenderTargetFlags_Resolvable flag must be set when creating the render target. When you want to copy the contents of the surface to the texture, the resolve command must be invoked.
commands->Resolve(mShadowMapRT);
// This is the texture where the surface will be resolved (when the command is executed)
Ptr<ITexture2D> shadowMapTexture = mShadowMapRT->GetResolveTexture();
RenderViews
RenderViews are special RenderTargets that have an associated window. In fact, a RenderView implements both the IRenderTarget and IRenderView interface.
To create a RenderView a window handle is needed.
Ptr<IRenderView> renderView = renderSystem->CreateRenderView(hWnd0);
// A render view can be used as a normal render target
commands->SetColorBuffer(renderView);
A RenderView maintains a backbuffer where all the commands are executed. To show the content of a render view it must be swapped to the window.
commands->Swap(rv);
The size of the internal render target managed by the RenderView and the size of the window does not necessarily need to match. In fact, the internal size of the backbuffer is only modified with the Resize() method of the IRenderView.
This is a example where the backbuffer is resized when the window changes its dimensions.
LRESULT CALLBACK NoesisWndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
switch (iMsg)
{
case WM_CREATE:
{
return 0;
}
case WM_SIZE:
{
if (wParam != SIZE_MINIMIZED)
{
IRenderView* rv = (IRenderView*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (rv != 0)
{
rv->Resize();
}
}
break;
}
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(hWnd, iMsg, wParam, lParam);
}
VertexBuffers, IndexBuffers and VertexSources
VertexBuffers are resources to store vertex information: positions, normals, tangents, etc. A VertexBuffer is an untyped raw buffer that later can be bound to the device through a VertexSource.
An IndexBuffers is used to store indices. Indices are needed to draw indexed primitives. An IndexBuffer can store 16-bits or 32-bits indices. A IndexBuffer is bound to the device with the SetIndices command.
A VertexSource is a set of vertex buffers with a given semantic. Several VertexBuffers can be associated to a VertexSource, each one in a stream. A VertexSource describes the information that is accessible when drawing primitives. You bind a VertexSource using the SetVertexSource command.
What follows is an example that creates a simple quad using a VertexBuffer, an IndexBuffer and a VertexSource.
struct QuadVertex
{
Vector3f pos;
NsFloat32 u,v;
};
QuadVertex vertices[4];
vertices[0].pos = Vector3f(-1.0f, 1.0f, 0.5f);
vertices[0].u = 0.0f;
vertices[0].v = 0.0f;
vertices[1].pos = Vector3f(1.0f, 1.0f, 0.5f);
vertices[1].u = 1.0f;
vertices[1].v = 0.0f;
vertices[2].pos = Vector3f(-1.0f, -1.0f, 0.5f);
vertices[2].u = 0.0f;
vertices[2].v = 1.0f;
vertices[3].pos = Vector3f(1.0f, -1.0f, 0.5f);
vertices[3].u = 1.0f;
vertices[3].v = 1.0f;
NsUInt16 idx[] = {0, 1, 2, 1, 3, 2};
// 1. The VertexBuffer is created and filled with vertex data
Ptr<IVertexBuffer> vb = mRenderSystem->CreateVertexBuffer(
4 * sizeof(QuadVertex), UpdateFrequency_Immutable, &vertices);
// 2. The IndexBuffer is created and filled with index data (16-bit indices are used here)
Ptr<IIndexBuffer> ib = mRenderSystem->CreateIndexBuffer(
6 * sizeof(NsUInt16), UpdateFrequency_Immutable, IndexFormat_16, idx);
// 3. A VertexSource describing the streams that will be used when drawing primitives
VertexSourceDesc vsDesc;
Stream stream;
stream.AddElement(VertexElement(VertexElementUsage_Position, 0, VertexElementType_Float3))
.AddElement(VertexElement(VertexElementUsage_TexCoord, 0, VertexElementType_Float2));
vsDesc.AddStream(stream);
// 4. Associate the stream 0 of the vertexsource with the VertexBuffer we just created
vs = mRenderSystem->CreateVertexSource(vsDesc);
vs->SetStreamData(0, vb);
Dynamic Data
VertexBuffers and IndexBuffers objects are directly stored inside the graphic device. That information is supposed to never change or do it infrequently. This usage information is given when those resources are created passing the UpdateFrequency_Default or the UpdateFrequency_Immutable flag.
Dynamic data is not managed using vertex buffers. Dynamic data is supposed to change frequently (at least one time per frame for example) and it is copied each time that resource is needed by the device. That copy is internally optimized by the render system.
To use dynamic data instead of binding a VertexBuffer to the VertexSource, you pass a raw pointer with the dynamic data.
Vector3f buffer[2];
buffer[0] = Vector3f(p0[0], p0[1], 0.0f);
buffer[1] = Vector3f(p1[0], p1[1], 0.0f);
vertexSource->SetStreamData(0, &buffer[0], 2 * sizeof(Vector3f));
commands->SetVertices(mVsWire);
CommandBuffers
CommandBuffers are created to store render commands. They are described in the Using Command Buffers section.