Byte counting WriteBuffer/Texture to submit

Only tag to submit when the total size is larger than the threshold,
so that we can make as few submits as possible meanwhile avoiding OOM.

Bug: chromium:1258986
Change-Id: I7190e1bb942bfaffc5cd424ce4743173735b25e3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/106418
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jie A Chen <jie.a.chen@intel.com>
This commit is contained in:
jchen10 2022-11-03 09:58:59 +00:00 committed by Dawn LUCI CQ
parent e8dd681f19
commit 61d6d21091
22 changed files with 292 additions and 149 deletions

View File

@ -1918,4 +1918,28 @@ ExecutionSerial DeviceBase::GetScheduledWorkDoneSerial() const {
return HasPendingCommands() ? GetPendingCommandSerial() : GetLastSubmittedCommandSerial(); return HasPendingCommands() ? GetPendingCommandSerial() : GetLastSubmittedCommandSerial();
} }
MaybeError DeviceBase::CopyFromStagingToBuffer(StagingBufferBase* source,
uint64_t sourceOffset,
BufferBase* destination,
uint64_t destinationOffset,
uint64_t size) {
DAWN_TRY(
CopyFromStagingToBufferImpl(source, sourceOffset, destination, destinationOffset, size));
if (GetDynamicUploader()->ShouldFlush()) {
ForceEventualFlushOfCommands();
}
return {};
}
MaybeError DeviceBase::CopyFromStagingToTexture(const StagingBufferBase* source,
const TextureDataLayout& src,
TextureCopy* dst,
const Extent3D& copySizePixels) {
DAWN_TRY(CopyFromStagingToTextureImpl(source, src, dst, copySizePixels));
if (GetDynamicUploader()->ShouldFlush()) {
ForceEventualFlushOfCommands();
}
return {};
}
} // namespace dawn::native } // namespace dawn::native

View File

@ -298,15 +298,15 @@ class DeviceBase : public RefCountedWithExternalCount {
void StoreCachedBlob(const CacheKey& key, const Blob& blob); void StoreCachedBlob(const CacheKey& key, const Blob& blob);
virtual ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) = 0; virtual ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) = 0;
virtual MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError CopyFromStagingToBuffer(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) = 0; uint64_t size);
virtual MaybeError CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError CopyFromStagingToTexture(const StagingBufferBase* source,
const TextureDataLayout& src, const TextureDataLayout& src,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) = 0; const Extent3D& copySizePixels);
DynamicUploader* GetDynamicUploader() const; DynamicUploader* GetDynamicUploader() const;
@ -405,6 +405,15 @@ class DeviceBase : public RefCountedWithExternalCount {
// The serial by which time all currently submitted or pending operations will be completed. // The serial by which time all currently submitted or pending operations will be completed.
ExecutionSerial GetScheduledWorkDoneSerial() const; ExecutionSerial GetScheduledWorkDoneSerial() const;
// For the commands being internally recorded in backend, that were not urgent to submit, this
// method makes them to be submitted as soon as possbile in next ticks.
virtual void ForceEventualFlushOfCommands() = 0;
// In the 'Normal' mode, currently recorded commands in the backend normally will be actually
// submitted in the next Tick. However in the 'Passive' mode, the submission will be postponed
// as late as possible, for example, until the client has explictly issued a submission.
enum class SubmitMode { Normal, Passive };
protected: protected:
// Constructor used only for mocking and testing. // Constructor used only for mocking and testing.
DeviceBase(); DeviceBase();
@ -515,6 +524,16 @@ class DeviceBase : public RefCountedWithExternalCount {
// Indicates whether the backend has pending commands to be submitted as soon as possible. // Indicates whether the backend has pending commands to be submitted as soon as possible.
virtual bool HasPendingCommands() const = 0; virtual bool HasPendingCommands() const = 0;
virtual MaybeError CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset,
BufferBase* destination,
uint64_t destinationOffset,
uint64_t size) = 0;
virtual MaybeError CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& src,
TextureCopy* dst,
const Extent3D& copySizePixels) = 0;
wgpu::ErrorCallback mUncapturedErrorCallback = nullptr; wgpu::ErrorCallback mUncapturedErrorCallback = nullptr;
void* mUncapturedErrorUserdata = nullptr; void* mUncapturedErrorUserdata = nullptr;

View File

@ -126,4 +126,25 @@ ResultOrError<UploadHandle> DynamicUploader::Allocate(uint64_t allocationSize,
uploadHandle.startOffset += additionalOffset; uploadHandle.startOffset += additionalOffset;
return uploadHandle; return uploadHandle;
} }
bool DynamicUploader::ShouldFlush() {
uint64_t kTotalAllocatedSizeThreshold = 64 * 1024 * 1024;
// We use total allocated size instead of pending-upload size to prevent Dawn from allocating
// too much GPU memory so that the risk of OOM can be minimized.
return GetTotalAllocatedSize() > kTotalAllocatedSizeThreshold;
}
uint64_t DynamicUploader::GetTotalAllocatedSize() {
uint64_t size = 0;
for (const auto& buffer : mReleasedStagingBuffers.IterateAll()) {
size += buffer->GetSize();
}
for (const auto& buffer : mRingBuffers) {
if (buffer->mStagingBuffer != nullptr) {
size += buffer->mStagingBuffer->GetSize();
}
}
return size;
}
} // namespace dawn::native } // namespace dawn::native

