Skip to content

skaarj1989/FrameGraph

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FrameGraph

CodeFactor GitHub

This is a renderer agnostic implementation of FrameGraph, inspired by the GDC presentation: FrameGraph: Extensible Rendering Architecture in Frostbite by Yuriy O'Donnell

Table of contents

How to use?

Resources

To integrate a resource with FrameGraph, the following requirements should be met. T is a type meeting the requirements of FrameGraph resource.

Expression Type Description
T{}
T
Is default/move constructible.
T::Desc
struct
Resource descriptor.
T::create
void(const T::Desc &, void *)
A function used by implementation to create transient resource.
T::destroy
void(const T::Desc &, void *)
A function used by implementation to destroy transient resource.
T::preRead
void(const T::Desc &, uint32_t flags, void *context)
(optional)
A function called before an execution lambda of a pass.
T::preWrite
void(const T::Desc &, uint32_t flags, void *context)
(optional)
A function called before an execution lambda of a pass.
T::toString
std::string(const T::Desc &)
(optional)
Static function used to embed resource descriptor inside graph node.

Basic

#include "fg/FrameGraph.hpp"

void renderFrame() {
  FrameGraph fg;

  struct PassData {
    FrameGraphResource target;
  };
  fg.addCallbackPass<PassData>("SimplePass",
    [&](FrameGraph::Builder &builder, PassData &data) {
      data.target = builder.create<FrameGraphTexture>("Foo", { 1280, 720 });
      data.target = builder.write(data.target);
    },
    [=](const PassData &data, FrameGraphPassResources &resources, void *) {
      auto &texture = resources.get<FrameGraphTexture>(data.target);
      // ...
    }
  );

  fg.compile();
  fg.execute(&renderContext);
}

Blackboard

Communication between modules

#include "fg/FrameGraph.hpp"
#include "fg/Blackboard.hpp"

struct GBufferData {
  FrameGraphResource depth;
  FrameGraphResource normal;
  FrameGraphResource albedo;
};
GBufferPass::GBufferPass(FrameGraph &fg, FrameGraphBlackboard &blackboard,
                         std::span<const Renderable> renderables) {
  blackboard.add<GBufferData>() = fg.addCallbackPass<GBufferData>(
    "GBuffer Pass",
    [&](FrameGraph::Builder &builder, GBufferData &data) {
      data.depth = builder.create<FrameGraphTexture>(
        "SceneDepth", {/* extent, pixelFormat ... */});
      data.depth = builder.write(data.depth);

      data.normal = builder.create<FrameGraphTexture>("Normal", {});
      data.normal = builder.write(data.normal);

      data.albedo = builder.create<FrameGraphTexture>("Albedo", {});
      data.albedo = builder.write(data.albedo);
    },
    [=](const GBufferData &data, FrameGraphPassResources &resources,
        void *ctx) {
      auto &rc = *static_cast<RenderContext *>(ctx);
      rc.beginRenderPass({
        resources.get<FrameGraphTexture>(data.depth),
        resources.get<FrameGraphTexture>(data.normal),
        resources.get<FrameGraphTexture>(data.albedo),
      });
      for (const auto &renderable : renderables)
        drawMesh(rc, renderable.mesh, renderable.material);
      rc.endRenderPass();
    });
}

struct SceneColorData {
  FrameGraphResource hdr;
};
DeferredLightingPass::DeferredLightingPass(FrameGraph &fg,
                                           FrameGraphBlackboard &blackboard) {
  const auto &gBuffer = blackboard.get<GBufferData>();

  blackboard.add<SceneColorData>() = fg.addCallbackPass<SceneColorData>(
    "GBuffer Pass",
    [&](FrameGraph::Builder &builder, SceneColorData &data) {
      builder.read(gBuffer.depth);
      builder.read(gBuffer.normal);
      builder.read(gBuffer.albedo);

      data.hdr = builder.create<FrameGraphTexture>("SceneColor", {});
      data.hdr = builder.write(data.hdr);
    },
    [=](const SceneColorData &data, FrameGraphPassResources &resources,
        void *ctx) {
      auto &rc = *static_cast<RenderContext *>(ctx);
      rc.beginRenderPass({resources.get<FrameGraphTexture>(data.hdr)})
        .bindTextures({
          resources.get<FrameGraphTexture>(gBuffer.depth),
          resources.get<FrameGraphTexture>(gBuffer.normal),
          resources.get<FrameGraphTexture>(gBuffer.albedo),
        })
        .drawFullScreenQuad()
        .endRenderPass();
    });
}

