Implement buffer lazy initialization before CopyBufferToBuffer

This patch implements buffer lazy initialization before
CopyBufferToBuffer() behind the toggle LazyClearBufferOnFirstUse.
- If the source buffer is not initialized, it will be cleared to 0
  before CopyBufferToBuffer().
- If the destination buffer is not initialized and the copy doesn't
  overwrite the whole buffer, it will be cleared to 0 before
  CopyBufferToBuffer(), otherwise the buffer shouldn't be cleared.

BUG=dawn:414
TEST=dawn_end2end_tests

Change-Id: I3d0512c6376a1ed8928e86f8e56fefebc16910fa
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/24360
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Jiawei Shao 2020-07-09 09:15:22 +00:00 committed by Commit Bot service account
parent d3bf2188aa
commit dab10eae8a
17 changed files with 339 additions and 50 deletions

View File

@ -313,10 +313,44 @@ namespace dawn_native { namespace d3d12 {
return mResourceAllocation.GetInfo().mMethod == allocationMethod; return mResourceAllocation.GetInfo().mMethod == allocationMethod;
} }
MaybeError Buffer::ClearBufferContentsToZero(CommandRecordingContext* commandContext) { MaybeError Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
// TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse
// instead when buffer lazy initialization is completely supported.
if (IsDataInitialized() ||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
return {};
}
DAWN_TRY(InitializeToZero(commandContext));
return {};
}
MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
uint64_t offset,
uint64_t size) {
// TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse
// instead when buffer lazy initialization is completely supported.
if (IsDataInitialized() ||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
return {};
}
if (IsFullBufferRange(offset, size)) {
SetIsDataInitialized();
} else {
DAWN_TRY(InitializeToZero(commandContext));
}
return {};
}
MaybeError Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)); ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse));
ASSERT(!IsDataInitialized()); ASSERT(!IsDataInitialized());
// TODO(jiawei.shao@intel.com): skip initializing the buffer when it is created on a heap
// that has already been zero initialized.
DAWN_TRY(ClearBuffer(commandContext, uint8_t(0u))); DAWN_TRY(ClearBuffer(commandContext, uint8_t(0u)));
SetIsDataInitialized(); SetIsDataInitialized();
GetDevice()->IncrementLazyClearCountForTesting(); GetDevice()->IncrementLazyClearCountForTesting();

View File

@ -44,7 +44,10 @@ namespace dawn_native { namespace d3d12 {
bool CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const; bool CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const;
bool CheckIsResidentForTesting() const; bool CheckIsResidentForTesting() const;
MaybeError ClearBufferContentsToZero(CommandRecordingContext* commandContext); MaybeError EnsureDataInitialized(CommandRecordingContext* commandContext);
MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
uint64_t offset,
uint64_t size);
private: private:
~Buffer() override; ~Buffer() override;
@ -63,6 +66,7 @@ namespace dawn_native { namespace d3d12 {
D3D12_RESOURCE_BARRIER* barrier, D3D12_RESOURCE_BARRIER* barrier,
wgpu::BufferUsage newUsage); wgpu::BufferUsage newUsage);
MaybeError InitializeToZero(CommandRecordingContext* commandContext);
MaybeError ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue); MaybeError ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue);
ResourceHeapAllocation mResourceAllocation; ResourceHeapAllocation mResourceAllocation;

View File

