feat(rendering): add volumetric performance coverage

This commit is contained in:
2026-04-19 00:27:13 +08:00
parent d2017b251c
commit f4fef59b2f
7 changed files with 501 additions and 25 deletions

View File

@@ -25,6 +25,8 @@
#include "../../RHI/integration/fixtures/RHIIntegrationFixture.h"
#include <filesystem>
#include <chrono>
#include <limits>
#include <memory>
#include <vector>
@@ -41,6 +43,15 @@ constexpr uint32_t kFrameWidth = 1280;
constexpr uint32_t kFrameHeight = 720;
constexpr const char* kCloudVolumeRelativePath = "Res/Volumes/cloud.nvdb";
struct StableFrameStats {
int warmupFrames = 0;
int measuredFrames = 0;
double averageFrameTimeMs = 0.0;
double minFrameTimeMs = 0.0;
double maxFrameTimeMs = 0.0;
double averageFps = 0.0;
};
inline Material* CreateVolumetricMaterial(
const char* name,
const char* path,
@@ -50,7 +61,10 @@ inline Material* CreateVolumetricMaterial(
float maxSteps = 2000.0f,
float ambientStrength = 0.005f,
const Vector3& lightDirection = Vector3(0.5f, 0.8f, 0.3f),
float lightSamples = 8.0f) {
float lightSamples = 8.0f,
float enableEntryHdda = 1.0f,
float enableEmptySpaceSkipping = 1.0f,
float enableEarlyTermination = 1.0f) {
auto* material = new Material();
IResource::ConstructParams params = {};
params.name = name;
@@ -66,6 +80,9 @@ inline Material* CreateVolumetricMaterial(
material->SetFloat("_AmbientStrength", ambientStrength);
material->SetFloat4("_LightDirection", Vector4(lightDirection, 0.0f));
material->SetFloat("_LightSamples", lightSamples);
material->SetFloat("_EnableEntryHdda", enableEntryHdda);
material->SetFloat("_EnableEmptySpaceSkipping", enableEmptySpaceSkipping);
material->SetFloat("_EnableEarlyTermination", enableEarlyTermination);
return material;
}
@@ -250,6 +267,72 @@ protected:
}
}
StableFrameStats RenderAndMeasureStableFrames(
int warmupFrameCount,
int measuredFrameCount,
const char* screenshotFilename = nullptr) {
StableFrameStats stats = {};
stats.warmupFrames = warmupFrameCount;
stats.measuredFrames = measuredFrameCount;
RHICommandQueue* commandQueue = GetCommandQueue();
RHISwapChain* swapChain = GetSwapChain();
if (commandQueue == nullptr || swapChain == nullptr) {
ADD_FAILURE() << "RenderAndMeasureStableFrames requires a valid command queue and swap chain";
return stats;
}
auto renderPresentedFrame = [&]() {
BeginRender();
RenderFrame();
swapChain->Present(0, 0);
};
for (int frameIndex = 0; frameIndex < warmupFrameCount; ++frameIndex) {
if (frameIndex > 0) {
commandQueue->WaitForPreviousFrame();
}
renderPresentedFrame();
}
if (warmupFrameCount > 0) {
commandQueue->WaitForPreviousFrame();
}
using Clock = std::chrono::steady_clock;
double totalFrameTimeMs = 0.0;
double minFrameTimeMs = std::numeric_limits<double>::max();
double maxFrameTimeMs = 0.0;
for (int frameIndex = 0; frameIndex < measuredFrameCount; ++frameIndex) {
const Clock::time_point frameStart = Clock::now();
renderPresentedFrame();
commandQueue->WaitForPreviousFrame();
const double frameTimeMs = std::chrono::duration<double, std::milli>(
Clock::now() - frameStart).count();
totalFrameTimeMs += frameTimeMs;
minFrameTimeMs = (std::min)(minFrameTimeMs, frameTimeMs);
maxFrameTimeMs = (std::max)(maxFrameTimeMs, frameTimeMs);
}
if (measuredFrameCount > 0) {
stats.averageFrameTimeMs = totalFrameTimeMs / static_cast<double>(measuredFrameCount);
stats.minFrameTimeMs = minFrameTimeMs;
stats.maxFrameTimeMs = maxFrameTimeMs;
stats.averageFps = stats.averageFrameTimeMs > 0.0
? 1000.0 / stats.averageFrameTimeMs
: 0.0;
}
if (screenshotFilename != nullptr && screenshotFilename[0] != '\0') {
BeginRender();
RenderFrame();
commandQueue->WaitForIdle();
EXPECT_TRUE(TakeScreenshot(screenshotFilename));
}
return stats;
}
RHIResourceView* GetCurrentBackBufferView() {
const int backBufferIndex = GetCurrentBackBufferIndex();
if (backBufferIndex < 0) {