Search Results for

    Show / Hide Table of Contents

    Framebuffer

    A FrameBuffer is the set of textures a render pass draws into: one or more colour attachments and, usually, a depth attachment. It says where the output goes, and its OutputDescription says what format that output is in.

    Every application already has one without creating it. swapChain.FrameBuffer is the framebuffer whose contents reach the screen. You create your own when drawing somewhere else: a shadow map, a G-buffer, an offscreen target you will sample later.

    Why it appears in two places

    A framebuffer feeding both the render pass and the pipeline's output description

    A framebuffer is named by the render pass that draws into it, and its OutputDescription is named by the pipeline that draws. Both have to agree on formats, sample count and attachment count, which is why you take the pipeline's Outputs from the framebuffer rather than describing it separately.

    Creation

    Create the textures first. A colour attachment needs TextureFlags.RenderTarget, plus ShaderResource if you intend to sample it afterwards. A depth attachment needs TextureFlags.DepthStencil:

    var colorTargetDescription = new TextureDescription()
    {
        Format = PixelFormat.R8G8B8A8_UNorm,
        Width = rtSize,
        Height = rtSize,
        Depth = 1,
        Layers = 1,
        Flags = TextureFlags.RenderTarget | TextureFlags.ShaderResource,
        CpuAccess = ResourceCpuAccess.None,
        MipLevels = 1,
        Type = TextureType.Texture2D,
        Usage = ResourceUsage.Default,
        SampleCount = TextureSampleCount.None,
    };
    var colorTarget = this.graphicsContext.Factory.CreateTexture(ref colorTargetDescription);
    
    var depthTargetDescription = colorTargetDescription;
    depthTargetDescription.Format = PixelFormat.D24_UNorm_S8_UInt;
    depthTargetDescription.Flags = TextureFlags.DepthStencil;
    var depthTarget = this.graphicsContext.Factory.CreateTexture(ref depthTargetDescription);
    
    // Wrap each texture in an attachment, then build the framebuffer...
    var depthAttachment = new FrameBufferAttachment(depthTarget, 0, 1);
    var colorAttachments = new[] { new FrameBufferAttachment(colorTarget, 0, 1) };
    
    this.frameBuffer = this.graphicsContext.Factory.CreateFrameBuffer(depthAttachment, colorAttachments);
    

    See Texture for the full set of description properties.

    FrameBufferAttachment

    An attachment is a texture plus the slice of it being drawn into:

    new FrameBufferAttachment(texture, arraySlice, sliceCount);
    

    arraySlice and sliceCount select part of a texture array or a cubemap. Pass 0, 1 for an ordinary 2D texture. Rendering into all six faces of a cubemap in one pass, for instance, uses 0, 6.

    FrameBuffer members

    Member Type Description
    ColorTargets FrameBufferAttachment[] The colour attachments, in the order the pixel shader writes them.
    DepthStencilTarget FrameBufferAttachment? The depth attachment, or null for a colour only pass.
    OutputDescription OutputDescription The formats and sample count, for a pipeline to compile against.
    Width, Height uint Dimensions, taken from the attachments.
    ArraySize uint Number of array slices rendered at once. 1 by default.
    SampleCount TextureSampleCount Multisample count shared by every attachment.
    RequireFlipProjection bool Whether the backend needs the projection flipped vertically when drawing into this target.
    BelongsToSwapChain bool true for the framebuffer a swapchain owns.
    Name string Debug name, shown in graphics debugging tools.
    Important

    Every attachment has to share the same width, height and sample count. Multiple render targets also need the backend to support them, which graphicsContext.Capabilities.IsMRTSupported reports.

    OutputDescription

    Property Type Description
    ColorAttachments OutputAttachmentDescription[] One entry per colour attachment, carrying its format.
    DepthAttachment OutputAttachmentDescription? The depth format, or null.
    SampleCount TextureSampleCount Samples per attachment.
    ArraySliceCount uint Number of views rendered.
    CachedHashCode int Precomputed hash, so pipeline compatibility checks are cheap.

    Drawing into it

    Build the pipeline against the framebuffer's output description:

    var pipelineDescription = new GraphicsPipelineDescription()
    {
        PrimitiveTopology = PrimitiveTopology.TriangleList,
        InputLayouts = triangleVertexLayouts,
        ResourceLayouts = new[] { triangleResourceLayout },
        Shaders = new GraphicsShaderStateDescription()
        {
            VertexShader = triangleVertexShader,
            PixelShader = trianglePixelShader,
        },
        RenderStates = new RenderStateDescription()
        {
            RasterizerState = RasterizerStates.None,
            BlendState = BlendStates.Opaque,
            DepthStencilState = DepthStencilStates.None,
        },
        Outputs = this.frameBuffer.OutputDescription,
    };
    
    this.trianglePipelineState = this.graphicsContext.Factory.CreateGraphicsPipeline(ref pipelineDescription);
    

    Then open a render pass on it:

    var commandBuffer = this.commandQueue.CommandBuffer();
    
    commandBuffer.Begin();
    
    RenderPassDescription renderPassDescription = new RenderPassDescription(this.frameBuffer, new ClearValue(ClearFlags.Target, Color.CornflowerBlue));
    commandBuffer.BeginRenderPass(ref renderPassDescription);
    
    commandBuffer.SetViewports(this.rTViewports);
    commandBuffer.SetScissorRectangles(this.scissors);
    commandBuffer.SetGraphicsPipelineState(this.trianglePipelineState);
    commandBuffer.SetResourceSet(this.triangleResourceSet);
    commandBuffer.SetVertexBuffers(this.triangleVertexBuffers);
    
    commandBuffer.Draw((uint)this.triangleVertexData.Length);
    
    commandBuffer.EndRenderPass();
    commandBuffer.End();
    commandBuffer.Commit();
    
    this.commandQueue.Submit();
    this.commandQueue.WaitIdle();
    
    Note

    The viewport is not part of the framebuffer or of the pipeline, so it has to be set inside every pass. Drawing into a 512 by 512 target with a viewport still sized for the window renders into the corner of it.

    Sampling the result

    To read a render target afterwards, transition it out of RenderTarget and into a shader resource state, outside the render pass:

    commandBuffer.EndRenderPass();
    commandBuffer.Barrier(new Texture.Barrier(colorTarget, Texture.StateFlags.PixelShaderResource));
    

    The texture also needs TextureFlags.ShaderResource at creation, and a slot declared as ResourceType.TextureView in the ResourceLayout of whatever samples it. See Barriers.

    Cleaning up

    A framebuffer does not own its attachments, so disposing it leaves the textures alive:

    frameBuffer.Dispose();
    colorTarget.Dispose();
    depthTarget.Dispose();
    
    In this article
    Back to top
    Generated by DocFX