@ -582,6 +582,10 @@ namespace dawn_native { namespace d3d12 {
Buffer* srcBuffer = ToBackend(copy->source.Get()); Buffer* srcBuffer = ToBackend(copy->source.Get());
Buffer* dstBuffer = ToBackend(copy->destination.Get()); Buffer* dstBuffer = ToBackend(copy->destination.Get());
DAWN_TRY(srcBuffer->EnsureDataInitialized(commandContext));
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(
commandContext, copy->destinationOffset, copy->size));
srcBuffer->TrackUsageAndTransitionNow(commandContext, srcBuffer->TrackUsageAndTransitionNow(commandContext,
wgpu::BufferUsage::CopySrc); wgpu::BufferUsage::CopySrc);
dstBuffer->TrackUsageAndTransitionNow(commandContext, dstBuffer->TrackUsageAndTransitionNow(commandContext,

View File

@ -337,15 +337,8 @@ namespace dawn_native { namespace d3d12 {
Buffer* dstBuffer = ToBackend(destination); Buffer* dstBuffer = ToBackend(destination);
// TODO(jiawei.shao@intel.com): use Toggle::LazyClearResourceOnFirstUse when the support of DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(commandRecordingContext,
// buffer lazy initialization is completed. destinationOffset, size));
if (IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse) && !dstBuffer->IsDataInitialized()) {
if (dstBuffer->IsFullBufferRange(destinationOffset, size)) {
dstBuffer->SetIsDataInitialized();
} else {
DAWN_TRY(dstBuffer->ClearBufferContentsToZero(commandRecordingContext));
}
}
CopyFromStagingToBufferImpl(commandRecordingContext, source, sourceOffset, destination, CopyFromStagingToBufferImpl(commandRecordingContext, source, sourceOffset, destination,
destinationOffset, size); destinationOffset, size);

View File

@ -31,7 +31,10 @@ namespace dawn_native { namespace metal {
const BufferDescriptor* descriptor); const BufferDescriptor* descriptor);
id<MTLBuffer> GetMTLBuffer() const; id<MTLBuffer> GetMTLBuffer() const;
void ClearBufferContentsToZero(CommandRecordingContext* commandContext); void EnsureDataInitialized(CommandRecordingContext* commandContext);
void EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
uint64_t offset,
uint64_t size);
private: private:
using BufferBase::BufferBase; using BufferBase::BufferBase;
@ -47,6 +50,7 @@ namespace dawn_native { namespace metal {
bool IsMapWritable() const override; bool IsMapWritable() const override;
MaybeError MapAtCreationImpl() override; MaybeError MapAtCreationImpl() override;
void InitializeToZero(CommandRecordingContext* commandContext);
void ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue); void ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue);
id<MTLBuffer> mMtlBuffer = nil; id<MTLBuffer> mMtlBuffer = nil;

View File

@ -134,7 +134,35 @@ namespace dawn_native { namespace metal {
mMtlBuffer = nil; mMtlBuffer = nil;
} }
void Buffer::ClearBufferContentsToZero(CommandRecordingContext* commandContext) { void Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) {
// TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse
// instead when buffer lazy initialization is completely supported.
if (IsDataInitialized() ||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
return;
}
InitializeToZero(commandContext);
}
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext,
uint64_t offset,
uint64_t size) {
// TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse
// instead when buffer lazy initialization is completely supported.
if (IsDataInitialized() ||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
return;
}
if (IsFullBufferRange(offset, size)) {
SetIsDataInitialized();
} else {
InitializeToZero(commandContext);
}
}
void Buffer::InitializeToZero(CommandRecordingContext* commandContext) {
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)); ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse));
ASSERT(!IsDataInitialized()); ASSERT(!IsDataInitialized());

View File