View File

@ -49,8 +49,11 @@ class DynamicUploader {
uint64_t offsetAlignment); uint64_t offsetAlignment);
void Deallocate(ExecutionSerial lastCompletedSerial); void Deallocate(ExecutionSerial lastCompletedSerial);
bool ShouldFlush();
private: private:
static constexpr uint64_t kRingBufferSize = 4 * 1024 * 1024; static constexpr uint64_t kRingBufferSize = 4 * 1024 * 1024;
uint64_t GetTotalAllocatedSize();
struct RingBuffer { struct RingBuffer {
std::unique_ptr<StagingBufferBase> mStagingBuffer; std::unique_ptr<StagingBufferBase> mStagingBuffer;

View File

@ -225,6 +225,7 @@ void QueueBase::APIOnSubmittedWorkDone(uint64_t signalValue,
} }
void QueueBase::TrackTask(std::unique_ptr<TrackTaskCallback> task) { void QueueBase::TrackTask(std::unique_ptr<TrackTaskCallback> task) {
GetDevice()->ForceEventualFlushOfCommands();
// we can move the task to the callback task manager, as it's ready to be called if there are no // we can move the task to the callback task manager, as it's ready to be called if there are no
// scheduled commands. // scheduled commands.
if (!GetDevice()->HasScheduledCommands()) { if (!GetDevice()->HasScheduledCommands()) {

View File

@ -488,8 +488,8 @@ MaybeError Buffer::ClearBuffer(CommandRecordingContext* commandContext,
memset(uploadHandle.mappedBuffer, clearValue, size); memset(uploadHandle.mappedBuffer, clearValue, size);
device->CopyFromStagingToBufferImpl(commandContext, uploadHandle.stagingBuffer, device->CopyFromStagingToBufferHelper(commandContext, uploadHandle.stagingBuffer,
uploadHandle.startOffset, this, offset, size); uploadHandle.startOffset, this, offset, size);
} }
return {}; return {};

View File

@ -60,6 +60,7 @@ MaybeError CommandRecordingContext::Open(ID3D12Device* d3d12Device,
} }
mIsOpen = true; mIsOpen = true;
mNeedsSubmit = false;
return {}; return {};
} }
@ -128,6 +129,7 @@ MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) {
} }
mIsOpen = false; mIsOpen = false;
mNeedsSubmit = false;
mSharedTextures.clear(); mSharedTextures.clear();
mHeapsPendingUsage.clear(); mHeapsPendingUsage.clear();
mTempBuffers.clear(); mTempBuffers.clear();
@ -162,6 +164,7 @@ void CommandRecordingContext::Release() {
mD3d12CommandList.Reset(); mD3d12CommandList.Reset();
mD3d12CommandList4.Reset(); mD3d12CommandList4.Reset();
mIsOpen = false; mIsOpen = false;
mNeedsSubmit = false;
mSharedTextures.clear(); mSharedTextures.clear();
mHeapsPendingUsage.clear(); mHeapsPendingUsage.clear();
mTempBuffers.clear(); mTempBuffers.clear();
@ -171,6 +174,14 @@ bool CommandRecordingContext::IsOpen() const {
return mIsOpen; return mIsOpen;
} }
bool CommandRecordingContext::NeedsSubmit() const {
return mNeedsSubmit;
}
void CommandRecordingContext::SetNeedsSubmit() {
mNeedsSubmit = true;
}
void CommandRecordingContext::AddToTempBuffers(Ref<Buffer> tempBuffer) { void CommandRecordingContext::AddToTempBuffers(Ref<Buffer> tempBuffer) {
mTempBuffers.emplace_back(tempBuffer); mTempBuffers.emplace_back(tempBuffer);
} }

View File

