Refactor the emulation of store and MSAA resolve
This patch refactors the implementation of the emulation on "store and MSAA resolve" store operation by moving all the function calls related to this toggle into one recursive function EncoderRenderPass(). This refactoring will also make it easier to implement more workarounds on Metal render pass. BUG=dawn:56 Change-Id: Ifc737407001e55863835ab994b735e088beda8c6 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7220 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
This commit is contained in:
parent
740995c0b1
commit
61c0db71de
|
@ -28,6 +28,7 @@ namespace dawn_native {
|
||||||
namespace dawn_native { namespace metal {
|
namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
|
struct GlobalEncoders;
|
||||||
|
|
||||||
class CommandBuffer : public CommandBufferBase {
|
class CommandBuffer : public CommandBufferBase {
|
||||||
public:
|
public:
|
||||||
|
@ -38,7 +39,16 @@ namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void EncodeComputePass(id<MTLCommandBuffer> commandBuffer);
|
void EncodeComputePass(id<MTLCommandBuffer> commandBuffer);
|
||||||
void EncodeRenderPass(id<MTLCommandBuffer> commandBuffer, BeginRenderPassCmd* renderPass);
|
void EncodeRenderPass(id<MTLCommandBuffer> commandBuffer,
|
||||||
|
MTLRenderPassDescriptor* mtlRenderPass,
|
||||||
|
GlobalEncoders* globalEncoders,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height);
|
||||||
|
|
||||||
|
void EncodeRenderPassInternal(id<MTLCommandBuffer> commandBuffer,
|
||||||
|
MTLRenderPassDescriptor* mtlRenderPass,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height);
|
||||||
|
|
||||||
CommandIterator mCommands;
|
CommandIterator mCommands;
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,8 +27,6 @@
|
||||||
|
|
||||||
namespace dawn_native { namespace metal {
|
namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
struct GlobalEncoders {
|
struct GlobalEncoders {
|
||||||
id<MTLBlitCommandEncoder> blit = nil;
|
id<MTLBlitCommandEncoder> blit = nil;
|
||||||
|
|
||||||
|
@ -46,10 +44,10 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
// Creates an autoreleased MTLRenderPassDescriptor matching desc
|
// Creates an autoreleased MTLRenderPassDescriptor matching desc
|
||||||
MTLRenderPassDescriptor* CreateMTLRenderPassDescriptor(
|
MTLRenderPassDescriptor* CreateMTLRenderPassDescriptor(BeginRenderPassCmd* renderPass) {
|
||||||
BeginRenderPassCmd* renderPass,
|
|
||||||
bool shouldEmulateStoreAndMSAAResolve) {
|
|
||||||
MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||||
|
|
||||||
for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) {
|
for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) {
|
||||||
|
@ -70,8 +68,7 @@ namespace dawn_native { namespace metal {
|
||||||
descriptor.colorAttachments[i].slice = attachmentInfo.view->GetBaseArrayLayer();
|
descriptor.colorAttachments[i].slice = attachmentInfo.view->GetBaseArrayLayer();
|
||||||
|
|
||||||
if (attachmentInfo.storeOp == dawn::StoreOp::Store) {
|
if (attachmentInfo.storeOp == dawn::StoreOp::Store) {
|
||||||
if (attachmentInfo.resolveTarget.Get() != nullptr &&
|
if (attachmentInfo.resolveTarget.Get() != nullptr) {
|
||||||
!shouldEmulateStoreAndMSAAResolve) {
|
|
||||||
descriptor.colorAttachments[i].resolveTexture =
|
descriptor.colorAttachments[i].resolveTexture =
|
||||||
ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture();
|
ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture();
|
||||||
descriptor.colorAttachments[i].resolveLevel =
|
descriptor.colorAttachments[i].resolveLevel =
|
||||||
|
@ -122,42 +119,33 @@ namespace dawn_native { namespace metal {
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do MSAA resolve in another render pass.
|
void ResolveInAnotherRenderPass(
|
||||||
void ResolveInAnotherRenderPass(id<MTLCommandBuffer> commandBuffer,
|
id<MTLCommandBuffer> commandBuffer,
|
||||||
BeginRenderPassCmd* renderPass) {
|
const MTLRenderPassDescriptor* mtlRenderPass,
|
||||||
ASSERT(renderPass->sampleCount > 1);
|
const std::array<id<MTLTexture>, kMaxColorAttachments>& resolveTextures) {
|
||||||
MTLRenderPassDescriptor* renderPassForResolve = nil;
|
MTLRenderPassDescriptor* mtlRenderPassForResolve =
|
||||||
for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) {
|
[MTLRenderPassDescriptor renderPassDescriptor];
|
||||||
auto& attachmentInfo = renderPass->colorAttachments[i];
|
for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
|
||||||
if (attachmentInfo.resolveTarget.Get() == nil ||
|
if (resolveTextures[i] == nil) {
|
||||||
attachmentInfo.storeOp != dawn::StoreOp::Store) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderPassForResolve == nil) {
|
mtlRenderPassForResolve.colorAttachments[i].texture =
|
||||||
renderPassForResolve = [MTLRenderPassDescriptor renderPassDescriptor];
|
mtlRenderPass.colorAttachments[i].texture;
|
||||||
}
|
mtlRenderPassForResolve.colorAttachments[i].loadAction = MTLLoadActionLoad;
|
||||||
renderPassForResolve.colorAttachments[i].texture =
|
mtlRenderPassForResolve.colorAttachments[i].storeAction =
|
||||||
ToBackend(attachmentInfo.view->GetTexture())->GetMTLTexture();
|
|
||||||
renderPassForResolve.colorAttachments[i].level = 0;
|
|
||||||
renderPassForResolve.colorAttachments[i].slice = 0;
|
|
||||||
|
|
||||||
renderPassForResolve.colorAttachments[i].storeAction =
|
|
||||||
MTLStoreActionMultisampleResolve;
|
MTLStoreActionMultisampleResolve;
|
||||||
renderPassForResolve.colorAttachments[i].resolveTexture =
|
mtlRenderPassForResolve.colorAttachments[i].resolveTexture = resolveTextures[i];
|
||||||
ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture();
|
mtlRenderPassForResolve.colorAttachments[i].resolveLevel =
|
||||||
renderPassForResolve.colorAttachments[i].resolveLevel =
|
mtlRenderPass.colorAttachments[i].resolveLevel;
|
||||||
attachmentInfo.resolveTarget->GetBaseMipLevel();
|
mtlRenderPassForResolve.colorAttachments[i].resolveSlice =
|
||||||
renderPassForResolve.colorAttachments[i].resolveSlice =
|
mtlRenderPass.colorAttachments[i].resolveSlice;
|
||||||
attachmentInfo.resolveTarget->GetBaseArrayLayer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderPassForResolve != nil) {
|
|
||||||
id<MTLRenderCommandEncoder> encoder =
|
id<MTLRenderCommandEncoder> encoder =
|
||||||
[commandBuffer renderCommandEncoderWithDescriptor:renderPassForResolve];
|
[commandBuffer renderCommandEncoderWithDescriptor:mtlRenderPassForResolve];
|
||||||
[encoder endEncoding];
|
[encoder endEncoding];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Handles a call to SetBindGroup, directing the commands to the correct encoder.
|
// Handles a call to SetBindGroup, directing the commands to the correct encoder.
|
||||||
// There is a single function that takes both encoders to factor code. Other approaches like
|
// There is a single function that takes both encoders to factor code. Other approaches like
|
||||||
|
@ -286,7 +274,8 @@ namespace dawn_native { namespace metal {
|
||||||
case Command::BeginRenderPass: {
|
case Command::BeginRenderPass: {
|
||||||
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
||||||
encoders.Finish();
|
encoders.Finish();
|
||||||
EncodeRenderPass(commandBuffer, cmd);
|
MTLRenderPassDescriptor* descriptor = CreateMTLRenderPassDescriptor(cmd);
|
||||||
|
EncodeRenderPass(commandBuffer, descriptor, &encoders, cmd->width, cmd->height);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Command::CopyBufferToBuffer: {
|
case Command::CopyBufferToBuffer: {
|
||||||
|
@ -630,7 +619,46 @@ namespace dawn_native { namespace metal {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBuffer::EncodeRenderPass(id<MTLCommandBuffer> commandBuffer,
|
void CommandBuffer::EncodeRenderPass(id<MTLCommandBuffer> commandBuffer,
|
||||||
BeginRenderPassCmd* renderPassCmd) {
|
MTLRenderPassDescriptor* mtlRenderPass,
|
||||||
|
GlobalEncoders* globalEncoders,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height) {
|
||||||
|
ASSERT(mtlRenderPass && globalEncoders);
|
||||||
|
|
||||||
|
Device* device = ToBackend(GetDevice());
|
||||||
|
|
||||||
|
// Handle Store + MSAA resolve workaround (Toggle EmulateStoreAndMSAAResolve).
|
||||||
|
if (device->IsToggleEnabled(Toggle::EmulateStoreAndMSAAResolve)) {
|
||||||
|
bool hasStoreAndMSAAResolve = false;
|
||||||
|
|
||||||
|
// Remove any store + MSAA resolve and remember them.
|
||||||
|
std::array<id<MTLTexture>, kMaxColorAttachments> resolveTextures = {};
|
||||||
|
for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
|
||||||
|
if (mtlRenderPass.colorAttachments[i].storeAction ==
|
||||||
|
MTLStoreActionStoreAndMultisampleResolve) {
|
||||||
|
hasStoreAndMSAAResolve = true;
|
||||||
|
resolveTextures[i] = mtlRenderPass.colorAttachments[i].resolveTexture;
|
||||||
|
|
||||||
|
mtlRenderPass.colorAttachments[i].storeAction = MTLStoreActionStore;
|
||||||
|
mtlRenderPass.colorAttachments[i].resolveTexture = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found a store + MSAA resolve we need to resolve in a different render pass.
|
||||||
|
if (hasStoreAndMSAAResolve) {
|
||||||
|
EncodeRenderPass(commandBuffer, mtlRenderPass, globalEncoders, width, height);
|
||||||
|
ResolveInAnotherRenderPass(commandBuffer, mtlRenderPass, resolveTextures);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EncodeRenderPassInternal(commandBuffer, mtlRenderPass, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandBuffer::EncodeRenderPassInternal(id<MTLCommandBuffer> commandBuffer,
|
||||||
|
MTLRenderPassDescriptor* mtlRenderPass,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height) {
|
||||||
RenderPipeline* lastPipeline = nullptr;
|
RenderPipeline* lastPipeline = nullptr;
|
||||||
id<MTLBuffer> indexBuffer = nil;
|
id<MTLBuffer> indexBuffer = nil;
|
||||||
uint32_t indexBufferBaseOffset = 0;
|
uint32_t indexBufferBaseOffset = 0;
|
||||||
|
@ -638,13 +666,9 @@ namespace dawn_native { namespace metal {
|
||||||
std::array<uint32_t, kMaxPushConstants> vertexPushConstants;
|
std::array<uint32_t, kMaxPushConstants> vertexPushConstants;
|
||||||
std::array<uint32_t, kMaxPushConstants> fragmentPushConstants;
|
std::array<uint32_t, kMaxPushConstants> fragmentPushConstants;
|
||||||
|
|
||||||
bool shouldEmulateStoreAndMSAAResolve =
|
|
||||||
GetDevice()->IsToggleEnabled(Toggle::EmulateStoreAndMSAAResolve);
|
|
||||||
// This will be autoreleased
|
// This will be autoreleased
|
||||||
id<MTLRenderCommandEncoder> encoder = [commandBuffer
|
id<MTLRenderCommandEncoder> encoder =
|
||||||
renderCommandEncoderWithDescriptor:CreateMTLRenderPassDescriptor(
|
[commandBuffer renderCommandEncoderWithDescriptor:mtlRenderPass];
|
||||||
renderPassCmd,
|
|
||||||
shouldEmulateStoreAndMSAAResolve)];
|
|
||||||
|
|
||||||
// Set default values for push constants
|
// Set default values for push constants
|
||||||
vertexPushConstants.fill(0);
|
vertexPushConstants.fill(0);
|
||||||
|
@ -663,9 +687,6 @@ namespace dawn_native { namespace metal {
|
||||||
case Command::EndRenderPass: {
|
case Command::EndRenderPass: {
|
||||||
mCommands.NextCommand<EndRenderPassCmd>();
|
mCommands.NextCommand<EndRenderPassCmd>();
|
||||||
[encoder endEncoding];
|
[encoder endEncoding];
|
||||||
if (renderPassCmd->sampleCount > 1 && shouldEmulateStoreAndMSAAResolve) {
|
|
||||||
ResolveInAnotherRenderPass(commandBuffer, renderPassCmd);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -770,12 +791,12 @@ namespace dawn_native { namespace metal {
|
||||||
rect.height = cmd->height;
|
rect.height = cmd->height;
|
||||||
|
|
||||||
// The scissor rect x + width must be <= render pass width
|
// The scissor rect x + width must be <= render pass width
|
||||||
if ((rect.x + rect.width) > renderPassCmd->width) {
|
if ((rect.x + rect.width) > width) {
|
||||||
rect.width = renderPassCmd->width - rect.x;
|
rect.width = width - rect.x;
|
||||||
}
|
}
|
||||||
// The scissor rect y + height must be <= render pass height
|
// The scissor rect y + height must be <= render pass height
|
||||||
if ((rect.y + rect.height > renderPassCmd->height)) {
|
if ((rect.y + rect.height > height)) {
|
||||||
rect.height = renderPassCmd->height - rect.y;
|
rect.height = height - rect.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
[encoder setScissorRect:rect];
|
[encoder setScissorRect:rect];
|
||||||
|
|
Loading…
Reference in New Issue