@ -721,6 +721,11 @@ namespace dawn_native { namespace metal {
case Command::CopyBufferToBuffer: { case Command::CopyBufferToBuffer: {
CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>(); CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
ToBackend(copy->source)->EnsureDataInitialized(commandContext);
ToBackend(copy->destination)
->EnsureDataInitializedAsDestination(commandContext,
copy->destinationOffset, copy->size);
[commandContext->EnsureBlit() [commandContext->EnsureBlit()
copyFromBuffer:ToBackend(copy->source)->GetMTLBuffer() copyFromBuffer:ToBackend(copy->source)->GetMTLBuffer()
sourceOffset:copy->sourceOffset sourceOffset:copy->sourceOffset

View File

@ -254,16 +254,9 @@ namespace dawn_native { namespace metal {
// this function. // this function.
ASSERT(size != 0); ASSERT(size != 0);
// TODO(jiawei.shao@intel.com): use Toggle::LazyClearResourceOnFirstUse when the support of ToBackend(destination)
// buffer lazy initialization is completed. ->EnsureDataInitializedAsDestination(GetPendingCommandContext(), destinationOffset,
if (IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse) && size);
!destination->IsDataInitialized()) {
if (destination->IsFullBufferRange(destinationOffset, size)) {
destination->SetIsDataInitialized();
} else {
ToBackend(destination)->ClearBufferContentsToZero(GetPendingCommandContext());
}
}
id<MTLBuffer> uploadBuffer = ToBackend(source)->GetBufferHandle(); id<MTLBuffer> uploadBuffer = ToBackend(source)->GetBufferHandle();
id<MTLBuffer> buffer = ToBackend(destination)->GetMTLBuffer(); id<MTLBuffer> buffer = ToBackend(destination)->GetMTLBuffer();

View File

@ -51,7 +51,33 @@ namespace dawn_native { namespace opengl {
return std::max(GetSize(), uint64_t(4u)); return std::max(GetSize(), uint64_t(4u));
} }
void Buffer::ClearBufferContentsToZero() { void Buffer::EnsureDataInitialized() {
// TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse
// instead when buffer lazy initialization is completely supported.
if (IsDataInitialized() ||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
return;
}
InitializeToZero();
}
void Buffer::EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size) {
// TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse
// instead when buffer lazy initialization is completely supported.
if (IsDataInitialized() ||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
return;
}
if (IsFullBufferRange(offset, size)) {
SetIsDataInitialized();
} else {
InitializeToZero();
}
}
void Buffer::InitializeToZero() {
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)); ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse));
ASSERT(!IsDataInitialized()); ASSERT(!IsDataInitialized());
@ -61,9 +87,9 @@ namespace dawn_native { namespace opengl {
const std::vector<uint8_t> clearValues(size, 0u); const std::vector<uint8_t> clearValues(size, 0u);
device->gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer); device->gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer);
device->gl.BufferSubData(GL_ARRAY_BUFFER, 0, size, clearValues.data()); device->gl.BufferSubData(GL_ARRAY_BUFFER, 0, size, clearValues.data());
device->IncrementLazyClearCountForTesting();
SetIsDataInitialized(); SetIsDataInitialized();
device->IncrementLazyClearCountForTesting();
} }
bool Buffer::IsMapWritable() const { bool Buffer::IsMapWritable() const {

View File

@ -29,7 +29,8 @@ namespace dawn_native { namespace opengl {
GLuint GetHandle() const; GLuint GetHandle() const;
void ClearBufferContentsToZero(); void EnsureDataInitialized();
void EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size);
private: private:
~Buffer() override; ~Buffer() override;
@ -44,6 +45,8 @@ namespace dawn_native { namespace opengl {
void* GetMappedPointerImpl() override; void* GetMappedPointerImpl() override;
uint64_t GetAppliedSize() const; uint64_t GetAppliedSize() const;
void InitializeToZero();
GLuint mBuffer = 0; GLuint mBuffer = 0;
void* mMappedData = nullptr; void* mMappedData = nullptr;
}; };

View File

@ -493,6 +493,10 @@ namespace dawn_native { namespace opengl {
case Command::CopyBufferToBuffer: { case Command::CopyBufferToBuffer: {
CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>(); CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
ToBackend(copy->source)->EnsureDataInitialized();
ToBackend(copy->destination)
->EnsureDataInitializedAsDestination(copy->destinationOffset, copy->size);
gl.BindBuffer(GL_PIXEL_PACK_BUFFER, ToBackend(copy->source)->GetHandle()); gl.BindBuffer(GL_PIXEL_PACK_BUFFER, ToBackend(copy->source)->GetHandle());
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER,
ToBackend(copy->destination)->GetHandle()); ToBackend(copy->destination)->GetHandle());

View File

@ -44,16 +44,7 @@ namespace dawn_native { namespace opengl {
size_t size) { size_t size) {
const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; const OpenGLFunctions& gl = ToBackend(GetDevice())->gl;
// TODO(jiawei.shao@intel.com): use Toggle::LazyClearResourceOnFirstUse when the support of ToBackend(buffer)->EnsureDataInitializedAsDestination(bufferOffset, size);
// buffer lazy initialization is completed.
if (GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse) &&
!buffer->IsDataInitialized()) {
if (buffer->IsFullBufferRange(bufferOffset, size)) {
buffer->SetIsDataInitialized();
} else {
ToBackend(buffer)->ClearBufferContentsToZero();
}
}
gl.BindBuffer(GL_ARRAY_BUFFER, ToBackend(buffer)->GetHandle()); gl.BindBuffer(GL_ARRAY_BUFFER, ToBackend(buffer)->GetHandle());
gl.BufferSubData(GL_ARRAY_BUFFER, bufferOffset, size, data); gl.BufferSubData(GL_ARRAY_BUFFER, bufferOffset, size, data);

View File

@ -278,14 +278,41 @@ namespace dawn_native { namespace vulkan {
} }
} }
void Buffer::ClearBufferContentsToZero(CommandRecordingContext* recordingContext) { void Buffer::EnsureDataInitialized(CommandRecordingContext* recordingContext) {
// TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse
// instead when buffer lazy initialization is completely supported.
if (IsDataInitialized() ||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
return;
}
InitializeToZero(recordingContext);
}
void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
uint64_t offset,
uint64_t size) {
// TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse
// instead when buffer lazy initialization is completely supported.
if (IsDataInitialized() ||
!GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) {
return;
}
if (IsFullBufferRange(offset, size)) {
SetIsDataInitialized();
} else {
InitializeToZero(recordingContext);
}
}
void Buffer::InitializeToZero(CommandRecordingContext* recordingContext) {
ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)); ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse));
ASSERT(!IsDataInitialized()); ASSERT(!IsDataInitialized());
ClearBuffer(recordingContext, 0u); ClearBuffer(recordingContext, 0u);
SetIsDataInitialized();
GetDevice()->IncrementLazyClearCountForTesting(); GetDevice()->IncrementLazyClearCountForTesting();
SetIsDataInitialized();
} }
void Buffer::ClearBuffer(CommandRecordingContext* recordingContext, uint32_t clearValue) { void Buffer::ClearBuffer(CommandRecordingContext* recordingContext, uint32_t clearValue) {

View File

@ -43,12 +43,16 @@ namespace dawn_native { namespace vulkan {
VkPipelineStageFlags* srcStages, VkPipelineStageFlags* srcStages,
VkPipelineStageFlags* dstStages); VkPipelineStageFlags* dstStages);
void ClearBufferContentsToZero(CommandRecordingContext* recordingContext); void EnsureDataInitialized(CommandRecordingContext* recordingContext);
void EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext,
uint64_t offset,
uint64_t size);
private: private:
~Buffer() override; ~Buffer() override;
using BufferBase::BufferBase; using BufferBase::BufferBase;
MaybeError Initialize(); MaybeError Initialize();
void InitializeToZero(CommandRecordingContext* recordingContext);
void ClearBuffer(CommandRecordingContext* recordingContext, uint32_t clearValue); void ClearBuffer(CommandRecordingContext* recordingContext, uint32_t clearValue);
// Dawn API // Dawn API

View File

@ -424,6 +424,10 @@ namespace dawn_native { namespace vulkan {
Buffer* srcBuffer = ToBackend(copy->source.Get()); Buffer* srcBuffer = ToBackend(copy->source.Get());
Buffer* dstBuffer = ToBackend(copy->destination.Get()); Buffer* dstBuffer = ToBackend(copy->destination.Get());
srcBuffer->EnsureDataInitialized(recordingContext);
dstBuffer->EnsureDataInitializedAsDestination(
recordingContext, copy->destinationOffset, copy->size);
srcBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); srcBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc);
dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst);