@ -37,6 +37,8 @@ class CommandRecordingContext {
ID3D12GraphicsCommandList4* GetCommandList4() const; ID3D12GraphicsCommandList4* GetCommandList4() const;
void Release(); void Release();
bool IsOpen() const; bool IsOpen() const;
bool NeedsSubmit() const;
void SetNeedsSubmit();
MaybeError ExecuteCommandList(Device* device); MaybeError ExecuteCommandList(Device* device);
@ -48,6 +50,7 @@ class CommandRecordingContext {
ComPtr<ID3D12GraphicsCommandList> mD3d12CommandList; ComPtr<ID3D12GraphicsCommandList> mD3d12CommandList;
ComPtr<ID3D12GraphicsCommandList4> mD3d12CommandList4; ComPtr<ID3D12GraphicsCommandList4> mD3d12CommandList4;
bool mIsOpen = false; bool mIsOpen = false;
bool mNeedsSubmit = false;
std::set<Texture*> mSharedTextures; std::set<Texture*> mSharedTextures;
std::vector<Heap*> mHeapsPendingUsage; std::vector<Heap*> mHeapsPendingUsage;

View File

@ -274,12 +274,16 @@ ResidencyManager* Device::GetResidencyManager() const {
return mResidencyManager.get(); return mResidencyManager.get();
} }
ResultOrError<CommandRecordingContext*> Device::GetPendingCommandContext() { ResultOrError<CommandRecordingContext*> Device::GetPendingCommandContext(
Device::SubmitMode submitMode) {
// Callers of GetPendingCommandList do so to record commands. Only reserve a command // Callers of GetPendingCommandList do so to record commands. Only reserve a command
// allocator when it is needed so we don't submit empty command lists // allocator when it is needed so we don't submit empty command lists
if (!mPendingCommands.IsOpen()) { if (!mPendingCommands.IsOpen()) {
DAWN_TRY(mPendingCommands.Open(mD3d12Device.Get(), mCommandAllocatorManager.get())); DAWN_TRY(mPendingCommands.Open(mD3d12Device.Get(), mCommandAllocatorManager.get()));
} }
if (submitMode == Device::SubmitMode::Normal) {
mPendingCommands.SetNeedsSubmit();
}
return &mPendingCommands; return &mPendingCommands;
} }
@ -309,9 +313,9 @@ MaybeError Device::ClearBufferToZero(CommandRecordingContext* commandContext,
memset(uploadHandle.mappedBuffer, 0u, kZeroBufferSize); memset(uploadHandle.mappedBuffer, 0u, kZeroBufferSize);
CopyFromStagingToBufferImpl(commandContext, uploadHandle.stagingBuffer, CopyFromStagingToBufferHelper(commandContext, uploadHandle.stagingBuffer,
uploadHandle.startOffset, mZeroBuffer.Get(), 0, uploadHandle.startOffset, mZeroBuffer.Get(), 0,
kZeroBufferSize); kZeroBufferSize);
mZeroBuffer->SetIsDataInitialized(); mZeroBuffer->SetIsDataInitialized();
} }
@ -346,7 +350,7 @@ MaybeError Device::TickImpl() {
mDepthStencilViewAllocator->Tick(completedSerial); mDepthStencilViewAllocator->Tick(completedSerial);
mUsedComObjectRefs.ClearUpTo(completedSerial); mUsedComObjectRefs.ClearUpTo(completedSerial);
if (mPendingCommands.IsOpen()) { if (mPendingCommands.IsOpen() && mPendingCommands.NeedsSubmit()) {
DAWN_TRY(ExecutePendingCommandContext()); DAWN_TRY(ExecutePendingCommandContext());
DAWN_TRY(NextSerial()); DAWN_TRY(NextSerial());
} }
@ -401,7 +405,13 @@ void Device::ReferenceUntilUnused(ComPtr<IUnknown> object) {
} }
bool Device::HasPendingCommands() const { bool Device::HasPendingCommands() const {
return mPendingCommands.IsOpen(); return mPendingCommands.NeedsSubmit();
}
void Device::ForceEventualFlushOfCommands() {
if (mPendingCommands.IsOpen()) {
mPendingCommands.SetNeedsSubmit();
}
} }
MaybeError Device::ExecutePendingCommandContext() { MaybeError Device::ExecutePendingCommandContext() {
@ -484,13 +494,13 @@ ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(si
return std::move(stagingBuffer); return std::move(stagingBuffer);
} }
MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError Device::CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) { uint64_t size) {
CommandRecordingContext* commandRecordingContext; CommandRecordingContext* commandRecordingContext;
DAWN_TRY_ASSIGN(commandRecordingContext, GetPendingCommandContext()); DAWN_TRY_ASSIGN(commandRecordingContext, GetPendingCommandContext(Device::SubmitMode::Passive));
Buffer* dstBuffer = ToBackend(destination); Buffer* dstBuffer = ToBackend(destination);
@ -499,18 +509,18 @@ MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source,
commandRecordingContext, destinationOffset, size)); commandRecordingContext, destinationOffset, size));
DAWN_UNUSED(cleared); DAWN_UNUSED(cleared);
CopyFromStagingToBufferImpl(commandRecordingContext, source, sourceOffset, destination, CopyFromStagingToBufferHelper(commandRecordingContext, source, sourceOffset, destination,
destinationOffset, size); destinationOffset, size);
return {}; return {};
} }
void Device::CopyFromStagingToBufferImpl(CommandRecordingContext* commandContext, void Device::CopyFromStagingToBufferHelper(CommandRecordingContext* commandContext,
StagingBufferBase* source, StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) { uint64_t size) {
ASSERT(commandContext != nullptr); ASSERT(commandContext != nullptr);
Buffer* dstBuffer = ToBackend(destination); Buffer* dstBuffer = ToBackend(destination);
StagingBuffer* srcBuffer = ToBackend(source); StagingBuffer* srcBuffer = ToBackend(source);
@ -521,12 +531,12 @@ void Device::CopyFromStagingToBufferImpl(CommandRecordingContext* commandContext
sourceOffset, size); sourceOffset, size);
} }
MaybeError Device::CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError Device::CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& src, const TextureDataLayout& src,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) { const Extent3D& copySizePixels) {
CommandRecordingContext* commandContext; CommandRecordingContext* commandContext;
DAWN_TRY_ASSIGN(commandContext, GetPendingCommandContext()); DAWN_TRY_ASSIGN(commandContext, GetPendingCommandContext(Device::SubmitMode::Passive));
Texture* texture = ToBackend(dst->texture.Get()); Texture* texture = ToBackend(dst->texture.Get());
SubresourceRange range = GetSubresourcesAffectedByCopy(*dst, copySizePixels); SubresourceRange range = GetSubresourcesAffectedByCopy(*dst, copySizePixels);

View File

@ -77,7 +77,8 @@ class Device final : public DeviceBase {
ComPtr<IDxcCompiler> GetDxcCompiler() const; ComPtr<IDxcCompiler> GetDxcCompiler() const;
ComPtr<IDxcValidator> GetDxcValidator() const; ComPtr<IDxcValidator> GetDxcValidator() const;
ResultOrError<CommandRecordingContext*> GetPendingCommandContext(); ResultOrError<CommandRecordingContext*> GetPendingCommandContext(
Device::SubmitMode submitMode = Device::SubmitMode::Normal);
MaybeError ClearBufferToZero(CommandRecordingContext* commandContext, MaybeError ClearBufferToZero(CommandRecordingContext* commandContext,
BufferBase* destination, BufferBase* destination,
@ -91,26 +92,28 @@ class Device final : public DeviceBase {
void ReferenceUntilUnused(ComPtr<IUnknown> object); void ReferenceUntilUnused(ComPtr<IUnknown> object);
void ForceEventualFlushOfCommands() override;
MaybeError ExecutePendingCommandContext(); MaybeError ExecutePendingCommandContext();
ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override; ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset,
BufferBase* destination,
uint64_t destinationOffset,
uint64_t size) override;
void CopyFromStagingToBufferHelper(CommandRecordingContext* commandContext,
StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) override; uint64_t size);
void CopyFromStagingToBufferImpl(CommandRecordingContext* commandContext, MaybeError CopyFromStagingToTextureImpl(const StagingBufferBase* source,
StagingBufferBase* source, const TextureDataLayout& src,
uint64_t sourceOffset, TextureCopy* dst,
BufferBase* destination, const Extent3D& copySizePixels) override;
uint64_t destinationOffset,
uint64_t size);
MaybeError CopyFromStagingToTexture(const StagingBufferBase* source,
const TextureDataLayout& src,
TextureCopy* dst,
const Extent3D& copySizePixels) override;
ResultOrError<ResourceHeapAllocation> AllocateMemory( ResultOrError<ResourceHeapAllocation> AllocateMemory(
D3D12_HEAP_TYPE heapType, D3D12_HEAP_TYPE heapType,

View File

@ -30,6 +30,8 @@ class CommandRecordingContext : NonMovable {
~CommandRecordingContext(); ~CommandRecordingContext();
id<MTLCommandBuffer> GetCommands(); id<MTLCommandBuffer> GetCommands();
void SetNeedsSubmit();
bool NeedsSubmit() const;
void MarkUsed(); void MarkUsed();
bool WasUsed() const; bool WasUsed() const;
@ -59,6 +61,7 @@ class CommandRecordingContext : NonMovable {
NSPRef<id<MTLComputeCommandEncoder>> mCompute; NSPRef<id<MTLComputeCommandEncoder>> mCompute;
NSPRef<id<MTLRenderCommandEncoder>> mRender; NSPRef<id<MTLRenderCommandEncoder>> mRender;
bool mInEncoder = false; bool mInEncoder = false;
bool mNeedsSubmit = false;
bool mUsed = false; bool mUsed = false;
}; };

View File

@ -29,6 +29,13 @@ id<MTLCommandBuffer> CommandRecordingContext::GetCommands() {
return mCommands.Get(); return mCommands.Get();
} }
void CommandRecordingContext::SetNeedsSubmit() {
mNeedsSubmit = true;
}
bool CommandRecordingContext::NeedsSubmit() const {
return mNeedsSubmit;
}
void CommandRecordingContext::MarkUsed() { void CommandRecordingContext::MarkUsed() {
mUsed = true; mUsed = true;
} }
@ -38,6 +45,7 @@ bool CommandRecordingContext::WasUsed() const {
MaybeError CommandRecordingContext::PrepareNextCommandBuffer(id<MTLCommandQueue> queue) { MaybeError CommandRecordingContext::PrepareNextCommandBuffer(id<MTLCommandQueue> queue) {
ASSERT(mCommands == nil); ASSERT(mCommands == nil);
ASSERT(!mNeedsSubmit);
ASSERT(!mUsed); ASSERT(!mUsed);
// The MTLCommandBuffer will be autoreleased by default. // The MTLCommandBuffer will be autoreleased by default.
@ -58,6 +66,7 @@ NSPRef<id<MTLCommandBuffer>> CommandRecordingContext::AcquireCommands() {
} }
ASSERT(!mInEncoder); ASSERT(!mInEncoder);
mNeedsSubmit = false;
mUsed = false; mUsed = false;
return std::move(mCommands); return std::move(mCommands);
} }

View File

@ -49,7 +49,8 @@ class Device final : public DeviceBase {
id<MTLDevice> GetMTLDevice(); id<MTLDevice> GetMTLDevice();
id<MTLCommandQueue> GetMTLQueue(); id<MTLCommandQueue> GetMTLQueue();
CommandRecordingContext* GetPendingCommandContext(); CommandRecordingContext* GetPendingCommandContext(
Device::SubmitMode submitMode = Device::SubmitMode::Normal);
MaybeError SubmitPendingCommandBuffer(); MaybeError SubmitPendingCommandBuffer();
Ref<Texture> CreateTextureWrappingIOSurface(const ExternalImageDescriptor* descriptor, Ref<Texture> CreateTextureWrappingIOSurface(const ExternalImageDescriptor* descriptor,
@ -57,15 +58,15 @@ class Device final : public DeviceBase {
void WaitForCommandsToBeScheduled(); void WaitForCommandsToBeScheduled();
ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override; ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) override; uint64_t size) override;
MaybeError CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& dataLayout, const TextureDataLayout& dataLayout,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) override; const Extent3D& copySizePixels) override;
uint32_t GetOptimalBytesPerRowAlignment() const override; uint32_t GetOptimalBytesPerRowAlignment() const override;
uint64_t GetOptimalBufferToTextureCopyOffsetAlignment() const override; uint64_t GetOptimalBufferToTextureCopyOffsetAlignment() const override;
@ -79,6 +80,8 @@ class Device final : public DeviceBase {
// single-byte buffer // single-byte buffer
id<MTLBuffer> GetDummyBlitMtlBuffer(); id<MTLBuffer> GetDummyBlitMtlBuffer();
void ForceEventualFlushOfCommands() override;
private: private:
Device(AdapterBase* adapter, Device(AdapterBase* adapter,
NSPRef<id<MTLDevice>> mtlDevice, NSPRef<id<MTLDevice>> mtlDevice,

View File

@ -343,7 +343,9 @@ ResultOrError<ExecutionSerial> Device::CheckAndUpdateCompletedSerials() {
} }
MaybeError Device::TickImpl() { MaybeError Device::TickImpl() {
DAWN_TRY(SubmitPendingCommandBuffer()); if (mCommandContext.NeedsSubmit()) {
DAWN_TRY(SubmitPendingCommandBuffer());
}
// Just run timestamp period calculation when timestamp feature is enabled and timestamp // Just run timestamp period calculation when timestamp feature is enabled and timestamp
// conversion is not disabled. // conversion is not disabled.
@ -366,17 +368,26 @@ id<MTLCommandQueue> Device::GetMTLQueue() {
return mCommandQueue.Get(); return mCommandQueue.Get();
} }
CommandRecordingContext* Device::GetPendingCommandContext() { CommandRecordingContext* Device::GetPendingCommandContext(Device::SubmitMode submitMode) {
if (submitMode == DeviceBase::SubmitMode::Normal) {
mCommandContext.SetNeedsSubmit();
}
mCommandContext.MarkUsed(); mCommandContext.MarkUsed();
return &mCommandContext; return &mCommandContext;
} }
bool Device::HasPendingCommands() const { bool Device::HasPendingCommands() const {
return mCommandContext.WasUsed(); return mCommandContext.NeedsSubmit();
}
void Device::ForceEventualFlushOfCommands() {
if (mCommandContext.WasUsed()) {
mCommandContext.SetNeedsSubmit();
}
} }
MaybeError Device::SubmitPendingCommandBuffer() { MaybeError Device::SubmitPendingCommandBuffer() {
if (!mCommandContext.WasUsed()) { if (!mCommandContext.NeedsSubmit()) {
return {}; return {};
} }
@ -428,42 +439,45 @@ ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(si
return std::move(stagingBuffer); return std::move(stagingBuffer);
} }
MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError Device::CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) { uint64_t size) {
// Metal validation layers forbid 0-sized copies, assert it is skipped prior to calling // Metal validation layers forbid 0-sized copies, assert it is skipped prior to calling
// this function. // this function.
ASSERT(size != 0); ASSERT(size != 0);
ToBackend(destination) ToBackend(destination)
->EnsureDataInitializedAsDestination(GetPendingCommandContext(), destinationOffset, size); ->EnsureDataInitializedAsDestination(
GetPendingCommandContext(DeviceBase::SubmitMode::Passive), destinationOffset, size);
id<MTLBuffer> uploadBuffer = ToBackend(source)->GetBufferHandle(); id<MTLBuffer> uploadBuffer = ToBackend(source)->GetBufferHandle();
id<MTLBuffer> buffer = ToBackend(destination)->GetMTLBuffer(); id<MTLBuffer> buffer = ToBackend(destination)->GetMTLBuffer();
[GetPendingCommandContext()->EnsureBlit() copyFromBuffer:uploadBuffer [GetPendingCommandContext(DeviceBase::SubmitMode::Passive)->EnsureBlit()
sourceOffset:sourceOffset copyFromBuffer:uploadBuffer
toBuffer:buffer sourceOffset:sourceOffset
destinationOffset:destinationOffset toBuffer:buffer
size:size]; destinationOffset:destinationOffset
size:size];
return {}; return {};
} }
// In Metal we don't write from the CPU to the texture directly which can be done using the // In Metal we don't write from the CPU to the texture directly which can be done using the
// replaceRegion function, because the function requires a non-private storage mode and Dawn // replaceRegion function, because the function requires a non-private storage mode and Dawn
// sets the private storage mode by default for all textures except IOSurfaces on macOS. // sets the private storage mode by default for all textures except IOSurfaces on macOS.
MaybeError Device::CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError Device::CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& dataLayout, const TextureDataLayout& dataLayout,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) { const Extent3D& copySizePixels) {
Texture* texture = ToBackend(dst->texture.Get()); Texture* texture = ToBackend(dst->texture.Get());
EnsureDestinationTextureInitialized(GetPendingCommandContext(), texture, *dst, copySizePixels); EnsureDestinationTextureInitialized(GetPendingCommandContext(DeviceBase::SubmitMode::Passive),
texture, *dst, copySizePixels);
RecordCopyBufferToTexture(GetPendingCommandContext(), ToBackend(source)->GetBufferHandle(), RecordCopyBufferToTexture(GetPendingCommandContext(DeviceBase::SubmitMode::Passive),
source->GetSize(), dataLayout.offset, dataLayout.bytesPerRow, ToBackend(source)->GetBufferHandle(), source->GetSize(),
dataLayout.rowsPerImage, texture, dst->mipLevel, dst->origin, dataLayout.offset, dataLayout.bytesPerRow, dataLayout.rowsPerImage,
dst->aspect, copySizePixels); texture, dst->mipLevel, dst->origin, dst->aspect, copySizePixels);
return {}; return {};
} }

View File

@ -217,11 +217,11 @@ bool Device::HasPendingCommands() const {
return false; return false;
} }
MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError Device::CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) { uint64_t size) {
if (IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { if (IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
destination->SetIsDataInitialized(); destination->SetIsDataInitialized();
} }
@ -238,10 +238,10 @@ MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source,
return {}; return {};
} }
MaybeError Device::CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError Device::CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& src, const TextureDataLayout& src,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) { const Extent3D& copySizePixels) {
return {}; return {};
} }
@ -556,6 +556,8 @@ float Device::GetTimestampPeriodInNS() const {
return 1.0f; return 1.0f;
} }
void Device::ForceEventualFlushOfCommands() {}
Texture::Texture(DeviceBase* device, const TextureDescriptor* descriptor, TextureState state) Texture::Texture(DeviceBase* device, const TextureDescriptor* descriptor, TextureState state)
: TextureBase(device, descriptor, state) {} : TextureBase(device, descriptor, state) {}

