Split Pipeline in Render and Compute, Metal part

This commit is contained in:
Corentin Wallez 2017-07-14 11:26:17 -04:00 committed by Corentin Wallez
parent e20c5ee9ff
commit 494a157f66
12 changed files with 238 additions and 160 deletions

View File

@ -108,16 +108,18 @@ if (NXT_ENABLE_METAL)
${METAL_DIR}/BufferMTL.h
${METAL_DIR}/CommandBufferMTL.mm
${METAL_DIR}/CommandBufferMTL.h
${METAL_DIR}/ComputePipelineMTL.mm
${METAL_DIR}/ComputePipelineMTL.h
${METAL_DIR}/DepthStencilStateMTL.mm
${METAL_DIR}/DepthStencilStateMTL.h
${METAL_DIR}/InputStateMTL.mm
${METAL_DIR}/InputStateMTL.h
${METAL_DIR}/MetalBackend.mm
${METAL_DIR}/MetalBackend.h
${METAL_DIR}/PipelineMTL.mm
${METAL_DIR}/PipelineMTL.h
${METAL_DIR}/PipelineLayoutMTL.mm
${METAL_DIR}/PipelineLayoutMTL.h
${METAL_DIR}/RenderPipelineMTL.mm
${METAL_DIR}/RenderPipelineMTL.h
${METAL_DIR}/ResourceUploader.mm
${METAL_DIR}/ResourceUploader.h
${METAL_DIR}/SamplerMTL.mm

View File