View File

@ -592,16 +592,8 @@ namespace dawn_native { namespace vulkan {
CommandRecordingContext* recordingContext = GetPendingRecordingContext(); CommandRecordingContext* recordingContext = GetPendingRecordingContext();
// TODO(jiawei.shao@intel.com): use Toggle::LazyClearResourceOnFirstUse when the support of ToBackend(destination)
// buffer lazy initialization is completed. ->EnsureDataInitializedAsDestination(recordingContext, destinationOffset, size);
if (IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse) &&
!destination->IsDataInitialized()) {
if (destination->IsFullBufferRange(destinationOffset, size)) {
destination->SetIsDataInitialized();
} else {
ToBackend(destination)->ClearBufferContentsToZero(recordingContext);
}
}
// Insert memory barrier to ensure host write operations are made visible before // Insert memory barrier to ensure host write operations are made visible before
// copying from the staging buffer. However, this barrier can be removed (see note below). // copying from the staging buffer. However, this barrier can be removed (see note below).

View File

@ -87,6 +87,179 @@ TEST_P(BufferZeroInitTest, WriteBufferToSubBuffer) {
} }
} }
// Test that the code path of CopyBufferToBuffer clears the source buffer correctly when it is the
// first use of the source buffer.
TEST_P(BufferZeroInitTest, CopyBufferToBufferSource) {
constexpr uint64_t kBufferSize = 16u;
constexpr wgpu::BufferUsage kBufferUsage =
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
wgpu::BufferDescriptor bufferDescriptor;
bufferDescriptor.size = kBufferSize;
bufferDescriptor.usage = kBufferUsage;
constexpr std::array<uint8_t, kBufferSize> kInitialData = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
wgpu::Buffer dstBuffer =
utils::CreateBufferFromData(device, kInitialData.data(), kBufferSize, kBufferUsage);
constexpr std::array<uint32_t, kBufferSize / sizeof(uint32_t)> kExpectedData = {{0, 0, 0, 0}};
// Full copy from the source buffer
{
wgpu::Buffer srcBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, kBufferSize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer));
EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), srcBuffer, 0,
kBufferSize / sizeof(uint32_t));
}
// Partial copy from the source buffer
// srcOffset == 0
{
constexpr uint64_t kSrcOffset = 0;
constexpr uint64_t kCopySize = kBufferSize / 2;
wgpu::Buffer srcBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(srcBuffer, kSrcOffset, dstBuffer, 0, kCopySize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer));
EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), srcBuffer, 0,
kBufferSize / sizeof(uint32_t));
}
// srcOffset > 0 and srcOffset + copySize == srcBufferSize
{
constexpr uint64_t kSrcOffset = kBufferSize / 2;
constexpr uint64_t kCopySize = kBufferSize - kSrcOffset;
wgpu::Buffer srcBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(srcBuffer, kSrcOffset, dstBuffer, 0, kCopySize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer));
EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), srcBuffer, 0,
kBufferSize / sizeof(uint32_t));
}
// srcOffset > 0 and srcOffset + copySize < srcBufferSize
{
constexpr uint64_t kSrcOffset = kBufferSize / 4;
constexpr uint64_t kCopySize = kBufferSize / 2;
wgpu::Buffer srcBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(srcBuffer, kSrcOffset, dstBuffer, 0, kCopySize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer));
EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), srcBuffer, 0,
kBufferSize / sizeof(uint32_t));
}
}
// Test that the code path of CopyBufferToBuffer clears the destination buffer correctly when it is
// the first use of the destination buffer.
TEST_P(BufferZeroInitTest, CopyBufferToBufferDestination) {
constexpr uint64_t kBufferSize = 16u;
constexpr wgpu::BufferUsage kBufferUsage =
wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
wgpu::BufferDescriptor bufferDescriptor;
bufferDescriptor.size = kBufferSize;
bufferDescriptor.usage = kBufferUsage;
const std::array<uint8_t, kBufferSize> kInitialData = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
wgpu::Buffer srcBuffer =
utils::CreateBufferFromData(device, kInitialData.data(), kBufferSize, kBufferUsage);
// Full copy from the source buffer doesn't need lazy initialization at all.
{
wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, kBufferSize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<const uint32_t*>(kInitialData.data()),
dstBuffer, 0, kBufferSize / sizeof(uint32_t));
}
// Partial copy from the source buffer needs lazy initialization.
// offset == 0
{
constexpr uint32_t kDstOffset = 0;
constexpr uint32_t kCopySize = kBufferSize / 2;
wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, kDstOffset, kCopySize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer));
std::array<uint8_t, kBufferSize> expectedData;
expectedData.fill(0);
for (uint32_t index = kDstOffset; index < kDstOffset + kCopySize; ++index) {
expectedData[index] = kInitialData[index - kDstOffset];
}
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(expectedData.data()), dstBuffer, 0,
kBufferSize / sizeof(uint32_t));
}
// offset > 0 and dstOffset + CopySize == kBufferSize
{
constexpr uint32_t kDstOffset = kBufferSize / 2;
constexpr uint32_t kCopySize = kBufferSize - kDstOffset;
wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, kDstOffset, kCopySize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer));
std::array<uint8_t, kBufferSize> expectedData;
expectedData.fill(0);
for (uint32_t index = kDstOffset; index < kDstOffset + kCopySize; ++index) {
expectedData[index] = kInitialData[index - kDstOffset];
}
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(expectedData.data()), dstBuffer, 0,
kBufferSize / sizeof(uint32_t));
}
// offset > 0 and dstOffset + CopySize < kBufferSize
{
constexpr uint32_t kDstOffset = kBufferSize / 4;
constexpr uint32_t kCopySize = kBufferSize / 2;
wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, kDstOffset, kCopySize);
wgpu::CommandBuffer commandBuffer = encoder.Finish();
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer));
std::array<uint8_t, kBufferSize> expectedData;
expectedData.fill(0);
for (uint32_t index = kDstOffset; index < kDstOffset + kCopySize; ++index) {
expectedData[index] = kInitialData[index - kDstOffset];
}
EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast<uint32_t*>(expectedData.data()), dstBuffer, 0,
kBufferSize / sizeof(uint32_t));
}
}
DAWN_INSTANTIATE_TEST(BufferZeroInitTest, DAWN_INSTANTIATE_TEST(BufferZeroInitTest,
D3D12Backend({"nonzero_clear_resources_on_creation_for_testing", D3D12Backend({"nonzero_clear_resources_on_creation_for_testing",
"lazy_clear_buffer_on_first_use"}), "lazy_clear_buffer_on_first_use"}),