blob: ef54f404a67b294f7994fc494a4f206e460a8be5 [file] [log] [blame]
// Copyright 2017 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn_native/metal/RenderPipelineMTL.h"
#include "dawn_native/metal/DeviceMTL.h"
#include "dawn_native/metal/PipelineLayoutMTL.h"
#include "dawn_native/metal/ShaderModuleMTL.h"
#include "dawn_native/metal/TextureMTL.h"
#include "dawn_native/metal/UtilsMetal.h"
namespace dawn_native { namespace metal {
namespace {
MTLVertexFormat VertexFormatType(dawn::VertexFormat format) {
switch (format) {
case dawn::VertexFormat::UChar2:
return MTLVertexFormatUChar2;
case dawn::VertexFormat::UChar4:
return MTLVertexFormatUChar4;
case dawn::VertexFormat::Char2:
return MTLVertexFormatChar2;
case dawn::VertexFormat::Char4:
return MTLVertexFormatChar4;
case dawn::VertexFormat::UChar2Norm:
return MTLVertexFormatUChar2Normalized;
case dawn::VertexFormat::UChar4Norm:
return MTLVertexFormatUChar4Normalized;
case dawn::VertexFormat::Char2Norm:
return MTLVertexFormatChar2Normalized;
case dawn::VertexFormat::Char4Norm:
return MTLVertexFormatChar4Normalized;
case dawn::VertexFormat::UShort2:
return MTLVertexFormatUShort2;
case dawn::VertexFormat::UShort4:
return MTLVertexFormatUShort4;
case dawn::VertexFormat::Short2:
return MTLVertexFormatShort2;
case dawn::VertexFormat::Short4:
return MTLVertexFormatShort4;
case dawn::VertexFormat::UShort2Norm:
return MTLVertexFormatUShort2Normalized;
case dawn::VertexFormat::UShort4Norm:
return MTLVertexFormatUShort4Normalized;
case dawn::VertexFormat::Short2Norm:
return MTLVertexFormatShort2Normalized;
case dawn::VertexFormat::Short4Norm:
return MTLVertexFormatShort4Normalized;
case dawn::VertexFormat::Half2:
return MTLVertexFormatHalf2;
case dawn::VertexFormat::Half4:
return MTLVertexFormatHalf4;
case dawn::VertexFormat::Float:
return MTLVertexFormatFloat;
case dawn::VertexFormat::Float2:
return MTLVertexFormatFloat2;
case dawn::VertexFormat::Float3:
return MTLVertexFormatFloat3;
case dawn::VertexFormat::Float4:
return MTLVertexFormatFloat4;
case dawn::VertexFormat::UInt:
return MTLVertexFormatUInt;
case dawn::VertexFormat::UInt2:
return MTLVertexFormatUInt2;
case dawn::VertexFormat::UInt3:
return MTLVertexFormatUInt3;
case dawn::VertexFormat::UInt4:
return MTLVertexFormatUInt4;
case dawn::VertexFormat::Int:
return MTLVertexFormatInt;
case dawn::VertexFormat::Int2:
return MTLVertexFormatInt2;
case dawn::VertexFormat::Int3:
return MTLVertexFormatInt3;
case dawn::VertexFormat::Int4:
return MTLVertexFormatInt4;
}
}
MTLVertexStepFunction InputStepModeFunction(dawn::InputStepMode mode) {
switch (mode) {
case dawn::InputStepMode::Vertex:
return MTLVertexStepFunctionPerVertex;
case dawn::InputStepMode::Instance:
return MTLVertexStepFunctionPerInstance;
}
}
MTLPrimitiveType MTLPrimitiveTopology(dawn::PrimitiveTopology primitiveTopology) {
switch (primitiveTopology) {
case dawn::PrimitiveTopology::PointList:
return MTLPrimitiveTypePoint;
case dawn::PrimitiveTopology::LineList:
return MTLPrimitiveTypeLine;
case dawn::PrimitiveTopology::LineStrip:
return MTLPrimitiveTypeLineStrip;
case dawn::PrimitiveTopology::TriangleList:
return MTLPrimitiveTypeTriangle;
case dawn::PrimitiveTopology::TriangleStrip:
return MTLPrimitiveTypeTriangleStrip;
}
}
MTLPrimitiveTopologyClass MTLInputPrimitiveTopology(
dawn::PrimitiveTopology primitiveTopology) {
switch (primitiveTopology) {
case dawn::PrimitiveTopology::PointList:
return MTLPrimitiveTopologyClassPoint;
case dawn::PrimitiveTopology::LineList:
case dawn::PrimitiveTopology::LineStrip:
return MTLPrimitiveTopologyClassLine;
case dawn::PrimitiveTopology::TriangleList:
case dawn::PrimitiveTopology::TriangleStrip:
return MTLPrimitiveTopologyClassTriangle;
}
}
MTLIndexType MTLIndexFormat(dawn::IndexFormat format) {
switch (format) {
case dawn::IndexFormat::Uint16:
return MTLIndexTypeUInt16;
case dawn::IndexFormat::Uint32:
return MTLIndexTypeUInt32;
}
}
MTLBlendFactor MetalBlendFactor(dawn::BlendFactor factor, bool alpha) {
switch (factor) {
case dawn::BlendFactor::Zero:
return MTLBlendFactorZero;
case dawn::BlendFactor::One:
return MTLBlendFactorOne;
case dawn::BlendFactor::SrcColor:
return MTLBlendFactorSourceColor;
case dawn::BlendFactor::OneMinusSrcColor:
return MTLBlendFactorOneMinusSourceColor;
case dawn::BlendFactor::SrcAlpha:
return MTLBlendFactorSourceAlpha;
case dawn::BlendFactor::OneMinusSrcAlpha:
return MTLBlendFactorOneMinusSourceAlpha;
case dawn::BlendFactor::DstColor:
return MTLBlendFactorDestinationColor;
case dawn::BlendFactor::OneMinusDstColor:
return MTLBlendFactorOneMinusDestinationColor;
case dawn::BlendFactor::DstAlpha:
return MTLBlendFactorDestinationAlpha;
case dawn::BlendFactor::OneMinusDstAlpha:
return MTLBlendFactorOneMinusDestinationAlpha;
case dawn::BlendFactor::SrcAlphaSaturated:
return MTLBlendFactorSourceAlphaSaturated;
case dawn::BlendFactor::BlendColor:
return alpha ? MTLBlendFactorBlendAlpha : MTLBlendFactorBlendColor;
case dawn::BlendFactor::OneMinusBlendColor:
return alpha ? MTLBlendFactorOneMinusBlendAlpha
: MTLBlendFactorOneMinusBlendColor;
}
}
MTLBlendOperation MetalBlendOperation(dawn::BlendOperation operation) {
switch (operation) {
case dawn::BlendOperation::Add:
return MTLBlendOperationAdd;
case dawn::BlendOperation::Subtract:
return MTLBlendOperationSubtract;
case dawn::BlendOperation::ReverseSubtract:
return MTLBlendOperationReverseSubtract;
case dawn::BlendOperation::Min:
return MTLBlendOperationMin;
case dawn::BlendOperation::Max:
return MTLBlendOperationMax;
}
}
MTLColorWriteMask MetalColorWriteMask(dawn::ColorWriteMask writeMask) {
MTLColorWriteMask mask = MTLColorWriteMaskNone;
if (writeMask & dawn::ColorWriteMask::Red) {
mask |= MTLColorWriteMaskRed;
}
if (writeMask & dawn::ColorWriteMask::Green) {
mask |= MTLColorWriteMaskGreen;
}
if (writeMask & dawn::ColorWriteMask::Blue) {
mask |= MTLColorWriteMaskBlue;
}
if (writeMask & dawn::ColorWriteMask::Alpha) {
mask |= MTLColorWriteMaskAlpha;
}
return mask;
}
void ComputeBlendDesc(MTLRenderPipelineColorAttachmentDescriptor* attachment,
const ColorStateDescriptor* descriptor) {
attachment.blendingEnabled = BlendEnabled(descriptor);
attachment.sourceRGBBlendFactor =
MetalBlendFactor(descriptor->colorBlend.srcFactor, false);
attachment.destinationRGBBlendFactor =
MetalBlendFactor(descriptor->colorBlend.dstFactor, false);
attachment.rgbBlendOperation = MetalBlendOperation(descriptor->colorBlend.operation);
attachment.sourceAlphaBlendFactor =
MetalBlendFactor(descriptor->alphaBlend.srcFactor, true);
attachment.destinationAlphaBlendFactor =
MetalBlendFactor(descriptor->alphaBlend.dstFactor, true);
attachment.alphaBlendOperation = MetalBlendOperation(descriptor->alphaBlend.operation);
attachment.writeMask = MetalColorWriteMask(descriptor->writeMask);
}
MTLStencilOperation MetalStencilOperation(dawn::StencilOperation stencilOperation) {
switch (stencilOperation) {
case dawn::StencilOperation::Keep:
return MTLStencilOperationKeep;
case dawn::StencilOperation::Zero:
return MTLStencilOperationZero;
case dawn::StencilOperation::Replace:
return MTLStencilOperationReplace;
case dawn::StencilOperation::Invert:
return MTLStencilOperationInvert;
case dawn::StencilOperation::IncrementClamp:
return MTLStencilOperationIncrementClamp;
case dawn::StencilOperation::DecrementClamp:
return MTLStencilOperationDecrementClamp;
case dawn::StencilOperation::IncrementWrap:
return MTLStencilOperationIncrementWrap;
case dawn::StencilOperation::DecrementWrap:
return MTLStencilOperationDecrementWrap;
}
}
MTLDepthStencilDescriptor* MakeDepthStencilDesc(
const DepthStencilStateDescriptor* descriptor) {
MTLDepthStencilDescriptor* mtlDepthStencilDescriptor = [MTLDepthStencilDescriptor new];
mtlDepthStencilDescriptor.depthCompareFunction =
ToMetalCompareFunction(descriptor->depthCompare);
mtlDepthStencilDescriptor.depthWriteEnabled = descriptor->depthWriteEnabled;
if (StencilTestEnabled(descriptor)) {
MTLStencilDescriptor* backFaceStencil = [MTLStencilDescriptor new];
MTLStencilDescriptor* frontFaceStencil = [MTLStencilDescriptor new];
backFaceStencil.stencilCompareFunction =
ToMetalCompareFunction(descriptor->stencilBack.compare);
backFaceStencil.stencilFailureOperation =
MetalStencilOperation(descriptor->stencilBack.failOp);
backFaceStencil.depthFailureOperation =
MetalStencilOperation(descriptor->stencilBack.depthFailOp);
backFaceStencil.depthStencilPassOperation =
MetalStencilOperation(descriptor->stencilBack.passOp);
backFaceStencil.readMask = descriptor->stencilReadMask;
backFaceStencil.writeMask = descriptor->stencilWriteMask;
frontFaceStencil.stencilCompareFunction =
ToMetalCompareFunction(descriptor->stencilFront.compare);
frontFaceStencil.stencilFailureOperation =
MetalStencilOperation(descriptor->stencilFront.failOp);
frontFaceStencil.depthFailureOperation =
MetalStencilOperation(descriptor->stencilFront.depthFailOp);
frontFaceStencil.depthStencilPassOperation =
MetalStencilOperation(descriptor->stencilFront.passOp);
frontFaceStencil.readMask = descriptor->stencilReadMask;
frontFaceStencil.writeMask = descriptor->stencilWriteMask;
mtlDepthStencilDescriptor.backFaceStencil = backFaceStencil;
mtlDepthStencilDescriptor.frontFaceStencil = frontFaceStencil;
[backFaceStencil release];
[frontFaceStencil release];
}
return mtlDepthStencilDescriptor;
}
MTLWinding MTLFrontFace(dawn::FrontFace face) {
switch (face) {
case dawn::FrontFace::CW:
return MTLWindingClockwise;
case dawn::FrontFace::CCW:
return MTLWindingCounterClockwise;
}
}
MTLCullMode ToMTLCullMode(dawn::CullMode mode) {
switch (mode) {
case dawn::CullMode::None:
return MTLCullModeNone;
case dawn::CullMode::Front:
return MTLCullModeFront;
case dawn::CullMode::Back:
return MTLCullModeBack;
}
}
} // anonymous namespace
RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
: RenderPipelineBase(device, descriptor),
mMtlIndexType(MTLIndexFormat(GetVertexInputDescriptor()->indexFormat)),
mMtlPrimitiveTopology(MTLPrimitiveTopology(GetPrimitiveTopology())),
mMtlFrontFace(MTLFrontFace(GetFrontFace())),
mMtlCullMode(ToMTLCullMode(GetCullMode())) {
auto mtlDevice = device->GetMTLDevice();
MTLRenderPipelineDescriptor* descriptorMTL = [MTLRenderPipelineDescriptor new];
const ShaderModule* vertexModule = ToBackend(descriptor->vertexStage->module);
const char* vertexEntryPoint = descriptor->vertexStage->entryPoint;
ShaderModule::MetalFunctionData vertexData = vertexModule->GetFunction(
vertexEntryPoint, ShaderStage::Vertex, ToBackend(GetLayout()));
descriptorMTL.vertexFunction = vertexData.function;
if (vertexData.needsStorageBufferLength) {
mStagesRequiringStorageBufferLength |= dawn::ShaderStageBit::Vertex;
}
const ShaderModule* fragmentModule = ToBackend(descriptor->fragmentStage->module);
const char* fragmentEntryPoint = descriptor->fragmentStage->entryPoint;
ShaderModule::MetalFunctionData fragmentData = fragmentModule->GetFunction(
fragmentEntryPoint, ShaderStage::Fragment, ToBackend(GetLayout()));
descriptorMTL.fragmentFunction = fragmentData.function;
if (fragmentData.needsStorageBufferLength) {
mStagesRequiringStorageBufferLength |= dawn::ShaderStageBit::Fragment;
}
if (HasDepthStencilAttachment()) {
// TODO(kainino@chromium.org): Handle depth-only and stencil-only formats.
dawn::TextureFormat depthStencilFormat = GetDepthStencilFormat();
descriptorMTL.depthAttachmentPixelFormat = MetalPixelFormat(depthStencilFormat);
descriptorMTL.stencilAttachmentPixelFormat = MetalPixelFormat(depthStencilFormat);
}
for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
descriptorMTL.colorAttachments[i].pixelFormat =
MetalPixelFormat(GetColorAttachmentFormat(i));
const ColorStateDescriptor* descriptor = GetColorStateDescriptor(i);
ComputeBlendDesc(descriptorMTL.colorAttachments[i], descriptor);
}
descriptorMTL.inputPrimitiveTopology = MTLInputPrimitiveTopology(GetPrimitiveTopology());
MTLVertexDescriptor* vertexDesc = MakeVertexDesc();
descriptorMTL.vertexDescriptor = vertexDesc;
[vertexDesc release];
descriptorMTL.sampleCount = GetSampleCount();
// TODO(kainino@chromium.org): push constants, textures, samplers
{
NSError* error = nil;
mMtlRenderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:descriptorMTL
error:&error];
[descriptorMTL release];
if (error != nil) {
NSLog(@" error => %@", error);
device->HandleError("Error creating rendering pipeline state");
return;
}
}
// Create depth stencil state and cache it, fetch the cached depth stencil state when we
// call setDepthStencilState() for a given render pipeline in CommandEncoder, in order to
// improve performance.
MTLDepthStencilDescriptor* depthStencilDesc =
MakeDepthStencilDesc(GetDepthStencilStateDescriptor());
mMtlDepthStencilState = [mtlDevice newDepthStencilStateWithDescriptor:depthStencilDesc];
[depthStencilDesc release];
}
RenderPipeline::~RenderPipeline() {
[mMtlRenderPipelineState release];
[mMtlDepthStencilState release];
}
MTLIndexType RenderPipeline::GetMTLIndexType() const {
return mMtlIndexType;
}
MTLPrimitiveType RenderPipeline::GetMTLPrimitiveTopology() const {
return mMtlPrimitiveTopology;
}
MTLWinding RenderPipeline::GetMTLFrontFace() const {
return mMtlFrontFace;
}
MTLCullMode RenderPipeline::GetMTLCullMode() const {
return mMtlCullMode;
}
void RenderPipeline::Encode(id<MTLRenderCommandEncoder> encoder) {
[encoder setRenderPipelineState:mMtlRenderPipelineState];
}
id<MTLDepthStencilState> RenderPipeline::GetMTLDepthStencilState() {
return mMtlDepthStencilState;
}
uint32_t RenderPipeline::GetMtlVertexBufferIndex(uint32_t dawnIndex) const {
ASSERT(dawnIndex < kMaxVertexBuffers);
return mMtlVertexBufferIndices[dawnIndex];
}
dawn::ShaderStageBit RenderPipeline::GetStagesRequiringStorageBufferLength() const {
return mStagesRequiringStorageBufferLength;
}
MTLVertexDescriptor* RenderPipeline::MakeVertexDesc() {
MTLVertexDescriptor* mtlVertexDescriptor = [MTLVertexDescriptor new];
// Vertex buffers are packed after all the buffers for the bind groups.
uint32_t mtlVertexBufferIndex =
ToBackend(GetLayout())->GetBufferBindingCount(ShaderStage::Vertex);
for (uint32_t dawnVertexBufferIndex : IterateBitSet(GetInputsSetMask())) {
const VertexBufferInfo& info = GetInput(dawnVertexBufferIndex);
MTLVertexBufferLayoutDescriptor* layoutDesc = [MTLVertexBufferLayoutDescriptor new];
if (info.stride == 0) {
// For MTLVertexStepFunctionConstant, the stepRate must be 0,
// but the stride must NOT be 0, so we made up it with
// max(attrib.offset + sizeof(attrib) for each attrib)
size_t max_stride = 0;
for (uint32_t attribIndex : IterateBitSet(GetAttributesSetMask())) {
const VertexAttributeInfo& attrib = GetAttribute(attribIndex);
// Only use the attributes that use the current input
if (attrib.inputSlot != dawnVertexBufferIndex) {
continue;
}
max_stride = std::max(max_stride,
VertexFormatSize(attrib.format) + size_t(attrib.offset));
}
layoutDesc.stepFunction = MTLVertexStepFunctionConstant;
layoutDesc.stepRate = 0;
// Metal requires the stride must be a multiple of 4 bytes, align it with next
// multiple of 4 if it's not.
layoutDesc.stride = Align(max_stride, 4);
} else {
layoutDesc.stepFunction = InputStepModeFunction(info.stepMode);
layoutDesc.stepRate = 1;
layoutDesc.stride = info.stride;
}
mtlVertexDescriptor.layouts[mtlVertexBufferIndex] = layoutDesc;
[layoutDesc release];
mMtlVertexBufferIndices[dawnVertexBufferIndex] = mtlVertexBufferIndex;
mtlVertexBufferIndex++;
}
for (uint32_t i : IterateBitSet(GetAttributesSetMask())) {
const VertexAttributeInfo& info = GetAttribute(i);
auto attribDesc = [MTLVertexAttributeDescriptor new];
attribDesc.format = VertexFormatType(info.format);
attribDesc.offset = info.offset;
attribDesc.bufferIndex = mMtlVertexBufferIndices[info.inputSlot];
mtlVertexDescriptor.attributes[i] = attribDesc;
[attribDesc release];
}
return mtlVertexDescriptor;
}
}} // namespace dawn_native::metal