// // 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.h: // Defines the class interface for RenderUtils, which contains many utility functions and shaders // for converting, blitting, copying as well as generating data, and many more. // #ifndef LIBANGLE_RENDERER_METAL_MTL_RENDER_UTILS_H_ #define LIBANGLE_RENDERER_METAL_MTL_RENDER_UTILS_H_ #import #include "libANGLE/angletypes.h" #include "libANGLE/renderer/metal/RenderTargetMtl.h" #include "libANGLE/renderer/metal/mtl_command_buffer.h" #include "libANGLE/renderer/metal/mtl_state_cache.h" #include "libANGLE/renderer/metal/shaders/constants.h" namespace rx { class BufferMtl; class ContextMtl; class DisplayMtl; class VisibilityBufferOffsetsMtl; namespace mtl { struct ClearRectParams { ClearRectParams() { clearWriteMaskArray.fill(MTLColorWriteMaskAll); } Optional clearColor; Optional clearDepth; Optional clearStencil; WriteMaskArray clearWriteMaskArray; const mtl::Format *colorFormat = nullptr; gl::Extents dstTextureSize; // Only clear enabled buffers gl::DrawBufferMask enabledBuffers; gl::Rectangle clearArea; bool flipY = false; }; struct BlitParams { gl::Extents dstTextureSize; gl::Rectangle dstRect; gl::Rectangle dstScissorRect; // Destination texture needs to have viewport Y flipped? // The difference between this param and unpackFlipY is that unpackFlipY is from // glCopyImageCHROMIUM()/glBlitFramebuffer(), and dstFlipY controls whether the final viewport // needs to be flipped when drawing to destination texture. It is possible to combine the two // flags before passing to RenderUtils. However, to avoid duplicated works, just pass the two // flags to RenderUtils, they will be combined internally by RenderUtils logic. bool dstFlipY = false; bool dstFlipX = false; TextureRef src; MipmapNativeLevel srcLevel = kZeroNativeMipLevel; uint32_t srcLayer = 0; // Source rectangle: // NOTE: if srcYFlipped=true, this rectangle will be converted internally to flipped rect before // blitting. gl::Rectangle srcRect; bool srcYFlipped = false; // source texture has data flipped in Y direction bool unpackFlipX = false; // flip texture data copying process in X direction bool unpackFlipY = false; // flip texture data copying process in Y direction }; struct ColorBlitParams : public BlitParams { ColorBlitParams() { blitWriteMaskArray.fill(MTLColorWriteMaskAll); } WriteMaskArray blitWriteMaskArray; gl::DrawBufferMask enabledBuffers; GLenum filter = GL_NEAREST; bool unpackPremultiplyAlpha = false; bool unpackUnmultiplyAlpha = false; bool dstLuminance = false; }; struct DepthStencilBlitParams : public BlitParams { TextureRef srcStencil; }; // Stencil blit via an intermediate buffer. NOTE: source depth texture parameter is ignored. // See DepthStencilBlitUtils::blitStencilViaCopyBuffer() struct StencilBlitViaBufferParams : public DepthStencilBlitParams { StencilBlitViaBufferParams(); StencilBlitViaBufferParams(const DepthStencilBlitParams &src); TextureRef dstStencil; MipmapNativeLevel dstStencilLevel = kZeroNativeMipLevel; uint32_t dstStencilLayer = 0; bool dstPackedDepthStencilFormat = false; }; struct TriFanOrLineLoopFromArrayParams { uint32_t firstVertex; uint32_t vertexCount; BufferRef dstBuffer; // Must be multiples of kIndexBufferOffsetAlignment uint32_t dstOffset; }; struct IndexConversionParams { gl::DrawElementsType srcType; uint32_t indexCount; const BufferRef &srcBuffer; uint32_t srcOffset; const BufferRef &dstBuffer; // Must be multiples of kIndexBufferOffsetAlignment uint32_t dstOffset; bool primitiveRestartEnabled = false; }; struct IndexGenerationParams { gl::DrawElementsType srcType; GLsizei indexCount; const void *indices; BufferRef dstBuffer; uint32_t dstOffset; bool primitiveRestartEnabled = false; }; struct CopyPixelsCommonParams { BufferRef buffer; uint32_t bufferStartOffset = 0; uint32_t bufferRowPitch = 0; TextureRef texture; }; struct CopyPixelsFromBufferParams : CopyPixelsCommonParams { uint32_t bufferDepthPitch = 0; // z offset is: // - slice index if texture is array. // - depth if texture is 3d. gl::Box textureArea; }; struct CopyPixelsToBufferParams : CopyPixelsCommonParams { gl::Rectangle textureArea; MipmapNativeLevel textureLevel = kZeroNativeMipLevel; uint32_t textureSliceOrDeph = 0; bool reverseTextureRowOrder; }; struct VertexFormatConvertParams { BufferRef srcBuffer; uint32_t srcBufferStartOffset = 0; uint32_t srcStride = 0; uint32_t srcDefaultAlphaData = 0; // casted as uint BufferRef dstBuffer; uint32_t dstBufferStartOffset = 0; uint32_t dstStride = 0; uint32_t dstComponents = 0; uint32_t vertexCount = 0; }; // Utils class for clear & blitting class ClearUtils final : angle::NonCopyable { public: ClearUtils() = delete; ClearUtils(const std::string &fragmentShaderName); ClearUtils(const ClearUtils &src); void onDestroy(); // Clear current framebuffer angle::Result clearWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const ClearRectParams ¶ms); private: void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx, uint32_t numColorAttachments); void setupClearWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const ClearRectParams ¶ms); id getClearDepthStencilState(const gl::Context *context, const ClearRectParams ¶ms); id getClearRenderPipelineState(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const ClearRectParams ¶ms); const std::string mFragmentShaderName; // Render pipeline cache for clear with draw: std::array mClearRenderPipelineCache; }; class ColorBlitUtils final : angle::NonCopyable { public: ColorBlitUtils() = delete; ColorBlitUtils(const std::string &fragmentShaderName); ColorBlitUtils(const ColorBlitUtils &src); void onDestroy(); // Blit texture data to current framebuffer angle::Result blitColorWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const ColorBlitParams ¶ms); private: void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx, uint32_t numColorAttachments, int alphaPremultiplyType, int sourceTextureType, RenderPipelineCache *cacheOut); void setupColorBlitWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const ColorBlitParams ¶ms); id getColorBlitRenderPipelineState(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const ColorBlitParams ¶ms); const std::string mFragmentShaderName; // Blit with draw pipeline caches: // First array dimension: number of outputs. // Second array dimension: source texture type (2d, ms, array, 3d, etc) using ColorBlitRenderPipelineCacheArray = std::array, kMaxRenderTargets>; ColorBlitRenderPipelineCacheArray mBlitRenderPipelineCache; ColorBlitRenderPipelineCacheArray mBlitPremultiplyAlphaRenderPipelineCache; ColorBlitRenderPipelineCacheArray mBlitUnmultiplyAlphaRenderPipelineCache; }; class DepthStencilBlitUtils final : angle::NonCopyable { public: void onDestroy(); angle::Result blitDepthStencilWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const DepthStencilBlitParams ¶ms); // Blit stencil data using intermediate buffer. This function is used on devices with no // support for direct stencil write in shader. Thus an intermediate buffer storing copied // stencil data is needed. // NOTE: this function shares the params struct with depth & stencil blit, but depth texture // parameter is not used. This function will break existing render pass. angle::Result blitStencilViaCopyBuffer(const gl::Context *context, const StencilBlitViaBufferParams ¶ms); private: void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx, int sourceDepthTextureType, int sourceStencilTextureType, RenderPipelineCache *cacheOut); void setupDepthStencilBlitWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const DepthStencilBlitParams ¶ms); id getDepthStencilBlitRenderPipelineState( const gl::Context *context, RenderCommandEncoder *cmdEncoder, const DepthStencilBlitParams ¶ms); id getStencilToBufferComputePipelineState( ContextMtl *ctx, const StencilBlitViaBufferParams ¶ms); std::array mDepthBlitRenderPipelineCache; std::array mStencilBlitRenderPipelineCache; std::array, mtl_shader::kTextureTypeCount> mDepthStencilBlitRenderPipelineCache; std::array>, mtl_shader::kTextureTypeCount> mStencilBlitToBufferComPipelineCache; // Intermediate buffer for storing copied stencil data. Used when device doesn't support // writing stencil in shader. BufferRef mStencilCopyBuffer; }; // util class for generating index buffer class IndexGeneratorUtils final : angle::NonCopyable { public: void onDestroy(); angle::Result convertIndexBufferGPU(ContextMtl *contextMtl, const IndexConversionParams ¶ms); angle::Result generateTriFanBufferFromArrays(ContextMtl *contextMtl, const TriFanOrLineLoopFromArrayParams ¶ms); // Generate triangle fan index buffer for glDrawElements(). angle::Result generateTriFanBufferFromElementsArray(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, uint32_t *indicesGenerated); angle::Result generateLineLoopBufferFromArrays(ContextMtl *contextMtl, const TriFanOrLineLoopFromArrayParams ¶ms); angle::Result generateLineLoopLastSegment(ContextMtl *contextMtl, uint32_t firstVertex, uint32_t lastVertex, const BufferRef &dstBuffer, uint32_t dstOffset); // Generate line loop index buffer for glDrawElements(). // Destination buffer must have at least 2x the number of original indices if primitive restart // is enabled. angle::Result generateLineLoopBufferFromElementsArray(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, uint32_t *indicesGenerated); // Generate line loop's last segment index buffer for glDrawElements(). // NOTE: this function assumes primitive restart is not enabled. angle::Result generateLineLoopLastSegmentFromElementsArray(ContextMtl *contextMtl, const IndexGenerationParams ¶ms); angle::Result generatePrimitiveRestartPointsBuffer(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, size_t *indicesGenerated); angle::Result generatePrimitiveRestartLinesBuffer(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, size_t *indicesGenerated); angle::Result generatePrimitiveRestartTrianglesBuffer(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, size_t *indicesGenerated); private: // Index generator compute pipelines: // - First dimension: index type. // - second dimension: source buffer's offset is aligned or not. using IndexConversionPipelineArray = std::array>, 2>, angle::EnumSize()>; AutoObjCPtr> getIndexConversionPipeline( ContextMtl *contextMtl, gl::DrawElementsType srcType, uint32_t srcOffset); // Get compute pipeline to generate tri fan/line loop index for glDrawElements(). AutoObjCPtr> getIndicesFromElemArrayGeneratorPipeline( ContextMtl *contextMtl, gl::DrawElementsType srcType, uint32_t srcOffset, NSString *shaderName, IndexConversionPipelineArray *pipelineCacheArray); // Defer loading of compute pipeline to generate tri fan index for glDrawArrays(). void ensureTriFanFromArrayGeneratorInitialized(ContextMtl *contextMtl); // Defer loading of compute pipeline to generate line loop index for glDrawArrays(). void ensureLineLoopFromArrayGeneratorInitialized(ContextMtl *contextMtl); angle::Result generateTriFanBufferFromElementsArrayGPU( ContextMtl *contextMtl, gl::DrawElementsType srcType, uint32_t indexCount, const BufferRef &srcBuffer, uint32_t srcOffset, const BufferRef &dstBuffer, // Must be multiples of kIndexBufferOffsetAlignment uint32_t dstOffset); angle::Result generateTriFanBufferFromElementsArrayCPU(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, uint32_t *indicesGenerated); angle::Result generateLineLoopBufferFromElementsArrayGPU( ContextMtl *contextMtl, gl::DrawElementsType srcType, uint32_t indexCount, const BufferRef &srcBuffer, uint32_t srcOffset, const BufferRef &dstBuffer, // Must be multiples of kIndexBufferOffsetAlignment uint32_t dstOffset); angle::Result generateLineLoopBufferFromElementsArrayCPU(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, uint32_t *indicesGenerated); angle::Result generateLineLoopLastSegmentFromElementsArrayCPU( ContextMtl *contextMtl, const IndexGenerationParams ¶ms); angle::Result generatePrimitiveRestartBuffer(ContextMtl *contextMtl, unsigned numVerticesPerPrimitive, const IndexGenerationParams ¶ms, size_t *indicesGenerated); IndexConversionPipelineArray mIndexConversionPipelineCaches; IndexConversionPipelineArray mTriFanFromElemArrayGeneratorPipelineCaches; AutoObjCPtr> mTriFanFromArraysGeneratorPipeline; IndexConversionPipelineArray mLineLoopFromElemArrayGeneratorPipelineCaches; AutoObjCPtr> mLineLoopFromArraysGeneratorPipeline; }; // Util class for handling visibility query result class VisibilityResultUtils final : angle::NonCopyable { public: void onDestroy(); void combineVisibilityResult(ContextMtl *contextMtl, bool keepOldValue, const VisibilityBufferOffsetsMtl &renderPassResultBufOffsets, const BufferRef &renderPassResultBuf, const BufferRef &finalResultBuf); private: AutoObjCPtr> getVisibilityResultCombPipeline(ContextMtl *contextMtl, bool keepOldValue); // Visibility combination compute pipeline: // - 0: This compute pipeline only combine the new values and discard old value. // - 1: This compute pipeline keep the old value and combine with new values. std::array>, 2> mVisibilityResultCombPipelines; }; // Util class for handling mipmap generation class MipmapUtils final : angle::NonCopyable { public: void onDestroy(); // Compute based mipmap generation. angle::Result generateMipmapCS(ContextMtl *contextMtl, const TextureRef &srcTexture, bool sRGBMipmap, NativeTexLevelArray *mipmapOutputViews); private: void ensure3DMipGeneratorPipelineInitialized(ContextMtl *contextMtl); void ensure2DMipGeneratorPipelineInitialized(ContextMtl *contextMtl); void ensure2DArrayMipGeneratorPipelineInitialized(ContextMtl *contextMtl); void ensureCubeMipGeneratorPipelineInitialized(ContextMtl *contextMtl); // Mipmaps generating compute pipeline: AutoObjCPtr> m3DMipGeneratorPipeline; AutoObjCPtr> m2DMipGeneratorPipeline; AutoObjCPtr> m2DArrayMipGeneratorPipeline; AutoObjCPtr> mCubeMipGeneratorPipeline; }; // Util class for handling pixels copy between buffers and textures class CopyPixelsUtils final : angle::NonCopyable { public: CopyPixelsUtils() = default; CopyPixelsUtils(const std::string &readShaderName, const std::string &writeShaderName); CopyPixelsUtils(const CopyPixelsUtils &src); void onDestroy(); angle::Result unpackPixelsFromBufferToTexture(ContextMtl *contextMtl, const angle::Format &srcAngleFormat, const CopyPixelsFromBufferParams ¶ms); angle::Result packPixelsFromTextureToBuffer(ContextMtl *contextMtl, const angle::Format &dstAngleFormat, const CopyPixelsToBufferParams ¶ms); private: AutoObjCPtr> getPixelsCopyPipeline(ContextMtl *contextMtl, const angle::Format &angleFormat, const TextureRef &texture, bool bufferWrite); // Copy pixels between buffer and texture compute pipelines: // - First dimension: pixel format. // - Second dimension: texture type * (buffer read/write flag) using PixelsCopyPipelineArray = std::array< std::array>, mtl_shader::kTextureTypeCount * 2>, angle::kNumANGLEFormats>; PixelsCopyPipelineArray mPixelsCopyPipelineCaches; const std::string mReadShaderName; const std::string mWriteShaderName; }; // Util class for handling vertex format conversion on GPU class VertexFormatConversionUtils final : angle::NonCopyable { public: void onDestroy(); // Convert vertex format to float. Compute shader version. angle::Result convertVertexFormatToFloatCS(ContextMtl *contextMtl, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); // Convert vertex format to float. Vertex shader version. This version should be used if // a render pass is active and we don't want to break it. Explicit memory barrier must be // supported. angle::Result convertVertexFormatToFloatVS(const gl::Context *context, RenderCommandEncoder *renderEncoder, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); // Expand number of components per vertex's attribute (or just simply copy components between // buffers with different stride and offset) angle::Result expandVertexFormatComponentsCS(ContextMtl *contextMtl, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); angle::Result expandVertexFormatComponentsVS(const gl::Context *context, RenderCommandEncoder *renderEncoder, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); private: void ensureComponentsExpandComputePipelineCreated(ContextMtl *contextMtl); AutoObjCPtr> getComponentsExpandRenderPipeline( ContextMtl *contextMtl, RenderCommandEncoder *renderEncoder); AutoObjCPtr> getFloatConverstionComputePipeline( ContextMtl *contextMtl, const angle::Format &srcAngleFormat); AutoObjCPtr> getFloatConverstionRenderPipeline( ContextMtl *contextMtl, RenderCommandEncoder *renderEncoder, const angle::Format &srcAngleFormat); template angle::Result setupCommonConvertVertexFormatToFloat(ContextMtl *contextMtl, EncoderType cmdEncoder, const PipelineType &pipeline, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); template angle::Result setupCommonExpandVertexFormatComponents(ContextMtl *contextMtl, EncoderType cmdEncoder, const PipelineType &pipeline, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); using ConvertToFloatCompPipelineArray = std::array>, angle::kNumANGLEFormats>; using ConvertToFloatRenderPipelineArray = std::array; ConvertToFloatCompPipelineArray mConvertToFloatCompPipelineCaches; ConvertToFloatRenderPipelineArray mConvertToFloatRenderPipelineCaches; AutoObjCPtr> mComponentsExpandCompPipeline; RenderPipelineCache mComponentsExpandRenderPipelineCache; }; // Util class for handling transform feedback class TransformFeedbackUtils { public: void onDestroy(); AutoObjCPtr> getTransformFeedbackRenderPipeline( ContextMtl *contextMtl, RenderCommandEncoder *cmdEncoder, mtl::RenderPipelineDesc &pipelineDesc); private: AutoObjCPtr> createMslXfbLibrary(ContextMtl *contextMtl, const std::string &translatedMsl); }; // RenderUtils: container class of various util classes above class RenderUtils : public Context, angle::NonCopyable { public: RenderUtils(DisplayMtl *display); ~RenderUtils() override; angle::Result initialize(); void onDestroy(); // Clear current framebuffer angle::Result clearWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const ClearRectParams ¶ms); // Blit texture data to current framebuffer angle::Result blitColorWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const angle::Format &srcAngleFormat, const ColorBlitParams ¶ms); // Same as above but blit the whole texture to the whole of current framebuffer. // This function assumes the framebuffer and the source texture have same size. angle::Result blitColorWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const angle::Format &srcAngleFormat, const TextureRef &srcTexture); angle::Result copyTextureWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const angle::Format &srcAngleFormat, const angle::Format &dstAngleFormat, const ColorBlitParams ¶ms); angle::Result blitDepthStencilWithDraw(const gl::Context *context, RenderCommandEncoder *cmdEncoder, const DepthStencilBlitParams ¶ms); // See DepthStencilBlitUtils::blitStencilViaCopyBuffer() angle::Result blitStencilViaCopyBuffer(const gl::Context *context, const StencilBlitViaBufferParams ¶ms); // See IndexGeneratorUtils angle::Result convertIndexBufferGPU(ContextMtl *contextMtl, const IndexConversionParams ¶ms); angle::Result generateTriFanBufferFromArrays(ContextMtl *contextMtl, const TriFanOrLineLoopFromArrayParams ¶ms); angle::Result generateTriFanBufferFromElementsArray(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, uint32_t *indicesGenerated); angle::Result generateLineLoopBufferFromArrays(ContextMtl *contextMtl, const TriFanOrLineLoopFromArrayParams ¶ms); angle::Result generateLineLoopLastSegment(ContextMtl *contextMtl, uint32_t firstVertex, uint32_t lastVertex, const BufferRef &dstBuffer, uint32_t dstOffset); angle::Result generateLineLoopBufferFromElementsArray(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, uint32_t *indicesGenerated); angle::Result generateLineLoopLastSegmentFromElementsArray(ContextMtl *contextMtl, const IndexGenerationParams ¶ms); void combineVisibilityResult(ContextMtl *contextMtl, bool keepOldValue, const VisibilityBufferOffsetsMtl &renderPassResultBufOffsets, const BufferRef &renderPassResultBuf, const BufferRef &finalResultBuf); // Compute based mipmap generation. Only possible for 3D texture for now. angle::Result generateMipmapCS(ContextMtl *contextMtl, const TextureRef &srcTexture, bool sRGBMipmap, NativeTexLevelArray *mipmapOutputViews); angle::Result unpackPixelsFromBufferToTexture(ContextMtl *contextMtl, const angle::Format &srcAngleFormat, const CopyPixelsFromBufferParams ¶ms); angle::Result packPixelsFromTextureToBuffer(ContextMtl *contextMtl, const angle::Format &dstAngleFormat, const CopyPixelsToBufferParams ¶ms); // See VertexFormatConversionUtils::convertVertexFormatToFloatCS() angle::Result convertVertexFormatToFloatCS(ContextMtl *contextMtl, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); // See VertexFormatConversionUtils::convertVertexFormatToFloatVS() angle::Result convertVertexFormatToFloatVS(const gl::Context *context, RenderCommandEncoder *renderEncoder, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); // See VertexFormatConversionUtils::expandVertexFormatComponentsCS() angle::Result expandVertexFormatComponentsCS(ContextMtl *contextMtl, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); // See VertexFormatConversionUtils::expandVertexFormatComponentsVS() angle::Result expandVertexFormatComponentsVS(const gl::Context *context, RenderCommandEncoder *renderEncoder, const angle::Format &srcAngleFormat, const VertexFormatConvertParams ¶ms); angle::Result createTransformFeedbackPSO(const gl::Context *context, RenderCommandEncoder *renderEncoder, mtl::RenderPipelineDesc &pipelineDesc); angle::Result generatePrimitiveRestartPointsBuffer(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, size_t *indicesGenerated); angle::Result generatePrimitiveRestartLinesBuffer(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, size_t *indicesGenerated); angle::Result generatePrimitiveRestartTrianglesBuffer(ContextMtl *contextMtl, const IndexGenerationParams ¶ms, size_t *indicesGenerated); private: // override ErrorHandler void handleError(GLenum error, const char *file, const char *function, unsigned int line) override; void handleError(NSError *error, const char *file, const char *function, unsigned int line) override; std::array()> mClearUtils; std::array()> mColorBlitUtils; ColorBlitUtils mCopyTextureFloatToUIntUtils; DepthStencilBlitUtils mDepthStencilBlitUtils; IndexGeneratorUtils mIndexUtils; VisibilityResultUtils mVisibilityResultUtils; MipmapUtils mMipmapUtils; std::array()> mCopyPixelsUtils; VertexFormatConversionUtils mVertexFormatUtils; TransformFeedbackUtils mTransformFeedbackUtils; }; } // namespace mtl } // namespace rx #endif /* LIBANGLE_RENDERER_METAL_MTL_RENDER_UTILS_H_ */