blob: 0eb42306d3ce89e535ebd8b1efe5e3091ecf6ddb [file] [log] [blame]
Timothy Liang5422f9a2018-08-10 10:57:55 -04001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrMtlGpuCommandBuffer.h"
9
10#include "GrColor.h"
Ethan Nicholas01063512018-10-08 16:58:25 -040011#include "GrFixedClip.h"
Timothy Liang5422f9a2018-08-10 10:57:55 -040012#include "GrMtlPipelineState.h"
13#include "GrMtlPipelineStateBuilder.h"
14#include "GrMtlRenderTarget.h"
Ethan Nicholas01063512018-10-08 16:58:25 -040015#include "GrRenderTargetPriv.h"
Timothy Liang5422f9a2018-08-10 10:57:55 -040016
17GrMtlGpuRTCommandBuffer::GrMtlGpuRTCommandBuffer(
Ethan Nicholas56d19a52018-10-15 11:26:20 -040018 GrMtlGpu* gpu, GrRenderTarget* rt, GrSurfaceOrigin origin, const SkRect& bounds,
Timothy Liang5422f9a2018-08-10 10:57:55 -040019 const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
20 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo)
21 : INHERITED(rt, origin)
22 , fGpu(gpu)
Ethan Nicholas391d3442018-10-15 13:00:59 -040023#ifdef SK_DEBUG
Ethan Nicholas56d19a52018-10-15 11:26:20 -040024 , fBounds(bounds)
Ethan Nicholas391d3442018-10-15 13:00:59 -040025#endif
Timothy Liang5422f9a2018-08-10 10:57:55 -040026 , fColorLoadAndStoreInfo(colorInfo)
27 , fStencilLoadAndStoreInfo(stencilInfo)
28 , fRenderPassDesc(this->createRenderPassDesc()) {
29 (void)fStencilLoadAndStoreInfo; // Silence unused var warning
Ethan Nicholas01063512018-10-08 16:58:25 -040030 const GrMtlStencilAttachment* stencil = static_cast<GrMtlStencilAttachment*>(
31 rt->renderTargetPriv().getStencilAttachment());
32 if (stencil) {
33 fRenderPassDesc.stencilAttachment.texture = stencil->stencilView();
34 }
Timothy Liang5422f9a2018-08-10 10:57:55 -040035 if (fColorLoadAndStoreInfo.fLoadOp == GrLoadOp::kClear) {
36 fCommandBufferInfo.fBounds = SkRect::MakeWH(fRenderTarget->width(),
37 fRenderTarget->height());
38 this->internalBegin();
39 this->internalEnd();
40 fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionLoad;
41 } else {
42 fCommandBufferInfo.fBounds.setEmpty();
43 }
Ethan Nicholas01063512018-10-08 16:58:25 -040044 switch (stencilInfo.fLoadOp) {
45 case GrLoadOp::kLoad:
46 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
47 break;
48 case GrLoadOp::kClear:
49 fCommandBufferInfo.fBounds = SkRect::MakeWH(fRenderTarget->width(),
50 fRenderTarget->height());
51 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionClear;
52 this->internalBegin();
53 this->internalEnd();
54 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
55 break;
56 case GrLoadOp::kDiscard:
57 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionDontCare;
58 break;
59 }
60 switch (stencilInfo.fStoreOp) {
61 case GrStoreOp::kStore:
62 fRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionStore;
63 break;
64 case GrStoreOp::kDiscard:
65 fRenderPassDesc.stencilAttachment.storeAction = MTLStoreActionDontCare;
66 break;
67 }
Timothy Liang5422f9a2018-08-10 10:57:55 -040068}
69
70GrMtlGpuRTCommandBuffer::~GrMtlGpuRTCommandBuffer() {
71 SkASSERT(fActiveRenderCmdEncoder == nil);
72}
73
74void GrMtlGpuRTCommandBuffer::internalBegin() {
75 SkASSERT(fActiveRenderCmdEncoder == nil);
76 fActiveRenderCmdEncoder =
77 [fGpu->commandBuffer()
78 renderCommandEncoderWithDescriptor: fRenderPassDesc];
79 SkASSERT(fActiveRenderCmdEncoder);
80 [fActiveRenderCmdEncoder setFrontFacingWinding: MTLWindingCounterClockwise];
81}
82
83void GrMtlGpuRTCommandBuffer::internalEnd() {
84 SkASSERT(fActiveRenderCmdEncoder);
85 [fActiveRenderCmdEncoder endEncoding];
86 fActiveRenderCmdEncoder = nil;
87 SkASSERT(fActiveRenderCmdEncoder == nil);
88}
89
90void GrMtlGpuRTCommandBuffer::submit() {
91 if (!fRenderTarget) {
92 return;
93 }
94 SkIRect iBounds;
95 fCommandBufferInfo.fBounds.roundOut(&iBounds);
96 fGpu->submitIndirectCommandBuffer(fRenderTarget, fOrigin, &iBounds);
97}
98
99void GrMtlGpuRTCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin,
100 const SkIRect& srcRect, const SkIPoint& dstPoint) {
101 // We cannot have an active encoder when we call copy since it requires its own
102 // command encoder.
103 SkASSERT(fActiveRenderCmdEncoder == nil);
104 fGpu->copySurface(fRenderTarget, fOrigin, src, srcOrigin, srcRect, dstPoint);
105}
106
107GrMtlPipelineState* GrMtlGpuRTCommandBuffer::prepareDrawState(
108 const GrPrimitiveProcessor& primProc,
109 const GrPipeline& pipeline,
110 const GrPipeline::FixedDynamicState* fixedDynamicState,
111 const GrMesh meshes[],
112 int meshCount) {
113 // TODO: resolve textures and regenerate mipmaps as needed
114 bool hasPoints = false;
115 for (int i = 0; i < meshCount; ++i) {
116 if (meshes[i].primitiveType() == GrPrimitiveType::kPoints) {
117 hasPoints = true;
118 break;
119 }
120 }
121 GrProgramDesc desc;
Greg Daniel7a82edf2018-12-04 10:54:34 -0500122 if (!GrProgramDesc::Build(&desc, primProc, hasPoints, pipeline, fGpu)) {
Timothy Liang5422f9a2018-08-10 10:57:55 -0400123 return nullptr;
124 }
125 desc.finalize();
126
Timothy Liang5422f9a2018-08-10 10:57:55 -0400127 const GrTextureProxy* const* primProcProxies = nullptr;
128 if (fixedDynamicState) {
129 primProcProxies = fixedDynamicState->fPrimitiveProcessorTextures;
130 }
Greg Daniel9a51a862018-11-30 10:18:14 -0500131 SkASSERT(SkToBool(primProcProxies) == SkToBool(primProc.numTextureSamplers()));
Timothy Liang5422f9a2018-08-10 10:57:55 -0400132
Greg Daniel9a51a862018-11-30 10:18:14 -0500133 // TODO: use resource provider for pipeline
134 GrMtlPipelineState* pipelineState =
135 GrMtlPipelineStateBuilder::CreatePipelineState(primProc, primProcProxies, pipeline,
136 &desc, fGpu);
137 if (!pipelineState) {
138 return nullptr;
139 }
Timothy Liang5422f9a2018-08-10 10:57:55 -0400140 // We cannot have an active encoder when we set the pipeline data since it requires its own
141 // command encoder.
142 SkASSERT(fActiveRenderCmdEncoder == nil);
143 pipelineState->setData(primProc, pipeline, primProcProxies);
144
145 return pipelineState;
146}
147
148void GrMtlGpuRTCommandBuffer::onDraw(const GrPrimitiveProcessor& primProc,
149 const GrPipeline& pipeline,
150 const GrPipeline::FixedDynamicState* fixedDynamicState,
151 const GrPipeline::DynamicStateArrays* dynamicStateArrays,
152 const GrMesh meshes[],
153 int meshCount,
154 const SkRect& bounds) {
155 SkASSERT(pipeline.renderTarget() == fRenderTarget);
156 if (!meshCount) {
157 return;
158 }
159 if (pipeline.isScissorEnabled()) {
160 return; // TODO: ScissorRects are not supported.
161 }
162
163 std::unique_ptr<GrMtlPipelineState> pipelineState(
164 this->prepareDrawState(primProc, pipeline, fixedDynamicState, meshes, meshCount));
165 if (!pipelineState) {
166 return;
167 }
168
169 this->internalBegin();
170 [fActiveRenderCmdEncoder setRenderPipelineState: pipelineState->mtlPipelineState()];
171
172 pipelineState->bind(fActiveRenderCmdEncoder);
Timothy Liangde0be802018-08-10 13:48:08 -0400173 pipelineState->setBlendConstants(fActiveRenderCmdEncoder, fRenderTarget->config(),
174 pipeline.getXferProcessor());
Ethan Nicholas01063512018-10-08 16:58:25 -0400175 pipelineState->setDepthStencilState(fActiveRenderCmdEncoder);
Timothy Liang5422f9a2018-08-10 10:57:55 -0400176
177 for (int i = 0; i < meshCount; ++i) {
178 const GrMesh& mesh = meshes[i];
179 SkASSERT(fActiveRenderCmdEncoder);
180 mesh.sendToGpu(this);
181 }
182 this->internalEnd();
183 fCommandBufferInfo.fBounds.join(bounds);
184}
185
Brian Osman9a9baae2018-11-05 15:06:26 -0500186void GrMtlGpuRTCommandBuffer::onClear(const GrFixedClip& clip, const SkPMColor4f& color) {
Ethan Nicholas56d19a52018-10-15 11:26:20 -0400187 // if we end up here from absClear, the clear bounds may be bigger than the RT proxy bounds -
188 // but in that case, scissor should be enabled, so this check should still succeed
189 SkASSERT(!clip.scissorEnabled() || clip.scissorRect().contains(fBounds));
Brian Osman9a9baae2018-11-05 15:06:26 -0500190 fRenderPassDesc.colorAttachments[0].clearColor = MTLClearColorMake(color.fR, color.fG, color.fB,
191 color.fA);
Ethan Nicholas56d19a52018-10-15 11:26:20 -0400192 fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
193 this->internalBegin();
194 this->internalEnd();
195 fRenderPassDesc.colorAttachments[0].loadAction = MTLLoadActionLoad;
196}
197
Ethan Nicholas01063512018-10-08 16:58:25 -0400198void GrMtlGpuRTCommandBuffer::onClearStencilClip(const GrFixedClip& clip, bool insideStencilMask) {
199 SkASSERT(!clip.hasWindowRectangles());
200
201 GrStencilAttachment* sb = fRenderTarget->renderTargetPriv().getStencilAttachment();
202 // this should only be called internally when we know we have a
203 // stencil buffer.
204 SkASSERT(sb);
205 int stencilBitCount = sb->bits();
206
207 // The contract with the callers does not guarantee that we preserve all bits in the stencil
208 // during this clear. Thus we will clear the entire stencil to the desired value.
209 if (insideStencilMask) {
210 fRenderPassDesc.stencilAttachment.clearStencil = (1 << (stencilBitCount - 1));
211 } else {
212 fRenderPassDesc.stencilAttachment.clearStencil = 0;
213 }
214
215 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionClear;
216 this->internalBegin();
217 this->internalEnd();
218 fRenderPassDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
219}
220
Timothy Liang5422f9a2018-08-10 10:57:55 -0400221MTLRenderPassDescriptor* GrMtlGpuRTCommandBuffer::createRenderPassDesc() const {
222 const static MTLLoadAction mtlLoadAction[] {
223 MTLLoadActionLoad,
224 MTLLoadActionClear,
225 MTLLoadActionDontCare
226 };
227 GR_STATIC_ASSERT((int)GrLoadOp::kLoad == 0);
228 GR_STATIC_ASSERT((int)GrLoadOp::kClear == 1);
229 GR_STATIC_ASSERT((int)GrLoadOp::kDiscard == 2);
230 SkASSERT(fColorLoadAndStoreInfo.fLoadOp <= GrLoadOp::kDiscard);
231
232 const static MTLStoreAction mtlStoreAction[] {
233 MTLStoreActionStore,
234 MTLStoreActionDontCare
235 };
236 GR_STATIC_ASSERT((int)GrStoreOp::kStore == 0);
237 GR_STATIC_ASSERT((int)GrStoreOp::kDiscard == 1);
238 SkASSERT(fColorLoadAndStoreInfo.fStoreOp <= GrStoreOp::kDiscard);
239
240 auto renderPassDesc = [[MTLRenderPassDescriptor alloc] init];
241 renderPassDesc.colorAttachments[0].texture =
242 static_cast<GrMtlRenderTarget*>(fRenderTarget)->mtlRenderTexture();
243 renderPassDesc.colorAttachments[0].slice = 0;
244 renderPassDesc.colorAttachments[0].level = 0;
Brian Osman9a9baae2018-11-05 15:06:26 -0500245 const SkPMColor4f& clearColor = fColorLoadAndStoreInfo.fClearColor;
Timothy Liang5422f9a2018-08-10 10:57:55 -0400246 renderPassDesc.colorAttachments[0].clearColor =
247 MTLClearColorMake(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
248 renderPassDesc.colorAttachments[0].loadAction =
249 mtlLoadAction[static_cast<int>(fColorLoadAndStoreInfo.fLoadOp)];
250 renderPassDesc.colorAttachments[0].storeAction =
251 mtlStoreAction[static_cast<int>(fColorLoadAndStoreInfo.fStoreOp)];
252 return renderPassDesc;
253}
254
255static MTLPrimitiveType gr_to_mtl_primitive(GrPrimitiveType primitiveType) {
256 const static MTLPrimitiveType mtlPrimitiveType[] {
257 MTLPrimitiveTypeTriangle,
258 MTLPrimitiveTypeTriangleStrip,
259 MTLPrimitiveTypePoint,
260 MTLPrimitiveTypeLine,
261 MTLPrimitiveTypeLineStrip
262 };
263 GR_STATIC_ASSERT((int)GrPrimitiveType::kTriangles == 0);
264 GR_STATIC_ASSERT((int)GrPrimitiveType::kTriangleStrip == 1);
265 GR_STATIC_ASSERT((int)GrPrimitiveType::kPoints == 2);
266 GR_STATIC_ASSERT((int)GrPrimitiveType::kLines == 3);
267 GR_STATIC_ASSERT((int)GrPrimitiveType::kLineStrip == 4);
268
269 SkASSERT(primitiveType <= GrPrimitiveType::kLineStrip);
270 return mtlPrimitiveType[static_cast<int>(primitiveType)];
271}
272
273void GrMtlGpuRTCommandBuffer::bindGeometry(const GrBuffer* vertexBuffer,
274 const GrBuffer* instanceBuffer) {
275 size_t bufferIndex = GrMtlUniformHandler::kLastUniformBinding + 1;
276 if (vertexBuffer) {
277 SkASSERT(!vertexBuffer->isCPUBacked());
278 SkASSERT(!vertexBuffer->isMapped());
279
280 auto mtlVertexBuffer = static_cast<const GrMtlBuffer*>(vertexBuffer)->mtlBuffer();
281 SkASSERT(mtlVertexBuffer);
282 [fActiveRenderCmdEncoder setVertexBuffer: mtlVertexBuffer
283 offset: 0
284 atIndex: bufferIndex++];
285 }
286 if (instanceBuffer) {
287 SkASSERT(!instanceBuffer->isCPUBacked());
288 SkASSERT(!instanceBuffer->isMapped());
289
290 auto mtlInstanceBuffer = static_cast<const GrMtlBuffer*>(instanceBuffer)->mtlBuffer();
291 SkASSERT(mtlInstanceBuffer);
292 [fActiveRenderCmdEncoder setVertexBuffer: mtlInstanceBuffer
293 offset: 0
294 atIndex: bufferIndex++];
295 }
296}
297
298void GrMtlGpuRTCommandBuffer::sendInstancedMeshToGpu(GrPrimitiveType primitiveType,
299 const GrBuffer* vertexBuffer,
300 int vertexCount,
301 int baseVertex,
302 const GrBuffer* instanceBuffer,
303 int instanceCount,
304 int baseInstance) {
305 this->bindGeometry(vertexBuffer, instanceBuffer);
306
307 SkASSERT(primitiveType != GrPrimitiveType::kLinesAdjacency); // Geometry shaders not supported.
308 [fActiveRenderCmdEncoder drawPrimitives: gr_to_mtl_primitive(primitiveType)
309 vertexStart: baseVertex
310 vertexCount: vertexCount
311 instanceCount: instanceCount
312 baseInstance: baseInstance];
313}
314
315void GrMtlGpuRTCommandBuffer::sendIndexedInstancedMeshToGpu(GrPrimitiveType primitiveType,
316 const GrBuffer* indexBuffer,
317 int indexCount,
318 int baseIndex,
319 const GrBuffer* vertexBuffer,
320 int baseVertex,
321 const GrBuffer* instanceBuffer,
322 int instanceCount,
323 int baseInstance,
324 GrPrimitiveRestart restart) {
325 this->bindGeometry(vertexBuffer, instanceBuffer);
326
327 SkASSERT(primitiveType != GrPrimitiveType::kLinesAdjacency); // Geometry shaders not supported.
328 id<MTLBuffer> mtlIndexBuffer;
329 if (indexBuffer) {
330 SkASSERT(!indexBuffer->isCPUBacked());
331 SkASSERT(!indexBuffer->isMapped());
332
333 mtlIndexBuffer = static_cast<const GrMtlBuffer*>(indexBuffer)->mtlBuffer();
334 SkASSERT(mtlIndexBuffer);
335 }
336
337 SkASSERT(restart == GrPrimitiveRestart::kNo);
338 [fActiveRenderCmdEncoder drawIndexedPrimitives: gr_to_mtl_primitive(primitiveType)
339 indexCount: indexCount
340 indexType: MTLIndexTypeUInt16
341 indexBuffer: mtlIndexBuffer
342 indexBufferOffset: sizeof(uint16_t) * baseIndex
343 instanceCount: instanceCount
344 baseVertex: baseVertex
345 baseInstance: baseInstance];
346}