Eagerly destroy CommandBuffer commands after submission
Command buffers hold references to all encoded objects. Freeing them eagerly significantly reduces the amount memory held before the JS GC clears the command buffers. Bug: dawn:262, dawn:372 Change-Id: I68dfa973f980fba8d94611ed1de3c593bdb91a63 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/26562 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Rafael Cintron <rafael.cintron@microsoft.com> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
f7905c3cb5
commit
76d9e34bbc
|
@ -30,13 +30,7 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandIterator::~CommandIterator() {
|
CommandIterator::~CommandIterator() {
|
||||||
ASSERT(mDataWasDestroyed);
|
ASSERT(IsEmpty());
|
||||||
|
|
||||||
if (!IsEmpty()) {
|
|
||||||
for (auto& block : mBlocks) {
|
|
||||||
free(block.block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandIterator::CommandIterator(CommandIterator&& other) {
|
CommandIterator::CommandIterator(CommandIterator&& other) {
|
||||||
|
@ -44,18 +38,13 @@ namespace dawn_native {
|
||||||
mBlocks = std::move(other.mBlocks);
|
mBlocks = std::move(other.mBlocks);
|
||||||
other.Reset();
|
other.Reset();
|
||||||
}
|
}
|
||||||
other.DataWasDestroyed();
|
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandIterator& CommandIterator::operator=(CommandIterator&& other) {
|
CommandIterator& CommandIterator::operator=(CommandIterator&& other) {
|
||||||
if (!other.IsEmpty()) {
|
ASSERT(IsEmpty());
|
||||||
mBlocks = std::move(other.mBlocks);
|
mBlocks = std::move(other.mBlocks);
|
||||||
other.Reset();
|
other.Reset();
|
||||||
} else {
|
|
||||||
mBlocks.clear();
|
|
||||||
}
|
|
||||||
other.DataWasDestroyed();
|
|
||||||
Reset();
|
Reset();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +55,7 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandIterator& CommandIterator::operator=(CommandAllocator&& allocator) {
|
CommandIterator& CommandIterator::operator=(CommandAllocator&& allocator) {
|
||||||
|
ASSERT(IsEmpty());
|
||||||
mBlocks = allocator.AcquireBlocks();
|
mBlocks = allocator.AcquireBlocks();
|
||||||
Reset();
|
Reset();
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -97,8 +87,17 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandIterator::DataWasDestroyed() {
|
void CommandIterator::MakeEmptyAsDataWasDestroyed() {
|
||||||
mDataWasDestroyed = true;
|
if (IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& block : mBlocks) {
|
||||||
|
free(block.block);
|
||||||
|
}
|
||||||
|
mBlocks.clear();
|
||||||
|
Reset();
|
||||||
|
ASSERT(IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommandIterator::IsEmpty() const {
|
bool CommandIterator::IsEmpty() const {
|
||||||
|
|
|
@ -91,10 +91,13 @@ namespace dawn_native {
|
||||||
return static_cast<T*>(NextData(sizeof(T) * count, alignof(T)));
|
return static_cast<T*>(NextData(sizeof(T) * count, alignof(T)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needs to be called if iteration was stopped early.
|
// Sets iterator to the beginning of the commands without emptying the list. This method can
|
||||||
|
// be used if iteration was stopped early and the iterator needs to be restarted.
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
void DataWasDestroyed();
|
// This method must to be called after commands have been deleted. This indicates that the
|
||||||
|
// commands have been submitted and they are no longer valid.
|
||||||
|
void MakeEmptyAsDataWasDestroyed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsEmpty() const;
|
bool IsEmpty() const;
|
||||||
|
@ -139,7 +142,6 @@ namespace dawn_native {
|
||||||
size_t mCurrentBlock = 0;
|
size_t mCurrentBlock = 0;
|
||||||
// Used to avoid a special case for empty iterators.
|
// Used to avoid a special case for empty iterators.
|
||||||
uint32_t mEndOfBlock = detail::kEndOfBlock;
|
uint32_t mEndOfBlock = detail::kEndOfBlock;
|
||||||
bool mDataWasDestroyed = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandAllocator {
|
class CommandAllocator {
|
||||||
|
|
|
@ -24,18 +24,39 @@
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
|
|
||||||
CommandBufferBase::CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor*)
|
CommandBufferBase::CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor*)
|
||||||
: ObjectBase(encoder->GetDevice()), mResourceUsages(encoder->AcquireResourceUsages()) {
|
: ObjectBase(encoder->GetDevice()),
|
||||||
|
mCommands(encoder->AcquireCommands()),
|
||||||
|
mResourceUsages(encoder->AcquireResourceUsages()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandBufferBase::CommandBufferBase(DeviceBase* device, ObjectBase::ErrorTag tag)
|
CommandBufferBase::CommandBufferBase(DeviceBase* device, ObjectBase::ErrorTag tag)
|
||||||
: ObjectBase(device, tag) {
|
: ObjectBase(device, tag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CommandBufferBase::~CommandBufferBase() {
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
CommandBufferBase* CommandBufferBase::MakeError(DeviceBase* device) {
|
CommandBufferBase* CommandBufferBase::MakeError(DeviceBase* device) {
|
||||||
return new CommandBufferBase(device, ObjectBase::kError);
|
return new CommandBufferBase(device, ObjectBase::kError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaybeError CommandBufferBase::ValidateCanUseInSubmitNow() const {
|
||||||
|
ASSERT(!IsError());
|
||||||
|
|
||||||
|
if (mDestroyed) {
|
||||||
|
return DAWN_VALIDATION_ERROR("Command buffer reused in submit");
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBufferBase::Destroy() {
|
||||||
|
FreeCommands(&mCommands);
|
||||||
|
mResourceUsages = {};
|
||||||
|
mDestroyed = true;
|
||||||
|
}
|
||||||
|
|
||||||
const CommandBufferResourceUsage& CommandBufferBase::GetResourceUsages() const {
|
const CommandBufferResourceUsage& CommandBufferBase::GetResourceUsages() const {
|
||||||
return mResourceUsages;
|
return mResourceUsages;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#include "dawn_native/dawn_platform.h"
|
#include "dawn_native/dawn_platform.h"
|
||||||
|
|
||||||
|
#include "dawn_native/CommandAllocator.h"
|
||||||
|
#include "dawn_native/Error.h"
|
||||||
#include "dawn_native/Forward.h"
|
#include "dawn_native/Forward.h"
|
||||||
#include "dawn_native/ObjectBase.h"
|
#include "dawn_native/ObjectBase.h"
|
||||||
#include "dawn_native/PassResourceUsage.h"
|
#include "dawn_native/PassResourceUsage.h"
|
||||||
|
@ -31,14 +33,24 @@ namespace dawn_native {
|
||||||
class CommandBufferBase : public ObjectBase {
|
class CommandBufferBase : public ObjectBase {
|
||||||
public:
|
public:
|
||||||
CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
|
CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
|
||||||
|
|
||||||
static CommandBufferBase* MakeError(DeviceBase* device);
|
static CommandBufferBase* MakeError(DeviceBase* device);
|
||||||
|
|
||||||
|
MaybeError ValidateCanUseInSubmitNow() const;
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
const CommandBufferResourceUsage& GetResourceUsages() const;
|
const CommandBufferResourceUsage& GetResourceUsages() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~CommandBufferBase();
|
||||||
|
|
||||||
|
CommandIterator mCommands;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CommandBufferBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
CommandBufferBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
||||||
|
|
||||||
CommandBufferResourceUsage mResourceUsages;
|
CommandBufferResourceUsage mResourceUsages;
|
||||||
|
bool mDestroyed = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsCompleteSubresourceCopiedTo(const TextureBase* texture,
|
bool IsCompleteSubresourceCopiedTo(const TextureBase* texture,
|
||||||
|
|
|
@ -188,7 +188,8 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
commands->DataWasDestroyed();
|
|
||||||
|
commands->MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkipCommand(CommandIterator* commands, Command type) {
|
void SkipCommand(CommandIterator* commands, Command type) {
|
||||||
|
|
|
@ -51,24 +51,11 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) {
|
void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) {
|
||||||
DeviceBase* device = GetDevice();
|
SubmitInternal(commandCount, commands);
|
||||||
if (device->ConsumedError(device->ValidateIsAlive())) {
|
|
||||||
// If device is lost, don't let any commands be submitted
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE_EVENT0(device->GetPlatform(), General, "Queue::Submit");
|
for (uint32_t i = 0; i < commandCount; ++i) {
|
||||||
if (device->IsValidationEnabled() &&
|
commands[i]->Destroy();
|
||||||
device->ConsumedError(ValidateSubmit(commandCount, commands))) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
ASSERT(!IsError());
|
|
||||||
|
|
||||||
if (device->ConsumedError(SubmitImpl(commandCount, commands))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
device->GetErrorScopeTracker()->TrackUntilLastSubmitComplete(
|
|
||||||
device->GetCurrentErrorScope());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueueBase::Signal(Fence* fence, uint64_t signalValue) {
|
void QueueBase::Signal(Fence* fence, uint64_t signalValue) {
|
||||||
|
@ -170,6 +157,7 @@ namespace dawn_native {
|
||||||
|
|
||||||
for (uint32_t i = 0; i < commandCount; ++i) {
|
for (uint32_t i = 0; i < commandCount; ++i) {
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(commands[i]));
|
DAWN_TRY(GetDevice()->ValidateObject(commands[i]));
|
||||||
|
DAWN_TRY(commands[i]->ValidateCanUseInSubmitNow());
|
||||||
|
|
||||||
const CommandBufferResourceUsage& usages = commands[i]->GetResourceUsages();
|
const CommandBufferResourceUsage& usages = commands[i]->GetResourceUsages();
|
||||||
|
|
||||||
|
@ -286,6 +274,28 @@ namespace dawn_native {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QueueBase::SubmitInternal(uint32_t commandCount, CommandBufferBase* const* commands) {
|
||||||
|
DeviceBase* device = GetDevice();
|
||||||
|
if (device->ConsumedError(device->ValidateIsAlive())) {
|
||||||
|
// If device is lost, don't let any commands be submitted
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE_EVENT0(device->GetPlatform(), General, "Queue::Submit");
|
||||||
|
if (device->IsValidationEnabled() &&
|
||||||
|
device->ConsumedError(ValidateSubmit(commandCount, commands))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ASSERT(!IsError());
|
||||||
|
|
||||||
|
if (device->ConsumedError(SubmitImpl(commandCount, commands))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->GetErrorScopeTracker()->TrackUntilLastSubmitComplete(
|
||||||
|
device->GetCurrentErrorScope());
|
||||||
|
}
|
||||||
|
|
||||||
void CopyTextureData(uint8_t* dstPointer,
|
void CopyTextureData(uint8_t* dstPointer,
|
||||||
const uint8_t* srcPointer,
|
const uint8_t* srcPointer,
|
||||||
uint32_t depth,
|
uint32_t depth,
|
||||||
|
|
|
@ -73,6 +73,8 @@ namespace dawn_native {
|
||||||
size_t dataSize,
|
size_t dataSize,
|
||||||
const TextureDataLayout* dataLayout,
|
const TextureDataLayout* dataLayout,
|
||||||
const Extent3D* writeSize) const;
|
const Extent3D* writeSize) const;
|
||||||
|
|
||||||
|
void SubmitInternal(uint32_t commandCount, CommandBufferBase* const* commands);
|
||||||
};
|
};
|
||||||
|
|
||||||
// A helper function used in Queue::WriteTexture. The destination data layout must not
|
// A helper function used in Queue::WriteTexture. The destination data layout must not
|
||||||
|
|
|
@ -535,11 +535,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
||||||
: CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) {
|
: CommandBufferBase(encoder, descriptor) {
|
||||||
}
|
|
||||||
|
|
||||||
CommandBuffer::~CommandBuffer() {
|
|
||||||
FreeCommands(&mCommands);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* commandContext) {
|
MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* commandContext) {
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#define DAWNNATIVE_D3D12_COMMANDBUFFERD3D12_H_
|
#define DAWNNATIVE_D3D12_COMMANDBUFFERD3D12_H_
|
||||||
|
|
||||||
#include "common/Constants.h"
|
#include "common/Constants.h"
|
||||||
#include "dawn_native/CommandAllocator.h"
|
|
||||||
#include "dawn_native/CommandBuffer.h"
|
#include "dawn_native/CommandBuffer.h"
|
||||||
#include "dawn_native/Error.h"
|
#include "dawn_native/Error.h"
|
||||||
#include "dawn_native/d3d12/Forward.h"
|
#include "dawn_native/d3d12/Forward.h"
|
||||||
|
@ -43,7 +42,6 @@ namespace dawn_native { namespace d3d12 {
|
||||||
MaybeError RecordCommands(CommandRecordingContext* commandContext);
|
MaybeError RecordCommands(CommandRecordingContext* commandContext);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~CommandBuffer() override;
|
|
||||||
MaybeError RecordComputePass(CommandRecordingContext* commandContext,
|
MaybeError RecordComputePass(CommandRecordingContext* commandContext,
|
||||||
BindGroupStateTracker* bindingTracker);
|
BindGroupStateTracker* bindingTracker);
|
||||||
MaybeError RecordRenderPass(CommandRecordingContext* commandContext,
|
MaybeError RecordRenderPass(CommandRecordingContext* commandContext,
|
||||||
|
@ -55,8 +53,6 @@ namespace dawn_native { namespace d3d12 {
|
||||||
RenderPassBuilder* renderPassBuilder);
|
RenderPassBuilder* renderPassBuilder);
|
||||||
void EmulateBeginRenderPass(CommandRecordingContext* commandContext,
|
void EmulateBeginRenderPass(CommandRecordingContext* commandContext,
|
||||||
const RenderPassBuilder* renderPassBuilder) const;
|
const RenderPassBuilder* renderPassBuilder) const;
|
||||||
|
|
||||||
CommandIterator mCommands;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace dawn_native::d3d12
|
}} // namespace dawn_native::d3d12
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#ifndef DAWNNATIVE_METAL_COMMANDBUFFERMTL_H_
|
#ifndef DAWNNATIVE_METAL_COMMANDBUFFERMTL_H_
|
||||||
#define DAWNNATIVE_METAL_COMMANDBUFFERMTL_H_
|
#define DAWNNATIVE_METAL_COMMANDBUFFERMTL_H_
|
||||||
|
|
||||||
#include "dawn_native/CommandAllocator.h"
|
|
||||||
#include "dawn_native/CommandBuffer.h"
|
#include "dawn_native/CommandBuffer.h"
|
||||||
#include "dawn_native/Error.h"
|
#include "dawn_native/Error.h"
|
||||||
|
|
||||||
|
@ -37,7 +36,6 @@ namespace dawn_native { namespace metal {
|
||||||
MaybeError FillCommands(CommandRecordingContext* commandContext);
|
MaybeError FillCommands(CommandRecordingContext* commandContext);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~CommandBuffer() override;
|
|
||||||
MaybeError EncodeComputePass(CommandRecordingContext* commandContext);
|
MaybeError EncodeComputePass(CommandRecordingContext* commandContext);
|
||||||
MaybeError EncodeRenderPass(CommandRecordingContext* commandContext,
|
MaybeError EncodeRenderPass(CommandRecordingContext* commandContext,
|
||||||
MTLRenderPassDescriptor* mtlRenderPass,
|
MTLRenderPassDescriptor* mtlRenderPass,
|
||||||
|
@ -48,8 +46,6 @@ namespace dawn_native { namespace metal {
|
||||||
MTLRenderPassDescriptor* mtlRenderPass,
|
MTLRenderPassDescriptor* mtlRenderPass,
|
||||||
uint32_t width,
|
uint32_t width,
|
||||||
uint32_t height);
|
uint32_t height);
|
||||||
|
|
||||||
CommandIterator mCommands;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace dawn_native::metal
|
}} // namespace dawn_native::metal
|
||||||
|
|
|
@ -524,11 +524,7 @@ namespace dawn_native { namespace metal {
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
||||||
: CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) {
|
: CommandBufferBase(encoder, descriptor) {
|
||||||
}
|
|
||||||
|
|
||||||
CommandBuffer::~CommandBuffer() {
|
|
||||||
FreeCommands(&mCommands);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeError CommandBuffer::FillCommands(CommandRecordingContext* commandContext) {
|
MaybeError CommandBuffer::FillCommands(CommandRecordingContext* commandContext) {
|
||||||
|
|
|
@ -329,11 +329,7 @@ namespace dawn_native { namespace null {
|
||||||
// CommandBuffer
|
// CommandBuffer
|
||||||
|
|
||||||
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
||||||
: CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) {
|
: CommandBufferBase(encoder, descriptor) {
|
||||||
}
|
|
||||||
|
|
||||||
CommandBuffer::~CommandBuffer() {
|
|
||||||
FreeCommands(&mCommands);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// QuerySet
|
// QuerySet
|
||||||
|
|
|
@ -216,10 +216,6 @@ namespace dawn_native { namespace null {
|
||||||
public:
|
public:
|
||||||
CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
|
CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
|
||||||
|
|
||||||
private:
|
|
||||||
~CommandBuffer() override;
|
|
||||||
|
|
||||||
CommandIterator mCommands;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class QuerySet final : public QuerySetBase {
|
class QuerySet final : public QuerySetBase {
|
||||||
|
|
|
@ -442,11 +442,7 @@ namespace dawn_native { namespace opengl {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
||||||
: CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) {
|
: CommandBufferBase(encoder, descriptor) {
|
||||||
}
|
|
||||||
|
|
||||||
CommandBuffer::~CommandBuffer() {
|
|
||||||
FreeCommands(&mCommands);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBuffer::Execute() {
|
void CommandBuffer::Execute() {
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#ifndef DAWNNATIVE_OPENGL_COMMANDBUFFERGL_H_
|
#ifndef DAWNNATIVE_OPENGL_COMMANDBUFFERGL_H_
|
||||||
#define DAWNNATIVE_OPENGL_COMMANDBUFFERGL_H_
|
#define DAWNNATIVE_OPENGL_COMMANDBUFFERGL_H_
|
||||||
|
|
||||||
#include "dawn_native/CommandAllocator.h"
|
|
||||||
#include "dawn_native/CommandBuffer.h"
|
#include "dawn_native/CommandBuffer.h"
|
||||||
|
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
|
@ -33,11 +32,8 @@ namespace dawn_native { namespace opengl {
|
||||||
void Execute();
|
void Execute();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~CommandBuffer() override;
|
|
||||||
void ExecuteComputePass();
|
void ExecuteComputePass();
|
||||||
void ExecuteRenderPass(BeginRenderPassCmd* renderPass);
|
void ExecuteRenderPass(BeginRenderPassCmd* renderPass);
|
||||||
|
|
||||||
CommandIterator mCommands;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace dawn_native::opengl
|
}} // namespace dawn_native::opengl
|
||||||
|
|
|
@ -330,11 +330,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
|
||||||
: CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) {
|
: CommandBufferBase(encoder, descriptor) {
|
||||||
}
|
|
||||||
|
|
||||||
CommandBuffer::~CommandBuffer() {
|
|
||||||
FreeCommands(&mCommands);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBuffer::RecordCopyImageWithTemporaryBuffer(
|
void CommandBuffer::RecordCopyImageWithTemporaryBuffer(
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#ifndef DAWNNATIVE_VULKAN_COMMANDBUFFERVK_H_
|
#ifndef DAWNNATIVE_VULKAN_COMMANDBUFFERVK_H_
|
||||||
#define DAWNNATIVE_VULKAN_COMMANDBUFFERVK_H_
|
#define DAWNNATIVE_VULKAN_COMMANDBUFFERVK_H_
|
||||||
|
|
||||||
#include "dawn_native/CommandAllocator.h"
|
|
||||||
#include "dawn_native/CommandBuffer.h"
|
#include "dawn_native/CommandBuffer.h"
|
||||||
#include "dawn_native/Error.h"
|
#include "dawn_native/Error.h"
|
||||||
|
|
||||||
|
@ -40,7 +39,6 @@ namespace dawn_native { namespace vulkan {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
|
CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
|
||||||
~CommandBuffer() override;
|
|
||||||
|
|
||||||
MaybeError RecordComputePass(CommandRecordingContext* recordingContext);
|
MaybeError RecordComputePass(CommandRecordingContext* recordingContext);
|
||||||
MaybeError RecordRenderPass(CommandRecordingContext* recordingContext,
|
MaybeError RecordRenderPass(CommandRecordingContext* recordingContext,
|
||||||
|
@ -49,8 +47,6 @@ namespace dawn_native { namespace vulkan {
|
||||||
const TextureCopy& srcCopy,
|
const TextureCopy& srcCopy,
|
||||||
const TextureCopy& dstCopy,
|
const TextureCopy& dstCopy,
|
||||||
const Extent3D& copySize);
|
const Extent3D& copySize);
|
||||||
|
|
||||||
CommandIterator mCommands;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace dawn_native::vulkan
|
}} // namespace dawn_native::vulkan
|
||||||
|
|
|
@ -63,7 +63,7 @@ TEST(CommandAllocator, DoNothingAllocator) {
|
||||||
TEST(CommandAllocator, DoNothingAllocatorWithIterator) {
|
TEST(CommandAllocator, DoNothingAllocatorWithIterator) {
|
||||||
CommandAllocator allocator;
|
CommandAllocator allocator;
|
||||||
CommandIterator iterator(std::move(allocator));
|
CommandIterator iterator(std::move(allocator));
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test basic usage of allocator + iterator
|
// Test basic usage of allocator + iterator
|
||||||
|
@ -108,7 +108,7 @@ TEST(CommandAllocator, Basic) {
|
||||||
hasNext = iterator.NextCommandId(&type);
|
hasNext = iterator.NextCommandId(&type);
|
||||||
ASSERT_FALSE(hasNext);
|
ASSERT_FALSE(hasNext);
|
||||||
|
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ TEST(CommandAllocator, BasicWithData) {
|
||||||
hasNext = iterator.NextCommandId(&type);
|
hasNext = iterator.NextCommandId(&type);
|
||||||
ASSERT_FALSE(hasNext);
|
ASSERT_FALSE(hasNext);
|
||||||
|
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ TEST(CommandAllocator, MultipleIterations) {
|
||||||
hasNext = iterator.NextCommandId(&type);
|
hasNext = iterator.NextCommandId(&type);
|
||||||
ASSERT_FALSE(hasNext);
|
ASSERT_FALSE(hasNext);
|
||||||
|
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Test large commands work
|
// Test large commands work
|
||||||
|
@ -230,7 +230,7 @@ TEST(CommandAllocator, LargeCommands) {
|
||||||
}
|
}
|
||||||
ASSERT_EQ(numCommands, kCommandCount);
|
ASSERT_EQ(numCommands, kCommandCount);
|
||||||
|
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test many small commands work
|
// Test many small commands work
|
||||||
|
@ -260,7 +260,7 @@ TEST(CommandAllocator, ManySmallCommands) {
|
||||||
}
|
}
|
||||||
ASSERT_EQ(numCommands, kCommandCount);
|
ASSERT_EQ(numCommands, kCommandCount);
|
||||||
|
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ________
|
/* ________
|
||||||
|
@ -325,7 +325,7 @@ TEST(CommandAllocator, IteratorReset) {
|
||||||
hasNext = iterator.NextCommandId(&type);
|
hasNext = iterator.NextCommandId(&type);
|
||||||
ASSERT_FALSE(hasNext);
|
ASSERT_FALSE(hasNext);
|
||||||
|
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ TEST(CommandAllocator, EmptyIterator) {
|
||||||
bool hasNext = iterator.NextCommandId(&type);
|
bool hasNext = iterator.NextCommandId(&type);
|
||||||
ASSERT_FALSE(hasNext);
|
ASSERT_FALSE(hasNext);
|
||||||
|
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
CommandAllocator allocator;
|
CommandAllocator allocator;
|
||||||
|
@ -350,8 +350,8 @@ TEST(CommandAllocator, EmptyIterator) {
|
||||||
bool hasNext = iterator2.NextCommandId(&type);
|
bool hasNext = iterator2.NextCommandId(&type);
|
||||||
ASSERT_FALSE(hasNext);
|
ASSERT_FALSE(hasNext);
|
||||||
|
|
||||||
iterator1.DataWasDestroyed();
|
iterator1.MakeEmptyAsDataWasDestroyed();
|
||||||
iterator2.DataWasDestroyed();
|
iterator2.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
CommandIterator iterator1;
|
CommandIterator iterator1;
|
||||||
|
@ -361,8 +361,8 @@ TEST(CommandAllocator, EmptyIterator) {
|
||||||
bool hasNext = iterator2.NextCommandId(&type);
|
bool hasNext = iterator2.NextCommandId(&type);
|
||||||
ASSERT_FALSE(hasNext);
|
ASSERT_FALSE(hasNext);
|
||||||
|
|
||||||
iterator1.DataWasDestroyed();
|
iterator1.MakeEmptyAsDataWasDestroyed();
|
||||||
iterator2.DataWasDestroyed();
|
iterator2.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +425,7 @@ TEST(CommandAllocator, AllocateDefaultInitializes) {
|
||||||
ASSERT_EQ(int44->value, 44);
|
ASSERT_EQ(int44->value, 44);
|
||||||
|
|
||||||
CommandIterator iterator(std::move(allocator));
|
CommandIterator iterator(std::move(allocator));
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that the allcator correctly defaults initalizes data for AllocateData
|
// Test that the allcator correctly defaults initalizes data for AllocateData
|
||||||
|
@ -445,5 +445,5 @@ TEST(CommandAllocator, AllocateDataDefaultInitializes) {
|
||||||
ASSERT_EQ(int35[2].value, 35);
|
ASSERT_EQ(int35[2].value, 35);
|
||||||
|
|
||||||
CommandIterator iterator(std::move(allocator));
|
CommandIterator iterator(std::move(allocator));
|
||||||
iterator.DataWasDestroyed();
|
iterator.MakeEmptyAsDataWasDestroyed();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,17 +19,17 @@
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
// Make our own Base - Backend object pair, reusing the CommandBuffer name
|
// Make our own Base - Backend object pair, reusing the AdapterBase name
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
class CommandBufferBase : public RefCounted {};
|
class AdapterBase : public RefCounted {};
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
||||||
using namespace dawn_native;
|
using namespace dawn_native;
|
||||||
|
|
||||||
class MyCommandBuffer : public CommandBufferBase {};
|
class MyAdapter : public AdapterBase {};
|
||||||
|
|
||||||
struct MyBackendTraits {
|
struct MyBackendTraits {
|
||||||
using CommandBufferType = MyCommandBuffer;
|
using AdapterType = MyAdapter;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Instanciate ToBackend for our "backend"
|
// Instanciate ToBackend for our "backend"
|
||||||
|
@ -41,48 +41,47 @@ auto ToBackend(T&& common) -> decltype(ToBackendBase<MyBackendTraits>(common)) {
|
||||||
// Test that ToBackend correctly converts pointers to base classes.
|
// Test that ToBackend correctly converts pointers to base classes.
|
||||||
TEST(ToBackend, Pointers) {
|
TEST(ToBackend, Pointers) {
|
||||||
{
|
{
|
||||||
MyCommandBuffer* cmdBuf = new MyCommandBuffer;
|
MyAdapter* adapter = new MyAdapter;
|
||||||
const CommandBufferBase* base = cmdBuf;
|
const AdapterBase* base = adapter;
|
||||||
|
|
||||||
auto backendCmdBuf = ToBackend(base);
|
auto backendAdapter = ToBackend(base);
|
||||||
static_assert(std::is_same<decltype(backendCmdBuf), const MyCommandBuffer*>::value, "");
|
static_assert(std::is_same<decltype(backendAdapter), const MyAdapter*>::value, "");
|
||||||
ASSERT_EQ(cmdBuf, backendCmdBuf);
|
ASSERT_EQ(adapter, backendAdapter);
|
||||||
|
|
||||||
cmdBuf->Release();
|
adapter->Release();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
MyCommandBuffer* cmdBuf = new MyCommandBuffer;
|
MyAdapter* adapter = new MyAdapter;
|
||||||
CommandBufferBase* base = cmdBuf;
|
AdapterBase* base = adapter;
|
||||||
|
|
||||||
auto backendCmdBuf = ToBackend(base);
|
auto backendAdapter = ToBackend(base);
|
||||||
static_assert(std::is_same<decltype(backendCmdBuf), MyCommandBuffer*>::value, "");
|
static_assert(std::is_same<decltype(backendAdapter), MyAdapter*>::value, "");
|
||||||
ASSERT_EQ(cmdBuf, backendCmdBuf);
|
ASSERT_EQ(adapter, backendAdapter);
|
||||||
|
|
||||||
cmdBuf->Release();
|
adapter->Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that ToBackend correctly converts Refs to base classes.
|
// Test that ToBackend correctly converts Refs to base classes.
|
||||||
TEST(ToBackend, Ref) {
|
TEST(ToBackend, Ref) {
|
||||||
{
|
{
|
||||||
MyCommandBuffer* cmdBuf = new MyCommandBuffer;
|
MyAdapter* adapter = new MyAdapter;
|
||||||
const Ref<CommandBufferBase> base(cmdBuf);
|
const Ref<AdapterBase> base(adapter);
|
||||||
|
|
||||||
const auto& backendCmdBuf = ToBackend(base);
|
const auto& backendAdapter = ToBackend(base);
|
||||||
static_assert(std::is_same<decltype(ToBackend(base)), const Ref<MyCommandBuffer>&>::value,
|
static_assert(std::is_same<decltype(ToBackend(base)), const Ref<MyAdapter>&>::value, "");
|
||||||
"");
|
ASSERT_EQ(adapter, backendAdapter.Get());
|
||||||
ASSERT_EQ(cmdBuf, backendCmdBuf.Get());
|
|
||||||
|
|
||||||
cmdBuf->Release();
|
adapter->Release();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
MyCommandBuffer* cmdBuf = new MyCommandBuffer;
|
MyAdapter* adapter = new MyAdapter;
|
||||||
Ref<CommandBufferBase> base(cmdBuf);
|
Ref<AdapterBase> base(adapter);
|
||||||
|
|
||||||
auto backendCmdBuf = ToBackend(base);
|
auto backendAdapter = ToBackend(base);
|
||||||
static_assert(std::is_same<decltype(ToBackend(base)), Ref<MyCommandBuffer>&>::value, "");
|
static_assert(std::is_same<decltype(ToBackend(base)), Ref<MyAdapter>&>::value, "");
|
||||||
ASSERT_EQ(cmdBuf, backendCmdBuf.Get());
|
ASSERT_EQ(adapter, backendAdapter.Get());
|
||||||
|
|
||||||
cmdBuf->Release();
|
adapter->Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,10 @@ namespace {
|
||||||
// Test submitting with a mapped buffer is disallowed
|
// Test submitting with a mapped buffer is disallowed
|
||||||
TEST_F(QueueSubmitValidationTest, SubmitWithMappedBuffer) {
|
TEST_F(QueueSubmitValidationTest, SubmitWithMappedBuffer) {
|
||||||
// Create a map-write buffer.
|
// Create a map-write buffer.
|
||||||
|
const uint64_t kBufferSize = 4;
|
||||||
wgpu::BufferDescriptor descriptor;
|
wgpu::BufferDescriptor descriptor;
|
||||||
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
|
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
|
||||||
descriptor.size = 4;
|
descriptor.size = kBufferSize;
|
||||||
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
||||||
|
|
||||||
// Create a fake copy destination buffer
|
// Create a fake copy destination buffer
|
||||||
|
@ -36,7 +37,7 @@ namespace {
|
||||||
wgpu::CommandBuffer commands;
|
wgpu::CommandBuffer commands;
|
||||||
{
|
{
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
encoder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, 4);
|
encoder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, kBufferSize);
|
||||||
commands = encoder.Finish();
|
commands = encoder.Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,17 +46,35 @@ namespace {
|
||||||
// Submitting when the buffer has never been mapped should succeed
|
// Submitting when the buffer has never been mapped should succeed
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, kBufferSize);
|
||||||
|
commands = encoder.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
// Map the buffer, submitting when the buffer is mapped should fail
|
// Map the buffer, submitting when the buffer is mapped should fail
|
||||||
buffer.MapWriteAsync(nullptr, nullptr);
|
buffer.MapAsync(wgpu::MapMode::Write, 0, kBufferSize, nullptr, nullptr);
|
||||||
|
|
||||||
// Try submitting before the callback is fired.
|
// Try submitting before the callback is fired.
|
||||||
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||||
|
|
||||||
WaitForAllOperations(device);
|
WaitForAllOperations(device);
|
||||||
|
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, kBufferSize);
|
||||||
|
commands = encoder.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
// Try submitting after the callback is fired.
|
// Try submitting after the callback is fired.
|
||||||
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||||
|
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, kBufferSize);
|
||||||
|
commands = encoder.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
// Unmap the buffer, queue submit should succeed
|
// Unmap the buffer, queue submit should succeed
|
||||||
buffer.Unmap();
|
buffer.Unmap();
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
@ -182,4 +201,53 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test it is invalid to submit a command buffer twice
|
||||||
|
TEST_F(QueueSubmitValidationTest, CommandBufferSubmittedTwice) {
|
||||||
|
wgpu::CommandBuffer commandBuffer = device.CreateCommandEncoder().Finish();
|
||||||
|
wgpu::Queue queue = device.GetDefaultQueue();
|
||||||
|
|
||||||
|
// Should succeed
|
||||||
|
queue.Submit(1, &commandBuffer);
|
||||||
|
|
||||||
|
// Should fail because command buffer was already submitted
|
||||||
|
ASSERT_DEVICE_ERROR(queue.Submit(1, &commandBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test resubmitting failed command buffers
|
||||||
|
TEST_F(QueueSubmitValidationTest, CommandBufferSubmittedFailed) {
|
||||||
|
// Create a map-write buffer
|
||||||
|
const uint64_t kBufferSize = 4;
|
||||||
|
wgpu::BufferDescriptor descriptor;
|
||||||
|
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
|
||||||
|
descriptor.size = kBufferSize;
|
||||||
|
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
||||||
|
|
||||||
|
// Create a destination buffer for the b2b copy
|
||||||
|
descriptor.usage = wgpu::BufferUsage::CopyDst;
|
||||||
|
descriptor.size = kBufferSize;
|
||||||
|
wgpu::Buffer targetBuffer = device.CreateBuffer(&descriptor);
|
||||||
|
|
||||||
|
// Create a command buffer that reads from the mappable buffer
|
||||||
|
wgpu::CommandBuffer commands;
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
encoder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, kBufferSize);
|
||||||
|
commands = encoder.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::Queue queue = device.GetDefaultQueue();
|
||||||
|
|
||||||
|
// Map the source buffer to force a failure
|
||||||
|
buffer.MapAsync(wgpu::MapMode::Write, 0, kBufferSize, nullptr, nullptr);
|
||||||
|
|
||||||
|
// Submitting a command buffer with a mapped buffer should fail
|
||||||
|
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||||
|
|
||||||
|
// Unmap buffer to fix the failure
|
||||||
|
buffer.Unmap();
|
||||||
|
|
||||||
|
// Resubmitting any command buffer, even if the problem was fixed, should fail
|
||||||
|
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
Loading…
Reference in New Issue