Refactor Serial tracking to be owned by Device frontend.

Move mCompletedSerial and mLastSubmittedSerial to Device frontend and
add getters and setters for the device backend to access such.
This is to aid the Device in taking more ownership of Serials and Ticking.

Bug: dawn:400
Change-Id: Ifa53ac294a871e484716842a3d212373b57847c4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/20480
Commit-Queue: Natasha Lee <natlee@microsoft.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Natasha Lee 2020-05-07 21:52:54 +00:00 committed by Commit Bot service account
parent 4f3811c064
commit 351c95a477
12 changed files with 186 additions and 160 deletions

View File

@ -124,6 +124,7 @@ namespace dawn_native {
// complete before proceeding with destruction.
// Assert that errors are device loss so that we can continue with destruction
AssertAndIgnoreDeviceLossError(WaitForIdleForDestruction());
ASSERT(mCompletedSerial == mLastSubmittedSerial);
break;
case State::BeingDisconnected:
@ -173,6 +174,7 @@ namespace dawn_native {
// Assert that errors are device losses so that we can continue with destruction.
AssertAndIgnoreDeviceLossError(WaitForIdleForDestruction());
ASSERT(mCompletedSerial == mLastSubmittedSerial);
mState = State::Disconnected;
// Now everything is as if the device was lost.
@ -292,6 +294,45 @@ namespace dawn_native {
return mFenceSignalTracker.get();
}
Serial DeviceBase::GetCompletedCommandSerial() const {
return mCompletedSerial;
}
Serial DeviceBase::GetLastSubmittedCommandSerial() const {
return mLastSubmittedSerial;
}
void DeviceBase::IncrementLastSubmittedCommandSerial() {
mLastSubmittedSerial++;
}
void DeviceBase::ArtificiallyIncrementSerials() {
mCompletedSerial++;
mLastSubmittedSerial++;
}
void DeviceBase::AssumeCommandsComplete() {
mLastSubmittedSerial++;
mCompletedSerial = mLastSubmittedSerial;
}
Serial DeviceBase::GetPendingCommandSerial() const {
return mLastSubmittedSerial + 1;
}
void DeviceBase::CheckPassedSerials() {
Serial completedSerial = CheckAndUpdateCompletedSerials();
ASSERT(completedSerial <= mLastSubmittedSerial);
// completedSerial should not be less than mCompletedSerial unless it is 0.
// It can be 0 when there's no fences to check.
ASSERT(completedSerial >= mCompletedSerial || completedSerial == 0);
if (completedSerial > mCompletedSerial) {
mCompletedSerial = completedSerial;
}
}
ResultOrError<const Format*> DeviceBase::GetInternalFormat(wgpu::TextureFormat format) const {
size_t index = ComputeFormatIndex(format);
if (index >= mFormatTable.size()) {

View File

@ -86,9 +86,9 @@ namespace dawn_native {
CommandEncoder* encoder,
const CommandBufferDescriptor* descriptor) = 0;
virtual Serial GetCompletedCommandSerial() const = 0;
virtual Serial GetLastSubmittedCommandSerial() const = 0;
virtual Serial GetPendingCommandSerial() const = 0;
Serial GetCompletedCommandSerial() const;
Serial GetLastSubmittedCommandSerial() const;
Serial GetPendingCommandSerial() const;
virtual MaybeError TickImpl() = 0;
// Many Dawn objects are completely immutable once created which means that if two
@ -220,6 +220,18 @@ namespace dawn_native {
MaybeError Initialize(QueueBase* defaultQueue);
void ShutDownBase();
// Incrememt mLastSubmittedSerial when we submit the next serial
void IncrementLastSubmittedCommandSerial();
// If there's no GPU work in flight we still need to artificially increment the serial
// so that CPU operations waiting on GPU completion can know they don't have to wait.
void ArtificiallyIncrementSerials();
// During shut down of device, some operations might have been started since the last submit
// and waiting on a serial that doesn't have a corresponding fence enqueued. Fake serials to
// make all commands look completed.
void AssumeCommandsComplete();
// Check for passed fences and set the new completed serial
void CheckPassedSerials();
private:
virtual ResultOrError<BindGroupBase*> CreateBindGroupImpl(
const BindGroupDescriptor* descriptor) = 0;
@ -281,6 +293,18 @@ namespace dawn_native {
void ConsumeError(std::unique_ptr<ErrorData> error);
// Each backend should implement to check their passed fences if there are any and return a
// completed serial. Return 0 should indicate no fences to check.
virtual Serial CheckAndUpdateCompletedSerials() = 0;
// mCompletedSerial tracks the last completed command serial that the fence has returned.
// mLastSubmittedSerial tracks the last submitted command serial.
// During device removal, the serials could be artificially incremented
// to make it appear as if commands have been compeleted. They can also be artificially
// incremented when no work is being done in the GPU so CPU operations don't have to wait on
// stale serials.
Serial mCompletedSerial = 0;
Serial mLastSubmittedSerial = 0;
// ShutDownImpl is used to clean up and release resources used by device, does not wait for
// GPU or check errors.
virtual void ShutDownImpl() = 0;

View File

@ -72,9 +72,10 @@ namespace dawn_native { namespace d3d12 {
// value.
mCommandQueue.As(&mD3d12SharingContract);
DAWN_TRY(CheckHRESULT(mD3d12Device->CreateFence(mLastSubmittedSerial, D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&mFence)),
"D3D12 create fence"));
DAWN_TRY(
CheckHRESULT(mD3d12Device->CreateFence(GetLastSubmittedCommandSerial(),
D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)),
"D3D12 create fence"));
mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
ASSERT(mFenceEvent != nullptr);
@ -198,44 +199,35 @@ namespace dawn_native { namespace d3d12 {
return &mPendingCommands;
}
Serial Device::GetCompletedCommandSerial() const {
return mCompletedSerial;
}
Serial Device::GetLastSubmittedCommandSerial() const {
return mLastSubmittedSerial;
}
Serial Device::GetPendingCommandSerial() const {
return mLastSubmittedSerial + 1;
}
MaybeError Device::TickImpl() {
// Perform cleanup operations to free unused objects
mCompletedSerial = mFence->GetCompletedValue();
CheckPassedSerials();
mResourceAllocatorManager->Tick(mCompletedSerial);
DAWN_TRY(mCommandAllocatorManager->Tick(mCompletedSerial));
mViewShaderVisibleDescriptorAllocator->Tick(mCompletedSerial);
mSamplerShaderVisibleDescriptorAllocator->Tick(mCompletedSerial);
mRenderTargetViewAllocator->Tick(mCompletedSerial);
mDepthStencilViewAllocator->Tick(mCompletedSerial);
mMapRequestTracker->Tick(mCompletedSerial);
mUsedComObjectRefs.ClearUpTo(mCompletedSerial);
// Perform cleanup operations to free unused objects
Serial completedSerial = GetCompletedCommandSerial();
mResourceAllocatorManager->Tick(completedSerial);
DAWN_TRY(mCommandAllocatorManager->Tick(completedSerial));
mViewShaderVisibleDescriptorAllocator->Tick(completedSerial);
mSamplerShaderVisibleDescriptorAllocator->Tick(completedSerial);
mRenderTargetViewAllocator->Tick(completedSerial);
mDepthStencilViewAllocator->Tick(completedSerial);
mMapRequestTracker->Tick(completedSerial);
mUsedComObjectRefs.ClearUpTo(completedSerial);
DAWN_TRY(ExecutePendingCommandContext());
DAWN_TRY(NextSerial());
return {};
}
MaybeError Device::NextSerial() {
mLastSubmittedSerial++;
return CheckHRESULT(mCommandQueue->Signal(mFence.Get(), mLastSubmittedSerial),
IncrementLastSubmittedCommandSerial();
return CheckHRESULT(mCommandQueue->Signal(mFence.Get(), GetLastSubmittedCommandSerial()),
"D3D12 command queue signal fence");
}
MaybeError Device::WaitForSerial(uint64_t serial) {
mCompletedSerial = mFence->GetCompletedValue();
if (mCompletedSerial < serial) {
CheckPassedSerials();
if (GetCompletedCommandSerial() < serial) {
DAWN_TRY(CheckHRESULT(mFence->SetEventOnCompletion(serial, mFenceEvent),
"D3D12 set event on completion"));
WaitForSingleObject(mFenceEvent, INFINITE);
@ -243,6 +235,10 @@ namespace dawn_native { namespace d3d12 {
return {};
}
Serial Device::CheckAndUpdateCompletedSerials() {
return mFence->GetCompletedValue();
}
void Device::ReferenceUntilUnused(ComPtr<IUnknown> object) {
mUsedComObjectRefs.Enqueue(object, GetPendingCommandSerial());
}
@ -447,11 +443,13 @@ namespace dawn_native { namespace d3d12 {
DAWN_TRY(NextSerial());
// Wait for all in-flight commands to finish executing
DAWN_TRY(WaitForSerial(mLastSubmittedSerial));
DAWN_TRY(WaitForSerial(GetLastSubmittedCommandSerial()));
// Call tick one last time so resources are cleaned up.
DAWN_TRY(TickImpl());
// Force all operations to look as if they were completed
AssumeCommandsComplete();
return {};
}
@ -461,16 +459,16 @@ namespace dawn_native { namespace d3d12 {
// Immediately forget about all pending commands
mPendingCommands.Release();
// GPU is no longer executing commands. Existing objects do not get freed until the device
// is destroyed. To ensure objects are always released, force the completed serial to be
// MAX.
mCompletedSerial = std::numeric_limits<Serial>::max();
// Some operations might have been started since the last submit and waiting
// on a serial that doesn't have a corresponding fence enqueued. Force all
// operations to look as if they were completed (because they were).
AssumeCommandsComplete();
if (mFenceEvent != nullptr) {
::CloseHandle(mFenceEvent);
}
mUsedComObjectRefs.ClearUpTo(mCompletedSerial);
mUsedComObjectRefs.ClearUpTo(GetCompletedCommandSerial());
ASSERT(mUsedComObjectRefs.Empty());
ASSERT(!mPendingCommands.IsOpen());

View File

@ -55,8 +55,6 @@ namespace dawn_native { namespace d3d12 {
CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder,
const CommandBufferDescriptor* descriptor) override;
Serial GetCompletedCommandSerial() const final override;
Serial GetLastSubmittedCommandSerial() const final override;
MaybeError TickImpl() override;
ID3D12Device* GetD3D12Device() const;
@ -75,7 +73,6 @@ namespace dawn_native { namespace d3d12 {
ComPtr<IDXGIFactory4> GetFactory() const;
ResultOrError<CommandRecordingContext*> GetPendingCommandContext();
Serial GetPendingCommandSerial() const override;
const D3D12DeviceInfo& GetDeviceInfo() const;
@ -156,10 +153,9 @@ namespace dawn_native { namespace d3d12 {
void ShutDownImpl() override;
MaybeError WaitForIdleForDestruction() override;
Serial mCompletedSerial = 0;
Serial mLastSubmittedSerial = 0;
ComPtr<ID3D12Fence> mFence;
HANDLE mFenceEvent = nullptr;
Serial CheckAndUpdateCompletedSerials() override;
ComPtr<ID3D12Device> mD3d12Device; // Device is owned by adapter and will not be outlived.
ComPtr<ID3D12CommandQueue> mCommandQueue;

View File

@ -46,15 +46,12 @@ namespace dawn_native { namespace metal {
CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder,
const CommandBufferDescriptor* descriptor) override;
Serial GetCompletedCommandSerial() const final override;
Serial GetLastSubmittedCommandSerial() const final override;
MaybeError TickImpl() override;
id<MTLDevice> GetMTLDevice();
id<MTLCommandQueue> GetMTLQueue();
CommandRecordingContext* GetPendingCommandContext();
Serial GetPendingCommandSerial() const override;
void SubmitPendingCommandBuffer();
MapRequestTracker* GetMapTracker() const;
@ -103,12 +100,12 @@ namespace dawn_native { namespace metal {
void InitTogglesFromDriver();
void ShutDownImpl() override;
MaybeError WaitForIdleForDestruction() override;
Serial CheckAndUpdateCompletedSerials() override;
id<MTLDevice> mMtlDevice = nil;
id<MTLCommandQueue> mCommandQueue = nil;
std::unique_ptr<MapRequestTracker> mMapTracker;
Serial mLastSubmittedSerial = 0;
CommandRecordingContext mCommandContext;
// The completed serial is updated in a Metal completion handler that can be fired on a

View File

@ -154,31 +154,30 @@ namespace dawn_native { namespace metal {
return new TextureView(texture, descriptor);
}
Serial Device::GetCompletedCommandSerial() const {
Serial Device::CheckAndUpdateCompletedSerials() {
if (GetCompletedCommandSerial() > mCompletedSerial) {
// sometimes we artificially increase the serials, in which case the completed serial in
// the device base will surpass the completed serial we have in the metal backend, so we
// must update ours when we see that the completed serial from the frontend has
// increased.
mCompletedSerial = GetCompletedCommandSerial();
}
static_assert(std::is_same<Serial, uint64_t>::value, "");
return mCompletedSerial.load();
}
Serial Device::GetLastSubmittedCommandSerial() const {
return mLastSubmittedSerial;
}
Serial Device::GetPendingCommandSerial() const {
return mLastSubmittedSerial + 1;
}
MaybeError Device::TickImpl() {
CheckPassedSerials();
Serial completedSerial = GetCompletedCommandSerial();
mMapTracker->Tick(completedSerial);
if (mCommandContext.GetCommands() != nil) {
SubmitPendingCommandBuffer();
} else if (completedSerial == mLastSubmittedSerial) {
} else if (completedSerial == GetLastSubmittedCommandSerial()) {
// If there's no GPU work in flight we still need to artificially increment the serial
// so that CPU operations waiting on GPU completion can know they don't have to wait.
mCompletedSerial++;
mLastSubmittedSerial++;
ArtificiallyIncrementSerials();
}
return {};
@ -208,7 +207,7 @@ namespace dawn_native { namespace metal {
return;
}
mLastSubmittedSerial++;
IncrementLastSubmittedCommandSerial();
// Acquire the pending command buffer, which is retained. It must be released later.
id<MTLCommandBuffer> pendingCommands = mCommandContext.AcquireCommands();
@ -231,7 +230,8 @@ namespace dawn_native { namespace metal {
// Update the completed serial once the completed handler is fired. Make a local copy of
// mLastSubmittedSerial so it is captured by value.
Serial pendingSerial = mLastSubmittedSerial;
Serial pendingSerial = GetLastSubmittedCommandSerial();
// this ObjC block runs on a different thread
[pendingCommands addCompletedHandler:^(id<MTLCommandBuffer>) {
TRACE_EVENT_ASYNC_END0(GetPlatform(), GPUWork, "DeviceMTL::SubmitPendingCommandBuffer",
pendingSerial);
@ -299,17 +299,22 @@ namespace dawn_native { namespace metal {
MaybeError Device::WaitForIdleForDestruction() {
[mCommandContext.AcquireCommands() release];
CheckPassedSerials();
// Wait for all commands to be finished so we can free resources
while (GetCompletedCommandSerial() != mLastSubmittedSerial) {
while (GetCompletedCommandSerial() != GetLastSubmittedCommandSerial()) {
usleep(100);
CheckPassedSerials();
}
// Artificially increase the serials so work that was pending knows it can complete.
mCompletedSerial++;
mLastSubmittedSerial++;
ArtificiallyIncrementSerials();
DAWN_TRY(TickImpl());
// Force all operations to look as if they were completed
AssumeCommandsComplete();
return {};
}

View File

@ -186,7 +186,7 @@ namespace dawn_native { namespace null {
MaybeError Device::WaitForIdleForDestruction() {
// Fake all commands being completed
mCompletedSerial = mLastSubmittedSerial;
AssumeCommandsComplete();
return {};
}
@ -221,23 +221,15 @@ namespace dawn_native { namespace null {
mMemoryUsage -= bytes;
}
Serial Device::GetCompletedCommandSerial() const {
return mCompletedSerial;
}
Serial Device::GetLastSubmittedCommandSerial() const {
return mLastSubmittedSerial;
}
Serial Device::GetPendingCommandSerial() const {
return mLastSubmittedSerial + 1;
}
MaybeError Device::TickImpl() {
SubmitPendingOperations();
return {};
}
Serial Device::CheckAndUpdateCompletedSerials() {
return GetLastSubmittedCommandSerial();
}
void Device::AddPendingOperation(std::unique_ptr<PendingOperation> operation) {
mPendingOperations.emplace_back(std::move(operation));
}
@ -247,8 +239,8 @@ namespace dawn_native { namespace null {
}
mPendingOperations.clear();
mCompletedSerial = mLastSubmittedSerial;
mLastSubmittedSerial++;
CheckPassedSerials();
IncrementLastSubmittedCommandSerial();
}
// BindGroupDataHolder

View File

@ -91,9 +91,6 @@ namespace dawn_native { namespace null {
CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder,
const CommandBufferDescriptor* descriptor) override;
Serial GetCompletedCommandSerial() const final override;
Serial GetLastSubmittedCommandSerial() const final override;
Serial GetPendingCommandSerial() const override;
MaybeError TickImpl() override;
void AddPendingOperation(std::unique_ptr<PendingOperation> operation);
@ -138,11 +135,11 @@ namespace dawn_native { namespace null {
TextureBase* texture,
const TextureViewDescriptor* descriptor) override;
Serial CheckAndUpdateCompletedSerials() override;
void ShutDownImpl() override;
MaybeError WaitForIdleForDestruction() override;
Serial mCompletedSerial = 0;
Serial mLastSubmittedSerial = 0;
std::vector<std::unique_ptr<PendingOperation>> mPendingOperations;
static constexpr size_t kMaxMemoryUsage = 256 * 1024 * 1024;

View File

@ -147,46 +147,37 @@ namespace dawn_native { namespace opengl {
void Device::SubmitFenceSync() {
GLsync sync = gl.FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
mLastSubmittedSerial++;
mFencesInFlight.emplace(sync, mLastSubmittedSerial);
}
Serial Device::GetCompletedCommandSerial() const {
return mCompletedSerial;
}
Serial Device::GetLastSubmittedCommandSerial() const {
return mLastSubmittedSerial;
}
Serial Device::GetPendingCommandSerial() const {
return mLastSubmittedSerial + 1;
IncrementLastSubmittedCommandSerial();
mFencesInFlight.emplace(sync, GetLastSubmittedCommandSerial());
}
MaybeError Device::TickImpl() {
CheckPassedFences();
CheckPassedSerials();
return {};
}
void Device::CheckPassedFences() {
Serial Device::CheckAndUpdateCompletedSerials() {
Serial fenceSerial = 0;
while (!mFencesInFlight.empty()) {
GLsync sync = mFencesInFlight.front().first;
Serial fenceSerial = mFencesInFlight.front().second;
Serial tentativeSerial = mFencesInFlight.front().second;
// Fence are added in order, so we can stop searching as soon
// as we see one that's not ready.
GLenum result = gl.ClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
if (result == GL_TIMEOUT_EXPIRED) {
continue;
return fenceSerial;
}
// Update fenceSerial since fence is ready.
fenceSerial = tentativeSerial;
gl.DeleteSync(sync);
mFencesInFlight.pop();
ASSERT(fenceSerial > mCompletedSerial);
mCompletedSerial = fenceSerial;
ASSERT(fenceSerial > GetCompletedCommandSerial());
}
return fenceSerial;
}
ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) {
@ -207,14 +198,17 @@ namespace dawn_native { namespace opengl {
// Some operations might have been started since the last submit and waiting
// on a serial that doesn't have a corresponding fence enqueued. Force all
// operations to look as if they were completed (because they were).
mCompletedSerial = mLastSubmittedSerial + 1;
AssumeCommandsComplete();
}
MaybeError Device::WaitForIdleForDestruction() {
gl.Finish();
CheckPassedFences();
CheckPassedSerials();
ASSERT(mFencesInFlight.empty());
Tick();
// Force all operations to look as if they were completed
AssumeCommandsComplete();
return {};
}

View File

@ -52,9 +52,6 @@ namespace dawn_native { namespace opengl {
CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder,
const CommandBufferDescriptor* descriptor) override;
Serial GetCompletedCommandSerial() const final override;
Serial GetLastSubmittedCommandSerial() const final override;
Serial GetPendingCommandSerial() const override;
MaybeError TickImpl() override;
ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
@ -96,12 +93,10 @@ namespace dawn_native { namespace opengl {
const TextureViewDescriptor* descriptor) override;
void InitTogglesFromDriver();
void CheckPassedFences();
Serial CheckAndUpdateCompletedSerials() override;
void ShutDownImpl() override;
MaybeError WaitForIdleForDestruction() override;
Serial mCompletedSerial = 0;
Serial mLastSubmittedSerial = 0;
std::queue<std::pair<GLsync, Serial>> mFencesInFlight;
GLFormatTable mFormatTable;

View File

@ -155,39 +155,28 @@ namespace dawn_native { namespace vulkan {
return TextureView::Create(texture, descriptor);
}
Serial Device::GetCompletedCommandSerial() const {
return mCompletedSerial;
}
Serial Device::GetLastSubmittedCommandSerial() const {
return mLastSubmittedSerial;
}
Serial Device::GetPendingCommandSerial() const {
return mLastSubmittedSerial + 1;
}
MaybeError Device::TickImpl() {
CheckPassedFences();
CheckPassedSerials();
RecycleCompletedCommands();
for (Ref<BindGroupLayout>& bgl :
mBindGroupLayoutsPendingDeallocation.IterateUpTo(mCompletedSerial)) {
bgl->FinishDeallocation(mCompletedSerial);
}
mBindGroupLayoutsPendingDeallocation.ClearUpTo(mCompletedSerial);
Serial completedSerial = GetCompletedCommandSerial();
mMapRequestTracker->Tick(mCompletedSerial);
mResourceMemoryAllocator->Tick(mCompletedSerial);
mDeleter->Tick(mCompletedSerial);
for (Ref<BindGroupLayout>& bgl :
mBindGroupLayoutsPendingDeallocation.IterateUpTo(completedSerial)) {
bgl->FinishDeallocation(completedSerial);
}
mBindGroupLayoutsPendingDeallocation.ClearUpTo(completedSerial);
mMapRequestTracker->Tick(completedSerial);
mResourceMemoryAllocator->Tick(completedSerial);
mDeleter->Tick(completedSerial);
if (mRecordingContext.used) {
DAWN_TRY(SubmitPendingCommands());
} else if (mCompletedSerial == mLastSubmittedSerial) {
} else if (completedSerial == GetLastSubmittedCommandSerial()) {
// If there's no GPU work in flight we still need to artificially increment the serial
// so that CPU operations waiting on GPU completion can know they don't have to wait.
mCompletedSerial++;
mLastSubmittedSerial++;
ArtificiallyIncrementSerials();
}
return {};
@ -271,12 +260,13 @@ namespace dawn_native { namespace vulkan {
mDeleter->DeleteWhenUnused(semaphore);
}
mLastSubmittedSerial++;
mFencesInFlight.emplace(fence, mLastSubmittedSerial);
IncrementLastSubmittedCommandSerial();
Serial lastSubmittedSerial = GetLastSubmittedCommandSerial();
mFencesInFlight.emplace(fence, lastSubmittedSerial);
CommandPoolAndBuffer submittedCommands = {mRecordingContext.commandPool,
mRecordingContext.commandBuffer};
mCommandsInFlight.Enqueue(submittedCommands, mLastSubmittedSerial);
mCommandsInFlight.Enqueue(submittedCommands, lastSubmittedSerial);
mRecordingContext = CommandRecordingContext();
DAWN_TRY(PrepareRecordingContext());
@ -468,11 +458,11 @@ namespace dawn_native { namespace vulkan {
return fence;
}
void Device::CheckPassedFences() {
Serial Device::CheckAndUpdateCompletedSerials() {
Serial fenceSerial = 0;
while (!mFencesInFlight.empty()) {
VkFence fence = mFencesInFlight.front().first;
Serial fenceSerial = mFencesInFlight.front().second;
Serial tentativeSerial = mFencesInFlight.front().second;
VkResult result = VkResult::WrapUnsafe(
INJECT_ERROR_OR_RUN(fn.GetFenceStatus(mVkDevice, fence), VK_ERROR_DEVICE_LOST));
// TODO: Handle DeviceLost error.
@ -481,15 +471,17 @@ namespace dawn_native { namespace vulkan {
// Fence are added in order, so we can stop searching as soon
// as we see one that's not ready.
if (result == VK_NOT_READY) {
return;
return fenceSerial;
}
// Update fenceSerial since fence is ready.
fenceSerial = tentativeSerial;
mUnusedFences.push_back(fence);
mFencesInFlight.pop();
ASSERT(fenceSerial > mCompletedSerial);
mCompletedSerial = fenceSerial;
ASSERT(fenceSerial > GetCompletedCommandSerial());
mFencesInFlight.pop();
}
return fenceSerial;
}
MaybeError Device::PrepareRecordingContext() {
@ -542,10 +534,10 @@ namespace dawn_native { namespace vulkan {
}
void Device::RecycleCompletedCommands() {
for (auto& commands : mCommandsInFlight.IterateUpTo(mCompletedSerial)) {
for (auto& commands : mCommandsInFlight.IterateUpTo(GetCompletedCommandSerial())) {
mUnusedCommands.push_back(commands);
}
mCommandsInFlight.ClearUpTo(mCompletedSerial);
mCommandsInFlight.ClearUpTo(GetCompletedCommandSerial());
}
ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) {
@ -726,13 +718,11 @@ namespace dawn_native { namespace vulkan {
// (so they are as good as waited on) or success.
DAWN_UNUSED(waitIdleResult);
CheckPassedFences();
// Make sure all fences are complete by explicitly waiting on them all
while (!mFencesInFlight.empty()) {
VkFence fence = mFencesInFlight.front().first;
Serial fenceSerial = mFencesInFlight.front().second;
ASSERT(fenceSerial > mCompletedSerial);
ASSERT(fenceSerial > GetCompletedCommandSerial());
VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT);
do {
@ -746,8 +736,10 @@ namespace dawn_native { namespace vulkan {
fn.DestroyFence(mVkDevice, fence, nullptr);
mFencesInFlight.pop();
mCompletedSerial = fenceSerial;
}
// Force all operations to look as if they were completed
AssumeCommandsComplete();
return {};
}
@ -790,7 +782,7 @@ namespace dawn_native { namespace vulkan {
// Some operations might have been started since the last submit and waiting
// on a serial that doesn't have a corresponding fence enqueued. Force all
// operations to look as if they were completed (because they were).
mCompletedSerial = mLastSubmittedSerial + 1;
AssumeCommandsComplete();
// Assert that errors are device loss so that we can continue with destruction
AssertAndIgnoreDeviceLossError(TickImpl());
@ -808,7 +800,7 @@ namespace dawn_native { namespace vulkan {
// Releasing the uploader enqueues buffers to be released.
// Call Tick() again to clear them before releasing the deleter.
mDeleter->Tick(mCompletedSerial);
mDeleter->Tick(GetCompletedCommandSerial());
mMapRequestTracker = nullptr;

View File

@ -63,7 +63,6 @@ namespace dawn_native { namespace vulkan {
RenderPassCache* GetRenderPassCache() const;
CommandRecordingContext* GetPendingRecordingContext();
Serial GetPendingCommandSerial() const override;
MaybeError SubmitPendingCommands();
void EnqueueDeferredDeallocation(BindGroupLayout* bindGroupLayout);
@ -82,8 +81,6 @@ namespace dawn_native { namespace vulkan {
CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder,
const CommandBufferDescriptor* descriptor) override;
Serial GetCompletedCommandSerial() const final override;
Serial GetLastSubmittedCommandSerial() const final override;
MaybeError TickImpl() override;
ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
@ -158,7 +155,7 @@ namespace dawn_native { namespace vulkan {
std::unique_ptr<external_semaphore::Service> mExternalSemaphoreService;
ResultOrError<VkFence> GetUnusedFence();
void CheckPassedFences();
Serial CheckAndUpdateCompletedSerials() override;
// We track which operations are in flight on the GPU with an increasing serial.
// This works only because we have a single queue. Each submit to a queue is associated
@ -167,8 +164,6 @@ namespace dawn_native { namespace vulkan {
std::queue<std::pair<VkFence, Serial>> mFencesInFlight;
// Fences in the unused list aren't reset yet.
std::vector<VkFence> mUnusedFences;
Serial mCompletedSerial = 0;
Serial mLastSubmittedSerial = 0;
MaybeError PrepareRecordingContext();
void RecycleCompletedCommands();