View File

@ -106,15 +106,15 @@ class Device final : public DeviceBase {
MaybeError SubmitPendingOperations(); MaybeError SubmitPendingOperations();
ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override; ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) override; uint64_t size) override;
MaybeError CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& src, const TextureDataLayout& src,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) override; const Extent3D& copySizePixels) override;
MaybeError IncrementMemoryUsage(uint64_t bytes); MaybeError IncrementMemoryUsage(uint64_t bytes);
void DecrementMemoryUsage(uint64_t bytes); void DecrementMemoryUsage(uint64_t bytes);
@ -124,6 +124,8 @@ class Device final : public DeviceBase {
float GetTimestampPeriodInNS() const override; float GetTimestampPeriodInNS() const override;
void ForceEventualFlushOfCommands() override;
private: private:
using DeviceBase::DeviceBase; using DeviceBase::DeviceBase;

View File

@ -418,18 +418,18 @@ ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(si
return DAWN_UNIMPLEMENTED_ERROR("Device unable to create staging buffer."); return DAWN_UNIMPLEMENTED_ERROR("Device unable to create staging buffer.");
} }
MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError Device::CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) { uint64_t size) {
return DAWN_UNIMPLEMENTED_ERROR("Device unable to copy from staging buffer."); return DAWN_UNIMPLEMENTED_ERROR("Device unable to copy from staging buffer.");
} }
MaybeError Device::CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError Device::CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& src, const TextureDataLayout& src,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) { const Extent3D& copySizePixels) {
return DAWN_UNIMPLEMENTED_ERROR("Device unable to copy from staging buffer to texture."); return DAWN_UNIMPLEMENTED_ERROR("Device unable to copy from staging buffer to texture.");
} }
@ -464,6 +464,8 @@ float Device::GetTimestampPeriodInNS() const {
return 1.0f; return 1.0f;
} }
void Device::ForceEventualFlushOfCommands() {}
const OpenGLFunctions& Device::GetGL() const { const OpenGLFunctions& Device::GetGL() const {
if (mContext) { if (mContext) {
mContext->MakeCurrent(); mContext->MakeCurrent();

View File

@ -68,21 +68,22 @@ class Device final : public DeviceBase {
MaybeError TickImpl() override; MaybeError TickImpl() override;
ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override; ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) override; uint64_t size) override;
MaybeError CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& src, const TextureDataLayout& src,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) override; const Extent3D& copySizePixels) override;
uint32_t GetOptimalBytesPerRowAlignment() const override; uint32_t GetOptimalBytesPerRowAlignment() const override;
uint64_t GetOptimalBufferToTextureCopyOffsetAlignment() const override; uint64_t GetOptimalBufferToTextureCopyOffsetAlignment() const override;
float GetTimestampPeriodInNS() const override; float GetTimestampPeriodInNS() const override;
void ForceEventualFlushOfCommands() override;
class Context { class Context {
public: public:

View File

@ -40,6 +40,7 @@ struct CommandRecordingContext {
// For Device state tracking only. // For Device state tracking only.
VkCommandPool commandPool = VK_NULL_HANDLE; VkCommandPool commandPool = VK_NULL_HANDLE;
bool needsSubmit = false;
bool used = false; bool used = false;
// In some cases command buffer will need to be split to accomodate driver bug workarounds. // In some cases command buffer will need to be split to accomodate driver bug workarounds.

View File

@ -230,7 +230,7 @@ MaybeError Device::TickImpl() {
mDeleter->Tick(completedSerial); mDeleter->Tick(completedSerial);
mDescriptorAllocatorsPendingDeallocation.ClearUpTo(completedSerial); mDescriptorAllocatorsPendingDeallocation.ClearUpTo(completedSerial);
if (mRecordingContext.used) { if (mRecordingContext.needsSubmit) {
DAWN_TRY(SubmitPendingCommands()); DAWN_TRY(SubmitPendingCommands());
} }
@ -282,18 +282,23 @@ void Device::EnqueueDeferredDeallocation(DescriptorSetAllocator* allocator) {
mDescriptorAllocatorsPendingDeallocation.Enqueue(allocator, GetPendingCommandSerial()); mDescriptorAllocatorsPendingDeallocation.Enqueue(allocator, GetPendingCommandSerial());
} }
CommandRecordingContext* Device::GetPendingRecordingContext() { CommandRecordingContext* Device::GetPendingRecordingContext(Device::SubmitMode submitMode) {
ASSERT(mRecordingContext.commandBuffer != VK_NULL_HANDLE); ASSERT(mRecordingContext.commandBuffer != VK_NULL_HANDLE);
mRecordingContext.needsSubmit |= (submitMode == DeviceBase::SubmitMode::Normal);
mRecordingContext.used = true; mRecordingContext.used = true;
return &mRecordingContext; return &mRecordingContext;
} }
bool Device::HasPendingCommands() const { bool Device::HasPendingCommands() const {
return mRecordingContext.used; return mRecordingContext.needsSubmit;
}
void Device::ForceEventualFlushOfCommands() {
mRecordingContext.needsSubmit |= mRecordingContext.used;
} }
MaybeError Device::SubmitPendingCommands() { MaybeError Device::SubmitPendingCommands() {
if (!mRecordingContext.used) { if (!mRecordingContext.needsSubmit) {
return {}; return {};
} }
@ -705,7 +710,7 @@ ResultOrError<ExecutionSerial> Device::CheckAndUpdateCompletedSerials() {
} }
MaybeError Device::PrepareRecordingContext() { MaybeError Device::PrepareRecordingContext() {
ASSERT(!mRecordingContext.used); ASSERT(!mRecordingContext.needsSubmit);
ASSERT(mRecordingContext.commandBuffer == VK_NULL_HANDLE); ASSERT(mRecordingContext.commandBuffer == VK_NULL_HANDLE);
ASSERT(mRecordingContext.commandPool == VK_NULL_HANDLE); ASSERT(mRecordingContext.commandPool == VK_NULL_HANDLE);
@ -812,16 +817,17 @@ ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(si
return std::move(stagingBuffer); return std::move(stagingBuffer);
} }
MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError Device::CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) { uint64_t size) {
// It is a validation error to do a 0-sized copy in Vulkan, check it is skipped prior to // It is a validation error to do a 0-sized copy in Vulkan, check it is skipped prior to
// calling this function. // calling this function.
ASSERT(size != 0); ASSERT(size != 0);
CommandRecordingContext* recordingContext = GetPendingRecordingContext(); CommandRecordingContext* recordingContext =
GetPendingRecordingContext(DeviceBase::SubmitMode::Passive);
ToBackend(destination) ToBackend(destination)
->EnsureDataInitializedAsDestination(recordingContext, destinationOffset, size); ->EnsureDataInitializedAsDestination(recordingContext, destinationOffset, size);
@ -845,15 +851,16 @@ MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source,
return {}; return {};
} }
MaybeError Device::CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError Device::CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& src, const TextureDataLayout& src,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) { const Extent3D& copySizePixels) {
// There is no need of a barrier to make host writes available and visible to the copy // There is no need of a barrier to make host writes available and visible to the copy
// operation for HOST_COHERENT memory. The Vulkan spec for vkQueueSubmit describes that it // operation for HOST_COHERENT memory. The Vulkan spec for vkQueueSubmit describes that it
// does an implicit availability, visibility and domain operation. // does an implicit availability, visibility and domain operation.
CommandRecordingContext* recordingContext = GetPendingRecordingContext(); CommandRecordingContext* recordingContext =
GetPendingRecordingContext(DeviceBase::SubmitMode::Passive);
VkBufferImageCopy region = ComputeBufferImageCopyRegion(src, *dst, copySizePixels); VkBufferImageCopy region = ComputeBufferImageCopyRegion(src, *dst, copySizePixels);
VkImageSubresourceLayers subresource = region.imageSubresource; VkImageSubresourceLayers subresource = region.imageSubresource;
@ -1118,7 +1125,7 @@ void Device::DestroyImpl() {
ToBackend(GetAdapter())->GetVulkanInstance()->StopListeningForDeviceMessages(this); ToBackend(GetAdapter())->GetVulkanInstance()->StopListeningForDeviceMessages(this);
// Immediately tag the recording context as unused so we don't try to submit it in Tick. // Immediately tag the recording context as unused so we don't try to submit it in Tick.
mRecordingContext.used = false; mRecordingContext.needsSubmit = false;
if (mRecordingContext.commandPool != VK_NULL_HANDLE) { if (mRecordingContext.commandPool != VK_NULL_HANDLE) {
// The VkCommandBuffer memory should be wholly owned by the pool and freed when it is // The VkCommandBuffer memory should be wholly owned by the pool and freed when it is
// destroyed, but that's not the case in some drivers and the leak memory. // destroyed, but that's not the case in some drivers and the leak memory.

View File

@ -65,7 +65,8 @@ class Device final : public DeviceBase {
ResourceMemoryAllocator* GetResourceMemoryAllocator() const; ResourceMemoryAllocator* GetResourceMemoryAllocator() const;
external_semaphore::Service* GetExternalSemaphoreService() const; external_semaphore::Service* GetExternalSemaphoreService() const;
CommandRecordingContext* GetPendingRecordingContext(); CommandRecordingContext* GetPendingRecordingContext(
Device::SubmitMode submitMode = Device::SubmitMode::Normal);
MaybeError SplitRecordingContext(CommandRecordingContext* recordingContext); MaybeError SplitRecordingContext(CommandRecordingContext* recordingContext);
MaybeError SubmitPendingCommands(); MaybeError SubmitPendingCommands();
@ -89,15 +90,15 @@ class Device final : public DeviceBase {
MaybeError TickImpl() override; MaybeError TickImpl() override;
ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override; ResultOrError<std::unique_ptr<StagingBufferBase>> CreateStagingBuffer(size_t size) override;
MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, MaybeError CopyFromStagingToBufferImpl(StagingBufferBase* source,
uint64_t sourceOffset, uint64_t sourceOffset,
BufferBase* destination, BufferBase* destination,
uint64_t destinationOffset, uint64_t destinationOffset,
uint64_t size) override; uint64_t size) override;
MaybeError CopyFromStagingToTexture(const StagingBufferBase* source, MaybeError CopyFromStagingToTextureImpl(const StagingBufferBase* source,
const TextureDataLayout& src, const TextureDataLayout& src,
TextureCopy* dst, TextureCopy* dst,
const Extent3D& copySizePixels) override; const Extent3D& copySizePixels) override;
// Return the fixed subgroup size to use for compute shaders on this device or 0 if none // Return the fixed subgroup size to use for compute shaders on this device or 0 if none
// needs to be set. // needs to be set.
@ -115,6 +116,8 @@ class Device final : public DeviceBase {
// Used to associate this device with validation layer messages. // Used to associate this device with validation layer messages.
const char* GetDebugPrefix() { return mDebugPrefix.c_str(); } const char* GetDebugPrefix() { return mDebugPrefix.c_str(); }
void ForceEventualFlushOfCommands() override;
private: private:
Device(Adapter* adapter, Device(Adapter* adapter,
const DeviceDescriptor* descriptor, const DeviceDescriptor* descriptor,

View File

@ -39,11 +39,11 @@ class DeviceMock : public DeviceBase {
(size_t), (size_t),
(override)); (override));
MOCK_METHOD(MaybeError, MOCK_METHOD(MaybeError,
CopyFromStagingToBuffer, CopyFromStagingToBufferImpl,
(StagingBufferBase*, uint64_t, BufferBase*, uint64_t, uint64_t), (StagingBufferBase*, uint64_t, BufferBase*, uint64_t, uint64_t),
(override)); (override));
MOCK_METHOD(MaybeError, MOCK_METHOD(MaybeError,
CopyFromStagingToTexture, CopyFromStagingToTextureImpl,
(const StagingBufferBase*, const TextureDataLayout&, TextureCopy*, const Extent3D&), (const StagingBufferBase*, const TextureDataLayout&, TextureCopy*, const Extent3D&),
(override)); (override));
@ -51,6 +51,7 @@ class DeviceMock : public DeviceBase {
MOCK_METHOD(uint64_t, GetOptimalBufferToTextureCopyOffsetAlignment, (), (const, override)); MOCK_METHOD(uint64_t, GetOptimalBufferToTextureCopyOffsetAlignment, (), (const, override));
MOCK_METHOD(float, GetTimestampPeriodInNS, (), (const, override)); MOCK_METHOD(float, GetTimestampPeriodInNS, (), (const, override));
MOCK_METHOD(void, ForceEventualFlushOfCommands, (), (override));
MOCK_METHOD(ResultOrError<Ref<BindGroupBase>>, MOCK_METHOD(ResultOrError<Ref<BindGroupBase>>,
CreateBindGroupImpl, CreateBindGroupImpl,