mirror of
https://github.com/wolfpld/tracy.git
synced 2026-08-27 05:28:24 +00:00
calibration stability
This commit is contained in:
@@ -105,7 +105,50 @@ namespace tracy
|
||||
uint32_t m_queryLimit = 0;
|
||||
|
||||
std::vector<uint64_t> m_shadowBuffer;
|
||||
uint64_t m_prevCalibGpuTime = 0;
|
||||
|
||||
struct Calibration {
|
||||
uint64_t cpuTime = 0;
|
||||
uint64_t gpuTime = 0;
|
||||
static bool WaitQueueIdle(WGPUQueue queue, WGPUInstance instance)
|
||||
{
|
||||
bool gpuDone = false;
|
||||
WGPUQueueWorkDoneCallbackInfo doneCB = {};
|
||||
doneCB.mode = WGPUCallbackMode_AllowProcessEvents;
|
||||
doneCB.callback = [](WGPUQueueWorkDoneStatus, WGPUStringView, void* userData, void*) {
|
||||
*static_cast<bool*>(userData) = true;
|
||||
};
|
||||
doneCB.userdata1 = &gpuDone;
|
||||
wgpuQueueOnSubmittedWorkDone(queue, doneCB);
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
|
||||
while (!gpuDone && std::chrono::steady_clock::now() < deadline)
|
||||
wgpuInstanceProcessEvents(instance);
|
||||
return gpuDone;
|
||||
}
|
||||
static const uint64_t* MapBufferSync(WGPUBuffer buffer, WGPUInstance instance)
|
||||
{
|
||||
struct MapCtx { WGPUMapAsyncStatus status = (WGPUMapAsyncStatus)0; } ctx;
|
||||
WGPUBufferMapCallbackInfo cbInfo = {};
|
||||
cbInfo.mode = WGPUCallbackMode_AllowProcessEvents;
|
||||
cbInfo.callback = [](WGPUMapAsyncStatus status, WGPUStringView, void* userData, void*) {
|
||||
auto* ctx = static_cast<MapCtx*>(userData);
|
||||
ctx->status = status;
|
||||
};
|
||||
cbInfo.userdata1 = &ctx;
|
||||
size_t offset = 0;
|
||||
size_t size = 2 * sizeof(uint64_t);
|
||||
wgpuBufferMapAsync(buffer, WGPUMapMode_Read, offset, size, cbInfo);
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
|
||||
while (ctx.status == 0 && std::chrono::steady_clock::now() < deadline)
|
||||
wgpuInstanceProcessEvents(instance);
|
||||
|
||||
if (ctx.status != WGPUMapAsyncStatus_Success) return nullptr;
|
||||
auto data = wgpuBufferGetConstMappedRange(buffer, offset, size);
|
||||
return static_cast<const uint64_t*>(data);
|
||||
}
|
||||
bool Update(uint64_t tcpu0, uint64_t tcpu1, uint64_t tgpu) { return false; }
|
||||
} m_calibration;
|
||||
|
||||
tracy_force_inline void SubmitQueueItem(tracy::QueueItem* item)
|
||||
{
|
||||
@@ -119,9 +162,6 @@ namespace tracy
|
||||
{
|
||||
ZoneScoped;
|
||||
|
||||
WGPUCommandEncoder enc = wgpuDeviceCreateCommandEncoder(m_device, nullptr);
|
||||
if (!enc) { TracyWebGPUPanic("Failed to create calibration command encoder.", return false); }
|
||||
|
||||
// wgpuCommandEncoderWriteTimestamp is deprecated and returns 0 on Metal.
|
||||
// Use a render pass with an actual draw call: on Metal TBDR, begin-of-pass
|
||||
// timestamps fire at tile rasterization start. An empty render pass (no
|
||||
@@ -140,7 +180,7 @@ namespace tracy
|
||||
WGPUShaderModuleDescriptor smDesc = {};
|
||||
smDesc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&wgslSrc);
|
||||
WGPUShaderModule calibShader = wgpuDeviceCreateShaderModule(m_device, &smDesc);
|
||||
if (!calibShader) { wgpuCommandEncoderRelease(enc); TracyWebGPUPanic("Failed to create calibration shader.", return false); }
|
||||
if (!calibShader) { TracyWebGPUPanic("Failed to create calibration shader.", return false); }
|
||||
|
||||
WGPUTextureDescriptor texDesc = {};
|
||||
texDesc.usage = WGPUTextureUsage_RenderAttachment;
|
||||
@@ -150,9 +190,9 @@ namespace tracy
|
||||
texDesc.mipLevelCount = 1;
|
||||
texDesc.sampleCount = 1;
|
||||
WGPUTexture tex = wgpuDeviceCreateTexture(m_device, &texDesc);
|
||||
if (!tex) { wgpuShaderModuleRelease(calibShader); wgpuCommandEncoderRelease(enc); TracyWebGPUPanic("Failed to create calibration scratch texture.", return false); }
|
||||
if (!tex) { wgpuShaderModuleRelease(calibShader); TracyWebGPUPanic("Failed to create calibration scratch texture.", return false); }
|
||||
WGPUTextureView texView = wgpuTextureCreateView(tex, nullptr);
|
||||
if (!texView) { wgpuTextureRelease(tex); wgpuShaderModuleRelease(calibShader); wgpuCommandEncoderRelease(enc); TracyWebGPUPanic("Failed to create calibration scratch texture view.", return false); }
|
||||
if (!texView) { wgpuTextureRelease(tex); wgpuShaderModuleRelease(calibShader); TracyWebGPUPanic("Failed to create calibration scratch texture view.", return false); }
|
||||
|
||||
WGPUColorTargetState colorTarget = {};
|
||||
colorTarget.format = WGPUTextureFormat_BGRA8Unorm;
|
||||
@@ -169,11 +209,13 @@ namespace tracy
|
||||
pipeDesc.multisample.count = 1;
|
||||
pipeDesc.fragment = &fragState;
|
||||
WGPURenderPipeline calibPipeline = wgpuDeviceCreateRenderPipeline(m_device, &pipeDesc);
|
||||
if (!calibPipeline) { wgpuTextureViewRelease(texView); wgpuTextureRelease(tex); wgpuShaderModuleRelease(calibShader); wgpuCommandEncoderRelease(enc); TracyWebGPUPanic("Failed to create calibration pipeline.", return false); }
|
||||
if (!calibPipeline) { wgpuTextureViewRelease(texView); wgpuTextureRelease(tex); wgpuShaderModuleRelease(calibShader); TracyWebGPUPanic("Failed to create calibration pipeline.", return false); }
|
||||
|
||||
//const uint64_t calibTicket = NextQueryId();
|
||||
//const uint32_t calibSlotB = RingIndex(calibTicket);
|
||||
//const uint32_t calibSlotE = calibSlotB + 1;
|
||||
//m_previousCheckpoint = m_queryCounter.load();
|
||||
|
||||
const uint32_t calibSlotB = 0;
|
||||
const uint32_t calibSlotE = 1;
|
||||
|
||||
@@ -193,86 +235,70 @@ namespace tracy
|
||||
passDesc.colorAttachments = &att;
|
||||
passDesc.timestampWrites = &anchorTs;
|
||||
|
||||
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(enc, &passDesc);
|
||||
wgpuRenderPassEncoderSetPipeline(pass, calibPipeline);
|
||||
wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
|
||||
wgpuRenderPassEncoderEnd(pass);
|
||||
wgpuRenderPassEncoderRelease(pass);
|
||||
int64_t minCpuRange = 999'999'999'999;
|
||||
for (int i=0; i<10; ++i)
|
||||
{
|
||||
WGPUCommandEncoder enc = wgpuDeviceCreateCommandEncoder(m_device, nullptr);
|
||||
if (!enc) { TracyWebGPUPanic("Failed to create calibration command encoder.", return false); }
|
||||
|
||||
WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(enc, &passDesc);
|
||||
wgpuRenderPassEncoderSetPipeline(pass, calibPipeline);
|
||||
wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
|
||||
wgpuRenderPassEncoderEnd(pass);
|
||||
wgpuRenderPassEncoderRelease(pass);
|
||||
|
||||
wgpuCommandEncoderResolveQuerySet(enc, m_querySet, calibSlotB, 2, m_resolveBuffer, calibSlotB * sizeof(uint64_t));
|
||||
wgpuCommandEncoderCopyBufferToBuffer(enc, m_resolveBuffer, calibSlotB * sizeof(uint64_t), m_readbackSlots[0].buffer, calibSlotB * sizeof(uint64_t), 2 * sizeof(uint64_t));
|
||||
|
||||
WGPUCommandBuffer cmd = wgpuCommandEncoderFinish(enc, nullptr);
|
||||
wgpuCommandEncoderRelease(enc);
|
||||
if (!cmd) { TracyWebGPUPanic("Failed to finish calibration command encoder.", return false); }
|
||||
|
||||
Calibration::WaitQueueIdle(m_queue, m_instance);
|
||||
int64_t cpu [2] = {};
|
||||
cpu[0] = Profiler::GetTime();
|
||||
wgpuQueueSubmit(m_queue, 1, &cmd);
|
||||
wgpuCommandBufferRelease(cmd);
|
||||
Calibration::WaitQueueIdle(m_queue, m_instance);
|
||||
cpu[1] = Profiler::GetTime();
|
||||
int64_t cpuRange = cpu[1] - cpu[0];
|
||||
auto gpu = Calibration::MapBufferSync(m_readbackSlots[0].buffer, m_instance);
|
||||
TracyWebGPUAssert(gpu != nullptr);
|
||||
fprintf(stdout, "CalibrateClocks() -> %llu | %llu | %lld /// %lld\n", gpu[0], gpu[1], gpu[1]-gpu[0], cpuRange);
|
||||
if (cpuRange < minCpuRange)
|
||||
{
|
||||
outCpuTime = cpu[1]; // static_cast<uint64_t>(t0 + (t1-t0)/2);
|
||||
outGpuTime = gpu[0];
|
||||
minCpuRange = cpuRange;
|
||||
}
|
||||
wgpuBufferUnmap(m_readbackSlots[0].buffer);
|
||||
|
||||
if (outGpuTime < m_calibration.gpuTime)
|
||||
fprintf(stdout, "CalibrateClocks() -> WARNING!!! going backwards!\n%llu\n%llu\n%lld\n", m_calibration.gpuTime, outGpuTime, outGpuTime - m_calibration.gpuTime);
|
||||
m_calibration.gpuTime = outGpuTime;
|
||||
}
|
||||
|
||||
wgpuRenderPipelineRelease(calibPipeline);
|
||||
wgpuShaderModuleRelease(calibShader);
|
||||
wgpuTextureViewRelease(texView);
|
||||
wgpuTextureRelease(tex);
|
||||
|
||||
wgpuCommandEncoderResolveQuerySet(enc, m_querySet, calibSlotB, 2, m_resolveBuffer, calibSlotB * sizeof(uint64_t));
|
||||
wgpuCommandEncoderCopyBufferToBuffer(enc, m_resolveBuffer, calibSlotB * sizeof(uint64_t), m_readbackSlots[0].buffer, calibSlotB * sizeof(uint64_t), 2 * sizeof(uint64_t));
|
||||
|
||||
WGPUCommandBuffer cmd = wgpuCommandEncoderFinish(enc, nullptr);
|
||||
wgpuCommandEncoderRelease(enc);
|
||||
if (!cmd) { TracyWebGPUPanic("Failed to finish calibration command encoder.", return false); }
|
||||
|
||||
auto t0 = Profiler::GetTime();
|
||||
wgpuQueueSubmit(m_queue, 1, &cmd);
|
||||
wgpuCommandBufferRelease(cmd);
|
||||
|
||||
// Wait for the GPU to finish executing the command buffer before mapping.
|
||||
bool gpuDone = false;
|
||||
WGPUQueueWorkDoneCallbackInfo doneCB = {};
|
||||
doneCB.mode = WGPUCallbackMode_AllowProcessEvents;
|
||||
doneCB.callback = [](WGPUQueueWorkDoneStatus, WGPUStringView, void* ud, void*) {
|
||||
*static_cast<bool*>(ud) = true;
|
||||
};
|
||||
doneCB.userdata1 = &gpuDone;
|
||||
wgpuQueueOnSubmittedWorkDone(m_queue, doneCB);
|
||||
|
||||
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(2);
|
||||
while (!gpuDone && std::chrono::steady_clock::now() < deadline)
|
||||
wgpuInstanceProcessEvents(m_instance);
|
||||
|
||||
struct MapCtx { WGPUBuffer buffer; uint32_t slotB; uint64_t gpuTime = 0; bool ok = false; };
|
||||
MapCtx mctx{ m_readbackSlots[0].buffer, calibSlotB };
|
||||
WGPUBufferMapCallbackInfo cbInfo = {};
|
||||
cbInfo.mode = WGPUCallbackMode_AllowProcessEvents;
|
||||
cbInfo.callback = [](WGPUMapAsyncStatus status, WGPUStringView, void* ud, void*) {
|
||||
auto* ctx = static_cast<MapCtx*>(ud);
|
||||
if (status != WGPUMapAsyncStatus_Success) return;
|
||||
const auto* ts = static_cast<const uint64_t*>(
|
||||
wgpuBufferGetConstMappedRange(ctx->buffer, ctx->slotB * sizeof(uint64_t), 2 * sizeof(uint64_t)));
|
||||
if (ts)
|
||||
{
|
||||
ctx->gpuTime = ts[0];
|
||||
ctx->ok = true;
|
||||
fprintf(stdout, "CalibrateClocks() -> %llu | %llu | %lld\n", ts[0], ts[1], ts[1]-ts[0]);
|
||||
}
|
||||
wgpuBufferUnmap(ctx->buffer);
|
||||
};
|
||||
cbInfo.userdata1 = &mctx;
|
||||
wgpuBufferMapAsync(m_readbackSlots[0].buffer, WGPUMapMode_Read,
|
||||
calibSlotB * sizeof(uint64_t), 2 * sizeof(uint64_t), cbInfo);
|
||||
|
||||
while (!mctx.ok && std::chrono::steady_clock::now() < deadline)
|
||||
wgpuInstanceProcessEvents(m_instance);
|
||||
//m_previousCheckpoint = m_queryCounter.load();
|
||||
|
||||
auto t1 = Profiler::GetTime();
|
||||
//outCpuTime = static_cast<uint64_t>(t0 + (t1-t0)/2);
|
||||
outCpuTime = t1;
|
||||
|
||||
if (!mctx.ok)
|
||||
{
|
||||
TracyWebGPUPanic("Failed to calibrate CPU/GPU clocks.", return false);
|
||||
}
|
||||
|
||||
outGpuTime = mctx.gpuTime;
|
||||
fprintf(stdout, "CalibrateClocks() -> %llu\n", outGpuTime);
|
||||
if (outGpuTime < m_prevCalibGpuTime)
|
||||
fprintf(stdout, "CalibrateClocks() -> WARNING!!! going backwards!\n%llu\n%llu\n%lld\n", m_prevCalibGpuTime, outGpuTime, outGpuTime-m_prevCalibGpuTime);
|
||||
m_prevCalibGpuTime = outGpuTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
static bool SetupDevice(WGPUDeviceDescriptor& deviceDescriptor)
|
||||
{
|
||||
static constexpr int MaxFeatures = 128;
|
||||
static WGPUFeatureName features [MaxFeatures] = {};
|
||||
|
||||
int n = deviceDescriptor.requiredFeatureCount;
|
||||
assert(n < MaxFeatures && "Too many required features in WGPUDeviceDescriptor");
|
||||
if (n > 0 && deviceDescriptor.requiredFeatures)
|
||||
memcpy(features, deviceDescriptor.requiredFeatures, n * sizeof(WGPUFeatureName));
|
||||
|
||||
features[n++] = WGPUFeatureName_TimestampQuery;
|
||||
|
||||
// piggy-back on WGPU_DAWN_TOGGLES_DESCRIPTOR_INIT to detect Dawn header
|
||||
# ifdef WGPU_DAWN_TOGGLES_DESCRIPTOR_INIT
|
||||
fprintf(stderr, "[INFO] [DAWN] ENABLING RAW TIMESTAMP TICKS (disabling ns conversion + quantization)\n");
|
||||
@@ -287,7 +313,20 @@ namespace tracy
|
||||
togglesDesc.enabledToggles = dawnEnabledToggles;
|
||||
togglesDesc.enabledToggleCount = 1;
|
||||
deviceDescriptor.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&togglesDesc);
|
||||
# else
|
||||
// wgpu-native: passTimestampWrites requires the non-standard
|
||||
// TIMESTAMP_QUERY_INSIDE_PASSES device feature in addition to
|
||||
// the standard TimestampQuery feature.
|
||||
fprintf(stderr, "[INFO] [WGPU] Requesting TimestampQueryInsidePasses native feature\n");
|
||||
{
|
||||
constexpr auto WGPUNativeFeature_TimestampQueryInsideEncoders = 0x00030024;
|
||||
constexpr auto WGPUNativeFeature_TimestampQueryInsidePasses = 0x00030025;
|
||||
features[n++] = (WGPUFeatureName)WGPUNativeFeature_TimestampQueryInsideEncoders;
|
||||
//features[n++] = (WGPUFeatureName)WGPUNativeFeature_TimestampQueryInsidePasses;
|
||||
}
|
||||
# endif
|
||||
deviceDescriptor.requiredFeatures = features;
|
||||
deviceDescriptor.requiredFeatureCount = static_cast<uint32_t>(n);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -597,17 +636,17 @@ namespace tracy
|
||||
|
||||
WGPUPassTimestampWrites m_timestampWrites = {};
|
||||
|
||||
void ResolveQueryBatch(uint32_t queryId)
|
||||
void ResolveQueryBatch(uint32_t queryBatchStartId)
|
||||
{
|
||||
// 32 queries = 32 * 8 bytes = 256 bytes
|
||||
const uint32_t blockStart = queryId - 30;
|
||||
TracyWebGPUAssert(blockStart % 32 == 0, return);
|
||||
TracyWebGPUAssert(queryBatchStartId % 32 == 0, return);
|
||||
queryBatchStartId = queryBatchStartId % m_ctx->m_queryLimit;
|
||||
|
||||
const uint64_t blockOffset = static_cast<uint64_t>(blockStart) * sizeof(uint64_t);
|
||||
const uint64_t blockOffset = static_cast<uint64_t>(queryBatchStartId) * sizeof(uint64_t);
|
||||
wgpuCommandEncoderResolveQuerySet(
|
||||
m_encoder,
|
||||
m_ctx->m_querySet,
|
||||
blockStart, 32,
|
||||
queryBatchStartId, 32,
|
||||
m_ctx->m_resolveBuffer,
|
||||
blockOffset // MUST be a multiple of (aligned to) 256...
|
||||
);
|
||||
@@ -624,11 +663,11 @@ namespace tracy
|
||||
);
|
||||
|
||||
// Advance this slot's high-water mark to cover the block just encoded.
|
||||
const uint64_t blockEnd = m_rawTicket + 2;
|
||||
const uint64_t blockEnd = m_rawTicket;
|
||||
uint64_t prev = slot.copiedUpto;
|
||||
while (prev < blockEnd &&
|
||||
!slot.copiedUpto.compare_exchange_weak(prev, blockEnd)) {}
|
||||
fprintf(stdout, "[TWG] WebGPUZoneScope [%d] (%d,%d)\n", (int)m_ctx->m_writeIdx, blockStart, queryId);
|
||||
fprintf(stdout, "[TWG] WebGPUZoneScope [%d] (%d,%d)\n", (int)m_ctx->m_writeIdx, queryBatchStartId, queryBatchStartId+32);
|
||||
}
|
||||
|
||||
tracy_force_inline void WriteQueueItem(const SourceLocationData* srcLocation, int32_t callstackDepth, uint32_t sourceLine, const char* sourceFile, size_t sourceFileLen, const char* functionName, size_t functionNameLen, const char* zoneName, size_t zoneNameLen)
|
||||
@@ -761,8 +800,8 @@ namespace tracy
|
||||
MemWrite(&item->gpuZoneEnd.context, m_ctx->GetId());
|
||||
Profiler::QueueSerialFinish();
|
||||
|
||||
if (m_queryId % 32 == 30)
|
||||
ResolveQueryBatch(m_queryId);
|
||||
if (m_queryId % 32 == 0)
|
||||
ResolveQueryBatch(m_queryId-32);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user