LukasBanana / LLGL

Low Level Graphics Library (LLGL) is a thin abstraction layer for the modern graphics APIs OpenGL, Direct3D, Vulkan, and Metal
BSD 3-Clause "New" or "Revised" License
2.05k stars 139 forks source link

Add "RenderPass" interface #19

Closed LukasBanana closed 6 years ago

LukasBanana commented 6 years ago

Metal has no function to clear the framebuffer attachments on demand. They can only be cleared when a new render-pass begins.

The same functionality is provided in Vulkan, even though it provides the functionality to clear the color attachments on demand, too.

The new RenderPass interface will be used for the GraphicsPipelineDescriptor structure instead of the RenderTarget interface to specfiy the color attachment formats.

Proposal:

enum class AttachmentLoadOp {
    Undefined, // Vulkan: VK_ATTACHMENT_LOAD_OP_DONT_CARE
    Load,      // Vulkan: VK_ATTACHMENT_LOAD_OP_CLEAR
    Clear,     // Vulkan: VK_ATTACHMENT_LOAD_OP_CLEAR
};

enum class AttachmentStoreOp {
    Undefined, // Vulkan: VK_ATTACHMENT_STORE_OP_DONT_CARE
    Store,     // Vulkan: VK_ATTACHMENT_STORE_OP_STORE
};

struct AttachmentFormatDescriptor {
    Format            format  = Format::Undefined;
    AttachmentLoadOp  loadOp  = AttachmentLoadOp::Undefined;
    AttachmentStoreOp storeOp = AttachmentStoreOp::Undefined;
};

struct RenderPassDescriptor {
    std::vector<AttachmentFormatDescriptor> colorAttachments;  // e.g. Format::RGBA8UNorm
    AttachmentFormatDescriptor              depthAttachment;   // e.g. Format::D24UNormS8UInt
    AttachmentFormatDescriptor              stencilAttachment; // e.g. Format::D24UNormS8UInt
};

struct GraphicsPipelineDescriptor {
    ShaderProgram*  shaderProgram  = nullptr;
    PipelineLayout* pipelineLayout = nullptr;
    RenderPass*     renderPass     = nullptr;
    /* ... */
};

class CommandBuffer {
    /* ... */
    void BeginRenderPass(
        RenderPass&       renderPass,
        RenderTarget&     renderTarget,
        std::uint32_t     numClearValues = 0,
        const ClearValue* clearValues    = nullptr
    );
    void EndRenderPass();
};

The render pass will replace the SetRenderTarget functions and RenderContext will finally inherit from RenderTarget:

Before:

myCmdBuffer->SetClearColor({ 1.0f, 0.0f, 0.0f, 1.0f });

myCmdBuffer->SetRenderTarget(*myRenderTarget);
myCmdBuffer->Clear(LLGL::Color);
/* ... */

myCmdBuffer->SetRenderTarget(*myRenderContext);
myCmdBuffer->Clear(LLGL::Color);
/* ... */

myRenderContext->Present();

After:

LLGL::ClearValue clearValue;
clearValue.color = { 1.0f, 0.0f, 0.0f, 1.0f };

myCmdBuffer->BeginRenderPass(*myRenderPass, *myRenderTarget, 1, &clearValue);
/* ... */
myCmdBuffer->EndRenderPass();

myCmdBuffer->BeginRenderPass(*myRenderPass, *myRenderContext, 1, &clearValue);
/* ... */
myCmdBuffer->EndRenderPass();

myRenderContext->Present();
amerkoleci commented 6 years ago

Why not adding concept of command encoder, maybe have the render pass command encoder and naturally you would support parallel command encoders on metal backend, what do you think? I'm adding something similar here https://github.com/amerkoleci/alimer/blob/master/Source/Alimer/Graphics/CommandBuffer.h

LukasBanana commented 6 years ago

First of all: thanks for your suggestion :)

I currently have no plans for adding support for multi threading (if that's what you mean by parallel command encoders). This is very tricky especially on the OpenGL backend. It's on my TODO list, but not scheduled yet.

Right now, the interface of LLGL is under heavy construction. But every change I make, has the consequence that I have to update all the five backends that I'm maintaining.

Moreover, I'm trying to keep the number of interfaces that are required to submit draw calls as low as possible. Adding another interface for a "command encoder" is currently not what I'm looking for.

LukasBanana commented 6 years ago

Done with a625707.