blob: 2749245ffc039a69e919dd401d14f82818edf0f3 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "experimental/graphite/src/mtl/MtlRenderPipeline.h"
#include "experimental/graphite/src/RenderPipelineDesc.h"
#include "experimental/graphite/src/mtl/MtlGpu.h"
#include "experimental/graphite/src/mtl/MtlUtils.h"
#include "include/private/SkSLString.h"
namespace skgpu::mtl {
static const char* kTestingOnlyShaders[] = {
// clear viewport to blue
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"using namespace metal;\n"
"\n"
"typedef struct {\n"
" float4 position [[position]];\n"
"} VertexOutput;\n"
"\n"
"vertex VertexOutput vertexMain(uint vertexID [[vertex_id]]) {\n"
" VertexOutput out;\n"
" float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
" out.position.xy = position * 2 - 1;\n"
" out.position.zw = float2(0.0, 1.0);\n"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentMain(VertexOutput in [[stage_in]]) {\n"
" return float4(0.0, 0.0, 1.0, 1.0);\n"
"}",
// clear subarea to given color, using uniform buffer
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"using namespace metal;\n"
"\n"
"typedef struct {\n"
" float4 position [[position]];\n"
"} VertexOutput;\n"
"\n"
"typedef struct {\n"
" float4 uPosXform;\n"
" float4 uColor;\n"
"} UniformData;\n"
"\n"
"vertex VertexOutput vertexMain(constant UniformData& uniforms [[buffer(0)]],\n"
" uint vertexID [[vertex_id]]) {\n"
" VertexOutput out;\n"
" float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
" out.position.xy = position * uniforms.uPosXform.xy + uniforms.uPosXform.zw;\n"
" out.position.zw = float2(0.0, 1.0);\n"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentMain(constant UniformData& uniforms [[buffer(0)]],\n"
" VertexOutput in [[stage_in]]) {\n"
" return uniforms.uColor;\n"
"}",
// draw triangles with given color, using uniform buffer and vertex data
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"using namespace metal;\n"
"\n"
"typedef struct {\n"
" float2 position [[attribute(0)]];\n"
"} VertexInput;\n"
"\n"
"typedef struct {\n"
" float4 position [[position]];\n"
"} VertexOutput;\n"
"\n"
"typedef struct {\n"
" float4 uPosXform;\n"
" float4 uColor;\n"
"} UniformData;\n"
"\n"
"vertex VertexOutput vertexMain(VertexInput in [[stage_in]],\n"
" constant UniformData& uniforms [[buffer(0)]],\n"
" uint vertexID [[vertex_id]]) {\n"
" VertexOutput out;\n"
" float2 position = in.position;\n"
" out.position.xy = position * uniforms.uPosXform.xy + uniforms.uPosXform.zw;\n"
" out.position.zw = float2(0.0, 1.0);\n"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentMain(constant UniformData& uniforms [[buffer(0)]],\n"
" VertexOutput in [[stage_in]]) {\n"
" return uniforms.uColor;\n"
"}",
// draw triangles with vertex ID and instance buffer
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"using namespace metal;\n"
"\n"
"typedef struct {\n"
" float2 position [[attribute(0)]];\n"
" float2 dims [[attribute(1)]];\n"
" float4 color [[attribute(2)]];\n"
"} InstanceInput;\n"
"\n"
"typedef struct {\n"
" float4 position [[position]];\n"
" float4 color;\n"
"} VertexOutput;\n"
"\n"
"vertex VertexOutput vertexMain(InstanceInput in [[stage_in]],\n"
" uint vertexID [[vertex_id]]) {\n"
" VertexOutput out;\n"
" float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
" out.position.xy = position * in.dims + in.position;\n"
" out.position.zw = float2(0.0, 1.0);\n"
" out.color = in.color;"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentMain(VertexOutput in [[stage_in]]) {\n"
" return in.color;\n"
"}",
};
static constexpr NSString* kTestingOnlyShaderLabels[] = {
@"Clear viewport to blue",
@"Clear rect with uniforms",
@"Draw triangles with uniform color",
@"Draw triangles with instance buffer"
};
static inline MTLVertexFormat attribute_type_to_mtlformat(VertexAttribType type) {
switch (type) {
case VertexAttribType::kFloat:
return MTLVertexFormatFloat;
case VertexAttribType::kFloat2:
return MTLVertexFormatFloat2;
case VertexAttribType::kFloat3:
return MTLVertexFormatFloat3;
case VertexAttribType::kFloat4:
return MTLVertexFormatFloat4;
case VertexAttribType::kHalf:
if (@available(macOS 10.13, iOS 11.0, *)) {
return MTLVertexFormatHalf;
} else {
return MTLVertexFormatInvalid;
}
case VertexAttribType::kHalf2:
return MTLVertexFormatHalf2;
case VertexAttribType::kHalf4:
return MTLVertexFormatHalf4;
case VertexAttribType::kInt2:
return MTLVertexFormatInt2;
case VertexAttribType::kInt3:
return MTLVertexFormatInt3;
case VertexAttribType::kInt4:
return MTLVertexFormatInt4;
case VertexAttribType::kByte:
if (@available(macOS 10.13, iOS 11.0, *)) {
return MTLVertexFormatChar;
} else {
return MTLVertexFormatInvalid;
}
case VertexAttribType::kByte2:
return MTLVertexFormatChar2;
case VertexAttribType::kByte4:
return MTLVertexFormatChar4;
case VertexAttribType::kUByte:
if (@available(macOS 10.13, iOS 11.0, *)) {
return MTLVertexFormatUChar;
} else {
return MTLVertexFormatInvalid;
}
case VertexAttribType::kUByte2:
return MTLVertexFormatUChar2;
case VertexAttribType::kUByte4:
return MTLVertexFormatUChar4;
case VertexAttribType::kUByte_norm:
if (@available(macOS 10.13, iOS 11.0, *)) {
return MTLVertexFormatUCharNormalized;
} else {
return MTLVertexFormatInvalid;
}
case VertexAttribType::kUByte4_norm:
return MTLVertexFormatUChar4Normalized;
case VertexAttribType::kShort2:
return MTLVertexFormatShort2;
case VertexAttribType::kShort4:
return MTLVertexFormatShort4;
case VertexAttribType::kUShort2:
return MTLVertexFormatUShort2;
case VertexAttribType::kUShort2_norm:
return MTLVertexFormatUShort2Normalized;
case VertexAttribType::kInt:
return MTLVertexFormatInt;
case VertexAttribType::kUInt:
return MTLVertexFormatUInt;
case VertexAttribType::kUShort_norm:
if (@available(macOS 10.13, iOS 11.0, *)) {
return MTLVertexFormatUShortNormalized;
} else {
return MTLVertexFormatInvalid;
}
case VertexAttribType::kUShort4_norm:
return MTLVertexFormatUShort4Normalized;
}
SK_ABORT("Unknown vertex attribute type");
}
static MTLVertexDescriptor* create_vertex_descriptor(const RenderPipelineDesc& desc) {
auto vertexDescriptor = [[MTLVertexDescriptor alloc] init];
int attributeIndex = 0;
int vertexAttributeCount = desc.numVertexAttributes();
size_t vertexAttributeOffset = 0;
for (const auto& attribute : desc.vertexAttributes()) {
MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex];
MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType());
SkASSERT(MTLVertexFormatInvalid != format);
mtlAttribute.format = format;
mtlAttribute.offset = vertexAttributeOffset;
mtlAttribute.bufferIndex = RenderPipeline::kVertexBufferIndex;
vertexAttributeOffset += attribute.sizeAlign4();
attributeIndex++;
}
SkASSERT(vertexAttributeOffset == desc.vertexStride());
if (vertexAttributeCount) {
MTLVertexBufferLayoutDescriptor* vertexBufferLayout =
vertexDescriptor.layouts[RenderPipeline::kVertexBufferIndex];
vertexBufferLayout.stepFunction = MTLVertexStepFunctionPerVertex;
vertexBufferLayout.stepRate = 1;
vertexBufferLayout.stride = vertexAttributeOffset;
}
int instanceAttributeCount = desc.numInstanceAttributes();
size_t instanceAttributeOffset = 0;
for (const auto& attribute : desc.instanceAttributes()) {
MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex];
MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType());
SkASSERT(MTLVertexFormatInvalid != format);
mtlAttribute.format = format;
mtlAttribute.offset = instanceAttributeOffset;
mtlAttribute.bufferIndex = RenderPipeline::kInstanceBufferIndex;
instanceAttributeOffset += attribute.sizeAlign4();
attributeIndex++;
}
SkASSERT(instanceAttributeOffset == desc.instanceStride());
if (instanceAttributeCount) {
MTLVertexBufferLayoutDescriptor* instanceBufferLayout =
vertexDescriptor.layouts[RenderPipeline::kInstanceBufferIndex];
instanceBufferLayout.stepFunction = MTLVertexStepFunctionPerInstance;
instanceBufferLayout.stepRate = 1;
instanceBufferLayout.stride = instanceAttributeOffset;
}
return vertexDescriptor;
}
sk_sp<RenderPipeline> RenderPipeline::Make(const Gpu* gpu, const skgpu::RenderPipelineDesc& desc) {
sk_cfp<MTLRenderPipelineDescriptor*> psoDescriptor([[MTLRenderPipelineDescriptor alloc] init]);
// Temp pipeline for now that just fills the viewport with blue
int shaderIndex = desc.testingOnlyShaderIndex();
SkSL::String shaderText;
shaderText.append(kTestingOnlyShaders[shaderIndex]);
auto metallib = CompileShaderLibrary(gpu, shaderText);
if (!metallib) {
return nullptr;
}
(*psoDescriptor).label = kTestingOnlyShaderLabels[shaderIndex];
(*psoDescriptor).vertexFunction =
[*metallib newFunctionWithName: @"vertexMain"];
(*psoDescriptor).fragmentFunction =
[*metallib newFunctionWithName: @"fragmentMain"];
// TODO: I *think* this gets cleaned up by the pipelineDescriptor?
(*psoDescriptor).vertexDescriptor = create_vertex_descriptor(desc);
// TODO: I *think* this gets cleaned up by the pipelineDescriptor as well?
auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init];
mtlColorAttachment.pixelFormat = MTLPixelFormatRGBA8Unorm;
mtlColorAttachment.blendingEnabled = FALSE;
mtlColorAttachment.writeMask = MTLColorWriteMaskAll;
(*psoDescriptor).colorAttachments[0] = mtlColorAttachment;
(*psoDescriptor).sampleCount = 1;
NSError* error;
sk_cfp<id<MTLRenderPipelineState>> pso(
[gpu->device() newRenderPipelineStateWithDescriptor:psoDescriptor.get()
error:&error]);
if (!pso) {
SkDebugf("Pipeline creation failure\n");
SkDebugf("Errors:\n%s", error.debugDescription.UTF8String);
return nullptr;
}
return sk_sp<RenderPipeline>(new RenderPipeline(std::move(pso), desc.vertexStride(),
desc.instanceStride()));
}
} // namespace skgpu::mtl