| // | |
| // Copyright 2019 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // mtl_render_utils.mm: | |
| // Implements the class methods for RenderUtils. | |
| // | |
| #include "libANGLE/renderer/metal/mtl_render_utils.h" | |
| #include <utility> | |
| #include "common/debug.h" | |
| #include "libANGLE/renderer/metal/BufferMtl.h" | |
| #include "libANGLE/renderer/metal/ContextMtl.h" | |
| #include "libANGLE/renderer/metal/DisplayMtl.h" | |
| #include "libANGLE/renderer/metal/mtl_common.h" | |
| #include "libANGLE/renderer/metal/mtl_utils.h" | |
| #include "libANGLE/renderer/metal/shaders/compiled/mtl_default_shaders.inc" | |
| #include "libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc" | |
| namespace rx | |
| { | |
| namespace mtl | |
| { | |
| namespace | |
| { | |
| #define SOURCE_BUFFER_ALIGNED_CONSTANT_NAME @"kSourceBufferAligned" | |
| #define SOURCE_IDX_IS_U8_CONSTANT_NAME @"kSourceIndexIsU8" | |
| #define SOURCE_IDX_IS_U16_CONSTANT_NAME @"kSourceIndexIsU16" | |
| #define SOURCE_IDX_IS_U32_CONSTANT_NAME @"kSourceIndexIsU32" | |
| struct ClearParamsUniform | |
| { | |
| float clearColor[4]; | |
| float clearDepth; | |
| float padding[3]; | |
| }; | |
| struct BlitParamsUniform | |
| { | |
| // 0: lower left, 1: lower right, 2: upper left, 3: upper right | |
| float srcTexCoords[4][2]; | |
| int srcLevel = 0; | |
| uint8_t srcLuminance = 0; // source texture is luminance texture | |
| uint8_t dstFlipY = 0; | |
| uint8_t dstLuminance = 0; // dest texture is luminace | |
| uint8_t padding1; | |
| float padding2[2]; | |
| }; | |
| struct IndexConversionUniform | |
| { | |
| uint32_t srcOffset; | |
| uint32_t indexCount; | |
| uint32_t padding[2]; | |
| }; | |
| template <typename T> | |
| angle::Result GenTriFanFromClientElements(ContextMtl *contextMtl, | |
| GLsizei count, | |
| const T *indices, | |
| const BufferRef &dstBuffer, | |
| uint32_t dstOffset) | |
| { | |
| ASSERT(count > 2); | |
| uint32_t *dstPtr = reinterpret_cast<uint32_t *>(dstBuffer->map(contextMtl) + dstOffset); | |
| T firstIdx; | |
| memcpy(&firstIdx, indices, sizeof(firstIdx)); | |
| for (GLsizei i = 2; i < count; ++i) | |
| { | |
| T srcPrevIdx, srcIdx; | |
| memcpy(&srcPrevIdx, indices + i - 1, sizeof(srcPrevIdx)); | |
| memcpy(&srcIdx, indices + i, sizeof(srcIdx)); | |
| uint32_t triIndices[3]; | |
| triIndices[0] = firstIdx; | |
| triIndices[1] = srcPrevIdx; | |
| triIndices[2] = srcIdx; | |
| memcpy(dstPtr + 3 * (i - 2), triIndices, sizeof(triIndices)); | |
| } | |
| dstBuffer->unmap(contextMtl); | |
| return angle::Result::Continue; | |
| } | |
| template <typename T> | |
| void GetFirstLastIndicesFromClientElements(GLsizei count, | |
| const T *indices, | |
| uint32_t *firstOut, | |
| uint32_t *lastOut) | |
| { | |
| *firstOut = 0; | |
| *lastOut = 0; | |
| memcpy(firstOut, indices, sizeof(indices[0])); | |
| memcpy(lastOut, indices + count - 1, sizeof(indices[0])); | |
| } | |
| } // namespace | |
| bool RenderUtils::IndexConvesionPipelineCacheKey::operator==( | |
| const IndexConvesionPipelineCacheKey &other) const | |
| { | |
| return srcType == other.srcType && srcBufferOffsetAligned == other.srcBufferOffsetAligned; | |
| } | |
| bool RenderUtils::IndexConvesionPipelineCacheKey::operator<( | |
| const IndexConvesionPipelineCacheKey &other) const | |
| { | |
| if (!srcBufferOffsetAligned && other.srcBufferOffsetAligned) | |
| { | |
| return true; | |
| } | |
| if (srcBufferOffsetAligned && !other.srcBufferOffsetAligned) | |
| { | |
| return false; | |
| } | |
| return static_cast<int>(srcType) < static_cast<int>(other.srcType); | |
| } | |
| RenderUtils::RenderUtils(DisplayMtl *display) : Context(display) {} | |
| RenderUtils::~RenderUtils() {} | |
| angle::Result RenderUtils::initialize() | |
| { | |
| auto re = initShaderLibrary(); | |
| if (re != angle::Result::Continue) | |
| { | |
| return re; | |
| } | |
| initClearResources(); | |
| initBlitResources(); | |
| return angle::Result::Continue; | |
| } | |
| void RenderUtils::onDestroy() | |
| { | |
| mDefaultShaders = nil; | |
| mClearRenderPipelineCache.clear(); | |
| mBlitRenderPipelineCache.clear(); | |
| mBlitPremultiplyAlphaRenderPipelineCache.clear(); | |
| mBlitUnmultiplyAlphaRenderPipelineCache.clear(); | |
| mIndexConversionPipelineCaches.clear(); | |
| mTriFanFromElemArrayGeneratorPipelineCaches.clear(); | |
| mTriFanFromArraysGeneratorPipeline = nil; | |
| } | |
| // override ErrorHandler | |
| void RenderUtils::handleError(GLenum glErrorCode, | |
| const char *file, | |
| const char *function, | |
| unsigned int line) | |
| { | |
| ERR() << "Metal backend encountered an internal error. Code=" << glErrorCode << "."; | |
| } | |
| void RenderUtils::handleError(NSError *nserror, | |
| const char *file, | |
| const char *function, | |
| unsigned int line) | |
| { | |
| if (!nserror) | |
| { | |
| return; | |
| } | |
| std::stringstream errorStream; | |
| ERR() << "Metal backend encountered an internal error: \n" | |
| << nserror.localizedDescription.UTF8String; | |
| } | |
| angle::Result RenderUtils::initShaderLibrary() | |
| { | |
| AutoObjCObj<NSError> err = nil; | |
| #if defined(ANGLE_MTL_DEBUG_INTERNAL_SHADERS) | |
| mDefaultShaders = CreateShaderLibrary(getDisplay()->getMetalDevice(), default_metallib_src, | |
| sizeof(default_metallib_src), &err); | |
| #else | |
| mDefaultShaders = | |
| CreateShaderLibraryFromBinary(getDisplay()->getMetalDevice(), compiled_default_metallib, | |
| compiled_default_metallib_len, &err); | |
| #endif | |
| if (err && !mDefaultShaders) | |
| { | |
| ANGLE_MTL_CHECK(this, false, err.get()); | |
| return angle::Result::Stop; | |
| } | |
| return angle::Result::Continue; | |
| } | |
| void RenderUtils::initClearResources() | |
| { | |
| ANGLE_MTL_OBJC_SCOPE | |
| { | |
| // Shader pipeline | |
| mClearRenderPipelineCache.setVertexShader( | |
| this, [[mDefaultShaders.get() newFunctionWithName:@"clearVS"] ANGLE_MTL_AUTORELEASE]); | |
| mClearRenderPipelineCache.setFragmentShader( | |
| this, [[mDefaultShaders.get() newFunctionWithName:@"clearFS"] ANGLE_MTL_AUTORELEASE]); | |
| } | |
| } | |
| void RenderUtils::initBlitResources() | |
| { | |
| ANGLE_MTL_OBJC_SCOPE | |
| { | |
| auto shaderLib = mDefaultShaders.get(); | |
| auto vertexShader = [[shaderLib newFunctionWithName:@"blitVS"] ANGLE_MTL_AUTORELEASE]; | |
| mBlitRenderPipelineCache.setVertexShader(this, vertexShader); | |
| mBlitRenderPipelineCache.setFragmentShader( | |
| this, [[shaderLib newFunctionWithName:@"blitFS"] ANGLE_MTL_AUTORELEASE]); | |
| mBlitPremultiplyAlphaRenderPipelineCache.setVertexShader(this, vertexShader); | |
| mBlitPremultiplyAlphaRenderPipelineCache.setFragmentShader( | |
| this, | |
| [[shaderLib newFunctionWithName:@"blitPremultiplyAlphaFS"] ANGLE_MTL_AUTORELEASE]); | |
| mBlitUnmultiplyAlphaRenderPipelineCache.setVertexShader(this, vertexShader); | |
| mBlitUnmultiplyAlphaRenderPipelineCache.setFragmentShader( | |
| this, [[shaderLib newFunctionWithName:@"blitUnmultiplyAlphaFS"] ANGLE_MTL_AUTORELEASE]); | |
| } | |
| } | |
| void RenderUtils::clearWithDraw(const gl::Context *context, | |
| RenderCommandEncoder *cmdEncoder, | |
| const ClearRectParams ¶ms) | |
| { | |
| auto overridedParams = params; | |
| // Make sure we don't clear attachment that doesn't exist | |
| const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc(); | |
| if (renderPassDesc.numColorAttachments == 0) | |
| { | |
| overridedParams.clearColor.reset(); | |
| } | |
| if (!renderPassDesc.depthAttachment.texture) | |
| { | |
| overridedParams.clearDepth.reset(); | |
| } | |
| if (!renderPassDesc.stencilAttachment.texture) | |
| { | |
| overridedParams.clearStencil.reset(); | |
| } | |
| if (!overridedParams.clearColor.valid() && !overridedParams.clearDepth.valid() && | |
| !overridedParams.clearStencil.valid()) | |
| { | |
| return; | |
| } | |
| setupClearWithDraw(context, cmdEncoder, overridedParams); | |
| // Draw the screen aligned quad | |
| cmdEncoder->draw(MTLPrimitiveTypeTriangle, 0, 6); | |
| // Invalidate current context's state | |
| auto contextMtl = GetImpl(context); | |
| contextMtl->invalidateState(context); | |
| } | |
| void RenderUtils::blitWithDraw(const gl::Context *context, | |
| RenderCommandEncoder *cmdEncoder, | |
| const BlitParams ¶ms) | |
| { | |
| if (!params.src) | |
| { | |
| return; | |
| } | |
| setupBlitWithDraw(context, cmdEncoder, params); | |
| // Draw the screen aligned quad | |
| cmdEncoder->draw(MTLPrimitiveTypeTriangle, 0, 6); | |
| // Invalidate current context's state | |
| ContextMtl *contextMtl = GetImpl(context); | |
| contextMtl->invalidateState(context); | |
| } | |
| void RenderUtils::setupClearWithDraw(const gl::Context *context, | |
| RenderCommandEncoder *cmdEncoder, | |
| const ClearRectParams ¶ms) | |
| { | |
| // Generate render pipeline state | |
| auto renderPipelineState = getClearRenderPipelineState(context, cmdEncoder, params); | |
| ASSERT(renderPipelineState); | |
| // Setup states | |
| setupDrawCommonStates(cmdEncoder); | |
| cmdEncoder->setRenderPipelineState(renderPipelineState); | |
| id<MTLDepthStencilState> dsState = getClearDepthStencilState(context, params); | |
| cmdEncoder->setDepthStencilState(dsState).setStencilRefVal(params.clearStencil.value()); | |
| // Viewports | |
| const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc(); | |
| MTLViewport viewport; | |
| MTLScissorRect scissorRect; | |
| RenderPassAttachmentDesc renderPassAttachment; | |
| if (renderPassDesc.numColorAttachments) | |
| { | |
| renderPassAttachment = renderPassDesc.colorAttachments[0]; | |
| } | |
| else if (renderPassDesc.depthAttachment.texture) | |
| { | |
| renderPassAttachment = renderPassDesc.depthAttachment; | |
| } | |
| else | |
| { | |
| ASSERT(renderPassDesc.stencilAttachment.texture); | |
| renderPassAttachment = renderPassDesc.stencilAttachment; | |
| } | |
| auto texture = renderPassAttachment.texture; | |
| viewport = | |
| GetViewport(params.clearArea, texture->height(renderPassAttachment.level), params.flipY); | |
| scissorRect = | |
| GetScissorRect(params.clearArea, texture->height(renderPassAttachment.level), params.flipY); | |
| cmdEncoder->setViewport(viewport); | |
| cmdEncoder->setScissorRect(scissorRect); | |
| // uniform | |
| ClearParamsUniform uniformParams; | |
| uniformParams.clearColor[0] = static_cast<float>(params.clearColor.value().red); | |
| uniformParams.clearColor[1] = static_cast<float>(params.clearColor.value().green); | |
| uniformParams.clearColor[2] = static_cast<float>(params.clearColor.value().blue); | |
| uniformParams.clearColor[3] = static_cast<float>(params.clearColor.value().alpha); | |
| uniformParams.clearDepth = params.clearDepth.value(); | |
| cmdEncoder->setVertexData(uniformParams, 0); | |
| cmdEncoder->setFragmentData(uniformParams, 0); | |
| } | |
| void RenderUtils::setupBlitWithDraw(const gl::Context *context, | |
| RenderCommandEncoder *cmdEncoder, | |
| const BlitParams ¶ms) | |
| { | |
| ASSERT(cmdEncoder->renderPassDesc().numColorAttachments == 1 && params.src); | |
| // Generate render pipeline state | |
| auto renderPipelineState = getBlitRenderPipelineState(context, cmdEncoder, params); | |
| ASSERT(renderPipelineState); | |
| // Setup states | |
| setupDrawCommonStates(cmdEncoder); | |
| cmdEncoder->setRenderPipelineState(renderPipelineState); | |
| cmdEncoder->setDepthStencilState(getDisplay()->getStateCache().getNullDepthStencilState(this)); | |
| // Viewport | |
| const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc(); | |
| const RenderPassColorAttachmentDesc &renderPassColorAttachment = | |
| renderPassDesc.colorAttachments[0]; | |
| auto texture = renderPassColorAttachment.texture; | |
| gl::Rectangle dstRect(params.dstOffset.x, params.dstOffset.y, params.srcRect.width, | |
| params.srcRect.height); | |
| MTLViewport viewportMtl = | |
| GetViewport(dstRect, texture->height(renderPassColorAttachment.level), params.dstFlipY); | |
| MTLScissorRect scissorRectMtl = | |
| GetScissorRect(dstRect, texture->height(renderPassColorAttachment.level), params.dstFlipY); | |
| cmdEncoder->setViewport(viewportMtl); | |
| cmdEncoder->setScissorRect(scissorRectMtl); | |
| cmdEncoder->setFragmentTexture(params.src, 0); | |
| // Uniform | |
| setupBlitWithDrawUniformData(cmdEncoder, params); | |
| } | |
| void RenderUtils::setupDrawCommonStates(RenderCommandEncoder *cmdEncoder) | |
| { | |
| cmdEncoder->setCullMode(MTLCullModeNone); | |
| cmdEncoder->setTriangleFillMode(MTLTriangleFillModeFill); | |
| cmdEncoder->setDepthBias(0, 0, 0); | |
| } | |
| id<MTLDepthStencilState> RenderUtils::getClearDepthStencilState(const gl::Context *context, | |
| const ClearRectParams ¶ms) | |
| { | |
| if (!params.clearDepth.valid() && !params.clearStencil.valid()) | |
| { | |
| // Doesn't clear depth nor stencil | |
| return getDisplay()->getStateCache().getNullDepthStencilState(this); | |
| } | |
| ContextMtl *contextMtl = GetImpl(context); | |
| DepthStencilDesc desc; | |
| desc.reset(); | |
| if (params.clearDepth.valid()) | |
| { | |
| // Clear depth state | |
| desc.depthWriteEnabled = true; | |
| } | |
| else | |
| { | |
| desc.depthWriteEnabled = false; | |
| } | |
| if (params.clearStencil.valid()) | |
| { | |
| // Clear stencil state | |
| desc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationReplace; | |
| desc.frontFaceStencil.writeMask = contextMtl->getStencilMask(); | |
| desc.backFaceStencil.depthStencilPassOperation = MTLStencilOperationReplace; | |
| desc.backFaceStencil.writeMask = contextMtl->getStencilMask(); | |
| } | |
| return getDisplay()->getStateCache().getDepthStencilState(getDisplay()->getMetalDevice(), desc); | |
| } | |
| id<MTLRenderPipelineState> RenderUtils::getClearRenderPipelineState( | |
| const gl::Context *context, | |
| RenderCommandEncoder *cmdEncoder, | |
| const ClearRectParams ¶ms) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| MTLColorWriteMask colorMask = contextMtl->getColorMask(); | |
| if (!params.clearColor.valid()) | |
| { | |
| colorMask = MTLColorWriteMaskNone; | |
| } | |
| RenderPipelineDesc pipelineDesc; | |
| const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc(); | |
| renderPassDesc.populateRenderPipelineOutputDesc(colorMask, &pipelineDesc.outputDescriptor); | |
| pipelineDesc.inputPrimitiveTopology = kPrimitiveTopologyClassTriangle; | |
| return mClearRenderPipelineCache.getRenderPipelineState(contextMtl, pipelineDesc); | |
| } | |
| id<MTLRenderPipelineState> RenderUtils::getBlitRenderPipelineState(const gl::Context *context, | |
| RenderCommandEncoder *cmdEncoder, | |
| const BlitParams ¶ms) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| RenderPipelineDesc pipelineDesc; | |
| const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc(); | |
| renderPassDesc.populateRenderPipelineOutputDesc(params.dstColorMask, | |
| &pipelineDesc.outputDescriptor); | |
| pipelineDesc.inputPrimitiveTopology = kPrimitiveTopologyClassTriangle; | |
| RenderPipelineCache *pipelineCache; | |
| if (params.unpackPremultiplyAlpha == params.unpackUnmultiplyAlpha) | |
| { | |
| pipelineCache = &mBlitRenderPipelineCache; | |
| } | |
| else if (params.unpackPremultiplyAlpha) | |
| { | |
| pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache; | |
| } | |
| else | |
| { | |
| pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache; | |
| } | |
| return pipelineCache->getRenderPipelineState(contextMtl, pipelineDesc); | |
| } | |
| void RenderUtils::setupBlitWithDrawUniformData(RenderCommandEncoder *cmdEncoder, | |
| const BlitParams ¶ms) | |
| { | |
| BlitParamsUniform uniformParams; | |
| uniformParams.dstFlipY = params.dstFlipY ? 1 : 0; | |
| uniformParams.srcLevel = params.srcLevel; | |
| uniformParams.dstLuminance = params.dstLuminance ? 1 : 0; | |
| // Compute source texCoords | |
| auto srcWidth = params.src->width(params.srcLevel); | |
| auto srcHeight = params.src->height(params.srcLevel); | |
| int x0 = params.srcRect.x0(); // left | |
| int x1 = params.srcRect.x1(); // right | |
| int y0 = params.srcRect.y0(); // lower | |
| int y1 = params.srcRect.y1(); // upper | |
| if (params.srcYFlipped) | |
| { | |
| // If source's Y has been flipped, such as default framebuffer, then adjust the real source | |
| // rectangle. | |
| y0 = srcHeight - y1; | |
| y1 = y0 + params.srcRect.height; | |
| std::swap(y0, y1); | |
| } | |
| if (params.unpackFlipY) | |
| { | |
| std::swap(y0, y1); | |
| } | |
| float u0 = (float)x0 / srcWidth; | |
| float u1 = (float)x1 / srcWidth; | |
| float v0 = (float)y0 / srcHeight; | |
| float v1 = (float)y1 / srcHeight; | |
| // lower left | |
| uniformParams.srcTexCoords[0][0] = u0; | |
| uniformParams.srcTexCoords[0][1] = v0; | |
| // lower right | |
| uniformParams.srcTexCoords[1][0] = u1; | |
| uniformParams.srcTexCoords[1][1] = v0; | |
| // upper left | |
| uniformParams.srcTexCoords[2][0] = u0; | |
| uniformParams.srcTexCoords[2][1] = v1; | |
| // upper right | |
| uniformParams.srcTexCoords[3][0] = u1; | |
| uniformParams.srcTexCoords[3][1] = v1; | |
| cmdEncoder->setVertexData(uniformParams, 0); | |
| cmdEncoder->setFragmentData(uniformParams, 0); | |
| } | |
| AutoObjCPtr<id<MTLComputePipelineState>> RenderUtils::getIndexConversionPipeline( | |
| ContextMtl *context, | |
| gl::DrawElementsType srcType, | |
| uint32_t srcOffset) | |
| { | |
| id<MTLDevice> metalDevice = context->getMetalDevice(); | |
| size_t elementSize = gl::GetDrawElementsTypeSize(srcType); | |
| bool aligned = (srcOffset % elementSize) == 0; | |
| IndexConvesionPipelineCacheKey key = {srcType, aligned}; | |
| auto &cache = mIndexConversionPipelineCaches[key]; | |
| if (!cache) | |
| { | |
| ANGLE_MTL_OBJC_SCOPE | |
| { | |
| auto shaderLib = mDefaultShaders.get(); | |
| id<MTLFunction> shader = nil; | |
| auto funcConstants = [[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE]; | |
| NSError *err = nil; | |
| [funcConstants setConstantValue:&aligned | |
| type:MTLDataTypeBool | |
| withName:SOURCE_BUFFER_ALIGNED_CONSTANT_NAME]; | |
| switch (srcType) | |
| { | |
| case gl::DrawElementsType::UnsignedByte: | |
| shader = [shaderLib newFunctionWithName:@"convertIndexU8ToU16"]; | |
| break; | |
| case gl::DrawElementsType::UnsignedShort: | |
| shader = [shaderLib newFunctionWithName:@"convertIndexU16" | |
| constantValues:funcConstants | |
| error:&err]; | |
| break; | |
| case gl::DrawElementsType::UnsignedInt: | |
| shader = [shaderLib newFunctionWithName:@"convertIndexU32" | |
| constantValues:funcConstants | |
| error:&err]; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| if (err && !shader) | |
| { | |
| ERR() << "Internal error: " << err.localizedDescription.UTF8String << "\n"; | |
| } | |
| ASSERT([shader ANGLE_MTL_AUTORELEASE]); | |
| cache = [[metalDevice newComputePipelineStateWithFunction:shader | |
| error:&err] ANGLE_MTL_AUTORELEASE]; | |
| if (err && !cache) | |
| { | |
| ERR() << "Internal error: " << err.localizedDescription.UTF8String << "\n"; | |
| } | |
| ASSERT(cache); | |
| } | |
| } | |
| return cache; | |
| } | |
| AutoObjCPtr<id<MTLComputePipelineState>> RenderUtils::getTriFanFromElemArrayGeneratorPipeline( | |
| ContextMtl *context, | |
| gl::DrawElementsType srcType, | |
| uint32_t srcOffset) | |
| { | |
| id<MTLDevice> metalDevice = context->getMetalDevice(); | |
| size_t elementSize = gl::GetDrawElementsTypeSize(srcType); | |
| bool aligned = (srcOffset % elementSize) == 0; | |
| IndexConvesionPipelineCacheKey key = {srcType, aligned}; | |
| auto &cache = mTriFanFromElemArrayGeneratorPipelineCaches[key]; | |
| if (!cache) | |
| { | |
| ANGLE_MTL_OBJC_SCOPE | |
| { | |
| auto shaderLib = mDefaultShaders.get(); | |
| id<MTLFunction> shader = nil; | |
| auto funcConstants = [[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE]; | |
| NSError *err = nil; | |
| bool isU8 = false; | |
| bool isU16 = false; | |
| bool isU32 = false; | |
| switch (srcType) | |
| { | |
| case gl::DrawElementsType::UnsignedByte: | |
| isU8 = true; | |
| break; | |
| case gl::DrawElementsType::UnsignedShort: | |
| isU16 = true; | |
| break; | |
| case gl::DrawElementsType::UnsignedInt: | |
| isU32 = true; | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| [funcConstants setConstantValue:&aligned | |
| type:MTLDataTypeBool | |
| withName:SOURCE_BUFFER_ALIGNED_CONSTANT_NAME]; | |
| [funcConstants setConstantValue:&isU8 | |
| type:MTLDataTypeBool | |
| withName:SOURCE_IDX_IS_U8_CONSTANT_NAME]; | |
| [funcConstants setConstantValue:&isU16 | |
| type:MTLDataTypeBool | |
| withName:SOURCE_IDX_IS_U16_CONSTANT_NAME]; | |
| [funcConstants setConstantValue:&isU32 | |
| type:MTLDataTypeBool | |
| withName:SOURCE_IDX_IS_U32_CONSTANT_NAME]; | |
| shader = [shaderLib newFunctionWithName:@"genTriFanIndicesFromElements" | |
| constantValues:funcConstants | |
| error:&err]; | |
| if (err && !shader) | |
| { | |
| ERR() << "Internal error: " << err.localizedDescription.UTF8String << "\n"; | |
| } | |
| ASSERT([shader ANGLE_MTL_AUTORELEASE]); | |
| cache = [[metalDevice newComputePipelineStateWithFunction:shader | |
| error:&err] ANGLE_MTL_AUTORELEASE]; | |
| if (err && !cache) | |
| { | |
| ERR() << "Internal error: " << err.localizedDescription.UTF8String << "\n"; | |
| } | |
| ASSERT(cache); | |
| } | |
| } | |
| return cache; | |
| } | |
| angle::Result RenderUtils::ensureTriFanFromArrayGeneratorInitialized(ContextMtl *context) | |
| { | |
| if (!mTriFanFromArraysGeneratorPipeline) | |
| { | |
| ANGLE_MTL_OBJC_SCOPE | |
| { | |
| id<MTLDevice> metalDevice = context->getMetalDevice(); | |
| auto shaderLib = mDefaultShaders.get(); | |
| NSError *err = nil; | |
| id<MTLFunction> shader = [shaderLib newFunctionWithName:@"genTriFanIndicesFromArray"]; | |
| [shader ANGLE_MTL_AUTORELEASE]; | |
| mTriFanFromArraysGeneratorPipeline = | |
| [[metalDevice newComputePipelineStateWithFunction:shader | |
| error:&err] ANGLE_MTL_AUTORELEASE]; | |
| if (err && !mTriFanFromArraysGeneratorPipeline) | |
| { | |
| ERR() << "Internal error: " << err.localizedDescription.UTF8String << "\n"; | |
| } | |
| ASSERT(mTriFanFromArraysGeneratorPipeline); | |
| } | |
| } | |
| return angle::Result::Continue; | |
| } | |
| angle::Result RenderUtils::convertIndexBuffer(const gl::Context *context, | |
| gl::DrawElementsType srcType, | |
| uint32_t indexCount, | |
| const BufferRef &srcBuffer, | |
| uint32_t srcOffset, | |
| const BufferRef &dstBuffer, | |
| uint32_t dstOffset) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder(); | |
| ASSERT(cmdEncoder); | |
| AutoObjCPtr<id<MTLComputePipelineState>> pipelineState = | |
| getIndexConversionPipeline(contextMtl, srcType, srcOffset); | |
| ASSERT(pipelineState); | |
| cmdEncoder->setComputePipelineState(pipelineState); | |
| ASSERT((dstOffset % kBufferSettingOffsetAlignment) == 0); | |
| IndexConversionUniform uniform; | |
| uniform.srcOffset = srcOffset; | |
| uniform.indexCount = indexCount; | |
| cmdEncoder->setData(uniform, 0); | |
| cmdEncoder->setBuffer(srcBuffer, 0, 1); | |
| cmdEncoder->setBuffer(dstBuffer, dstOffset, 2); | |
| ANGLE_TRY(dispatchCompute(context, cmdEncoder, pipelineState, indexCount)); | |
| return angle::Result::Continue; | |
| } | |
| angle::Result RenderUtils::generateTriFanBufferFromArrays(const gl::Context *context, | |
| const TriFanFromArrayParams ¶ms) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder(); | |
| ASSERT(cmdEncoder); | |
| ANGLE_TRY(ensureTriFanFromArrayGeneratorInitialized(contextMtl)); | |
| ASSERT(params.vertexCount > 2); | |
| cmdEncoder->setComputePipelineState(mTriFanFromArraysGeneratorPipeline); | |
| ASSERT((params.dstOffset % kBufferSettingOffsetAlignment) == 0); | |
| struct TriFanArrayParams | |
| { | |
| uint firstVertex; | |
| uint vertexCountFrom3rd; | |
| uint padding[2]; | |
| } uniform; | |
| uniform.firstVertex = params.firstVertex; | |
| uniform.vertexCountFrom3rd = params.vertexCount - 2; | |
| cmdEncoder->setData(uniform, 0); | |
| cmdEncoder->setBuffer(params.dstBuffer, params.dstOffset, 2); | |
| ANGLE_TRY(dispatchCompute(context, cmdEncoder, mTriFanFromArraysGeneratorPipeline, | |
| uniform.vertexCountFrom3rd)); | |
| return angle::Result::Continue; | |
| } | |
| angle::Result RenderUtils::generateTriFanBufferFromElementsArray( | |
| const gl::Context *context, | |
| const IndexGenerationParams ¶ms) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| const gl::VertexArray *vertexArray = context->getState().getVertexArray(); | |
| const gl::Buffer *elementBuffer = vertexArray->getElementArrayBuffer(); | |
| if (elementBuffer) | |
| { | |
| size_t srcOffset = reinterpret_cast<size_t>(params.indices); | |
| ANGLE_CHECK(contextMtl, srcOffset <= std::numeric_limits<uint32_t>::max(), | |
| "Index offset is too large", GL_INVALID_VALUE); | |
| return generateTriFanBufferFromElementsArrayGPU( | |
| context, params.srcType, params.indexCount, | |
| GetImpl(elementBuffer)->getCurrentBuffer(context), static_cast<uint32_t>(srcOffset), | |
| params.dstBuffer, params.dstOffset); | |
| } | |
| else | |
| { | |
| return generateTriFanBufferFromElementsArrayCPU(context, params); | |
| } | |
| } | |
| angle::Result RenderUtils::generateTriFanBufferFromElementsArrayGPU( | |
| const gl::Context *context, | |
| gl::DrawElementsType srcType, | |
| uint32_t indexCount, | |
| const BufferRef &srcBuffer, | |
| uint32_t srcOffset, | |
| const BufferRef &dstBuffer, | |
| // Must be multiples of kBufferSettingOffsetAlignment | |
| uint32_t dstOffset) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder(); | |
| ASSERT(cmdEncoder); | |
| AutoObjCPtr<id<MTLComputePipelineState>> pipelineState = | |
| getTriFanFromElemArrayGeneratorPipeline(contextMtl, srcType, srcOffset); | |
| ASSERT(pipelineState); | |
| cmdEncoder->setComputePipelineState(pipelineState); | |
| ASSERT((dstOffset % kBufferSettingOffsetAlignment) == 0); | |
| ASSERT(indexCount > 2); | |
| IndexConversionUniform uniform; | |
| uniform.srcOffset = srcOffset; | |
| uniform.indexCount = indexCount - 2; // Only start from the 3rd element. | |
| cmdEncoder->setData(uniform, 0); | |
| cmdEncoder->setBuffer(srcBuffer, 0, 1); | |
| cmdEncoder->setBuffer(dstBuffer, dstOffset, 2); | |
| ANGLE_TRY(dispatchCompute(context, cmdEncoder, pipelineState, uniform.indexCount)); | |
| return angle::Result::Continue; | |
| } | |
| angle::Result RenderUtils::generateTriFanBufferFromElementsArrayCPU( | |
| const gl::Context *context, | |
| const IndexGenerationParams ¶ms) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| switch (params.srcType) | |
| { | |
| case gl::DrawElementsType::UnsignedByte: | |
| return GenTriFanFromClientElements(contextMtl, params.indexCount, | |
| static_cast<const uint8_t *>(params.indices), | |
| params.dstBuffer, params.dstOffset); | |
| case gl::DrawElementsType::UnsignedShort: | |
| return GenTriFanFromClientElements(contextMtl, params.indexCount, | |
| static_cast<const uint16_t *>(params.indices), | |
| params.dstBuffer, params.dstOffset); | |
| case gl::DrawElementsType::UnsignedInt: | |
| return GenTriFanFromClientElements(contextMtl, params.indexCount, | |
| static_cast<const uint32_t *>(params.indices), | |
| params.dstBuffer, params.dstOffset); | |
| default: | |
| UNREACHABLE(); | |
| } | |
| return angle::Result::Stop; | |
| } | |
| angle::Result RenderUtils::generateLineLoopLastSegment(const gl::Context *context, | |
| uint32_t firstVertex, | |
| uint32_t lastVertex, | |
| const BufferRef &dstBuffer, | |
| uint32_t dstOffset) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| uint8_t *ptr = dstBuffer->map(contextMtl); | |
| uint32_t indices[2] = {lastVertex, firstVertex}; | |
| memcpy(ptr, indices, sizeof(indices)); | |
| dstBuffer->unmap(contextMtl); | |
| return angle::Result::Continue; | |
| } | |
| angle::Result RenderUtils::generateLineLoopLastSegmentFromElementsArray( | |
| const gl::Context *context, | |
| const IndexGenerationParams ¶ms) | |
| { | |
| ContextMtl *contextMtl = GetImpl(context); | |
| const gl::VertexArray *vertexArray = context->getState().getVertexArray(); | |
| const gl::Buffer *elementBuffer = vertexArray->getElementArrayBuffer(); | |
| if (elementBuffer) | |
| { | |
| size_t srcOffset = reinterpret_cast<size_t>(params.indices); | |
| ANGLE_CHECK(contextMtl, srcOffset <= std::numeric_limits<uint32_t>::max(), | |
| "Index offset is too large", GL_INVALID_VALUE); | |
| BufferMtl *bufferMtl = GetImpl(elementBuffer); | |
| std::pair<uint32_t, uint32_t> firstLast; | |
| ANGLE_TRY(bufferMtl->getFirstLastIndices(context, params.srcType, | |
| static_cast<uint32_t>(srcOffset), | |
| params.indexCount, &firstLast)); | |
| return generateLineLoopLastSegment(context, firstLast.first, firstLast.second, | |
| params.dstBuffer, params.dstOffset); | |
| } | |
| else | |
| { | |
| return generateLineLoopLastSegmentFromElementsArrayCPU(context, params); | |
| } | |
| } | |
| angle::Result RenderUtils::generateLineLoopLastSegmentFromElementsArrayCPU( | |
| const gl::Context *context, | |
| const IndexGenerationParams ¶ms) | |
| { | |
| uint32_t first, last; | |
| switch (params.srcType) | |
| { | |
| case gl::DrawElementsType::UnsignedByte: | |
| GetFirstLastIndicesFromClientElements( | |
| params.indexCount, static_cast<const uint8_t *>(params.indices), &first, &last); | |
| break; | |
| case gl::DrawElementsType::UnsignedShort: | |
| GetFirstLastIndicesFromClientElements( | |
| params.indexCount, static_cast<const uint16_t *>(params.indices), &first, &last); | |
| break; | |
| case gl::DrawElementsType::UnsignedInt: | |
| GetFirstLastIndicesFromClientElements( | |
| params.indexCount, static_cast<const uint32_t *>(params.indices), &first, &last); | |
| break; | |
| default: | |
| UNREACHABLE(); | |
| return angle::Result::Stop; | |
| } | |
| return generateLineLoopLastSegment(context, first, last, params.dstBuffer, params.dstOffset); | |
| } | |
| angle::Result RenderUtils::dispatchCompute(const gl::Context *context, | |
| ComputeCommandEncoder *cmdEncoder, | |
| id<MTLComputePipelineState> pipelineState, | |
| size_t numThreads) | |
| { | |
| NSUInteger w = pipelineState.threadExecutionWidth; | |
| MTLSize threadsPerThreadgroup = MTLSizeMake(w, 1, 1); | |
| if (getDisplay()->getFeatures().hasNonUniformDispatch.enabled) | |
| { | |
| MTLSize threads = MTLSizeMake(numThreads, 1, 1); | |
| cmdEncoder->dispatchNonUniform(threads, threadsPerThreadgroup); | |
| } | |
| else | |
| { | |
| MTLSize groups = MTLSizeMake((numThreads + w - 1) / w, 1, 1); | |
| cmdEncoder->dispatch(groups, threadsPerThreadgroup); | |
| } | |
| return angle::Result::Continue; | |
| } | |
| } // namespace mtl | |
| } // namespace rx |