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:
parent
0ecc48ecb7
commit
0d6619848d
|
@ -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
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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 texture’s 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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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"}),
|
||||
|
|
|
@ -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, ©Size);
|
||||
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, ©Size);
|
||||
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"}));
|
||||
|
|
Loading…
Reference in New Issue