mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-15 16:16:08 +00:00
Have Queue timeline tasks resolve in order
Use QueueBase to track fences in flight and map requests so that they can be resolved in the order they were added. Before these tasks were separately tracked in FenceSignalTracker and MapRequestTracker, so tasks would be resolving out of order. Bug: dawn:404 Change-Id: I8b58fb72c99f43bc4593f56e08920d48ac506157 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/29441 Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Natasha Lee <natlee@microsoft.com>
This commit is contained in:
committed by
Commit Bot service account
parent
a7b0fdc90f
commit
51af1b428f
@@ -301,6 +301,7 @@ source_set("dawn_end2end_tests_sources") {
|
||||
"end2end/PrimitiveTopologyTests.cpp",
|
||||
"end2end/QueryTests.cpp",
|
||||
"end2end/QueueTests.cpp",
|
||||
"end2end/QueueTimelineTests.cpp",
|
||||
"end2end/RenderBundleTests.cpp",
|
||||
"end2end/RenderPassLoadOpTests.cpp",
|
||||
"end2end/RenderPassTests.cpp",
|
||||
|
||||
@@ -901,6 +901,17 @@ void DawnTestBase::FlushWire() {
|
||||
}
|
||||
}
|
||||
|
||||
void DawnTestBase::WaitForAllOperations() {
|
||||
wgpu::Queue queue = device.GetDefaultQueue();
|
||||
wgpu::Fence fence = queue.CreateFence();
|
||||
|
||||
// Force the currently submitted operations to completed.
|
||||
queue.Signal(fence, 1);
|
||||
while (fence.GetCompletedValue() < 1) {
|
||||
WaitABit();
|
||||
}
|
||||
}
|
||||
|
||||
DawnTestBase::ReadbackReservation DawnTestBase::ReserveReadback(uint64_t readbackSize) {
|
||||
// For now create a new MapRead buffer for each readback
|
||||
// TODO(cwallez@chromium.org): eventually make bigger buffers and allocate linearly?
|
||||
|
||||
@@ -313,6 +313,7 @@ class DawnTestBase {
|
||||
|
||||
void WaitABit();
|
||||
void FlushWire();
|
||||
void WaitForAllOperations();
|
||||
|
||||
bool SupportsExtensions(const std::vector<const char*>& extensions);
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@ TEST_P(DeviceLostTest, FenceSignalTickOnCompletion) {
|
||||
wgpu::Fence fence = queue.CreateFence(&descriptor);
|
||||
|
||||
queue.Signal(fence, 2);
|
||||
device.Tick();
|
||||
WaitForAllOperations();
|
||||
|
||||
// callback should have device lost status
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr))
|
||||
|
||||
164
src/tests/end2end/QueueTimelineTests.cpp
Normal file
164
src/tests/end2end/QueueTimelineTests.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright 2020 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include "tests/DawnTest.h"
|
||||
|
||||
using namespace testing;
|
||||
|
||||
class MockMapCallback {
|
||||
public:
|
||||
MOCK_METHOD(void, Call, (WGPUBufferMapAsyncStatus status, void* userdata));
|
||||
};
|
||||
|
||||
static std::unique_ptr<MockMapCallback> mockMapCallback;
|
||||
static void ToMockMapCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
|
||||
EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Success);
|
||||
mockMapCallback->Call(status, userdata);
|
||||
}
|
||||
|
||||
class MockFenceOnCompletionCallback {
|
||||
public:
|
||||
MOCK_METHOD(void, Call, (WGPUFenceCompletionStatus status, void* userdata));
|
||||
};
|
||||
|
||||
static std::unique_ptr<MockFenceOnCompletionCallback> mockFenceOnCompletionCallback;
|
||||
static void ToMockFenceOnCompletionCallback(WGPUFenceCompletionStatus status, void* userdata) {
|
||||
EXPECT_EQ(status, WGPUFenceCompletionStatus_Success);
|
||||
mockFenceOnCompletionCallback->Call(status, userdata);
|
||||
}
|
||||
|
||||
class QueueTimelineTests : public DawnTest {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
DawnTest::SetUp();
|
||||
|
||||
mockMapCallback = std::make_unique<MockMapCallback>();
|
||||
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
||||
|
||||
wgpu::BufferDescriptor descriptor;
|
||||
descriptor.size = 4;
|
||||
descriptor.usage = wgpu::BufferUsage::MapRead;
|
||||
mMapReadBuffer = device.CreateBuffer(&descriptor);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
mockFenceOnCompletionCallback = nullptr;
|
||||
mockMapCallback = nullptr;
|
||||
DawnTest::TearDown();
|
||||
}
|
||||
|
||||
wgpu::Buffer mMapReadBuffer;
|
||||
};
|
||||
|
||||
// Test that mMapReadBuffer.MapAsync callback happens before fence.OnCompletion callback
|
||||
// when queue.Signal is called after mMapReadBuffer.MapAsync. The callback order should
|
||||
// happen in the order the functions are called.
|
||||
TEST_P(QueueTimelineTests, MapReadSignalOnComplete) {
|
||||
testing::InSequence sequence;
|
||||
EXPECT_CALL(*mockMapCallback, Call(WGPUBufferMapAsyncStatus_Success, this)).Times(1);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this))
|
||||
.Times(1);
|
||||
|
||||
mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, 0, ToMockMapCallback, this);
|
||||
|
||||
wgpu::Fence fence = queue.CreateFence();
|
||||
|
||||
queue.Signal(fence, 1);
|
||||
fence.OnCompletion(1u, ToMockFenceOnCompletionCallback, this);
|
||||
|
||||
WaitForAllOperations();
|
||||
mMapReadBuffer.Unmap();
|
||||
}
|
||||
|
||||
// Test that fence.OnCompletion callback happens before mMapReadBuffer.MapAsync callback when
|
||||
// queue.Signal is called before mMapReadBuffer.MapAsync. The callback order should
|
||||
// happen in the order the functions are called.
|
||||
TEST_P(QueueTimelineTests, SignalMapReadOnComplete) {
|
||||
testing::InSequence sequence;
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*mockMapCallback, Call(WGPUBufferMapAsyncStatus_Success, this)).Times(1);
|
||||
|
||||
wgpu::Fence fence = queue.CreateFence();
|
||||
queue.Signal(fence, 2);
|
||||
|
||||
mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, 0, ToMockMapCallback, this);
|
||||
|
||||
fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, this);
|
||||
WaitForAllOperations();
|
||||
mMapReadBuffer.Unmap();
|
||||
}
|
||||
|
||||
// Test that fence.OnCompletion callback happens before mMapReadBuffer.MapAsync callback when
|
||||
// queue.Signal is called before mMapReadBuffer.MapAsync. The callback order should
|
||||
// happen in the order the functions are called
|
||||
TEST_P(QueueTimelineTests, SignalOnCompleteMapRead) {
|
||||
testing::InSequence sequence;
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*mockMapCallback, Call(WGPUBufferMapAsyncStatus_Success, this)).Times(1);
|
||||
|
||||
wgpu::Fence fence = queue.CreateFence();
|
||||
queue.Signal(fence, 2);
|
||||
|
||||
fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, this);
|
||||
|
||||
mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, 0, ToMockMapCallback, this);
|
||||
|
||||
WaitForAllOperations();
|
||||
mMapReadBuffer.Unmap();
|
||||
}
|
||||
|
||||
TEST_P(QueueTimelineTests, SurroundWithFenceSignals) {
|
||||
testing::InSequence sequence;
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 2))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 3))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*mockMapCallback, Call(WGPUBufferMapAsyncStatus_Success, this)).Times(1);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 5))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 6))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 8))
|
||||
.Times(1);
|
||||
|
||||
wgpu::Fence fence = queue.CreateFence();
|
||||
queue.Signal(fence, 2);
|
||||
queue.Signal(fence, 4);
|
||||
|
||||
fence.OnCompletion(1u, ToMockFenceOnCompletionCallback, this + 0);
|
||||
fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, this + 2);
|
||||
|
||||
mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, 0, ToMockMapCallback, this);
|
||||
queue.Signal(fence, 6);
|
||||
fence.OnCompletion(3u, ToMockFenceOnCompletionCallback, this + 3);
|
||||
fence.OnCompletion(5u, ToMockFenceOnCompletionCallback, this + 5);
|
||||
fence.OnCompletion(6u, ToMockFenceOnCompletionCallback, this + 6);
|
||||
|
||||
queue.Signal(fence, 8);
|
||||
fence.OnCompletion(8u, ToMockFenceOnCompletionCallback, this + 8);
|
||||
|
||||
WaitForAllOperations();
|
||||
mMapReadBuffer.Unmap();
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(QueueTimelineTests,
|
||||
D3D12Backend(),
|
||||
MetalBackend(),
|
||||
OpenGLBackend(),
|
||||
VulkanBackend());
|
||||
@@ -46,10 +46,6 @@ class FenceValidationTest : public ValidationTest {
|
||||
fence.OnCompletion(value, ToMockFenceOnCompletionCallback, expectation);
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
device.Tick();
|
||||
}
|
||||
|
||||
wgpu::Queue queue;
|
||||
|
||||
private:
|
||||
@@ -127,7 +123,7 @@ TEST_F(FenceValidationTest, OnCompletionLargerThanSignaled) {
|
||||
.Times(1);
|
||||
fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, nullptr);
|
||||
|
||||
Flush();
|
||||
WaitForAllOperations(device);
|
||||
}
|
||||
|
||||
TEST_F(FenceValidationTest, GetCompletedValueInsideCallback) {
|
||||
@@ -142,7 +138,7 @@ TEST_F(FenceValidationTest, GetCompletedValueInsideCallback) {
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 3u);
|
||||
}));
|
||||
|
||||
Flush();
|
||||
WaitForAllOperations(device);
|
||||
}
|
||||
|
||||
TEST_F(FenceValidationTest, GetCompletedValueAfterCallback) {
|
||||
@@ -155,7 +151,7 @@ TEST_F(FenceValidationTest, GetCompletedValueAfterCallback) {
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr))
|
||||
.Times(1);
|
||||
|
||||
Flush();
|
||||
WaitForAllOperations(device);
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 2u);
|
||||
}
|
||||
|
||||
@@ -178,12 +174,12 @@ TEST_F(FenceValidationTest, SignalSuccess) {
|
||||
|
||||
// Success
|
||||
queue.Signal(fence, 2);
|
||||
Flush();
|
||||
WaitForAllOperations(device);
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 2u);
|
||||
|
||||
// Success increasing fence value by more than 1
|
||||
queue.Signal(fence, 6);
|
||||
Flush();
|
||||
WaitForAllOperations(device);
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 6u);
|
||||
}
|
||||
|
||||
@@ -211,11 +207,11 @@ TEST_F(FenceValidationTest, DISABLED_SignalWrongQueueDoesNotUpdateValue) {
|
||||
ASSERT_DEVICE_ERROR(queue2.Signal(fence, 2));
|
||||
|
||||
// Fence value should be unchanged.
|
||||
Flush();
|
||||
WaitForAllOperations(device);
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 1u);
|
||||
|
||||
// Signaling with 2 on the correct queue should succeed
|
||||
queue.Signal(fence, 2);
|
||||
Flush();
|
||||
WaitForAllOperations(device);
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 2u);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user