@ -16,11 +16,12 @@
#include "backend/Commands.h"
#include "backend/metal/BufferMTL.h"
#include "backend/metal/ComputePipelineMTL.h"
#include "backend/metal/DepthStencilStateMTL.h"
#include "backend/metal/InputStateMTL.h"
#include "backend/metal/MetalBackend.h"
#include "backend/metal/PipelineMTL.h"
#include "backend/metal/PipelineLayoutMTL.h"
#include "backend/metal/RenderPipelineMTL.h"
#include "backend/metal/SamplerMTL.h"
#include "backend/metal/TextureMTL.h"
@ -146,7 +147,8 @@ namespace metal {
void CommandBuffer::FillCommands(id<MTLCommandBuffer> commandBuffer) {
Command type;
Pipeline* lastPipeline = nullptr;
ComputePipeline* lastComputePipeline = nullptr;
RenderPipeline* lastRenderPipeline = nullptr;
id<MTLBuffer> indexBuffer = nil;
uint32_t indexBufferOffset = 0;
MTLIndexType indexType = MTLIndexTypeUInt32;
@ -267,10 +269,9 @@ namespace metal {
{
DispatchCmd* dispatch = commands.NextCommand<DispatchCmd>();
ASSERT(encoders.compute);
ASSERT(lastPipeline->IsCompute());
[encoders.compute dispatchThreadgroups:MTLSizeMake(dispatch->x, dispatch->y, dispatch->z)
threadsPerThreadgroup: lastPipeline->GetLocalWorkGroupSize()];
threadsPerThreadgroup: lastComputePipeline->GetLocalWorkGroupSize()];
}
break;
@ -326,20 +327,25 @@ namespace metal {
}
break;
case Command::SetPipeline:
case Command::SetComputePipeline:
{
SetPipelineCmd* cmd = commands.NextCommand<SetPipelineCmd>();
lastPipeline = ToBackend(cmd->pipeline).Get();
SetComputePipelineCmd* cmd = commands.NextCommand<SetComputePipelineCmd>();
lastComputePipeline = ToBackend(cmd->pipeline).Get();
if (lastPipeline->IsCompute()) {
ASSERT(encoders.compute);
lastPipeline->Encode(encoders.compute);
} else {
ASSERT(encoders.render);
DepthStencilState* depthStencilState = ToBackend(lastPipeline->GetDepthStencilState());
[encoders.render setDepthStencilState:depthStencilState->GetMTLDepthStencilState()];
lastPipeline->Encode(encoders.render);
}
ASSERT(encoders.compute);
lastComputePipeline->Encode(encoders.compute);
}
break;
case Command::SetRenderPipeline:
{
SetRenderPipelineCmd* cmd = commands.NextCommand<SetRenderPipelineCmd>();
lastRenderPipeline = ToBackend(cmd->pipeline).Get();
ASSERT(encoders.render);
DepthStencilState* depthStencilState = ToBackend(lastRenderPipeline->GetDepthStencilState());
[encoders.render setDepthStencilState:depthStencilState->GetMTLDepthStencilState()];
lastRenderPipeline->Encode(encoders.render);
}
break;
@ -369,12 +375,6 @@ namespace metal {
const auto& layout = group->GetLayout()->GetBindingInfo();
if (lastPipeline->IsCompute()) {
ASSERT(encoders.compute);
} else {
ASSERT(encoders.render);
}
// TODO(kainino@chromium.org): Maintain buffers and offsets arrays in BindGroup so that we
// only have to do one setVertexBuffers and one setFragmentBuffers call here.
for (size_t binding = 0; binding < layout.mask.size(); ++binding) {
@ -390,15 +390,18 @@ namespace metal {
uint32_t fragIndex = 0;
uint32_t computeIndex = 0;
if (vertStage) {
vertIndex = ToBackend(lastPipeline->GetLayout())->
ASSERT(lastRenderPipeline != nullptr);
vertIndex = ToBackend(lastRenderPipeline->GetLayout())->
GetBindingIndexInfo(nxt::ShaderStage::Vertex)[groupIndex][binding];
}
if (fragStage) {
fragIndex = ToBackend(lastPipeline->GetLayout())->
ASSERT(lastRenderPipeline != nullptr);
fragIndex = ToBackend(lastRenderPipeline->GetLayout())->
GetBindingIndexInfo(nxt::ShaderStage::Fragment)[groupIndex][binding];
}
if (computeStage) {
computeIndex = ToBackend(lastPipeline->GetLayout())->
ASSERT(lastComputePipeline != nullptr);
computeIndex = ToBackend(lastComputePipeline->GetLayout())->
GetBindingIndexInfo(nxt::ShaderStage::Compute)[groupIndex][binding];
}

View File

@ -12,27 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef BACKEND_METAL_PIPELINEMTL_H_
#define BACKEND_METAL_PIPELINEMTL_H_
#ifndef BACKEND_METAL_COMPUTEPIPELINEMTL_H_
#define BACKEND_METAL_COMPUTEPIPELINEMTL_H_
#include "backend/Pipeline.h"
#include "backend/ComputePipeline.h"
#import <Metal/Metal.h>
namespace backend {
namespace metal {
class Pipeline : public PipelineBase {
class ComputePipeline : public ComputePipelineBase {
public:
Pipeline(PipelineBuilder* builder);
~Pipeline();
ComputePipeline(ComputePipelineBuilder* builder);
~ComputePipeline();
void Encode(id<MTLRenderCommandEncoder> encoder);
void Encode(id<MTLComputeCommandEncoder> encoder);
MTLSize GetLocalWorkGroupSize() const;
private:
id<MTLRenderPipelineState> mtlRenderPipelineState = nil;
id<MTLComputePipelineState> mtlComputePipelineState = nil;
MTLSize localWorkgroupSize;
};
@ -40,4 +38,4 @@ namespace metal {
}
}
#endif // BACKEND_METAL_PIPELINEMTL_H_
#endif // BACKEND_METAL_COMPUTEPIPELINEMTL_H_

View File

@ -0,0 +1,59 @@
// Copyright 2017 The NXT 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 "backend/metal/ComputePipelineMTL.h"
#include "backend/metal/MetalBackend.h"
#include "backend/metal/ShaderModuleMTL.h"
namespace backend {
namespace metal {
ComputePipeline::ComputePipeline(ComputePipelineBuilder* builder)
: ComputePipelineBase(builder) {
auto mtlDevice = ToBackend(builder->GetDevice())->GetMTLDevice();
const auto& module = ToBackend(builder->GetStageInfo(nxt::ShaderStage::Compute).module);
const auto& entryPoint = builder->GetStageInfo(nxt::ShaderStage::Compute).entryPoint;
id<MTLFunction> function = module->GetFunction(entryPoint.c_str());
NSError *error = nil;
mtlComputePipelineState = [mtlDevice
newComputePipelineStateWithFunction:function error:&error];
if (error != nil) {
NSLog(@" error => %@", error);
builder->HandleError("Error creating pipeline state");
return;
}
// Copy over the local workgroup size as it is passed to dispatch explicitly in Metal
localWorkgroupSize = module->GetLocalWorkGroupSize(entryPoint);
}
ComputePipeline::~ComputePipeline() {
[mtlComputePipelineState release];
}
void ComputePipeline::Encode(id<MTLComputeCommandEncoder> encoder) {
[encoder setComputePipelineState:mtlComputePipelineState];
}
MTLSize ComputePipeline::GetLocalWorkGroupSize() const {
return localWorkgroupSize;
}
}
}

View File

@ -15,10 +15,11 @@
#include "backend/metal/MetalBackend.h"
#include "backend/metal/BufferMTL.h"
#include "backend/metal/CommandBufferMTL.h"
#include "backend/metal/ComputePipelineMTL.h"
#include "backend/metal/DepthStencilStateMTL.h"
#include "backend/metal/InputStateMTL.h"
#include "backend/metal/PipelineMTL.h"
#include "backend/metal/PipelineLayoutMTL.h"
#include "backend/metal/RenderPipelineMTL.h"
#include "backend/metal/SamplerMTL.h"
#include "backend/metal/ShaderModuleMTL.h"
#include "backend/metal/TextureMTL.h"

View File

@ -38,14 +38,15 @@ namespace metal {
class Buffer;
class BufferView;
class CommandBuffer;
class ComputePipeline;
class DepthStencilState;
class Device;
class InputState;
class Framebuffer;
class Pipeline;
class InputState;
class PipelineLayout;
class Queue;
class RenderPass;
class RenderPipeline;
class Sampler;
class ShaderModule;
class Texture;
@ -57,14 +58,15 @@ namespace metal {
using BufferType = Buffer;
using BufferViewType = BufferView;
using CommandBufferType = CommandBuffer;
using ComputePipelineType = ComputePipeline;
using DepthStencilStateType = DepthStencilState;
using DeviceType = Device;
using InputStateType = InputState;
using FramebufferType = Framebuffer;
using PipelineType = Pipeline;
using InputStateType = InputState;
using PipelineLayoutType = PipelineLayout;
using QueueType = Queue;
using RenderPassType = RenderPass;
using RenderPipelineType = RenderPipeline;
using SamplerType = Sampler;
using ShaderModuleType = ShaderModule;
using TextureType = Texture;
@ -89,13 +91,14 @@ namespace metal {
BufferBase* CreateBuffer(BufferBuilder* builder) override;
BufferViewBase* CreateBufferView(BufferViewBuilder* builder) override;
CommandBufferBase* CreateCommandBuffer(CommandBufferBuilder* builder) override;
ComputePipelineBase* CreateComputePipeline(ComputePipelineBuilder* builder) override;
DepthStencilStateBase* CreateDepthStencilState(DepthStencilStateBuilder* builder) override;
InputStateBase* CreateInputState(InputStateBuilder* builder) override;
FramebufferBase* CreateFramebuffer(FramebufferBuilder* builder) override;
PipelineBase* CreatePipeline(PipelineBuilder* builder) override;
PipelineLayoutBase* CreatePipelineLayout(PipelineLayoutBuilder* builder) override;
QueueBase* CreateQueue(QueueBuilder* builder) override;
RenderPassBase* CreateRenderPass(RenderPassBuilder* builder) override;
RenderPipelineBase* CreateRenderPipeline(RenderPipelineBuilder* builder) override;
SamplerBase* CreateSampler(SamplerBuilder* builder) override;
ShaderModuleBase* CreateShaderModule(ShaderModuleBuilder* builder) override;
TextureBase* CreateTexture(TextureBuilder* builder) override;

View File

@ -16,9 +16,10 @@
#include "backend/metal/BufferMTL.h"
#include "backend/metal/CommandBufferMTL.h"
#include "backend/metal/ComputePipelineMTL.h"
#include "backend/metal/DepthStencilStateMTL.h"
#include "backend/metal/InputStateMTL.h"
#include "backend/metal/PipelineMTL.h"
#include "backend/metal/RenderPipelineMTL.h"
#include "backend/metal/PipelineLayoutMTL.h"
#include "backend/metal/ResourceUploader.h"
#include "backend/metal/SamplerMTL.h"
@ -91,17 +92,17 @@ namespace metal {
CommandBufferBase* Device::CreateCommandBuffer(CommandBufferBuilder* builder) {
return new CommandBuffer(builder);
}
ComputePipelineBase* Device::CreateComputePipeline(ComputePipelineBuilder* builder) {
return new ComputePipeline(builder);
}
DepthStencilStateBase* Device::CreateDepthStencilState(DepthStencilStateBuilder* builder) {
return new DepthStencilState(builder);
}
InputStateBase* Device::CreateInputState(InputStateBuilder* builder) {
return new InputState(builder);
}
FramebufferBase* Device::CreateFramebuffer(FramebufferBuilder* builder) {
return new Framebuffer(builder);
}
PipelineBase* Device::CreatePipeline(PipelineBuilder* builder) {
return new Pipeline(builder);
InputStateBase* Device::CreateInputState(InputStateBuilder* builder) {
return new InputState(builder);
}
PipelineLayoutBase* Device::CreatePipelineLayout(PipelineLayoutBuilder* builder) {
return new PipelineLayout(builder);
@ -112,6 +113,9 @@ namespace metal {
RenderPassBase* Device::CreateRenderPass(RenderPassBuilder* builder) {
return new RenderPass(builder);
}
RenderPipelineBase* Device::CreateRenderPipeline(RenderPipelineBuilder* builder) {
return new RenderPipeline(builder);
}
SamplerBase* Device::CreateSampler(SamplerBuilder* builder) {
return new Sampler(builder);
}

View File

@ -1,112 +0,0 @@
// Copyright 2017 The NXT 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 "backend/metal/PipelineMTL.h"
#include "backend/metal/DepthStencilStateMTL.h"
#include "backend/metal/InputStateMTL.h"
#include "backend/metal/MetalBackend.h"
#include "backend/metal/PipelineLayoutMTL.h"
#include "backend/metal/ShaderModuleMTL.h"
namespace backend {
namespace metal {
Pipeline::Pipeline(PipelineBuilder* builder)
: PipelineBase(builder) {
auto mtlDevice = ToBackend(builder->GetDevice())->GetMTLDevice();
if (IsCompute()) {
const auto& module = ToBackend(builder->GetStageInfo(nxt::ShaderStage::Compute).module);
const auto& entryPoint = builder->GetStageInfo(nxt::ShaderStage::Compute).entryPoint;
id<MTLFunction> function = module->GetFunction(entryPoint.c_str());
NSError *error = nil;
mtlComputePipelineState = [mtlDevice
newComputePipelineStateWithFunction:function error:&error];
if (error != nil) {
NSLog(@" error => %@", error);
builder->HandleError("Error creating pipeline state");
return;
}
// Copy over the local workgroup size as it is passed to dispatch explicitly in Metal
localWorkgroupSize = module->GetLocalWorkGroupSize(entryPoint);
} else {
MTLRenderPipelineDescriptor* descriptor = [MTLRenderPipelineDescriptor new];
for (auto stage : IterateStages(GetStageMask())) {
const auto& module = ToBackend(builder->GetStageInfo(stage).module);
const auto& entryPoint = builder->GetStageInfo(stage).entryPoint;
id<MTLFunction> function = module->GetFunction(entryPoint.c_str());
switch (stage) {
case nxt::ShaderStage::Vertex:
descriptor.vertexFunction = function;
break;
case nxt::ShaderStage::Fragment:
descriptor.fragmentFunction = function;
break;
case nxt::ShaderStage::Compute:
UNREACHABLE();
}
}
// TODO(cwallez@chromium.org): get the attachment formats from the subpass
descriptor.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
descriptor.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float;
InputState* inputState = ToBackend(GetInputState());
descriptor.vertexDescriptor = inputState->GetMTLVertexDescriptor();
// TODO(kainino@chromium.org): push constants, textures, samplers
NSError *error = nil;
mtlRenderPipelineState = [mtlDevice
newRenderPipelineStateWithDescriptor:descriptor error:&error];
if (error != nil) {
NSLog(@" error => %@", error);
builder->HandleError("Error creating pipeline state");
return;
}
[descriptor release];
}
}
Pipeline::~Pipeline() {
[mtlRenderPipelineState release];
[mtlComputePipelineState release];
}
void Pipeline::Encode(id<MTLRenderCommandEncoder> encoder) {
ASSERT(!IsCompute());
[encoder setRenderPipelineState:mtlRenderPipelineState];
}
void Pipeline::Encode(id<MTLComputeCommandEncoder> encoder) {
ASSERT(IsCompute());
[encoder setComputePipelineState:mtlComputePipelineState];
}
MTLSize Pipeline::GetLocalWorkGroupSize() const {
return localWorkgroupSize;
}
}
}

View File

@ -0,0 +1,39 @@
// Copyright 2017 The NXT 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.
#ifndef BACKEND_METAL_RENDERPIPELINEMTL_H_
#define BACKEND_METAL_RENDERPIPELINEMTL_H_
#include "backend/RenderPipeline.h"
#import <Metal/Metal.h>
namespace backend {
namespace metal {
class RenderPipeline : public RenderPipelineBase {
public:
RenderPipeline(RenderPipelineBuilder* builder);
~RenderPipeline();
void Encode(id<MTLRenderCommandEncoder> encoder);
private:
id<MTLRenderPipelineState> mtlRenderPipelineState = nil;
};
}
}
#endif // BACKEND_METAL_RENDERPIPELINEMTL_H_

View File

@ -0,0 +1,81 @@
// Copyright 2017 The NXT 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 "backend/metal/RenderPipelineMTL.h"
#include "backend/metal/DepthStencilStateMTL.h"
#include "backend/metal/InputStateMTL.h"
#include "backend/metal/MetalBackend.h"
#include "backend/metal/PipelineLayoutMTL.h"
#include "backend/metal/ShaderModuleMTL.h"
namespace backend {
namespace metal {
RenderPipeline::RenderPipeline(RenderPipelineBuilder* builder)
: RenderPipelineBase(builder) {
auto mtlDevice = ToBackend(builder->GetDevice())->GetMTLDevice();
MTLRenderPipelineDescriptor* descriptor = [MTLRenderPipelineDescriptor new];
for (auto stage : IterateStages(GetStageMask())) {
const auto& module = ToBackend(builder->GetStageInfo(stage).module);
const auto& entryPoint = builder->GetStageInfo(stage).entryPoint;
id<MTLFunction> function = module->GetFunction(entryPoint.c_str());
switch (stage) {
case nxt::ShaderStage::Vertex:
descriptor.vertexFunction = function;
break;
case nxt::ShaderStage::Fragment:
descriptor.fragmentFunction = function;
break;
case nxt::ShaderStage::Compute:
UNREACHABLE();
}
}
// TODO(cwallez@chromium.org): get the attachment formats from the subpass
descriptor.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
descriptor.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float;
InputState* inputState = ToBackend(GetInputState());
descriptor.vertexDescriptor = inputState->GetMTLVertexDescriptor();
// TODO(kainino@chromium.org): push constants, textures, samplers
NSError *error = nil;
mtlRenderPipelineState = [mtlDevice
newRenderPipelineStateWithDescriptor:descriptor error:&error];
if (error != nil) {
NSLog(@" error => %@", error);
builder->HandleError("Error creating pipeline state");
return;
}
[descriptor release];
}
RenderPipeline::~RenderPipeline() {
[mtlRenderPipelineState release];
}
void RenderPipeline::Encode(id<MTLRenderCommandEncoder> encoder) {
[encoder setRenderPipelineState:mtlRenderPipelineState];
}
}
}