Metal: Implement lazy texture clears

Bug: dawn:145
Change-Id: I73d161002cb09498e41838a10e9ac1db996c955d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/14781
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2020-01-16 00:12:10 +00:00 committed by Commit Bot service account
parent 0ecc48ecb7
commit 0d6619848d
13 changed files with 813 additions and 61 deletions

View File

@ -14,7 +14,10 @@
#include "dawn_native/CommandBuffer.h"
#include "common/BitSetIterator.h"
#include "dawn_native/CommandEncoder.h"
#include "dawn_native/Commands.h"
#include "dawn_native/Format.h"
#include "dawn_native/Texture.h"
namespace dawn_native {
@ -47,4 +50,92 @@ namespace dawn_native {
}
return false;
}
void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass) {
for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
auto& attachmentInfo = renderPass->colorAttachments[i];
TextureViewBase* view = attachmentInfo.view.Get();
bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr;
ASSERT(view->GetLayerCount() == 1);
ASSERT(view->GetLevelCount() == 1);
// If the loadOp is Load, but the subresource is not initialized, use Clear instead.
if (attachmentInfo.loadOp == wgpu::LoadOp::Load &&
!view->GetTexture()->IsSubresourceContentInitialized(
view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1)) {
attachmentInfo.loadOp = wgpu::LoadOp::Clear;
attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f};
}
if (hasResolveTarget) {
// We need to set the resolve target to initialized so that it does not get
// cleared later in the pipeline. The texture will be resolved from the
// source color attachment, which will be correctly initialized.
TextureViewBase* resolveView = attachmentInfo.resolveTarget.Get();
resolveView->GetTexture()->SetIsSubresourceContentInitialized(
true, resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(),
resolveView->GetBaseArrayLayer(), resolveView->GetLayerCount());
}
switch (attachmentInfo.storeOp) {
case wgpu::StoreOp::Store:
view->GetTexture()->SetIsSubresourceContentInitialized(
true, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
break;
case wgpu::StoreOp::Clear:
view->GetTexture()->SetIsSubresourceContentInitialized(
false, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
break;
default:
UNREACHABLE();
break;
}
}
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
auto& attachmentInfo = renderPass->depthStencilAttachment;
TextureViewBase* view = attachmentInfo.view.Get();
// If the depth stencil texture has not been initialized, we want to use loadop
// clear to init the contents to 0's
if (!view->GetTexture()->IsSubresourceContentInitialized(
view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
view->GetLayerCount())) {
if (view->GetTexture()->GetFormat().HasDepth() &&
attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) {
attachmentInfo.clearDepth = 0.0f;
attachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
}
if (view->GetTexture()->GetFormat().HasStencil() &&
attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) {
attachmentInfo.clearStencil = 0u;
attachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
}
}
// If these have different store ops, make them both Store because we can't track
// initialized state separately yet. TODO(crbug.com/dawn/145)
if (attachmentInfo.depthStoreOp != attachmentInfo.stencilStoreOp) {
attachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
attachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
}
if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Store &&
attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store) {
view->GetTexture()->SetIsSubresourceContentInitialized(
true, view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
view->GetLayerCount());
} else {
ASSERT(attachmentInfo.depthStoreOp == wgpu::StoreOp::Clear &&
attachmentInfo.stencilStoreOp == wgpu::StoreOp::Clear);
view->GetTexture()->SetIsSubresourceContentInitialized(
false, view->GetBaseMipLevel(), view->GetLevelCount(),
view->GetBaseArrayLayer(), view->GetLayerCount());
}
}
}
} // namespace dawn_native

View File

@ -23,6 +23,8 @@
namespace dawn_native {
struct BeginRenderPassCmd;
class CommandBufferBase : public ObjectBase {
public:
CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
@ -39,6 +41,8 @@ namespace dawn_native {
const Extent3D copySize,
const uint32_t mipLevel);
void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass);
} // namespace dawn_native
#endif // DAWNNATIVE_COMMANDBUFFER_H_

View File

