Skip to content

Commit 4bc0c3e

Browse files
committed
fgviewer: visualize intermediate buffers (#9862)
* 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
1 parent dc7fc54 commit 4bc0c3e

25 files changed

Lines changed: 1436 additions & 239 deletions

filament/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ set(SRCS
156156
src/ds/StructureDescriptorSet.cpp
157157
src/fg/Blackboard.cpp
158158
src/fg/DependencyGraph.cpp
159+
src/fg/FgviewerManager.cpp
159160
src/fg/FrameGraph.cpp
160161
src/fg/FrameGraphPass.cpp
161162
src/fg/FrameGraphResources.cpp

filament/backend/src/DataReshaper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ class DataReshaper {
284284
switch (srcType) {
285285
case UBYTE: reshaper = reshapeImageImpl<float, uint8_t>; break;
286286
case FLOAT: reshaper = reshapeImageImpl<float, float>; break;
287+
case HALF: reshaper = reshapeImageImpl<float, math::half>; break;
287288
case INT: reshaper = reshapeImageImpl<float, int32_t>; break;
288289
case UINT: reshaper = reshapeImageImpl<float, uint32_t>; break;
289290
case UINT_10F_11F_11F_REV:
@@ -300,6 +301,7 @@ class DataReshaper {
300301
switch (srcType) {
301302
case UBYTE: reshaper = reshapeImageImpl<int32_t, uint8_t>; break;
302303
case FLOAT: reshaper = reshapeImageImpl<int32_t, float>; break;
304+
case HALF: reshaper = reshapeImageImpl<int32_t, math::half>; break;
303305
case INT: reshaper = reshapeImageImpl<int32_t, int32_t>; break;
304306
case UINT: reshaper = reshapeImageImpl<int32_t, uint32_t>; break;
305307
case UINT_10F_11F_11F_REV:
@@ -316,6 +318,7 @@ class DataReshaper {
316318
switch (srcType) {
317319
case UBYTE: reshaper = reshapeImageImpl<uint32_t, uint8_t>; break;
318320
case FLOAT: reshaper = reshapeImageImpl<uint32_t, float>; break;
321+
case HALF: reshaper = reshapeImageImpl<uint32_t, math::half>; break;
319322
case INT: reshaper = reshapeImageImpl<uint32_t, int32_t>; break;
320323
case UINT: reshaper = reshapeImageImpl<uint32_t, uint32_t>; break;
321324
case UINT_10F_11F_11F_REV:

filament/src/PostProcessManager.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3687,7 +3687,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blitDepth(FrameGraph& fg,
36873687

36883688
FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
36893689
utils::StaticString outputBufferName, FrameGraphId<FrameGraphTexture> const input,
3690-
FrameGraphTexture::Descriptor outDesc) noexcept {
3690+
FrameGraphTexture::Descriptor outDesc, utils::CString customPassName) noexcept {
36913691

36923692
// Don't do anything if we're not a MSAA buffer
36933693
auto const& inDesc = fg.getDescriptor(input);
@@ -3701,7 +3701,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
37013701
// through shaders or some other manipulation.
37023702
if ((isDepthFormat(inDesc.format) || isStencilFormat(inDesc.format)) &&
37033703
(!mDepthStencilResolveSupported)) {
3704-
return resolveDepthWithShader(fg, outputBufferName, input, outDesc);
3704+
return resolveDepthWithShader(fg, outputBufferName, input, outDesc,
3705+
std::move(customPassName));
37053706
}
37063707

37073708
outDesc.width = inDesc.width;
@@ -3714,7 +3715,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
37143715
FrameGraphId<FrameGraphTexture> output;
37153716
};
37163717

3717-
auto const& ppResolve = fg.addPass<ResolveData>("resolve",
3718+
auto const& ppResolve = fg.addPass<ResolveData>(
3719+
customPassName.empty() ? "resolve" : customPassName.c_str(),
37183720
[&](FrameGraph::Builder& builder, auto& data) {
37193721
data.input = builder.read(input, FrameGraphTexture::Usage::BLIT_SRC);
37203722
data.output = builder.createTexture(outputBufferName, outDesc);
@@ -3742,7 +3744,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
37423744

37433745
FrameGraphId<FrameGraphTexture> PostProcessManager::resolveDepthWithShader(FrameGraph& fg,
37443746
utils::StaticString outputBufferName, FrameGraphId<FrameGraphTexture> const input,
3745-
FrameGraphTexture::Descriptor outDesc) noexcept {
3747+
FrameGraphTexture::Descriptor outDesc, utils::CString customPassName) noexcept {
37463748

37473749
// Don't do anything if we're not a MSAA buffer
37483750
auto const& inDesc = fg.getDescriptor(input);
@@ -3765,7 +3767,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolveDepthWithShader(Frame
37653767
FrameGraphId<FrameGraphTexture> output;
37663768
};
37673769

3768-
auto const& ppResolve = fg.addPass<ResolveData>("resolveDepthWithShader",
3770+
auto const& ppResolve = fg.addPass<ResolveData>(
3771+
customPassName.empty() ? "resolveDepthWithShader" : customPassName.c_str(),
37693772
[&](FrameGraph::Builder& builder, auto& data) {
37703773
data.input = builder.sample(input);
37713774
data.output = builder.createTexture(outputBufferName, outDesc);

filament/src/PostProcessManager.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,16 +302,19 @@ class PostProcessManager {
302302

303303
// Resolves base level of input and outputs a texture from outDesc.
304304
// outDesc with, height, format and samples will be overridden.
305-
FrameGraphId<FrameGraphTexture> resolve(FrameGraph& fg,
306-
utils::StaticString outputBufferName, FrameGraphId<FrameGraphTexture> input,
307-
FrameGraphTexture::Descriptor outDesc) noexcept;
305+
// customPassName is an optional parameter to override the default "resolve" pass name.
306+
FrameGraphId<FrameGraphTexture> resolve(FrameGraph& fg, utils::StaticString outputBufferName,
307+
FrameGraphId<FrameGraphTexture> input, FrameGraphTexture::Descriptor outDesc,
308+
utils::CString customPassName = {}) noexcept;
308309

309310
// Resolves base level of input and outputs a texture from outDesc using a shader instead of
310311
// driver-implemented API.
311312
// outDesc with, height, format and samples will be overridden.
313+
// customPassName is an optional parameter to override the default "resolveDepthWithShader" pass
314+
// name.
312315
FrameGraphId<FrameGraphTexture> resolveDepthWithShader(FrameGraph& fg,
313316
utils::StaticString outputBufferName, FrameGraphId<FrameGraphTexture> input,
314-
FrameGraphTexture::Descriptor outDesc) noexcept;
317+
FrameGraphTexture::Descriptor outDesc, utils::CString customPassName = {}) noexcept;
315318

316319
FrameGraphId<FrameGraphTexture> gaussianBlurPass(FrameGraph& fg,
317320
FrameGraphId<FrameGraphTexture> input,

filament/src/details/Engine.cpp

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@
9696
#include <stdlib.h>
9797
#include <string.h>
9898

99+
#if FILAMENT_ENABLE_FGVIEWER
100+
#include "fg/FgviewerManager.h"
101+
#endif
102+
99103
#include "generated/resources/materials.h"
100104

101105
using namespace filament::math;
@@ -108,6 +112,27 @@ using namespace filaflat;
108112

109113
namespace {
110114

115+
#if FILAMENT_ENABLE_FGVIEWER || FILAMENT_ENABLE_MATDBG
116+
utils::CString getPortString(std::string_view serviceType) {
117+
#ifndef __ANDROID__
118+
char const* portString = getenv(serviceType.data());
119+
#else
120+
char const* portString = [&]() -> char const*{
121+
if (serviceType == "FILAMENT_MATDBG_PORT") {
122+
return "8081";
123+
} else if (serviceType == "FILAMENT_FGVIEWER_PORT") {
124+
return "8085";
125+
}
126+
return nullptr;
127+
}();
128+
#endif
129+
if (portString) {
130+
return utils::CString { portString };
131+
}
132+
return {};
133+
}
134+
#endif // FILAMENT_ENABLE_FGVIEWER || FILAMENT_ENABLE_MATDBG
135+
111136
Platform::DriverConfig getDriverConfig(FEngine* instance) {
112137
Platform::DriverConfig const driverConfig {
113138
.featureFlagManager = instance,
@@ -870,13 +895,8 @@ int FEngine::loop() {
870895
}
871896

872897
#if FILAMENT_ENABLE_MATDBG
873-
#ifdef __ANDROID__
874-
const char* portString = "8081";
875-
#else
876-
const char* portString = getenv("FILAMENT_MATDBG_PORT");
877-
#endif
878-
if (portString != nullptr) {
879-
const int port = atoi(portString);
898+
if (auto portString = getPortString("FILAMENT_MATDBG_PORT"); !portString.empty()) {
899+
const int port = atoi(portString.c_str());
880900

881901
ShaderLanguage preferredLanguage = ShaderLanguage::UNSPECIFIED;
882902
if (mBackend == Backend::METAL) {
@@ -900,20 +920,13 @@ int FEngine::loop() {
900920
#endif
901921

902922
#if FILAMENT_ENABLE_FGVIEWER // NOLINT(*-include-cleaner)
903-
#ifdef __ANDROID__
904-
const char* fgviewerPortString = "8085";
905-
#else
906-
const char* fgviewerPortString = getenv("FILAMENT_FGVIEWER_PORT");
907-
#endif
908-
if (fgviewerPortString != nullptr) {
909-
const int fgviewerPort = atoi(fgviewerPortString);
910-
debug.fgviewerServer = new fgviewer::DebugServer(fgviewerPort);
911-
923+
if (auto portString = getPortString("FILAMENT_FGVIEWER_PORT"); !portString.empty()) {
924+
debug.fgviewer = new FgviewerManager(*this, std::move(portString));
912925
// Sometimes the server can fail to spin up (e.g. if the above port is already in use).
913926
// When this occurs, carry onward, developers can look at civetweb.txt for details.
914-
if (!debug.fgviewerServer->isReady()) {
915-
delete debug.fgviewerServer;
916-
debug.fgviewerServer = nullptr;
927+
if (!debug.fgviewer->isReady()) {
928+
delete debug.fgviewer;
929+
debug.fgviewer = nullptr;
917930
}
918931
}
919932
#endif
@@ -930,8 +943,8 @@ int FEngine::loop() {
930943
}
931944
#endif
932945
#if FILAMENT_ENABLE_FGVIEWER
933-
if(debug.fgviewerServer) {
934-
delete debug.fgviewerServer;
946+
if (debug.fgviewer) {
947+
delete debug.fgviewer;
935948
}
936949
#endif
937950

@@ -1723,7 +1736,7 @@ FixedCapacityVector<Variant> FEngine::getMaterialCompileVariants(
17231736
if (Variant::isValidDepthVariant(depthVariant)) {
17241737
// if we have a valid depth variant, add the stereo and skinning bits
17251738
depthVariant.setStereo(view->hasStereo());
1726-
1739+
17271740
size_t const depthStart = variants.size();
17281741
variants.push_back(depthVariant);
17291742
apply(depthStart, skinning, &Variant::setSkinning);

filament/src/details/Engine.h

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
#include "details/Skybox.h"
4848
#include "details/Sync.h"
4949

50-
5150
#include <private/filament/EngineEnums.h>
5251

5352
#include <private/backend/CommandBufferQueue.h>
@@ -99,16 +98,8 @@ using MaterialKey = uint32_t;
9998
} // namespace filament::matdbg
10099
#endif
101100

102-
#if FILAMENT_ENABLE_FGVIEWER
103-
#include <fgviewer/DebugServer.h>
104-
#else
105-
namespace filament::fgviewer {
106-
class DebugServer;
107-
} // namespace filament::fgviewer
108-
#endif
109-
110101
namespace filament {
111-
102+
class FgviewerManager;
112103
class Renderer;
113104
class MaterialParser;
114105
class TextureCacheDisposer;
@@ -771,7 +762,7 @@ class FEngine : public Engine, public utils::FeatureFlagManager {
771762
bool combine_multiview_images = false;
772763
} stereo;
773764
matdbg::DebugServer* server = nullptr;
774-
fgviewer::DebugServer* fgviewerServer = nullptr;
765+
FgviewerManager* fgviewer = nullptr;
775766
} debug;
776767
};
777768

filament/src/details/Renderer.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,13 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
884884
*/
885885
FrameGraph fg(*mResourceAllocator,
886886
isProtectedContent ? FrameGraph::Mode::PROTECTED : FrameGraph::Mode::UNPROTECTED);
887+
888+
#if FILAMENT_ENABLE_FGVIEWER
889+
if (UTILS_LIKELY(engine.debug.fgviewer)) {
890+
fg.setFgviewerData(engine.debug.fgviewer, &view);
891+
}
892+
#endif
893+
887894
auto& blackboard = fg.getBlackboard();
888895

889896
PostProcessManager::ScreenSpaceRefConfig const ssrConfig = PostProcessManager::prepareMipmapSSR(
@@ -1550,18 +1557,10 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
15501557
// fg.forwardResource(fgViewRenderTarget, debug ? debug : input);
15511558

15521559
fg.forwardResource(fgViewRenderTarget, input);
1553-
15541560
fg.present(fgViewRenderTarget);
15551561

15561562
fg.compile();
15571563

1558-
#if FILAMENT_ENABLE_FGVIEWER
1559-
fgviewer::DebugServer* fgviewerServer = engine.debug.fgviewerServer;
1560-
if (UTILS_LIKELY(fgviewerServer)) {
1561-
fgviewerServer->update(view.getViewHandle(), fg.getFrameGraphInfo(view.getName()));
1562-
}
1563-
#endif
1564-
15651564
//utils::io::sstream graphviz;
15661565
//fg.export_graphviz(graphviz, view.getName());
15671566
//DLOG(INFO) << graphviz.c_str();

filament/src/details/View.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@
8484

8585
using namespace utils;
8686

87+
#if FILAMENT_ENABLE_FGVIEWER
88+
#include "fg/FgviewerManager.h"
89+
#endif
90+
8791
namespace filament {
8892

8993
using namespace backend;
@@ -172,10 +176,10 @@ FView::FView(FEngine& engine)
172176
#endif
173177

174178
#if FILAMENT_ENABLE_FGVIEWER
175-
fgviewer::DebugServer* fgviewerServer = engine.debug.fgviewerServer;
176-
if (UTILS_LIKELY(fgviewerServer)) {
179+
FgviewerManager* fgviewerManager = engine.debug.fgviewer;
180+
if (UTILS_LIKELY(fgviewerManager)) {
177181
mFrameGraphViewerViewHandle =
178-
fgviewerServer->createView(utils::CString(getName()));
182+
fgviewerManager->createView(utils::CString(getName()));
179183
}
180184
#endif
181185

@@ -225,9 +229,9 @@ void FView::terminate(FEngine& engine) {
225229
#endif
226230

227231
#if FILAMENT_ENABLE_FGVIEWER
228-
fgviewer::DebugServer* fgviewerServer = engine.debug.fgviewerServer;
229-
if (UTILS_LIKELY(fgviewerServer)) {
230-
fgviewerServer->destroyView(mFrameGraphViewerViewHandle);
232+
FgviewerManager* fgviewerManager = engine.debug.fgviewer;
233+
if (UTILS_LIKELY(fgviewerManager)) {
234+
fgviewerManager->destroyView(mFrameGraphViewerViewHandle);
231235
}
232236
#endif
233237
}

0 commit comments

Comments
 (0)