Query API: Re-enable Timestamp Query on Metal
Add timestamp query back in Metal supported extensions, and calculate the timestamp period based on CPU timestamps and GPU timestamps sampled on device. There is a crash issue (dawn:545) on macOS 10.15 on AMD devices, but cannot be reproduced on macOS 11.0+, we can just disable it on AMD with macOS 10.15 in the following CL. Bug: dawn:434 Change-Id: Icb4823e7c3115776e64c6a41fd0aea0f6536ccdf Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/51720 Commit-Queue: Hao Li <hao.x.li@intel.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
adc5cddde0
commit
84481bbb35
|
@ -221,10 +221,9 @@ namespace dawn_native { namespace metal {
|
||||||
if ([*mDevice supportsFamily:MTLGPUFamilyMac2] ||
|
if ([*mDevice supportsFamily:MTLGPUFamilyMac2] ||
|
||||||
[*mDevice supportsFamily:MTLGPUFamilyApple5]) {
|
[*mDevice supportsFamily:MTLGPUFamilyApple5]) {
|
||||||
mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery);
|
mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery);
|
||||||
|
// TODO(crbug.com/dawn/545): Crash occurs if we only call WriteTimestamp in a
|
||||||
// TODO(crbug.com/dawn/434): Not enable timestamp query here becuase it's not
|
// command encoder without any copy commands on Metal on AMD GPU.
|
||||||
// clear how to convert timestamps to nanoseconds on Metal.
|
mSupportedExtensions.EnableExtension(Extension::TimestampQuery);
|
||||||
// See https://github.com/gpuweb/gpuweb/issues/1325
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (@available(macOS 10.11, iOS 11.0, *)) {
|
if (@available(macOS 10.11, iOS 11.0, *)) {
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
|
|
||||||
namespace dawn_native { namespace metal {
|
namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct KalmanInfo;
|
||||||
|
}
|
||||||
|
|
||||||
class Device : public DeviceBase {
|
class Device : public DeviceBase {
|
||||||
public:
|
public:
|
||||||
static ResultOrError<Device*> Create(AdapterBase* adapter,
|
static ResultOrError<Device*> Create(AdapterBase* adapter,
|
||||||
|
@ -127,6 +131,15 @@ namespace dawn_native { namespace metal {
|
||||||
// a different thread so we guard access to it with a mutex.
|
// a different thread so we guard access to it with a mutex.
|
||||||
std::mutex mLastSubmittedCommandsMutex;
|
std::mutex mLastSubmittedCommandsMutex;
|
||||||
NSPRef<id<MTLCommandBuffer>> mLastSubmittedCommands;
|
NSPRef<id<MTLCommandBuffer>> mLastSubmittedCommands;
|
||||||
|
|
||||||
|
// The current estimation of timestamp period
|
||||||
|
float mTimestampPeriod = 1.0f;
|
||||||
|
// The base of CPU timestamp and GPU timestamp to measure the linear regression between GPU
|
||||||
|
// and CPU timestamps.
|
||||||
|
MTLTimestamp mCpuTimestamp API_AVAILABLE(macos(10.15), ios(14.0)) = 0;
|
||||||
|
MTLTimestamp mGpuTimestamp API_AVAILABLE(macos(10.15), ios(14.0)) = 0;
|
||||||
|
// The parameters for kalman filter
|
||||||
|
std::unique_ptr<KalmanInfo> mKalmanInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace dawn_native::metal
|
}} // namespace dawn_native::metal
|
||||||
|
|
|
@ -42,6 +42,69 @@
|
||||||
|
|
||||||
namespace dawn_native { namespace metal {
|
namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// The time interval for each round of kalman filter
|
||||||
|
static constexpr uint64_t kFilterIntervalInMs = static_cast<uint64_t>(NSEC_PER_SEC / 10);
|
||||||
|
|
||||||
|
struct KalmanInfo {
|
||||||
|
float filterValue; // The estimation value
|
||||||
|
float kalmanGain; // The kalman gain
|
||||||
|
float R; // The covariance of the observation noise
|
||||||
|
float P; // The a posteriori estimate covariance
|
||||||
|
};
|
||||||
|
|
||||||
|
// A simplified kalman filter for estimating timestamp period based on measured values
|
||||||
|
float KalmanFilter(KalmanInfo* info, float measuredValue) {
|
||||||
|
// Optimize kalman gain
|
||||||
|
info->kalmanGain = info->P / (info->P + info->R);
|
||||||
|
|
||||||
|
// Correct filter value
|
||||||
|
info->filterValue =
|
||||||
|
info->kalmanGain * measuredValue + (1.0 - info->kalmanGain) * info->filterValue;
|
||||||
|
// Update estimate covariance
|
||||||
|
info->P = (1.0f - info->kalmanGain) * info->P;
|
||||||
|
return info->filterValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void API_AVAILABLE(macos(10.15), ios(14))
|
||||||
|
UpdateTimestampPeriod(id<MTLDevice> device,
|
||||||
|
KalmanInfo* info,
|
||||||
|
MTLTimestamp* cpuTimestampStart,
|
||||||
|
MTLTimestamp* gpuTimestampStart,
|
||||||
|
float* timestampPeriod) {
|
||||||
|
// The filter value is converged to an optimal value when the kalman gain is less than
|
||||||
|
// 0.01. At this time, the weight of the measured value is too small to change the next
|
||||||
|
// filter value, the sampling and calculations do not need to continue anymore.
|
||||||
|
if (info->kalmanGain < 0.01f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MTLTimestamp cpuTimestampEnd = 0, gpuTimestampEnd = 0;
|
||||||
|
[device sampleTimestamps:&cpuTimestampEnd gpuTimestamp:&gpuTimestampEnd];
|
||||||
|
|
||||||
|
// Update the timestamp start values when timestamp reset happens
|
||||||
|
if (cpuTimestampEnd < *cpuTimestampStart || gpuTimestampEnd < *gpuTimestampStart) {
|
||||||
|
*cpuTimestampStart = cpuTimestampEnd;
|
||||||
|
*gpuTimestampStart = gpuTimestampEnd;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpuTimestampEnd - *cpuTimestampStart >= kFilterIntervalInMs) {
|
||||||
|
// The measured timestamp period
|
||||||
|
float measurement = (cpuTimestampEnd - *cpuTimestampStart) /
|
||||||
|
static_cast<float>(gpuTimestampEnd - *gpuTimestampStart);
|
||||||
|
|
||||||
|
// Measurement update
|
||||||
|
*timestampPeriod = KalmanFilter(info, measurement);
|
||||||
|
|
||||||
|
*cpuTimestampStart = cpuTimestampEnd;
|
||||||
|
*gpuTimestampStart = gpuTimestampEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
ResultOrError<Device*> Device::Create(AdapterBase* adapter,
|
ResultOrError<Device*> Device::Create(AdapterBase* adapter,
|
||||||
NSPRef<id<MTLDevice>> mtlDevice,
|
NSPRef<id<MTLDevice>> mtlDevice,
|
||||||
|
@ -70,6 +133,27 @@ namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
mCommandQueue.Acquire([*mMtlDevice newCommandQueue]);
|
mCommandQueue.Acquire([*mMtlDevice newCommandQueue]);
|
||||||
|
|
||||||
|
if (GetAdapter()->GetSupportedExtensions().IsEnabled(Extension::TimestampQuery)) {
|
||||||
|
// Make a best guess of timestamp period based on device vendor info, and converge it to
|
||||||
|
// an accurate value by the following calculations.
|
||||||
|
mTimestampPeriod =
|
||||||
|
gpu_info::IsIntel(GetAdapter()->GetPCIInfo().vendorId) ? 83.333f : 1.0f;
|
||||||
|
|
||||||
|
// Initialize kalman filter parameters
|
||||||
|
mKalmanInfo = std::make_unique<KalmanInfo>();
|
||||||
|
mKalmanInfo->filterValue = 0.0f;
|
||||||
|
mKalmanInfo->kalmanGain = 0.5f;
|
||||||
|
mKalmanInfo->R =
|
||||||
|
0.0001f; // The smaller this value is, the smaller the error of measured value is,
|
||||||
|
// the more we can trust the measured value.
|
||||||
|
mKalmanInfo->P = 1.0f;
|
||||||
|
|
||||||
|
if (@available(macos 10.15, iOS 14.0, *)) {
|
||||||
|
// Sample CPU timestamp and GPU timestamp for first time at device creation
|
||||||
|
[*mMtlDevice sampleTimestamps:&mCpuTimestamp gpuTimestamp:&mGpuTimestamp];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return DeviceBase::Initialize(new Queue(this));
|
return DeviceBase::Initialize(new Queue(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,6 +277,14 @@ namespace dawn_native { namespace metal {
|
||||||
SubmitPendingCommandBuffer();
|
SubmitPendingCommandBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Just run timestamp period calculation when timestamp extension is enabled.
|
||||||
|
if (GetAdapter()->GetSupportedExtensions().IsEnabled(Extension::TimestampQuery)) {
|
||||||
|
if (@available(macos 10.15, iOS 14.0, *)) {
|
||||||
|
UpdateTimestampPeriod(GetMTLDevice(), mKalmanInfo.get(), &mCpuTimestamp,
|
||||||
|
&mGpuTimestamp, &mTimestampPeriod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,7 +464,7 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
|
|
||||||
float Device::GetTimestampPeriodInNS() const {
|
float Device::GetTimestampPeriodInNS() const {
|
||||||
return 1.0f;
|
return mTimestampPeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
}} // namespace dawn_native::metal
|
}} // namespace dawn_native::metal
|
||||||
|
|
|
@ -534,8 +534,8 @@ TEST_P(TimestampQueryTests, QuerySetCreation) {
|
||||||
|
|
||||||
// Test calling timestamp query from command encoder
|
// Test calling timestamp query from command encoder
|
||||||
TEST_P(TimestampQueryTests, TimestampOnCommandEncoder) {
|
TEST_P(TimestampQueryTests, TimestampOnCommandEncoder) {
|
||||||
// TODO(hao.x.li@intel.com): Crash occurs if we only call WriteTimestamp in a command encoder
|
// TODO(crbug.com/dawn/545): Crash occurs if we only call WriteTimestamp in a command encoder
|
||||||
// without any copy commands on Metal on AMD GPU. See https://crbug.com/dawn/545.
|
// without any copy commands on Metal on AMD GPU.
|
||||||
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsAMD());
|
DAWN_SUPPRESS_TEST_IF(IsMetal() && IsAMD());
|
||||||
|
|
||||||
constexpr uint32_t kQueryCount = 2;
|
constexpr uint32_t kQueryCount = 2;
|
||||||
|
|
Loading…
Reference in New Issue