@ -567,7 +567,10 @@ namespace dawn_native { namespace d3d12 {
Device* device = ToBackend(GetDevice());
DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator();
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
if (GetFormat().isRenderable) {
if (GetFormat().HasDepthOrStencil()) {
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE);
@ -587,15 +590,14 @@ namespace dawn_native { namespace d3d12 {
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
}
commandList->ClearDepthStencilView(dsvHandle, clearFlags, clearColor, clearColor, 0,
nullptr);
commandList->ClearDepthStencilView(dsvHandle, clearFlags, fClearColor, clearColor,
0, nullptr);
} else {
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET);
DescriptorHeapHandle rtvHeap;
DAWN_TRY_ASSIGN(rtvHeap, descriptorHeapAllocator->AllocateCPUHeap(
D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1));
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetCPUHandle(0);
const float fClearColor = static_cast<float>(clearColor);
const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
fClearColor};
@ -623,9 +625,7 @@ namespace dawn_native { namespace d3d12 {
UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
std::fill(reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer),
reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer + bufferSize),
clearColor);
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST);

View File

@ -48,13 +48,21 @@ namespace dawn_native { namespace metal {
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
auto& attachmentInfo = renderPass->colorAttachments[i];
if (attachmentInfo.loadOp == wgpu::LoadOp::Clear) {
descriptor.colorAttachments[i].loadAction = MTLLoadActionClear;
descriptor.colorAttachments[i].clearColor =
MTLClearColorMake(attachmentInfo.clearColor.r, attachmentInfo.clearColor.g,
attachmentInfo.clearColor.b, attachmentInfo.clearColor.a);
} else {
descriptor.colorAttachments[i].loadAction = MTLLoadActionLoad;
switch (attachmentInfo.loadOp) {
case wgpu::LoadOp::Clear:
descriptor.colorAttachments[i].loadAction = MTLLoadActionClear;
descriptor.colorAttachments[i].clearColor = MTLClearColorMake(
attachmentInfo.clearColor.r, attachmentInfo.clearColor.g,
attachmentInfo.clearColor.b, attachmentInfo.clearColor.a);
break;
case wgpu::LoadOp::Load:
descriptor.colorAttachments[i].loadAction = MTLLoadActionLoad;
break;
default:
UNREACHABLE();
break;
}
descriptor.colorAttachments[i].texture =
@ -62,19 +70,32 @@ namespace dawn_native { namespace metal {
descriptor.colorAttachments[i].level = attachmentInfo.view->GetBaseMipLevel();
descriptor.colorAttachments[i].slice = attachmentInfo.view->GetBaseArrayLayer();
if (attachmentInfo.storeOp == wgpu::StoreOp::Store) {
if (attachmentInfo.resolveTarget.Get() != nullptr) {
descriptor.colorAttachments[i].resolveTexture =
ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture();
descriptor.colorAttachments[i].resolveLevel =
attachmentInfo.resolveTarget->GetBaseMipLevel();
descriptor.colorAttachments[i].resolveSlice =
attachmentInfo.resolveTarget->GetBaseArrayLayer();
descriptor.colorAttachments[i].storeAction =
kMTLStoreActionStoreAndMultisampleResolve;
} else {
descriptor.colorAttachments[i].storeAction = MTLStoreActionStore;
}
bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr;
switch (attachmentInfo.storeOp) {
case wgpu::StoreOp::Store:
if (hasResolveTarget) {
descriptor.colorAttachments[i].resolveTexture =
ToBackend(attachmentInfo.resolveTarget->GetTexture())
->GetMTLTexture();
descriptor.colorAttachments[i].resolveLevel =
attachmentInfo.resolveTarget->GetBaseMipLevel();
descriptor.colorAttachments[i].resolveSlice =
attachmentInfo.resolveTarget->GetBaseArrayLayer();
descriptor.colorAttachments[i].storeAction =
kMTLStoreActionStoreAndMultisampleResolve;
} else {
descriptor.colorAttachments[i].storeAction = MTLStoreActionStore;
}
break;
case wgpu::StoreOp::Clear:
descriptor.colorAttachments[i].storeAction = MTLStoreActionDontCare;
break;
default:
UNREACHABLE();
break;
}
}
@ -88,25 +109,67 @@ namespace dawn_native { namespace metal {
if (format.HasDepth()) {
descriptor.depthAttachment.texture = texture;
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
if (attachmentInfo.depthLoadOp == wgpu::LoadOp::Clear) {
descriptor.depthAttachment.loadAction = MTLLoadActionClear;
descriptor.depthAttachment.clearDepth = attachmentInfo.clearDepth;
} else {
descriptor.depthAttachment.loadAction = MTLLoadActionLoad;
switch (attachmentInfo.depthStoreOp) {
case wgpu::StoreOp::Store:
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
break;
case wgpu::StoreOp::Clear:
descriptor.depthAttachment.storeAction = MTLStoreActionDontCare;
break;
default:
UNREACHABLE();
break;
}
switch (attachmentInfo.depthLoadOp) {
case wgpu::LoadOp::Clear:
descriptor.depthAttachment.loadAction = MTLLoadActionClear;
descriptor.depthAttachment.clearDepth = attachmentInfo.clearDepth;
break;
case wgpu::LoadOp::Load:
descriptor.depthAttachment.loadAction = MTLLoadActionLoad;
break;
default:
UNREACHABLE();
break;
}
}
if (format.HasStencil()) {
descriptor.stencilAttachment.texture = texture;
descriptor.stencilAttachment.storeAction = MTLStoreActionStore;
if (attachmentInfo.stencilLoadOp == wgpu::LoadOp::Clear) {
descriptor.stencilAttachment.loadAction = MTLLoadActionClear;
descriptor.stencilAttachment.clearStencil = attachmentInfo.clearStencil;
} else {
descriptor.stencilAttachment.loadAction = MTLLoadActionLoad;
switch (attachmentInfo.stencilStoreOp) {
case wgpu::StoreOp::Store:
descriptor.stencilAttachment.storeAction = MTLStoreActionStore;
break;
case wgpu::StoreOp::Clear:
descriptor.stencilAttachment.storeAction = MTLStoreActionDontCare;
break;
default:
UNREACHABLE();
break;
}
switch (attachmentInfo.stencilLoadOp) {
case wgpu::LoadOp::Clear:
descriptor.stencilAttachment.loadAction = MTLLoadActionClear;
descriptor.stencilAttachment.clearStencil = attachmentInfo.clearStencil;
break;
case wgpu::LoadOp::Load:
descriptor.stencilAttachment.loadAction = MTLLoadActionLoad;
break;
default:
UNREACHABLE();
break;
}
}
}
@ -379,6 +442,25 @@ namespace dawn_native { namespace metal {
return copy;
}
void EnsureSourceTextureInitialized(Texture* texture,
const Extent3D& size,
const TextureCopy& src) {
// TODO(crbug.com/dawn/145): Specify multiple layers based on |size|
texture->EnsureSubresourceContentInitialized(src.mipLevel, 1, src.arrayLayer, 1);
}
void EnsureDestinationTextureInitialized(Texture* texture,
const Extent3D& size,
const TextureCopy& dst) {
// TODO(crbug.com/dawn/145): Specify multiple layers based on |size|
if (IsCompleteSubresourceCopiedTo(texture, size, dst.mipLevel)) {
texture->SetIsSubresourceContentInitialized(true, dst.mipLevel, 1, dst.arrayLayer,
1);
} else {
texture->EnsureSubresourceContentInitialized(dst.mipLevel, 1, dst.arrayLayer, 1);
}
}
// Keeps track of the dirty bind groups so they can be lazily applied when we know the
// pipeline state.
// Bind groups may be inherited because bind groups are packed in the buffer /
@ -589,21 +671,47 @@ namespace dawn_native { namespace metal {
}
void CommandBuffer::FillCommands(CommandRecordingContext* commandContext) {
const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass;
size_t nextPassNumber = 0;
auto LazyClearForPass = [](const PassResourceUsage& usages) {
for (size_t i = 0; i < usages.textures.size(); ++i) {
Texture* texture = ToBackend(usages.textures[i]);
// Clear textures that are not output attachments. Output attachments will be
// cleared in CreateMTLRenderPassDescriptor by setting the loadop to clear when the
// texture subresource has not been initialized before the render pass.
if (!(usages.textureUsages[i] & wgpu::TextureUsage::OutputAttachment)) {
texture->EnsureSubresourceContentInitialized(0, texture->GetNumMipLevels(), 0,
texture->GetArrayLayers());
}
}
};
Command type;
while (mCommands.NextCommandId(&type)) {
switch (type) {
case Command::BeginComputePass: {
mCommands.NextCommand<BeginComputePassCmd>();
LazyClearForPass(passResourceUsages[nextPassNumber]);
commandContext->EndBlit();
EncodeComputePass(commandContext);
nextPassNumber++;
} break;
case Command::BeginRenderPass: {
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
LazyClearForPass(passResourceUsages[nextPassNumber]);
commandContext->EndBlit();
LazyClearRenderPassAttachments(cmd);
MTLRenderPassDescriptor* descriptor = CreateMTLRenderPassDescriptor(cmd);
EncodeRenderPass(commandContext, descriptor, cmd->width, cmd->height);
nextPassNumber++;
} break;
case Command::CopyBufferToBuffer: {
@ -625,6 +733,8 @@ namespace dawn_native { namespace metal {
Buffer* buffer = ToBackend(src.buffer.Get());
Texture* texture = ToBackend(dst.texture.Get());
EnsureDestinationTextureInitialized(texture, copy->copySize, copy->destination);
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(dst.mipLevel);
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
dst.origin, copySize, texture->GetFormat(), virtualSizeAtLevel,
@ -652,6 +762,8 @@ namespace dawn_native { namespace metal {
Texture* texture = ToBackend(src.texture.Get());
Buffer* buffer = ToBackend(dst.buffer.Get());
EnsureSourceTextureInitialized(texture, copy->copySize, copy->source);
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(src.mipLevel);
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
src.origin, copySize, texture->GetFormat(), virtualSizeAtLevel,
@ -677,6 +789,10 @@ namespace dawn_native { namespace metal {
Texture* srcTexture = ToBackend(copy->source.texture.Get());
Texture* dstTexture = ToBackend(copy->destination.texture.Get());
EnsureSourceTextureInitialized(srcTexture, copy->copySize, copy->source);
EnsureDestinationTextureInitialized(dstTexture, copy->copySize,
copy->destination);
[commandContext->EnsureBlit()
copyFromTexture:srcTexture->GetMTLTexture()
sourceSlice:copy->source.arrayLayer

View File

@ -42,9 +42,20 @@ namespace dawn_native { namespace metal {
id<MTLTexture> GetMTLTexture();
void EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount);
private:
void DestroyImpl() override;
MaybeError ClearTexture(uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
TextureBase::ClearValue clearValue);
id<MTLTexture> mMtlTexture = nil;
};

View File

@ -14,8 +14,12 @@
#include "dawn_native/metal/TextureMTL.h"
#include "common/Constants.h"
#include "common/Math.h"
#include "common/Platform.h"
#include "dawn_native/DynamicUploader.h"
#include "dawn_native/metal/DeviceMTL.h"
#include "dawn_native/metal/StagingBufferMTL.h"
namespace dawn_native { namespace metal {
@ -318,6 +322,11 @@ namespace dawn_native { namespace metal {
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor);
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc];
[mtlDesc release];
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
device->ConsumedError(ClearTexture(0, GetNumMipLevels(), 0, GetArrayLayers(),
TextureBase::ClearValue::NonZero));
}
}
Texture::Texture(Device* device, const TextureDescriptor* descriptor, id<MTLTexture> mtlTexture)
@ -336,6 +345,9 @@ namespace dawn_native { namespace metal {
iosurface:ioSurface
plane:plane];
[mtlDesc release];
// TODO(enga): Set as uninitialized if IOSurface isn't initialized.
SetIsSubresourceContentInitialized(true, 0, 1, 0, 1);
}
Texture::~Texture() {
@ -353,6 +365,198 @@ namespace dawn_native { namespace metal {
return mMtlTexture;
}
MaybeError Texture::ClearTexture(uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount,
TextureBase::ClearValue clearValue) {
Device* device = ToBackend(GetDevice());
CommandRecordingContext* commandContext = device->GetPendingCommandContext();
const uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
const double dClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.0 : 1.0;
if ((GetUsage() & wgpu::TextureUsage::OutputAttachment) != 0) {
ASSERT(GetFormat().isRenderable);
// End the blit encoder if it is open.
commandContext->EndBlit();
if (GetFormat().HasDepthOrStencil()) {
// Create a render pass to clear each subresource.
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
for (uint32_t arrayLayer = baseArrayLayer;
arrayLayer < baseArrayLayer + layerCount; arrayLayer++) {
if (clearValue == TextureBase::ClearValue::Zero &&
IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
// Skip lazy clears if already initialized.
continue;
}
MTLRenderPassDescriptor* descriptor =
[MTLRenderPassDescriptor renderPassDescriptor];
if (GetFormat().HasDepth()) {
descriptor.depthAttachment.texture = GetMTLTexture();
descriptor.depthAttachment.loadAction = MTLLoadActionClear;
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
descriptor.depthAttachment.clearDepth = dClearColor;
}
if (GetFormat().HasStencil()) {
descriptor.stencilAttachment.texture = GetMTLTexture();
descriptor.stencilAttachment.loadAction = MTLLoadActionClear;
descriptor.stencilAttachment.storeAction = MTLStoreActionStore;
descriptor.stencilAttachment.clearStencil =
static_cast<uint32_t>(clearColor);
}
commandContext->BeginRender(descriptor);
commandContext->EndRender();
}
}
} else {
ASSERT(GetFormat().IsColor());
MTLRenderPassDescriptor* descriptor = nil;
uint32_t attachment = 0;
// Create multiple render passes with each subresource as a color attachment to
// clear them all.
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
for (uint32_t arrayLayer = baseArrayLayer;
arrayLayer < baseArrayLayer + layerCount; arrayLayer++) {
if (clearValue == TextureBase::ClearValue::Zero &&
IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
// Skip lazy clears if already initialized.
continue;
}
if (descriptor == nil) {
descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
}
descriptor.colorAttachments[attachment].texture = GetMTLTexture();
descriptor.colorAttachments[attachment].loadAction = MTLLoadActionClear;
descriptor.colorAttachments[attachment].storeAction = MTLStoreActionStore;
descriptor.colorAttachments[attachment].clearColor =
MTLClearColorMake(dClearColor, dClearColor, dClearColor, dClearColor);
descriptor.colorAttachments[attachment].level = level;
descriptor.colorAttachments[attachment].slice = arrayLayer;
attachment++;
if (attachment == kMaxColorAttachments) {
attachment = 0;
commandContext->BeginRender(descriptor);
commandContext->EndRender();
descriptor = nil;
}
}
}
if (descriptor != nil) {
commandContext->BeginRender(descriptor);
commandContext->EndRender();
}
}
} else {
// Compute the buffer size big enough to fill the largest mip.
Extent3D largestMipSize = GetMipLevelVirtualSize(baseMipLevel);
// Metal validation layers: sourceBytesPerRow must be at least 64.
uint32_t largestMipBytesPerRow = std::max(
(largestMipSize.width / GetFormat().blockWidth) * GetFormat().blockByteSize, 64u);
// Metal validation layers: sourceBytesPerImage must be at least 512.
uint64_t largestMipBytesPerImage =
std::max(static_cast<uint64_t>(largestMipBytesPerRow) *
(largestMipSize.height / GetFormat().blockHeight),
512llu);
// TODO(enga): Multiply by largestMipSize.depth and do a larger 3D copy to clear a whole
// range of subresources when tracking that is improved.
uint64_t bufferSize = largestMipBytesPerImage * 1;
if (bufferSize > std::numeric_limits<NSUInteger>::max()) {
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer.");
}
DynamicUploader* uploader = device->GetDynamicUploader();
UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
id<MTLBlitCommandEncoder> encoder = commandContext->EnsureBlit();
id<MTLBuffer> uploadBuffer = ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle();
// Encode a buffer to texture copy to clear each subresource.
for (uint32_t level = baseMipLevel; level < baseMipLevel + levelCount; ++level) {
Extent3D virtualSize = GetMipLevelVirtualSize(level);
for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount;
++arrayLayer) {
if (clearValue == TextureBase::ClearValue::Zero &&
IsSubresourceContentInitialized(level, 1, arrayLayer, 1)) {
// Skip lazy clears if already initialized.
continue;
}
// If the textures pixel format is a combined depth/stencil format, then
// options must be set to either blit the depth attachment portion or blit the
// stencil attachment portion.
std::array<MTLBlitOption, 3> blitOptions = {
MTLBlitOptionNone, MTLBlitOptionDepthFromDepthStencil,
MTLBlitOptionStencilFromDepthStencil};
auto blitOptionStart = blitOptions.begin();
auto blitOptionEnd = blitOptionStart + 1;
if (GetFormat().format == wgpu::TextureFormat::Depth24PlusStencil8) {
blitOptionStart = blitOptions.begin() + 1;
blitOptionEnd = blitOptionStart + 2;
}
for (auto it = blitOptionStart; it != blitOptionEnd; ++it) {
[encoder copyFromBuffer:uploadBuffer
sourceOffset:uploadHandle.startOffset
sourceBytesPerRow:largestMipBytesPerRow
sourceBytesPerImage:largestMipBytesPerImage
sourceSize:MTLSizeMake(virtualSize.width, virtualSize.height,
1)
toTexture:GetMTLTexture()
destinationSlice:arrayLayer
destinationLevel:level
destinationOrigin:MTLOriginMake(0, 0, 0)
options:(*it)];
}
}
}
}
if (clearValue == TextureBase::ClearValue::Zero) {
SetIsSubresourceContentInitialized(true, baseMipLevel, levelCount, baseArrayLayer,
layerCount);
device->IncrementLazyClearCountForTesting();
}
return {};
}
void Texture::EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
uint32_t levelCount,
uint32_t baseArrayLayer,
uint32_t layerCount) {
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
return;
}
if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer,
layerCount)) {
// If subresource has not been initialized, clear it to black as it could
// contain dirty bits from recycled memory
GetDevice()->ConsumedError(ClearTexture(baseMipLevel, levelCount, baseArrayLayer,
layerCount, TextureBase::ClearValue::Zero));
}
}
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
: TextureViewBase(texture, descriptor) {
id<MTLTexture> mtlTexture = ToBackend(texture)->GetMTLTexture();

View File

@ -193,12 +193,15 @@ namespace dawn_native { namespace opengl {
Device* device = ToBackend(GetDevice());
const OpenGLFunctions& gl = device->gl;
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
if (GetFormat().isRenderable) {
if (GetFormat().HasDepthOrStencil()) {
bool doDepthClear = GetFormat().HasDepth();
bool doStencilClear = GetFormat().HasStencil();
GLfloat depth = clearColor;
GLfloat depth = fClearColor;
GLint stencil = clearColor;
if (doDepthClear) {
gl.DepthMask(GL_TRUE);
@ -265,8 +268,7 @@ namespace dawn_native { namespace opengl {
// Fill the buffer with clear color
uint8_t* clearBuffer = nullptr;
DAWN_TRY(srcBuffer->MapAtCreation(&clearBuffer));
std::fill(reinterpret_cast<uint32_t*>(clearBuffer),
reinterpret_cast<uint32_t*>(clearBuffer + descriptor.size), clearColor);
memset(clearBuffer, clearColor, descriptor.size);
srcBuffer->Unmap();
// Bind buffer and texture, and make the buffer to texture copy

View File

@ -190,6 +190,7 @@ namespace dawn_native { namespace vulkan {
!view->GetTexture()->IsSubresourceContentInitialized(
view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1)) {
loadOp = wgpu::LoadOp::Clear;
attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f};
}
if (hasResolveTarget) {

View File

@ -684,18 +684,18 @@ namespace dawn_native { namespace vulkan {
range.baseArrayLayer = baseArrayLayer;
range.layerCount = layerCount;
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst);
if (GetFormat().isRenderable) {
if (GetFormat().HasDepthOrStencil()) {
VkClearDepthStencilValue clearDepthStencilValue[1];
clearDepthStencilValue[0].depth = clearColor;
clearDepthStencilValue[0].depth = fClearColor;
clearDepthStencilValue[0].stencil = clearColor;
device->fn.CmdClearDepthStencilImage(recordingContext->commandBuffer, GetHandle(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
clearDepthStencilValue, 1, &range);
} else {
float fClearColor = static_cast<float>(clearColor);
VkClearColorValue clearColorValue = {
{fClearColor, fClearColor, fClearColor, fClearColor}};
device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(),
@ -717,9 +717,7 @@ namespace dawn_native { namespace vulkan {
UploadHandle uploadHandle;
DAWN_TRY_ASSIGN(uploadHandle,
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
std::fill(reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer),
reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer + bufferSize),
clearColor);
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
// compute the buffer image copy to set the clear region of entire texture
dawn_native::BufferCopy bufferCopy;

View File

@ -911,4 +911,5 @@ namespace detail {
template class ExpectEq<uint8_t>;
template class ExpectEq<uint32_t>;
template class ExpectEq<RGBA8>;
template class ExpectEq<float>;
} // namespace detail

View File

@ -48,6 +48,15 @@
sizeof(RGBA8), \
new detail::ExpectEq<RGBA8>(expected, (width) * (height)))
#define EXPECT_PIXEL_FLOAT_EQ(expected, texture, x, y) \
AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(float), \
new detail::ExpectEq<float>(expected))
#define EXPECT_TEXTURE_FLOAT_EQ(expected, texture, x, y, width, height, level, slice) \
AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \
sizeof(float), \
new detail::ExpectEq<float>(expected, (width) * (height)))
#define EXPECT_LAZY_CLEAR(N, statement) \
if (UsesWire()) { \
statement; \
@ -377,6 +386,7 @@ namespace detail {
extern template class ExpectEq<uint8_t>;
extern template class ExpectEq<uint32_t>;
extern template class ExpectEq<RGBA8>;
extern template class ExpectEq<float>;
} // namespace detail
#endif // TESTS_DAWNTEST_H_

View File

@ -14,6 +14,7 @@
#include "tests/DawnTest.h"
#include "common/Constants.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/WGPUHelpers.h"
@ -26,8 +27,8 @@ class NonzeroTextureCreationTests : public DawnTest {
constexpr static uint32_t kSize = 128;
};
// Test that texture clears to 1's because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) {
// Test that texture clears 0xFF because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, TextureCreationClears) {
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.size.width = kSize;
@ -40,11 +41,34 @@ TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) {
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
wgpu::Texture texture = device.CreateTexture(&descriptor);
RGBA8 filledWithOnes(255, 255, 255, 255);
EXPECT_PIXEL_RGBA8_EQ(filledWithOnes, texture, 0, 0);
RGBA8 filled(255, 255, 255, 255);
EXPECT_PIXEL_RGBA8_EQ(filled, texture, 0, 0);
}
// Test that non-zero mip level clears to 1's because toggle is enabled.
// Test that a depth texture clears 0xFF because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, Depth32TextureCreationDepthClears) {
// Copies from depth textures not supported on the OpenGL backend right now.
DAWN_SKIP_TEST_IF(IsOpenGL());
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.size.width = kSize;
descriptor.size.height = kSize;
descriptor.size.depth = 1;
descriptor.arrayLayerCount = 1;
descriptor.sampleCount = 1;
descriptor.mipLevelCount = 1;
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
descriptor.format = wgpu::TextureFormat::Depth32Float;
// We can only really test Depth32Float here because Depth24Plus(Stencil8)? may be in an unknown
// format.
// TODO(crbug.com/dawn/145): Test other formats via sampling.
wgpu::Texture texture = device.CreateTexture(&descriptor);
EXPECT_PIXEL_FLOAT_EQ(1.f, texture, 0, 0);
}
// Test that non-zero mip level clears 0xFF because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, MipMapClears) {
constexpr uint32_t mipLevels = 4;
@ -61,15 +85,15 @@ TEST_P(NonzeroTextureCreationTests, MipMapClears) {
wgpu::Texture texture = device.CreateTexture(&descriptor);
std::vector<RGBA8> expected;
RGBA8 filledWithOnes(255, 255, 255, 255);
RGBA8 filled(255, 255, 255, 255);
for (uint32_t i = 0; i < kSize * kSize; ++i) {
expected.push_back(filledWithOnes);
expected.push_back(filled);
}
uint32_t mipSize = kSize >> 2;
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, mipSize, mipSize, 2, 0);
}
// Test that non-zero array layers clears to 1's because toggle is enabled.
// Test that non-zero array layers clears 0xFF because toggle is enabled.
TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) {
constexpr uint32_t arrayLayers = 4;
@ -86,15 +110,15 @@ TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) {
wgpu::Texture texture = device.CreateTexture(&descriptor);
std::vector<RGBA8> expected;
RGBA8 filledWithOnes(255, 255, 255, 255);
RGBA8 filled(255, 255, 255, 255);
for (uint32_t i = 0; i < kSize * kSize; ++i) {
expected.push_back(filledWithOnes);
expected.push_back(filled);
}
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 2);
}
// Test that nonrenderable texture formats clear to 1's because toggle is enabled
// Test that nonrenderable texture formats clear 0x01 because toggle is enabled
TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
@ -123,11 +147,11 @@ TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
std::vector<uint32_t> expected(bufferSize, 1);
std::vector<uint32_t> expected(bufferSize, 0x01010101);
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8);
}
// Test that textures with more than 1 array layers and nonrenderable texture formats clear to 1's
// Test that textures with more than 1 array layers and nonrenderable texture formats clear to 0x01
// because toggle is enabled
TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayers) {
// TODO(natlee@microsoft.com): skip for now on opengl because TextureClear nonrenderable
@ -161,14 +185,129 @@ TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayer
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
std::vector<uint32_t> expectedWithZeros(bufferSize, 1);
EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8);
std::vector<uint32_t> expected(bufferSize, 0x01010101);
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8);
}
// Test that all subresources of a renderable texture are filled because the toggle is enabled.
TEST_P(NonzeroTextureCreationTests, AllSubresourcesFilled) {
// TODO(crbug.com/dawn/145): Implement on other platforms.
DAWN_SKIP_TEST_IF(!IsMetal());
wgpu::TextureDescriptor baseDescriptor;
baseDescriptor.dimension = wgpu::TextureDimension::e2D;
baseDescriptor.size.width = kSize;
baseDescriptor.size.height = kSize;
baseDescriptor.size.depth = 1;
baseDescriptor.sampleCount = 1;
baseDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
baseDescriptor.mipLevelCount = 1;
baseDescriptor.arrayLayerCount = 1;
baseDescriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
RGBA8 filled(255, 255, 255, 255);
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
// Some textures may be cleared with render pass load/store ops.
// Test above the max attachment count.
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, 0, i);
}
}
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
descriptor.mipLevelCount = 3;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.mipLevelCount; ++i) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, i, 0);
}
}
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
// Some textures may be cleared with render pass load/store ops.
// Test above the max attachment count.
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
descriptor.mipLevelCount = 3;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
for (uint32_t j = 0; j < descriptor.mipLevelCount; ++j) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, j, i);
}
}
}
}
// Test that all subresources of a nonrenderable texture are filled because the toggle is enabled.
TEST_P(NonzeroTextureCreationTests, NonRenderableAllSubresourcesFilled) {
// TODO(crbug.com/dawn/145): Implement on other platforms.
DAWN_SKIP_TEST_IF(!IsMetal());
wgpu::TextureDescriptor baseDescriptor;
baseDescriptor.dimension = wgpu::TextureDimension::e2D;
baseDescriptor.size.width = kSize;
baseDescriptor.size.height = kSize;
baseDescriptor.size.depth = 1;
baseDescriptor.sampleCount = 1;
baseDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
baseDescriptor.mipLevelCount = 1;
baseDescriptor.arrayLayerCount = 1;
baseDescriptor.usage = wgpu::TextureUsage::CopySrc;
RGBA8 filled(1, 1, 1, 1);
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
// Some textures may be cleared with render pass load/store ops.
// Test above the max attachment count.
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, 0, i);
}
}
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
descriptor.mipLevelCount = 3;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.mipLevelCount; ++i) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, i, 0);
}
}
{
wgpu::TextureDescriptor descriptor = baseDescriptor;
// Some textures may be cleared with render pass load/store ops.
// Test above the max attachment count.
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
descriptor.mipLevelCount = 3;
wgpu::Texture texture = device.CreateTexture(&descriptor);
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
for (uint32_t j = 0; j < descriptor.mipLevelCount; ++j) {
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, j, i);
}
}
}
}
DAWN_INSTANTIATE_TEST(NonzeroTextureCreationTests,
ForceToggles(D3D12Backend,
{"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),
ForceToggles(MetalBackend,
{"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),
ForceToggles(OpenGLBackend,
{"nonzero_clear_resources_on_creation_for_testing"},
{"lazy_clear_resource_on_first_use"}),

View File

@ -127,7 +127,12 @@ TEST_P(TextureZeroInitTest, RenderingMipMapClearsToZero) {
utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
// Specify loadOp Load. Clear should be used to zero-initialize.
renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
// Specify non-zero clear color. It should still be cleared to zero.
renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f};
renderPass.renderPassInfo.cColorAttachments[0].attachment = view;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
// Texture's first usage is in BeginRenderPass's call to RecordRenderPass
@ -155,7 +160,12 @@ TEST_P(TextureZeroInitTest, RenderingArrayLayerClearsToZero) {
utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
// Specify loadOp Load. Clear should be used to zero-initialize.
renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
// Specify non-zero clear color. It should still be cleared to zero.
renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f};
renderPass.renderPassInfo.cColorAttachments[0].attachment = view;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
@ -327,6 +337,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepth) {
utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()},
depthStencilTexture.CreateView());
renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
// Set clearDepth to non-zero. It should still be cleared to 0 by the loadOp.
renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.5f;
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 0;
renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
@ -366,6 +378,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingStencil) {
renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.0f;
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
// Set clearStencil to non-zero. It should still be cleared to 0 by the loadOp.
renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 2;
renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
@ -760,6 +774,166 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencilStoreOpClear) {
}
}
// Test that if one mip of a texture is initialized and another is uninitialized, lazy clearing the
// uninitialized mip does not clear the initialized mip.
TEST_P(TextureZeroInitTest, PreservesInitializedMip) {
// TODO(crbug.com/dawn/145): Fix this on other backends
DAWN_SKIP_TEST_IF(!IsMetal());
wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor(
2, 1,
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled,
kColorFormat);
wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor);
wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat);
wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
// Fill the sample texture's second mip with data
uint32_t mipSize = kSize >> 1;
std::vector<uint8_t> data(kFormatBlockByteSize * mipSize * mipSize, 2);
wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
wgpu::TextureCopyView textureCopyView =
utils::CreateTextureCopyView(sampleTexture, 1, 0, {0, 0, 0});
wgpu::Extent3D copySize = {mipSize, mipSize, 1};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
wgpu::CommandBuffer commands = encoder.Finish();
// Expect 0 lazy clears because the texture subresource will be completely copied to
EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
// Create render pipeline
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest();
renderPipelineDescriptor.cColorStates[0].format = kColorFormat;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
// Create bindgroup
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
{{0, sampler}, {1, sampleTexture.CreateView()}});
// Encode pass and submit
encoder = device.CreateCommandEncoder();
utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0};
renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear;
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.SetPipeline(renderPipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(6, 1, 0, 0);
pass.EndPass();
commands = encoder.Finish();
// Expect 1 lazy clears, because not all mips of the sample texture are initialized by
// copyBufferToTexture.
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
// Expect the rendered texture to be cleared since we copied from the uninitialized first
// mip.
std::vector<RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0,
kSize, kSize, 0, 0));
// Expect the first mip to have been lazy cleared to 0.
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), sampleTexture, 0, 0,
kSize, kSize, 0, 0));
// Expect the second mip to still be filled with 2.
std::vector<RGBA8> expectedWithTwos(mipSize * mipSize, {2, 2, 2, 2});
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0,
mipSize, mipSize, 1, 0));
}
// Test that if one layer of a texture is initialized and another is uninitialized, lazy clearing
// the uninitialized layer does not clear the initialized layer.
TEST_P(TextureZeroInitTest, PreservesInitializedArrayLayer) {
// TODO(crbug.com/dawn/145): Fix this on other backends
DAWN_SKIP_TEST_IF(!IsMetal());
wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor(
1, 2,
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled,
kColorFormat);
wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor);
wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat);
wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
// Fill the sample texture's second array layer with data
std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 2);
wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
wgpu::TextureCopyView textureCopyView =
utils::CreateTextureCopyView(sampleTexture, 0, 1, {0, 0, 0});
wgpu::Extent3D copySize = {kSize, kSize, 1};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
wgpu::CommandBuffer commands = encoder.Finish();
// Expect 0 lazy clears because the texture subresource will be completely copied to
EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
// Create render pipeline
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest();
renderPipelineDescriptor.cColorStates[0].format = kColorFormat;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
// Only sample from the uninitialized first layer.
wgpu::TextureViewDescriptor textureViewDescriptor;
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
textureViewDescriptor.arrayLayerCount = 1;
// Create bindgroup
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
{{0, sampler}, {1, sampleTexture.CreateView(&textureViewDescriptor)}});
// Encode pass and submit
encoder = device.CreateCommandEncoder();
utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0};
renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear;
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
pass.SetPipeline(renderPipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(6, 1, 0, 0);
pass.EndPass();
commands = encoder.Finish();
// Expect 1 lazy clears, because not all array layers of the sample texture are initialized by
// copyBufferToTexture.
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
// Expect the rendered texture to be cleared since we copied from the uninitialized first
// array layer.
std::vector<RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0,
kSize, kSize, 0, 0));
// Expect the first array layer to have been lazy cleared to 0.
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), sampleTexture, 0, 0,
kSize, kSize, 0, 0));
// Expect the second array layer to still be filled with 2.
std::vector<RGBA8> expectedWithTwos(kSize * kSize, {2, 2, 2, 2});
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0,
kSize, kSize, 0, 1));
}
DAWN_INSTANTIATE_TEST(
TextureZeroInitTest,
ForceToggles(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),
@ -767,4 +941,5 @@ DAWN_INSTANTIATE_TEST(
{"nonzero_clear_resources_on_creation_for_testing"},
{"use_d3d12_render_pass"}),
ForceToggles(OpenGLBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
ForceToggles(MetalBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
ForceToggles(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));