void renderFrame(std::span<const Renderable> renderables) {
  FrameGraph fg;
  FrameGraphBlackboard blackboard;

  GBufferPass{fg, blackboard, renderables};
  DeferredLightingPass{fg, blackboard};

  fg.compile();
  fg.execute(&renderContext);
}

Automatic resource bindings and barriers

Implement preRead/preWrite in a resource struct.

Code snippets taken from my Vulkan renderer

// For convenience, use an implicit conversion operator.

struct Attachment /* 21 bits */ {
  uint32_t index{0};
  std::optional<uint32_t> layer;
  std::optional<uint32_t> face;
  std::optional<ClearValue> clearValue;

  operator uint32_t() const;
};
Attachment decodeAttachment(uint32_t flags);

struct Location /* 7 bits */ {
  uint32_t set{0};
  uint32_t binding{0};

  operator uint32_t() const;
};
Location decodeLocation(uint32_t flags);

struct BindingInfo /* 13 bits */ {
  Location location;
  uint32_t pipelineStage{0};

  operator uint32_t() const;
};
BindingInfo decodeBindingInfo(uint32_t flags);

struct TextureRead /* 15 bits */ {
  BindingInfo binding;

  enum class Type { CombinedImageSampler, SampledImage, StorageImage }; // 2 bits
  Type type;

  operator uint32_t() const;
};
TextureRead decodeTextureRead(uint32_t flags);
struct FrameGraphTexture {
  struct Desc { /* extent, pixel format ... */ };

  void preRead(const Desc &desc, uint32_t flags, void *ctx) {
    auto &rc = *static_cast<RenderContext *>(ctx);
    // Decode flags, build DescriptorSet tables, insert barriers
  }
  void preWrite(const Desc &desc, uint32_t flags, void *ctx) {
    auto &rc = *static_cast<RenderContext *>(ctx);
    // Decode flags, build attachments (e.g VkRenderingInfo), insert barriers
  }
};
struct Data {
  FrameGraphResource output;
};
fg.addCallbackPass<Data>(
  "FXAA",
  [&](FrameGraph::Builder &builder, Data &data) {
    builder.read(input, TextureRead{
                          .binding =
                            {
                              .location = {.set = 2, .binding = 0},
                              .pipelineStage = PipelineStage_FragmentShader,
                            },
                          .type = TextureRead::Type::CombinedImageSampler,
                        });

    const auto &inputDesc = fg.getDescriptor<FrameGraphTexture>(input);
    data.output = builder.create<FrameGraphTexture>("AA", inputDesc);
    data.output = builder.write(data.output, Attachment{.index = 0});
  },
  [=](const Data &, const FrameGraphPassResources &, void *ctx) {
    auto &rc = *static_cast<RenderContext *>(ctx);
    renderFullScreenPostProcess(rc);
  });

Visualization

// Built in graphviz writer.
std::ofstream{"fg.dot"} << fg;

graph (Graph created by one of tests)

Custom writer

Implement a struct with the following methods:

struct JsonWriter {
  nlohmann::json j;

  void operator()(const PassNode &node,
                  const std::vector<ResourceNode> &resourceNodes) {
    // ...
  }
  void operator()(const ResourceNode &node, const ResourceEntry &entry,
                  const std::vector<PassNode> &passNodes) {
    // ...
  }

  void flush(std::ostream &os) const { os << std::setw(2) << j << "\n"; }
};
std::ofstream f{"fg.json"};
fg.debugOutput(f, JsonWriter{});

Visualization tool

https://skaarj1989.github.io/FrameGraph/

viewer (see viewer branch)

Installation

git submodule init
git submodule add https://github.com/skaarj1989/FrameGraph.git extern/FrameGraph
add_subdirectory(extern/FrameGraph)
target_link_libraries(YourProject PRIVATE fg::FrameGraph)

Another possibility is to use FetchContent:

include(FetchContent)

FetchContent_Declare(
  FrameGraph
  GIT_REPOSITORY https://github.com/skaarj1989/FrameGraph.git
  GIT_TAG master)
FetchContent_MakeAvailable(FrameGraph)

target_link_libraries(YourProject PRIVATE fg::FrameGraph)

Example

https://github.com/skaarj1989/FrameGraph-Example

License

MIT