mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-06 21:25:58 +00:00
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 "dawn_native/CommandBuffer.h"
|
||||||
|
|
||||||
|
#include "common/BitSetIterator.h"
|
||||||
#include "dawn_native/CommandEncoder.h"
|
#include "dawn_native/CommandEncoder.h"
|
||||||
|
#include "dawn_native/Commands.h"
|
||||||
|
#include "dawn_native/Format.h"
|
||||||
#include "dawn_native/Texture.h"
|
#include "dawn_native/Texture.h"
|
||||||
|
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
@ -47,4 +50,92 @@ namespace dawn_native {
|
|||||||
}
|
}
|
||||||
return false;
|
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
|
} // namespace dawn_native
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
|
|
||||||
|
struct BeginRenderPassCmd;
|
||||||
|
|
||||||
class CommandBufferBase : public ObjectBase {
|
class CommandBufferBase : public ObjectBase {
|
||||||
public:
|
public:
|
||||||
CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
|
CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor);
|
||||||
@ -39,6 +41,8 @@ namespace dawn_native {
|
|||||||
const Extent3D copySize,
|
const Extent3D copySize,
|
||||||
const uint32_t mipLevel);
|
const uint32_t mipLevel);
|
||||||
|
|
||||||
|
void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass);
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
||||||
#endif // DAWNNATIVE_COMMANDBUFFER_H_
|
#endif // DAWNNATIVE_COMMANDBUFFER_H_
|
||||||
|
@ -567,7 +567,10 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
|
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator();
|
DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator();
|
||||||
|
|
||||||
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
||||||
|
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
|
||||||
|
|
||||||
if (GetFormat().isRenderable) {
|
if (GetFormat().isRenderable) {
|
||||||
if (GetFormat().HasDepthOrStencil()) {
|
if (GetFormat().HasDepthOrStencil()) {
|
||||||
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE);
|
||||||
@ -587,15 +590,14 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
|
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandList->ClearDepthStencilView(dsvHandle, clearFlags, clearColor, clearColor, 0,
|
commandList->ClearDepthStencilView(dsvHandle, clearFlags, fClearColor, clearColor,
|
||||||
nullptr);
|
0, nullptr);
|
||||||
} else {
|
} else {
|
||||||
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET);
|
||||||
DescriptorHeapHandle rtvHeap;
|
DescriptorHeapHandle rtvHeap;
|
||||||
DAWN_TRY_ASSIGN(rtvHeap, descriptorHeapAllocator->AllocateCPUHeap(
|
DAWN_TRY_ASSIGN(rtvHeap, descriptorHeapAllocator->AllocateCPUHeap(
|
||||||
D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1));
|
D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1));
|
||||||
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetCPUHandle(0);
|
D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetCPUHandle(0);
|
||||||
const float fClearColor = static_cast<float>(clearColor);
|
|
||||||
const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
|
const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor,
|
||||||
fClearColor};
|
fClearColor};
|
||||||
|
|
||||||
@ -623,9 +625,7 @@ namespace dawn_native { namespace d3d12 {
|
|||||||
UploadHandle uploadHandle;
|
UploadHandle uploadHandle;
|
||||||
DAWN_TRY_ASSIGN(uploadHandle,
|
DAWN_TRY_ASSIGN(uploadHandle,
|
||||||
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
|
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
|
||||||
std::fill(reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer),
|
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
|
||||||
reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer + bufferSize),
|
|
||||||
clearColor);
|
|
||||||
|
|
||||||
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST);
|
TransitionUsageNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||||
|
|
||||||
|
@ -48,13 +48,21 @@ namespace dawn_native { namespace metal {
|
|||||||
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
|
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
|
||||||
auto& attachmentInfo = renderPass->colorAttachments[i];
|
auto& attachmentInfo = renderPass->colorAttachments[i];
|
||||||
|
|
||||||
if (attachmentInfo.loadOp == wgpu::LoadOp::Clear) {
|
switch (attachmentInfo.loadOp) {
|
||||||
descriptor.colorAttachments[i].loadAction = MTLLoadActionClear;
|
case wgpu::LoadOp::Clear:
|
||||||
descriptor.colorAttachments[i].clearColor =
|
descriptor.colorAttachments[i].loadAction = MTLLoadActionClear;
|
||||||
MTLClearColorMake(attachmentInfo.clearColor.r, attachmentInfo.clearColor.g,
|
descriptor.colorAttachments[i].clearColor = MTLClearColorMake(
|
||||||
attachmentInfo.clearColor.b, attachmentInfo.clearColor.a);
|
attachmentInfo.clearColor.r, attachmentInfo.clearColor.g,
|
||||||
} else {
|
attachmentInfo.clearColor.b, attachmentInfo.clearColor.a);
|
||||||
descriptor.colorAttachments[i].loadAction = MTLLoadActionLoad;
|
break;
|
||||||
|
|
||||||
|
case wgpu::LoadOp::Load:
|
||||||
|
descriptor.colorAttachments[i].loadAction = MTLLoadActionLoad;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
descriptor.colorAttachments[i].texture =
|
descriptor.colorAttachments[i].texture =
|
||||||
@ -62,19 +70,32 @@ namespace dawn_native { namespace metal {
|
|||||||
descriptor.colorAttachments[i].level = attachmentInfo.view->GetBaseMipLevel();
|
descriptor.colorAttachments[i].level = attachmentInfo.view->GetBaseMipLevel();
|
||||||
descriptor.colorAttachments[i].slice = attachmentInfo.view->GetBaseArrayLayer();
|
descriptor.colorAttachments[i].slice = attachmentInfo.view->GetBaseArrayLayer();
|
||||||
|
|
||||||
if (attachmentInfo.storeOp == wgpu::StoreOp::Store) {
|
bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr;
|
||||||
if (attachmentInfo.resolveTarget.Get() != nullptr) {
|
|
||||||
descriptor.colorAttachments[i].resolveTexture =
|
switch (attachmentInfo.storeOp) {
|
||||||
ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture();
|
case wgpu::StoreOp::Store:
|
||||||
descriptor.colorAttachments[i].resolveLevel =
|
if (hasResolveTarget) {
|
||||||
attachmentInfo.resolveTarget->GetBaseMipLevel();
|
descriptor.colorAttachments[i].resolveTexture =
|
||||||
descriptor.colorAttachments[i].resolveSlice =
|
ToBackend(attachmentInfo.resolveTarget->GetTexture())
|
||||||
attachmentInfo.resolveTarget->GetBaseArrayLayer();
|
->GetMTLTexture();
|
||||||
descriptor.colorAttachments[i].storeAction =
|
descriptor.colorAttachments[i].resolveLevel =
|
||||||
kMTLStoreActionStoreAndMultisampleResolve;
|
attachmentInfo.resolveTarget->GetBaseMipLevel();
|
||||||
} else {
|
descriptor.colorAttachments[i].resolveSlice =
|
||||||
descriptor.colorAttachments[i].storeAction = MTLStoreActionStore;
|
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()) {
|
if (format.HasDepth()) {
|
||||||
descriptor.depthAttachment.texture = texture;
|
descriptor.depthAttachment.texture = texture;
|
||||||
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
|
|
||||||
|
|
||||||
if (attachmentInfo.depthLoadOp == wgpu::LoadOp::Clear) {
|
switch (attachmentInfo.depthStoreOp) {
|
||||||
descriptor.depthAttachment.loadAction = MTLLoadActionClear;
|
case wgpu::StoreOp::Store:
|
||||||
descriptor.depthAttachment.clearDepth = attachmentInfo.clearDepth;
|
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
|
||||||
} else {
|
break;
|
||||||
descriptor.depthAttachment.loadAction = MTLLoadActionLoad;
|
|
||||||
|
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()) {
|
if (format.HasStencil()) {
|
||||||
descriptor.stencilAttachment.texture = texture;
|
descriptor.stencilAttachment.texture = texture;
|
||||||
descriptor.stencilAttachment.storeAction = MTLStoreActionStore;
|
|
||||||
|
|
||||||
if (attachmentInfo.stencilLoadOp == wgpu::LoadOp::Clear) {
|
switch (attachmentInfo.stencilStoreOp) {
|
||||||
descriptor.stencilAttachment.loadAction = MTLLoadActionClear;
|
case wgpu::StoreOp::Store:
|
||||||
descriptor.stencilAttachment.clearStencil = attachmentInfo.clearStencil;
|
descriptor.stencilAttachment.storeAction = MTLStoreActionStore;
|
||||||
} else {
|
break;
|
||||||
descriptor.stencilAttachment.loadAction = MTLLoadActionLoad;
|
|
||||||
|
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;
|
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
|
// Keeps track of the dirty bind groups so they can be lazily applied when we know the
|
||||||
// pipeline state.
|
// pipeline state.
|
||||||
// Bind groups may be inherited because bind groups are packed in the buffer /
|
// 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) {
|
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;
|
Command type;
|
||||||
while (mCommands.NextCommandId(&type)) {
|
while (mCommands.NextCommandId(&type)) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Command::BeginComputePass: {
|
case Command::BeginComputePass: {
|
||||||
mCommands.NextCommand<BeginComputePassCmd>();
|
mCommands.NextCommand<BeginComputePassCmd>();
|
||||||
|
|
||||||
|
LazyClearForPass(passResourceUsages[nextPassNumber]);
|
||||||
commandContext->EndBlit();
|
commandContext->EndBlit();
|
||||||
|
|
||||||
EncodeComputePass(commandContext);
|
EncodeComputePass(commandContext);
|
||||||
|
|
||||||
|
nextPassNumber++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Command::BeginRenderPass: {
|
case Command::BeginRenderPass: {
|
||||||
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
||||||
|
|
||||||
|
LazyClearForPass(passResourceUsages[nextPassNumber]);
|
||||||
commandContext->EndBlit();
|
commandContext->EndBlit();
|
||||||
|
|
||||||
|
LazyClearRenderPassAttachments(cmd);
|
||||||
MTLRenderPassDescriptor* descriptor = CreateMTLRenderPassDescriptor(cmd);
|
MTLRenderPassDescriptor* descriptor = CreateMTLRenderPassDescriptor(cmd);
|
||||||
EncodeRenderPass(commandContext, descriptor, cmd->width, cmd->height);
|
EncodeRenderPass(commandContext, descriptor, cmd->width, cmd->height);
|
||||||
|
|
||||||
|
nextPassNumber++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Command::CopyBufferToBuffer: {
|
case Command::CopyBufferToBuffer: {
|
||||||
@ -625,6 +733,8 @@ namespace dawn_native { namespace metal {
|
|||||||
Buffer* buffer = ToBackend(src.buffer.Get());
|
Buffer* buffer = ToBackend(src.buffer.Get());
|
||||||
Texture* texture = ToBackend(dst.texture.Get());
|
Texture* texture = ToBackend(dst.texture.Get());
|
||||||
|
|
||||||
|
EnsureDestinationTextureInitialized(texture, copy->copySize, copy->destination);
|
||||||
|
|
||||||
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(dst.mipLevel);
|
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(dst.mipLevel);
|
||||||
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
||||||
dst.origin, copySize, texture->GetFormat(), virtualSizeAtLevel,
|
dst.origin, copySize, texture->GetFormat(), virtualSizeAtLevel,
|
||||||
@ -652,6 +762,8 @@ namespace dawn_native { namespace metal {
|
|||||||
Texture* texture = ToBackend(src.texture.Get());
|
Texture* texture = ToBackend(src.texture.Get());
|
||||||
Buffer* buffer = ToBackend(dst.buffer.Get());
|
Buffer* buffer = ToBackend(dst.buffer.Get());
|
||||||
|
|
||||||
|
EnsureSourceTextureInitialized(texture, copy->copySize, copy->source);
|
||||||
|
|
||||||
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(src.mipLevel);
|
Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(src.mipLevel);
|
||||||
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
TextureBufferCopySplit splittedCopies = ComputeTextureBufferCopySplit(
|
||||||
src.origin, copySize, texture->GetFormat(), virtualSizeAtLevel,
|
src.origin, copySize, texture->GetFormat(), virtualSizeAtLevel,
|
||||||
@ -677,6 +789,10 @@ namespace dawn_native { namespace metal {
|
|||||||
Texture* srcTexture = ToBackend(copy->source.texture.Get());
|
Texture* srcTexture = ToBackend(copy->source.texture.Get());
|
||||||
Texture* dstTexture = ToBackend(copy->destination.texture.Get());
|
Texture* dstTexture = ToBackend(copy->destination.texture.Get());
|
||||||
|
|
||||||
|
EnsureSourceTextureInitialized(srcTexture, copy->copySize, copy->source);
|
||||||
|
EnsureDestinationTextureInitialized(dstTexture, copy->copySize,
|
||||||
|
copy->destination);
|
||||||
|
|
||||||
[commandContext->EnsureBlit()
|
[commandContext->EnsureBlit()
|
||||||
copyFromTexture:srcTexture->GetMTLTexture()
|
copyFromTexture:srcTexture->GetMTLTexture()
|
||||||
sourceSlice:copy->source.arrayLayer
|
sourceSlice:copy->source.arrayLayer
|
||||||
|
@ -42,9 +42,20 @@ namespace dawn_native { namespace metal {
|
|||||||
|
|
||||||
id<MTLTexture> GetMTLTexture();
|
id<MTLTexture> GetMTLTexture();
|
||||||
|
|
||||||
|
void EnsureSubresourceContentInitialized(uint32_t baseMipLevel,
|
||||||
|
uint32_t levelCount,
|
||||||
|
uint32_t baseArrayLayer,
|
||||||
|
uint32_t layerCount);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DestroyImpl() override;
|
void DestroyImpl() override;
|
||||||
|
|
||||||
|
MaybeError ClearTexture(uint32_t baseMipLevel,
|
||||||
|
uint32_t levelCount,
|
||||||
|
uint32_t baseArrayLayer,
|
||||||
|
uint32_t layerCount,
|
||||||
|
TextureBase::ClearValue clearValue);
|
||||||
|
|
||||||
id<MTLTexture> mMtlTexture = nil;
|
id<MTLTexture> mMtlTexture = nil;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,8 +14,12 @@
|
|||||||
|
|
||||||
#include "dawn_native/metal/TextureMTL.h"
|
#include "dawn_native/metal/TextureMTL.h"
|
||||||
|
|
||||||
|
#include "common/Constants.h"
|
||||||
|
#include "common/Math.h"
|
||||||
#include "common/Platform.h"
|
#include "common/Platform.h"
|
||||||
|
#include "dawn_native/DynamicUploader.h"
|
||||||
#include "dawn_native/metal/DeviceMTL.h"
|
#include "dawn_native/metal/DeviceMTL.h"
|
||||||
|
#include "dawn_native/metal/StagingBufferMTL.h"
|
||||||
|
|
||||||
namespace dawn_native { namespace metal {
|
namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
@ -318,6 +322,11 @@ namespace dawn_native { namespace metal {
|
|||||||
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor);
|
MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor);
|
||||||
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc];
|
mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc];
|
||||||
[mtlDesc release];
|
[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)
|
Texture::Texture(Device* device, const TextureDescriptor* descriptor, id<MTLTexture> mtlTexture)
|
||||||
@ -336,6 +345,9 @@ namespace dawn_native { namespace metal {
|
|||||||
iosurface:ioSurface
|
iosurface:ioSurface
|
||||||
plane:plane];
|
plane:plane];
|
||||||
[mtlDesc release];
|
[mtlDesc release];
|
||||||
|
|
||||||
|
// TODO(enga): Set as uninitialized if IOSurface isn't initialized.
|
||||||
|
SetIsSubresourceContentInitialized(true, 0, 1, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::~Texture() {
|
Texture::~Texture() {
|
||||||
@ -353,6 +365,198 @@ namespace dawn_native { namespace metal {
|
|||||||
return mMtlTexture;
|
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)
|
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
|
||||||
: TextureViewBase(texture, descriptor) {
|
: TextureViewBase(texture, descriptor) {
|
||||||
id<MTLTexture> mtlTexture = ToBackend(texture)->GetMTLTexture();
|
id<MTLTexture> mtlTexture = ToBackend(texture)->GetMTLTexture();
|
||||||
|
@ -193,12 +193,15 @@ namespace dawn_native { namespace opengl {
|
|||||||
|
|
||||||
Device* device = ToBackend(GetDevice());
|
Device* device = ToBackend(GetDevice());
|
||||||
const OpenGLFunctions& gl = device->gl;
|
const OpenGLFunctions& gl = device->gl;
|
||||||
|
|
||||||
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
||||||
|
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
|
||||||
|
|
||||||
if (GetFormat().isRenderable) {
|
if (GetFormat().isRenderable) {
|
||||||
if (GetFormat().HasDepthOrStencil()) {
|
if (GetFormat().HasDepthOrStencil()) {
|
||||||
bool doDepthClear = GetFormat().HasDepth();
|
bool doDepthClear = GetFormat().HasDepth();
|
||||||
bool doStencilClear = GetFormat().HasStencil();
|
bool doStencilClear = GetFormat().HasStencil();
|
||||||
GLfloat depth = clearColor;
|
GLfloat depth = fClearColor;
|
||||||
GLint stencil = clearColor;
|
GLint stencil = clearColor;
|
||||||
if (doDepthClear) {
|
if (doDepthClear) {
|
||||||
gl.DepthMask(GL_TRUE);
|
gl.DepthMask(GL_TRUE);
|
||||||
@ -265,8 +268,7 @@ namespace dawn_native { namespace opengl {
|
|||||||
// Fill the buffer with clear color
|
// Fill the buffer with clear color
|
||||||
uint8_t* clearBuffer = nullptr;
|
uint8_t* clearBuffer = nullptr;
|
||||||
DAWN_TRY(srcBuffer->MapAtCreation(&clearBuffer));
|
DAWN_TRY(srcBuffer->MapAtCreation(&clearBuffer));
|
||||||
std::fill(reinterpret_cast<uint32_t*>(clearBuffer),
|
memset(clearBuffer, clearColor, descriptor.size);
|
||||||
reinterpret_cast<uint32_t*>(clearBuffer + descriptor.size), clearColor);
|
|
||||||
srcBuffer->Unmap();
|
srcBuffer->Unmap();
|
||||||
|
|
||||||
// Bind buffer and texture, and make the buffer to texture copy
|
// Bind buffer and texture, and make the buffer to texture copy
|
||||||
|
@ -190,6 +190,7 @@ namespace dawn_native { namespace vulkan {
|
|||||||
!view->GetTexture()->IsSubresourceContentInitialized(
|
!view->GetTexture()->IsSubresourceContentInitialized(
|
||||||
view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1)) {
|
view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1)) {
|
||||||
loadOp = wgpu::LoadOp::Clear;
|
loadOp = wgpu::LoadOp::Clear;
|
||||||
|
attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasResolveTarget) {
|
if (hasResolveTarget) {
|
||||||
|
@ -684,18 +684,18 @@ namespace dawn_native { namespace vulkan {
|
|||||||
range.baseArrayLayer = baseArrayLayer;
|
range.baseArrayLayer = baseArrayLayer;
|
||||||
range.layerCount = layerCount;
|
range.layerCount = layerCount;
|
||||||
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
|
||||||
|
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
|
||||||
|
|
||||||
TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst);
|
TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst);
|
||||||
if (GetFormat().isRenderable) {
|
if (GetFormat().isRenderable) {
|
||||||
if (GetFormat().HasDepthOrStencil()) {
|
if (GetFormat().HasDepthOrStencil()) {
|
||||||
VkClearDepthStencilValue clearDepthStencilValue[1];
|
VkClearDepthStencilValue clearDepthStencilValue[1];
|
||||||
clearDepthStencilValue[0].depth = clearColor;
|
clearDepthStencilValue[0].depth = fClearColor;
|
||||||
clearDepthStencilValue[0].stencil = clearColor;
|
clearDepthStencilValue[0].stencil = clearColor;
|
||||||
device->fn.CmdClearDepthStencilImage(recordingContext->commandBuffer, GetHandle(),
|
device->fn.CmdClearDepthStencilImage(recordingContext->commandBuffer, GetHandle(),
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
clearDepthStencilValue, 1, &range);
|
clearDepthStencilValue, 1, &range);
|
||||||
} else {
|
} else {
|
||||||
float fClearColor = static_cast<float>(clearColor);
|
|
||||||
VkClearColorValue clearColorValue = {
|
VkClearColorValue clearColorValue = {
|
||||||
{fClearColor, fClearColor, fClearColor, fClearColor}};
|
{fClearColor, fClearColor, fClearColor, fClearColor}};
|
||||||
device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(),
|
device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(),
|
||||||
@ -717,9 +717,7 @@ namespace dawn_native { namespace vulkan {
|
|||||||
UploadHandle uploadHandle;
|
UploadHandle uploadHandle;
|
||||||
DAWN_TRY_ASSIGN(uploadHandle,
|
DAWN_TRY_ASSIGN(uploadHandle,
|
||||||
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
|
uploader->Allocate(bufferSize, device->GetPendingCommandSerial()));
|
||||||
std::fill(reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer),
|
memset(uploadHandle.mappedBuffer, clearColor, bufferSize);
|
||||||
reinterpret_cast<uint32_t*>(uploadHandle.mappedBuffer + bufferSize),
|
|
||||||
clearColor);
|
|
||||||
|
|
||||||
// compute the buffer image copy to set the clear region of entire texture
|
// compute the buffer image copy to set the clear region of entire texture
|
||||||
dawn_native::BufferCopy bufferCopy;
|
dawn_native::BufferCopy bufferCopy;
|
||||||
|
@ -911,4 +911,5 @@ namespace detail {
|
|||||||
template class ExpectEq<uint8_t>;
|
template class ExpectEq<uint8_t>;
|
||||||
template class ExpectEq<uint32_t>;
|
template class ExpectEq<uint32_t>;
|
||||||
template class ExpectEq<RGBA8>;
|
template class ExpectEq<RGBA8>;
|
||||||
|
template class ExpectEq<float>;
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
@ -48,6 +48,15 @@
|
|||||||
sizeof(RGBA8), \
|
sizeof(RGBA8), \
|
||||||
new detail::ExpectEq<RGBA8>(expected, (width) * (height)))
|
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) \
|
#define EXPECT_LAZY_CLEAR(N, statement) \
|
||||||
if (UsesWire()) { \
|
if (UsesWire()) { \
|
||||||
statement; \
|
statement; \
|
||||||
@ -377,6 +386,7 @@ namespace detail {
|
|||||||
extern template class ExpectEq<uint8_t>;
|
extern template class ExpectEq<uint8_t>;
|
||||||
extern template class ExpectEq<uint32_t>;
|
extern template class ExpectEq<uint32_t>;
|
||||||
extern template class ExpectEq<RGBA8>;
|
extern template class ExpectEq<RGBA8>;
|
||||||
|
extern template class ExpectEq<float>;
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
#endif // TESTS_DAWNTEST_H_
|
#endif // TESTS_DAWNTEST_H_
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "tests/DawnTest.h"
|
#include "tests/DawnTest.h"
|
||||||
|
|
||||||
|
#include "common/Constants.h"
|
||||||
#include "utils/ComboRenderPipelineDescriptor.h"
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
||||||
#include "utils/WGPUHelpers.h"
|
#include "utils/WGPUHelpers.h"
|
||||||
|
|
||||||
@ -26,8 +27,8 @@ class NonzeroTextureCreationTests : public DawnTest {
|
|||||||
constexpr static uint32_t kSize = 128;
|
constexpr static uint32_t kSize = 128;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test that texture clears to 1's because toggle is enabled.
|
// Test that texture clears 0xFF because toggle is enabled.
|
||||||
TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) {
|
TEST_P(NonzeroTextureCreationTests, TextureCreationClears) {
|
||||||
wgpu::TextureDescriptor descriptor;
|
wgpu::TextureDescriptor descriptor;
|
||||||
descriptor.dimension = wgpu::TextureDimension::e2D;
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||||
descriptor.size.width = kSize;
|
descriptor.size.width = kSize;
|
||||||
@ -40,11 +41,34 @@ TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) {
|
|||||||
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
|
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
|
||||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||||
|
|
||||||
RGBA8 filledWithOnes(255, 255, 255, 255);
|
RGBA8 filled(255, 255, 255, 255);
|
||||||
EXPECT_PIXEL_RGBA8_EQ(filledWithOnes, texture, 0, 0);
|
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) {
|
TEST_P(NonzeroTextureCreationTests, MipMapClears) {
|
||||||
constexpr uint32_t mipLevels = 4;
|
constexpr uint32_t mipLevels = 4;
|
||||||
|
|
||||||
@ -61,15 +85,15 @@ TEST_P(NonzeroTextureCreationTests, MipMapClears) {
|
|||||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||||
|
|
||||||
std::vector<RGBA8> expected;
|
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) {
|
for (uint32_t i = 0; i < kSize * kSize; ++i) {
|
||||||
expected.push_back(filledWithOnes);
|
expected.push_back(filled);
|
||||||
}
|
}
|
||||||
uint32_t mipSize = kSize >> 2;
|
uint32_t mipSize = kSize >> 2;
|
||||||
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, mipSize, mipSize, 2, 0);
|
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) {
|
TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) {
|
||||||
constexpr uint32_t arrayLayers = 4;
|
constexpr uint32_t arrayLayers = 4;
|
||||||
|
|
||||||
@ -86,15 +110,15 @@ TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) {
|
|||||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||||
|
|
||||||
std::vector<RGBA8> expected;
|
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) {
|
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);
|
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) {
|
TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
|
||||||
wgpu::TextureDescriptor descriptor;
|
wgpu::TextureDescriptor descriptor;
|
||||||
descriptor.dimension = wgpu::TextureDimension::e2D;
|
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||||
@ -123,11 +147,11 @@ TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
|
|||||||
wgpu::CommandBuffer commands = encoder.Finish();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
queue.Submit(1, &commands);
|
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);
|
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
|
// because toggle is enabled
|
||||||
TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayers) {
|
TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayers) {
|
||||||
// TODO(natlee@microsoft.com): skip for now on opengl because TextureClear nonrenderable
|
// 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();
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
queue.Submit(1, &commands);
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
std::vector<uint32_t> expectedWithZeros(bufferSize, 1);
|
std::vector<uint32_t> expected(bufferSize, 0x01010101);
|
||||||
EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8);
|
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,
|
DAWN_INSTANTIATE_TEST(NonzeroTextureCreationTests,
|
||||||
ForceToggles(D3D12Backend,
|
ForceToggles(D3D12Backend,
|
||||||
{"nonzero_clear_resources_on_creation_for_testing"},
|
{"nonzero_clear_resources_on_creation_for_testing"},
|
||||||
{"lazy_clear_resource_on_first_use"}),
|
{"lazy_clear_resource_on_first_use"}),
|
||||||
|
ForceToggles(MetalBackend,
|
||||||
|
{"nonzero_clear_resources_on_creation_for_testing"},
|
||||||
|
{"lazy_clear_resource_on_first_use"}),
|
||||||
ForceToggles(OpenGLBackend,
|
ForceToggles(OpenGLBackend,
|
||||||
{"nonzero_clear_resources_on_creation_for_testing"},
|
{"nonzero_clear_resources_on_creation_for_testing"},
|
||||||
{"lazy_clear_resource_on_first_use"}),
|
{"lazy_clear_resource_on_first_use"}),
|
||||||
|
@ -127,7 +127,12 @@ TEST_P(TextureZeroInitTest, RenderingMipMapClearsToZero) {
|
|||||||
|
|
||||||
utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
|
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;
|
renderPass.renderPassInfo.cColorAttachments[0].attachment = view;
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
{
|
{
|
||||||
// Texture's first usage is in BeginRenderPass's call to RecordRenderPass
|
// 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);
|
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;
|
renderPass.renderPassInfo.cColorAttachments[0].attachment = view;
|
||||||
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
{
|
{
|
||||||
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
||||||
@ -327,6 +337,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepth) {
|
|||||||
utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()},
|
utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()},
|
||||||
depthStencilTexture.CreateView());
|
depthStencilTexture.CreateView());
|
||||||
renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
|
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.stencilLoadOp = wgpu::LoadOp::Clear;
|
||||||
renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 0;
|
renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 0;
|
||||||
renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
|
renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
|
||||||
@ -366,6 +378,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingStencil) {
|
|||||||
renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
|
renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
|
||||||
renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.0f;
|
renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.0f;
|
||||||
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
|
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.depthStoreOp = wgpu::StoreOp::Store;
|
||||||
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = 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(
|
DAWN_INSTANTIATE_TEST(
|
||||||
TextureZeroInitTest,
|
TextureZeroInitTest,
|
||||||
ForceToggles(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),
|
ForceToggles(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),
|
||||||
@ -767,4 +941,5 @@ DAWN_INSTANTIATE_TEST(
|
|||||||
{"nonzero_clear_resources_on_creation_for_testing"},
|
{"nonzero_clear_resources_on_creation_for_testing"},
|
||||||
{"use_d3d12_render_pass"}),
|
{"use_d3d12_render_pass"}),
|
||||||
ForceToggles(OpenGLBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
|
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"}));
|
ForceToggles(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user