Skip to content

fgviewer: visualize intermediate buffers#9862

Merged
poweifeng merged 3 commits intomainfrom
pf/fgviewer-buffer-view
Apr 21, 2026
Merged

fgviewer: visualize intermediate buffers#9862
poweifeng merged 3 commits intomainfrom
pf/fgviewer-buffer-view

Conversation

@poweifeng
Copy link
Copy Markdown
Contributor

  • Intermediate Buffer Visualization: Enabled live monitoring of internal FrameGraph render targets directly in the fgviewer web UI.
  • HTTP Polling Architecture: Switched from WebSocket binary pushes to native <img> polling (/api/image), eliminating severe frontend memory leaks and GC thrashing.
  • Robust Resource Tracking: Replaced string-based lookups with (ViewId, ResourceId) composite keys to prevent cross-view collisions and ensure accurate reads.
  • Format Post-Processing: Extracted readback conversions (HDR tonemapping, depth normalization, MSAA downsampling, single-channel expansion) into DebugServer.
  • UI Polish: Added a live-updating full-screen image modal and explicitly filtered internal debug passes from the Graphviz/JSON exports to prevent DOM thrashing.
  • Currently Unsupported:
    • Mipmaps & Subresources: Reading specific mip levels or array layers is explicitly skipped.
    • Shadowmaps: Variance Shadow Maps (VSM) will physically evaluate to 0.0 and appear completely empty/black in scenes without active shadow casters (due to inverted-Z).
    • Stencil: Resolving and reading stencil buffer data is not supported by the backend.
image

@poweifeng poweifeng added the internal Issue/PR does not affect clients label Mar 31, 2026
@show50726
Copy link
Copy Markdown
Contributor

It looks great! Thank you!

Couple questions:

  1. Since a texture could be written several times within a frame, is the image shown on the web page always the final state of the texture, or can we specify the timing?
  2. Is it tested on android as well?

Comment thread filament/src/details/Renderer.cpp Outdated
Comment thread filament/src/fg/FrameGraph.cpp Outdated
@poweifeng poweifeng force-pushed the pf/fgviewer-buffer-view branch 2 times, most recently from f16d488 to a1089b8 Compare March 31, 2026 21:32
@poweifeng
Copy link
Copy Markdown
Contributor Author

It looks great! Thank you!

Couple questions:

  1. Since a texture could be written several times within a frame, is the image shown on the web page always the final state of the texture, or can we specify the timing?
  2. Is it tested on android as well?
  1. There is no way to specify timing. It's always the result of the buffer after the last pass (dependents on the output of the previous passes). Which buffer is written more than once?
  2. No, it's not tested beyond MacOS + metal/vk. The changes made here are pretty complicated (to get all the formats working). If ok, I'd like to check this in and then iterate on the other backends and platforms.

@show50726
Copy link
Copy Markdown
Contributor

It looks great! Thank you!
Couple questions:

  1. Since a texture could be written several times within a frame, is the image shown on the web page always the final state of the texture, or can we specify the timing?
  2. Is it tested on android as well?
  1. There is no way to specify timing. It's always the result of the buffer after the last pass (dependents on the output of the previous passes). Which buffer is written more than once?
  2. No, it's not tested beyond MacOS + metal/vk. The changes made here are pretty complicated (to get all the formats working). If ok, I'd like to check this in and then iterate on the other backends and platforms.

Got it! For your question, like Structure Buffer and Bloom Out Texture in your screenshot are both written by 2 passes?

Comment thread filament/src/fg/FgviewerManager.h Outdated
Comment thread filament/src/details/Renderer.cpp Outdated
@poweifeng poweifeng force-pushed the pf/fgviewer-buffer-view branch from a1089b8 to 2dc479d Compare April 2, 2026 04:47
@poweifeng
Copy link
Copy Markdown
Contributor Author

It looks great! Thank you!
Couple questions:

  1. Since a texture could be written several times within a frame, is the image shown on the web page always the final state of the texture, or can we specify the timing?
  2. Is it tested on android as well?
  1. There is no way to specify timing. It's always the result of the buffer after the last pass (dependents on the output of the previous passes). Which buffer is written more than once?
  2. No, it's not tested beyond MacOS + metal/vk. The changes made here are pretty complicated (to get all the formats working). If ok, I'd like to check this in and then iterate on the other backends and platforms.

Got it! For your question, like Structure Buffer and Bloom Out Texture in your screenshot are both written by 2 passes?

I see, I think different mip levels and layers are written to by different passes, but I'm guessing, for one layer/level in one texture, it's only ever written to once per-frame.

Comment thread filament/src/fg/FgviewerManager.h Outdated
Comment on lines +33 to +36
static void handleFgReadbacks(FEngine& engine, FrameGraph& fg,
backend::Handle<backend::HwRenderTarget> viewTarget, uint32_t viewId);
static void update(FEngine& engine, FrameGraph& fg, FView const& view);
static void tick(FEngine& engine);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some documentation would be nice

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread filament/src/details/Engine.h Outdated
Comment on lines +573 to +591
#if FILAMENT_ENABLE_FGVIEWER
struct ReadbackRequest {
using Callback = std::function<void(fgviewer::DebugServer::PixelBuffer, uint32_t, uint32_t,
fgviewer::DebugServer::PixelDataFormat, fgviewer::DebugServer::FormatInfo)>;
fgviewer::ViewHandle viewId;
uint32_t id;
utils::CString name;
Callback callback;
};

