// // 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. // // RenderBufferMtl.mm: // Implements the class methods for RenderBufferMtl. // #include "libANGLE/renderer/metal/RenderBufferMtl.h" #include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/ImageMtl.h" #include "libANGLE/renderer/metal/mtl_format_utils.h" #include "libANGLE/renderer/metal/mtl_utils.h" namespace rx { RenderbufferMtl::RenderbufferMtl(const gl::RenderbufferState &state) : RenderbufferImpl(state) {} RenderbufferMtl::~RenderbufferMtl() {} void RenderbufferMtl::onDestroy(const gl::Context *context) { releaseTexture(); } void RenderbufferMtl::releaseTexture() { mTexture = nullptr; mImplicitMSTexture = nullptr; } angle::Result RenderbufferMtl::setStorageImpl(const gl::Context *context, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { ContextMtl *contextMtl = mtl::GetImpl(context); if (mTexture != nullptr && mTexture->valid()) { // Check against the state if we need to recreate the storage. if (internalformat != mState.getFormat().info->internalFormat || width != mState.getWidth() || height != mState.getHeight() || samples != mState.getSamples()) { releaseTexture(); } } const gl::InternalFormat &internalFormat = gl::GetSizedInternalFormatInfo(internalformat); angle::FormatID angleFormatId = angle::Format::InternalFormatToID(internalFormat.sizedInternalFormat); mFormat = contextMtl->getPixelFormat(angleFormatId); uint32_t actualSamples; if (samples == 0) { actualSamples = 1; } else { // We always start at at least 2 samples actualSamples = static_cast(std::max(2, samples)); const gl::TextureCaps &textureCaps = contextMtl->getTextureCaps().get(mFormat.intendedFormatId); actualSamples = textureCaps.getNearestSamples(actualSamples); ANGLE_MTL_CHECK(contextMtl, actualSamples != 0, GL_INVALID_VALUE); } if ((mTexture == nullptr || !mTexture->valid()) && (width != 0 && height != 0)) { if (actualSamples == 1 || (mFormat.getCaps().resolve)) { ANGLE_TRY(mtl::Texture::Make2DTexture( contextMtl, mFormat, static_cast(width), static_cast(height), 1, /* renderTargetOnly */ false, /* allowFormatView */ mFormat.hasDepthAndStencilBits(), &mTexture)); // Use implicit resolve for depth stencil texture whenever possible. This is because // for depth stencil texture, if stencil needs to be blitted, a formatted clone has // to be created. And it is expensive to clone a multisample texture. if (actualSamples > 1) { // This format must supports implicit resolve ASSERT(mFormat.getCaps().resolve); ANGLE_TRY(mtl::Texture::Make2DMSTexture( contextMtl, mFormat, static_cast(width), static_cast(height), actualSamples, /* renderTargetOnly */ true, /* allowFormatView */ mFormat.hasDepthAndStencilBits(), &mImplicitMSTexture)); } } else { ANGLE_TRY(mtl::Texture::Make2DMSTexture( contextMtl, mFormat, static_cast(width), static_cast(height), actualSamples, /* renderTargetOnly */ false, /* allowFormatView */ mFormat.hasDepthAndStencilBits(), &mTexture)); } mRenderTarget.setWithImplicitMSTexture(mTexture, mImplicitMSTexture, mtl::kZeroNativeMipLevel, 0, mFormat); // For emulated channels that GL texture intends to not have, // we need to initialize their content. bool emulatedChannels = mtl::IsFormatEmulated(mFormat); if (emulatedChannels) { gl::ImageIndex index; if (actualSamples > 1) { index = gl::ImageIndex::Make2DMultisample(); } else { index = gl::ImageIndex::Make2D(0); } ANGLE_TRY(mtl::InitializeTextureContents(context, mTexture, mFormat, mtl::ImageNativeIndex(index, 0))); if (mImplicitMSTexture) { ANGLE_TRY(mtl::InitializeTextureContents( context, mImplicitMSTexture, mFormat, mtl::ImageNativeIndex(gl::ImageIndex::Make2DMultisample(), 0))); } } // if (emulatedChannels) bool isDepthStencil = mFormat.hasDepthOrStencilBits(); if (isDepthStencil) { gl::ImageIndex index; if (actualSamples > 1) { index = gl::ImageIndex::Make2DMultisample(); } else { index = gl::ImageIndex::Make2D(0); } ANGLE_TRY(mtl::InitializeDepthStencilTextureContentsGPU( context, mTexture, mFormat, mtl::ImageNativeIndex(index, 0))); if (mImplicitMSTexture) { ANGLE_TRY(mtl::InitializeDepthStencilTextureContentsGPU( context, mImplicitMSTexture, mFormat, mtl::ImageNativeIndex(gl::ImageIndex::Make2DMultisample(), 0))); } } } return angle::Result::Continue; } angle::Result RenderbufferMtl::setStorage(const gl::Context *context, GLenum internalformat, GLsizei width, GLsizei height) { return setStorageImpl(context, 0, internalformat, width, height); } angle::Result RenderbufferMtl::setStorageMultisample(const gl::Context *context, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, gl::MultisamplingMode mode) { return setStorageImpl(context, samples, internalformat, width, height); } angle::Result RenderbufferMtl::setStorageEGLImageTarget(const gl::Context *context, egl::Image *image) { releaseTexture(); ContextMtl *contextMtl = mtl::GetImpl(context); ImageMtl *imageMtl = mtl::GetImpl(image); mTexture = imageMtl->getTexture(); const angle::FormatID angleFormatId = angle::Format::InternalFormatToID(image->getFormat().info->sizedInternalFormat); mFormat = contextMtl->getPixelFormat(angleFormatId); mRenderTarget.set(mTexture, mtl::kZeroNativeMipLevel, 0, mFormat); return angle::Result::Continue; } angle::Result RenderbufferMtl::getAttachmentRenderTarget(const gl::Context *context, GLenum binding, const gl::ImageIndex &imageIndex, GLsizei samples, FramebufferAttachmentRenderTarget **rtOut) { ASSERT(mTexture && mTexture->valid()); *rtOut = &mRenderTarget; return angle::Result::Continue; } angle::Result RenderbufferMtl::initializeContents(const gl::Context *context, const gl::ImageIndex &imageIndex) { if (imageIndex.valid()) return mtl::InitializeTextureContents( context, mTexture, mFormat, mtl::ImageNativeIndex::FromBaseZeroGLIndex(imageIndex)); else return mtl::InitializeTextureContents( context, mTexture, mFormat, mtl::ImageNativeIndex::FromBaseZeroGLIndex(gl::ImageIndex::Make2D(0))); } }