- Added enums for write mask and adding suppooort for that across all backends.

- Batch calls to bind argument buffers
- Fix swapchain creation for Editor
This commit is contained in:
moudgils
2021-06-08 22:35:48 -07:00
parent c6d0887210
commit e795dd5210
10 changed files with 213 additions and 44 deletions
@@ -160,6 +160,24 @@ namespace AZ
StencilState m_stencil;
};
enum class WriteChannel : uint32_t
{
ColorWriteMaskRed = 0,
ColorWriteMaskGreen,
ColorWriteMaskBlue,
ColorWriteMaskAlpha,
};
enum class WriteChannelMask : uint32_t
{
ColorWriteMaskNone = 0,
ColorWriteMaskRed = AZ_BIT(static_cast<uint32_t>(WriteChannel::ColorWriteMaskRed)),
ColorWriteMaskGreen = AZ_BIT(static_cast<uint32_t>(WriteChannel::ColorWriteMaskGreen)),
ColorWriteMaskBlue = AZ_BIT(static_cast<uint32_t>(WriteChannel::ColorWriteMaskBlue)),
ColorWriteMaskAlpha = AZ_BIT(static_cast<uint32_t>(WriteChannel::ColorWriteMaskAlpha)),
ColorWriteMaskAll = ColorWriteMaskRed | ColorWriteMaskGreen | ColorWriteMaskBlue | ColorWriteMaskAlpha
};
struct TargetBlendState
{
AZ_TYPE_INFO(TargetBlendState, "{2CDF00FE-614D-44FC-929F-E6B50C348578}");
@@ -10,6 +10,7 @@
*
*/
#include "RHI/Atom_RHI_DX12_precompiled.h"
#include <Atom/RHI.Reflect/Bits.h>
#include <RHI/Conversions.h>
#include <RHI/Buffer.h>
#include <RHI/Image.h>
@@ -1268,7 +1269,7 @@ namespace AZ
dst.BlendOpAlpha = ConvertBlendOp(src.m_blendAlphaOp);
dst.DestBlend = ConvertBlendFactor(src.m_blendDest);
dst.DestBlendAlpha = ConvertBlendFactor(src.m_blendAlphaDest);
dst.RenderTargetWriteMask = src.m_writeMask;
dst.RenderTargetWriteMask = ConvertColorWriteMask(src.m_writeMask);
dst.SrcBlend = ConvertBlendFactor(src.m_blendSource);
dst.SrcBlendAlpha = ConvertBlendFactor(src.m_blendAlphaSource);
dst.LogicOp = D3D12_LOGIC_OP_CLEAR;
@@ -1355,6 +1356,28 @@ namespace AZ
};
return table[(uint32_t)mask];
}
uint32_t ConvertColorWriteMask(uint8_t writeMask)
{
uint32_t dflags = 0;
if (RHI::CheckBitsAny(writeMask, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskRed)))
{
dflags |= D3D12_COLOR_WRITE_ENABLE_RED;
}
if (RHI::CheckBitsAny(writeMask, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskGreen)))
{
dflags |= D3D12_COLOR_WRITE_ENABLE_GREEN;
}
if (RHI::CheckBitsAny(writeMask, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskBlue)))
{
dflags |= D3D12_COLOR_WRITE_ENABLE_BLUE;
}
if (RHI::CheckBitsAny(writeMask, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskAlpha)))
{
dflags |= D3D12_COLOR_WRITE_ENABLE_ALPHA;
}
return dflags;
}
D3D12_DEPTH_STENCIL_DESC ConvertDepthStencilState(const RHI::DepthStencilState& depthStencil)
{
@@ -164,5 +164,7 @@ namespace AZ
uint32_t shaderRegisterSpace,
D3D12_SHADER_VISIBILITY shaderVisibility,
D3D12_STATIC_SAMPLER_DESC& staticSamplerDesc);
uint32_t ConvertColorWriteMask(uint8_t writeMask);
}
}
@@ -13,6 +13,7 @@
#include <Atom/RHI.Reflect/Bits.h>
#include <AzCore/Debug/EventTrace.h>
#include <AzCore/std/algorithm.h>
#include <RHI/ArgumentBuffer.h>
#include <RHI/Buffer.h>
#include <RHI/BufferMemoryView.h>
@@ -249,68 +250,88 @@ namespace AZ
ShaderResourceBindings& bindings = GetShaderResourceBindingsByPipelineType(stateType);
const PipelineLayout& pipelineLayout = pipelineState->GetPipelineLayout();
for (uint32_t srgIndex = 0; srgIndex < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++srgIndex)
uint32_t bufferVertexRegisterIdMin = RHI::Limits::Pipeline::ShaderResourceGroupCountMax;
uint32_t bufferFragmentOrComputeRegisterIdMin = RHI::Limits::Pipeline::ShaderResourceGroupCountMax;
uint32_t bufferVertexRegisterIdMax = 0;
uint32_t bufferFragmentOrComputeRegisterIdMax = 0;
MetalArgumentBufferArray mtlVertexArgBuffers;
MetalArgumentBufferArrayOffsets mtlVertexArgBufferOffsets;
MetalArgumentBufferArray mtlFragmentOrComputeArgBuffers;
MetalArgumentBufferArrayOffsets mtlFragmentOrComputeArgBufferOffsets;
mtlVertexArgBuffers.fill(nil);
mtlFragmentOrComputeArgBuffers.fill(nil);
mtlVertexArgBufferOffsets.fill(0);
mtlFragmentOrComputeArgBufferOffsets.fill(0);
for (uint32_t slot = 0; slot < RHI::Limits::Pipeline::ShaderResourceGroupCountMax; ++slot)
{
const ShaderResourceGroup* shaderResourceGroup = bindings.m_srgsBySlot[srgIndex];
uint32_t slotIndex = pipelineLayout.GetSlotByIndex(srgIndex);
const ShaderResourceGroup* shaderResourceGroup = bindings.m_srgsBySlot[slot];
uint32_t slotIndex = pipelineLayout.GetIndexBySlot(slot);
if(!shaderResourceGroup || slotIndex == RHI::Limits::Pipeline::ShaderResourceGroupCountMax)
{
continue;
}
uint32_t srgVisIndex = pipelineLayout.GetSlotByIndex(shaderResourceGroup->GetBindingSlot());
uint32_t srgVisIndex = pipelineLayout.GetIndexBySlot(shaderResourceGroup->GetBindingSlot());
const RHI::ShaderStageMask& srgVisInfo = pipelineLayout.GetSrgVisibility(srgVisIndex);
if (bindings.m_srgsByIndex[srgIndex] != shaderResourceGroup)
bool isSrgUpdatd = bindings.m_srgsByIndex[slot] != shaderResourceGroup;
if(isSrgUpdatd)
{
bindings.m_srgsByIndex[srgIndex] = shaderResourceGroup;
bindings.m_srgsByIndex[slot] = shaderResourceGroup;
auto& compiledArgBuffer = shaderResourceGroup->GetCompiledArgumentBuffer();
id<MTLBuffer> argBuffer = compiledArgBuffer.GetArgEncoderBuffer();
size_t argBufferOffset = compiledArgBuffer.GetOffset();
if(srgVisInfo != RHI::ShaderStageMask::None)
{
//For graphics and compute encoder bind the argument buffer
//For graphics and compute shader stages, cache all the argument buffers, offsets and track the min/max indices
if(m_commandEncoderType == CommandEncoderType::Render)
{
id<MTLRenderCommandEncoder> renderEncoder = GetEncoder<id<MTLRenderCommandEncoder>>();
uint8_t numBitsSet = RHI::CountBitsSet(static_cast<uint64_t>(srgVisInfo));
if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Vertex)
{
[renderEncoder setVertexBuffer:argBuffer
offset:argBufferOffset
atIndex:slotIndex];
mtlVertexArgBuffers[slotIndex] = argBuffer;
mtlVertexArgBufferOffsets[slotIndex] = argBufferOffset;
bufferVertexRegisterIdMin = AZStd::min(slotIndex, bufferVertexRegisterIdMin);
bufferVertexRegisterIdMax = AZStd::max(slotIndex, bufferVertexRegisterIdMax);
}
if( numBitsSet > 1 || srgVisInfo == RHI::ShaderStageMask::Fragment)
{
[renderEncoder setFragmentBuffer:argBuffer
offset:argBufferOffset
atIndex:slotIndex];
mtlFragmentOrComputeArgBuffers[slotIndex] = argBuffer;
mtlFragmentOrComputeArgBufferOffsets[slotIndex] = argBufferOffset;
bufferFragmentOrComputeRegisterIdMin = AZStd::min(slotIndex, bufferFragmentOrComputeRegisterIdMin);
bufferFragmentOrComputeRegisterIdMax = AZStd::max(slotIndex, bufferFragmentOrComputeRegisterIdMax);
}
}
else if(m_commandEncoderType == CommandEncoderType::Compute)
{
id<MTLComputeCommandEncoder> computeEncoder = GetEncoder<id<MTLComputeCommandEncoder>>();
[computeEncoder setBuffer:argBuffer
offset:argBufferOffset
atIndex:pipelineLayout.GetSlotByIndex(srgIndex)];
mtlFragmentOrComputeArgBuffers[slotIndex] = argBuffer;
mtlFragmentOrComputeArgBufferOffsets[slotIndex] = argBufferOffset;
bufferFragmentOrComputeRegisterIdMin = AZStd::min(slotIndex, bufferFragmentOrComputeRegisterIdMin);
bufferFragmentOrComputeRegisterIdMax = AZStd::max(slotIndex, bufferFragmentOrComputeRegisterIdMax);
}
}
}
//Check againgst the srg resources visibility hash as it is possible for draw items to have different PSO in the same pass.
//Check if the srg has been updated or if the srg resources visibility hash has been updated
//as it is possible for draw items to have different PSOs in the same pass.
const AZ::HashValue64 srgResourcesVisHash = pipelineLayout.GetSrgResourcesVisibilityHash(srgVisIndex);
if(bindings.m_srgVisHashByIndex[srgIndex] != srgResourcesVisHash)
if(bindings.m_srgVisHashByIndex[slot] != srgResourcesVisHash || isSrgUpdatd)
{
bindings.m_srgVisHashByIndex[srgIndex] = srgResourcesVisHash;
bindings.m_srgVisHashByIndex[slot] = srgResourcesVisHash;
if(srgVisInfo != RHI::ShaderStageMask::None)
{
const ShaderResourceGroupVisibility& srgResourcesVisInfo = pipelineLayout.GetSrgResourcesVisibility(srgVisIndex);
//For graphics and compute encoder bind the argument buffer and
//make the resource resident for the duration of the work associated with the current scope
//and ensure that it's in a format compatible with the appropriate metal function.
//For graphics and compute encoder make the resource resident (call UseResource) for the duration
//of the work associated with the current scope and ensure that it's in a
//format compatible with the appropriate metal function.
if(m_commandEncoderType == CommandEncoderType::Render)
{
shaderResourceGroup->AddUntrackedResourcesToEncoder(m_encoder, srgResourcesVisInfo);
@@ -322,9 +343,81 @@ namespace AZ
}
}
}
//For graphics and compute encoder bind all the argument buffers
if(m_commandEncoderType == CommandEncoderType::Render)
{
BindArgumentBuffers(RHI::ShaderStage::Vertex, bufferVertexRegisterIdMin, bufferVertexRegisterIdMax, mtlVertexArgBuffers, mtlVertexArgBufferOffsets);
BindArgumentBuffers(RHI::ShaderStage::Fragment, bufferFragmentOrComputeRegisterIdMin, bufferFragmentOrComputeRegisterIdMax, mtlFragmentOrComputeArgBuffers, mtlFragmentOrComputeArgBufferOffsets);
}
else if(m_commandEncoderType == CommandEncoderType::Compute)
{
BindArgumentBuffers(RHI::ShaderStage::Compute, bufferFragmentOrComputeRegisterIdMin, bufferFragmentOrComputeRegisterIdMax, mtlFragmentOrComputeArgBuffers, mtlFragmentOrComputeArgBufferOffsets);
}
return true;
}
void CommandList::BindArgumentBuffers(RHI::ShaderStage shaderStage, uint16_t registerIdMin, uint16_t registerIdMax, MetalArgumentBufferArray& mtlArgBuffers, MetalArgumentBufferArrayOffsets mtlArgBufferOffsets)
{
//Metal Api only lets you bind multiple argument buffers in an array as long as there are no gaps in the array
//In order to accomodate that we break up the calls when a gap is noticed in the array and reconfigure the NSRange.
uint16_t startingIndex = registerIdMin;
bool trackingRange = true;
for(int i = registerIdMin; i <= registerIdMax+1; i++)
{
if(trackingRange)
{
if(mtlArgBuffers[i] == nil)
{
NSRange range = { startingIndex, i-startingIndex };
switch(shaderStage)
{
case RHI::ShaderStage::Vertex:
{
id<MTLRenderCommandEncoder> renderEncoder = GetEncoder<id<MTLRenderCommandEncoder>>();
[renderEncoder setVertexBuffers:&mtlArgBuffers[startingIndex]
offsets:&mtlArgBufferOffsets[startingIndex]
withRange:range];
break;
}
case RHI::ShaderStage::Fragment:
{
id<MTLRenderCommandEncoder> renderEncoder = GetEncoder<id<MTLRenderCommandEncoder>>();
[renderEncoder setFragmentBuffers:&mtlArgBuffers[startingIndex]
offsets:&mtlArgBufferOffsets[startingIndex]
withRange:range];
break;
}
case RHI::ShaderStage::Compute:
{
id<MTLComputeCommandEncoder> computeEncoder = GetEncoder<id<MTLComputeCommandEncoder>>();
[computeEncoder setBuffers:&mtlArgBuffers[startingIndex]
offsets:&mtlArgBufferOffsets[startingIndex]
withRange:range];
break;
}
default:
{
AZ_Assert(false, "Not supported");
}
}
trackingRange = false;
}
}
else
{
if(mtlArgBuffers[i] != nil)
{
startingIndex = i;
trackingRange = true;
}
}
}
}
void CommandList::Submit(const RHI::DrawItem& drawItem)
{
@@ -486,7 +579,7 @@ namespace AZ
void CommandList::SetStreamBuffers(const RHI::StreamBufferView* streams, uint32_t count)
{
int bufferArrayLen = 0;
uint16_t bufferArrayLen = 0;
AZStd::array<id<MTLBuffer>, METAL_MAX_ENTRIES_BUFFER_ARG_TABLE> mtlStreamBuffers;
AZStd::array<NSUInteger, METAL_MAX_ENTRIES_BUFFER_ARG_TABLE> mtlStreamBufferOffsets;
@@ -102,6 +102,10 @@ namespace AZ
AZStd::array<AZ::HashValue64, RHI::Limits::Pipeline::ShaderResourceGroupCountMax> m_srgVisHashByIndex;
};
using MetalArgumentBufferArray = AZStd::array<id<MTLBuffer>, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>;
using MetalArgumentBufferArrayOffsets = AZStd::array<NSUInteger, RHI::Limits::Pipeline::ShaderResourceGroupCountMax>;
void BindArgumentBuffers(RHI::ShaderStage shaderStage, uint16_t registerIdMin, uint16_t registerIdMax, MetalArgumentBufferArray& mtlArgBuffers, MetalArgumentBufferArrayOffsets mtlArgBufferOffsets);
ShaderResourceBindings& GetShaderResourceBindingsByPipelineType(RHI::PipelineStateType pipelineType);
//! This is kept as a separate struct so that we can robustly reset it. Every property
@@ -12,6 +12,7 @@
#include "Atom_RHI_Metal_precompiled.h"
#include <Atom/RHI.Reflect/ImageDescriptor.h>
#include <Atom/RHI.Reflect/Bits.h>
#include <RHI/Conversions.h>
#include <RHI/Conversions_Platform.h>
#include <RHI/Image.h>
@@ -456,8 +457,25 @@ namespace AZ
MTLColorWriteMask ConvertColorWriteMask(AZ::u8 writeMask)
{
//todo::Based on the mask set the correct writemask
return MTLColorWriteMaskAll;
MTLColorWriteMask colorMask = MTLColorWriteMaskNone;
if (RHI::CheckBitsAny(writeMask, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskRed)))
{
colorMask |= MTLColorWriteMaskRed;
}
if (RHI::CheckBitsAny(writeMask, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskGreen)))
{
colorMask |= MTLColorWriteMaskGreen;
}
if (RHI::CheckBitsAny(writeMask, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskBlue)))
{
colorMask |= MTLColorWriteMaskBlue;
}
if (RHI::CheckBitsAny(writeMask, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskAlpha)))
{
colorMask |= MTLColorWriteMaskAlpha;
}
return colorMask;
}
MTLVertexFormat ConvertVertexFormat(RHI::Format format)
@@ -125,12 +125,12 @@ namespace AZ
size_t PipelineLayout::GetSlotByIndex(size_t index) const
{
return m_slotToIndexTable[index];
return m_indexToSlotTable[index];
}
size_t PipelineLayout::GetIndexBySlot(size_t slot) const
{
return m_indexToSlotTable[slot];
return m_slotToIndexTable[slot];
}
const RHI::ShaderStageMask& PipelineLayout::GetSrgVisibility(uint32_t index) const
@@ -73,6 +73,10 @@ namespace AZ
m_metalView.metalLayer.drawableSize = CGSizeMake(descriptor.m_dimensions.m_imageWidth, descriptor.m_dimensions.m_imageHeight);
}
else
{
AddSubView();
}
m_drawables.resize(descriptor.m_dimensions.m_imageCount);
@@ -83,6 +87,20 @@ namespace AZ
return RHI::ResultCode::Success;
}
void SwapChain::AddSubView()
{
NativeViewType* superView = reinterpret_cast<NativeViewType*>(m_nativeWindow);
CGFloat screenScale = Platform::GetScreenScale();
CGRect screenBounds = [superView bounds];
m_metalView = [[RHIMetalView alloc] initWithFrame: screenBounds
scale: screenScale
device: m_mtlDevice];
[m_metalView retain];
[superView addSubview: m_metalView];
}
void SwapChain::ShutdownInternal()
{
if (m_viewController)
@@ -161,16 +179,7 @@ namespace AZ
}
else
{
NativeViewType* superView = reinterpret_cast<NativeViewType*>(m_nativeWindow);
CGFloat screenScale = Platform::GetScreenScale();
CGRect screenBounds = [superView bounds];
m_metalView = [[RHIMetalView alloc] initWithFrame: screenBounds
scale: screenScale
device: m_mtlDevice];
[m_metalView retain];
[superView addSubview: m_metalView];
AddSubView();
}
}
return RHI::ResultCode::Success;
@@ -49,6 +49,8 @@ namespace AZ
RHI::ResultCode ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) override;
//////////////////////////////////////////////////////////////////////////
void AddSubView();
id <MTLCommandBuffer> m_mtlCommandBuffer;
RHIMetalView* m_metalView = nullptr;
NativeViewControllerType* m_viewController = nullptr;
@@ -334,19 +334,19 @@ namespace AZ
VkColorComponentFlags ConvertComponentFlags(uint8_t sflags)
{
VkColorComponentFlags dflags = 0;
if (RHI::CheckBitsAny(sflags, static_cast<uint8_t>(1)))
if (RHI::CheckBitsAny(sflags, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskRed)))
{
dflags |= VK_COLOR_COMPONENT_R_BIT;
}
if (RHI::CheckBitsAny(sflags, static_cast<uint8_t>(2)))
if (RHI::CheckBitsAny(sflags, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskGreen)))
{
dflags |= VK_COLOR_COMPONENT_G_BIT;
}
if (RHI::CheckBitsAny(sflags, static_cast<uint8_t>(4)))
if (RHI::CheckBitsAny(sflags, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskBlue)))
{
dflags |= VK_COLOR_COMPONENT_B_BIT;
}
if (RHI::CheckBitsAny(sflags, static_cast<uint8_t>(8)))
if (RHI::CheckBitsAny(sflags, static_cast<uint8_t>(RHI::WriteChannelMask::ColorWriteMaskAlpha)))
{
dflags |= VK_COLOR_COMPONENT_A_BIT;
}