void requestTextureReadback(fgviewer::ViewHandle viewId, uint32_t id,
utils::CString const& name,
std::function<void(fgviewer::DebugServer::PixelBuffer, uint32_t, uint32_t,
fgviewer::DebugServer::PixelDataFormat, fgviewer::DebugServer::FormatInfo)>&&
callback);

utils::Mutex mReadbackRequestsMutex;
std::vector<ReadbackRequest> mReadbackRequests;
#endif
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this needs to be on Engine? Could it be on the new FgviewerManager?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to FgviewerManager

case VK_FORMAT_D16_UNORM: return PixelDataType::USHORT;
case VK_FORMAT_D32_SFLOAT: return PixelDataType::FLOAT;
case VK_FORMAT_X8_D24_UNORM_PACK32: return PixelDataType::UINT;
case VK_FORMAT_B10G11R11_UFLOAT_PACK32: return PixelDataType::UINT_10F_11F_11F_REV;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move all the VK changes in a different CL (or PR), seems unrelated.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread filament/src/details/Renderer.cpp Outdated
// fg.forwardResource(fgViewRenderTarget, debug ? debug : input);

fg.forwardResource(fgViewRenderTarget, input);
FgviewerManager::handleFgReadbacks(engine, fg, viewRenderTarget, view.getViewHandle());
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this have to be done before present()?

IF not, you could hide all this inside the FRameGraph itself.

Like you could call handleFgReadbacks at the beginging of compile(), update() at the end of compile() and tick() at the end of execute().

It would be all contained inside the FrameGraph, and FgviewerManager could become an implementation detail of FrameGraph.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved all the fgviewer logic to FrameGraph

@poweifeng poweifeng force-pushed the pf/fgviewer-buffer-view branch 2 times, most recently from 5a16ffe to 3da9dea Compare April 14, 2026 05:59
* Intermediate Buffer Visualization: Enabled live monitoring of internal
  `FrameGraph` render targets directly in the `fgviewer` web UI.
* HTTP Polling Architecture: Switched from WebSocket binary pushes to native
  `<img>` polling (`/api/image`)
* Robust Resource Tracking: Replaced string-based lookups with
  `(ViewId, ResourceId)` composite keys to prevent cross-view collisions and
  ensure accurate reads.
* Format Post-Processing: Extracted readback conversions (HDR tonemapping,
  depth normalization, MSAA downsampling, single-channel expansion) into
  `DebugServer`.
* UI Polish: Added a live-updating full-screen image modal and explicitly
  filtered internal debug passes from the Graphviz/JSON exports to prevent
  DOM thrashing.
* WIP: Currently Unsupported:
  * GL backend: failed with:
      OpenGL framebuffer error 0x8cd6 (GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) in "readTexture" at line 4198
  * Mipmaps & Subresources: Reading specific mip levels or array layers is
    explicitly skipped.
  * Shadowmaps: Variance Shadow Maps (VSM) will physically evaluate to `0.0`
    and appear completely empty/black in scenes without active shadow
    casters (due to inverted-Z).
  * Stencil: Resolving and reading stencil buffer data is not supported by
    the backend.
* WIP: Untested outside of MacOS+metal/vk
@poweifeng poweifeng force-pushed the pf/fgviewer-buffer-view branch from 3da9dea to 496a1ef Compare April 14, 2026 06:08
@poweifeng poweifeng enabled auto-merge (squash) April 21, 2026 19:15
@poweifeng poweifeng merged commit 2ae4dc4 into main Apr 21, 2026
18 checks passed
@poweifeng poweifeng deleted the pf/fgviewer-buffer-view branch April 21, 2026 20:30
poweifeng added a commit that referenced this pull request Apr 21, 2026
* Intermediate Buffer Visualization: Enabled live monitoring of internal
  `FrameGraph` render targets directly in the `fgviewer` web UI.
* HTTP Polling Architecture: Switched from WebSocket binary pushes to native
  `<img>` polling (`/api/image`)
* Robust Resource Tracking: Replaced string-based lookups with
  `(ViewId, ResourceId)` composite keys to prevent cross-view collisions and
  ensure accurate reads.
* Format Post-Processing: Extracted readback conversions (HDR tonemapping,
  depth normalization, MSAA downsampling, single-channel expansion) into
  `DebugServer`.
* UI Polish: Added a live-updating full-screen image modal and explicitly
  filtered internal debug passes from the Graphviz/JSON exports to prevent
  DOM thrashing.
* WIP: Currently Unsupported:
  * GL backend: failed with:
      OpenGL framebuffer error 0x8cd6 (GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) in "readTexture" at line 4198
  * Mipmaps & Subresources: Reading specific mip levels or array layers is
    explicitly skipped.
  * Shadowmaps: Variance Shadow Maps (VSM) will physically evaluate to `0.0`
    and appear completely empty/black in scenes without active shadow
    casters (due to inverted-Z).
  * Stencil: Resolving and reading stencil buffer data is not supported by
    the backend.
* WIP: Untested outside of MacOS+metal/vk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

internal Issue/PR does not affect clients

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants