Commit 80d4901a by Le Hoang Quyen Committed by Commit Bot

Metal: Support integer textures.

Bug: angleproject:2634 Bug: angleproject:5154 Change-Id: Iffea26fe2c683557b4fa7c13fddf3523294b47d4 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2433329 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 96714af8
......@@ -297,7 +297,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
void queueEventSignal(const mtl::SharedEventRef &event, uint64_t value);
void serverWaitEvent(const mtl::SharedEventRef &event, uint64_t value);
const MTLClearColor &getClearColorValue() const;
const mtl::ClearColorValue &getClearColorValue() const;
MTLColorWriteMask getColorMask() const;
float getClearDepthValue() const;
uint32_t getClearStencilValue() const;
......@@ -529,7 +529,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
mtl::RenderPipelineDesc mRenderPipelineDesc;
mtl::DepthStencilDesc mDepthStencilDesc;
mtl::BlendDesc mBlendDesc;
MTLClearColor mClearColor;
mtl::ClearColorValue mClearColor;
uint32_t mClearStencil = 0;
uint32_t mStencilRefFront = 0;
uint32_t mStencilRefBack = 0;
......
......@@ -780,10 +780,9 @@ angle::Result ContextMtl::syncState(const gl::Context *context,
// NOTE(hqle): ES 3.0 feature.
break;
case gl::State::DIRTY_BIT_CLEAR_COLOR:
mClearColor.red = glState.getColorClearValue().red;
mClearColor.green = glState.getColorClearValue().green;
mClearColor.blue = glState.getColorClearValue().blue;
mClearColor.alpha = glState.getColorClearValue().alpha;
mClearColor = mtl::ClearColorValue(
glState.getColorClearValue().red, glState.getColorClearValue().green,
glState.getColorClearValue().blue, glState.getColorClearValue().alpha);
break;
case gl::State::DIRTY_BIT_CLEAR_DEPTH:
break;
......@@ -1123,7 +1122,7 @@ void ContextMtl::invalidateRenderPipeline()
mDirtyBits.set(DIRTY_BIT_RENDER_PIPELINE);
}
const MTLClearColor &ContextMtl::getClearColorValue() const
const mtl::ClearColorValue &ContextMtl::getClearColorValue() const
{
return mClearColor;
}
......
......@@ -120,6 +120,7 @@ class FramebufferMtl : public FramebufferImpl
private:
void reset();
bool checkPackedDepthStencilAttachment() const;
angle::Result invalidateImpl(ContextMtl *contextMtl, size_t count, const GLenum *attachments);
angle::Result blitWithDraw(const gl::Context *context,
FramebufferMtl *srcFrameBuffer,
......@@ -161,10 +162,6 @@ class FramebufferMtl : public FramebufferImpl
mtl::RenderCommandEncoder *ensureRenderPassStarted(const gl::Context *context,
const mtl::RenderPassDesc &desc);
void overrideClearColor(const mtl::TextureRef &texture,
MTLClearColor clearColor,
MTLClearColor *colorOut);
angle::Result updateColorRenderTarget(const gl::Context *context, size_t colorIndexGL);
angle::Result updateDepthRenderTarget(const gl::Context *context);
angle::Result updateStencilRenderTarget(const gl::Context *context);
......@@ -185,6 +182,9 @@ class FramebufferMtl : public FramebufferImpl
RenderTargetMtl *mStencilRenderTarget = nullptr;
mtl::RenderPassDesc mRenderPassDesc;
const mtl::Format *mRenderPassFirstColorAttachmentFormat = nullptr;
bool mRenderPassAttachmentsSameColorType = false;
// Flag indicating the render pass start is a clean start or a resume from interruption such
// as by a compute pass.
bool mRenderPassCleanStart = false;
......
......@@ -23,6 +23,18 @@
namespace rx
{
namespace
{
// Override clear color based on texture's write mask
void OverrideMTLClearColor(const mtl::TextureRef &texture,
const mtl::ClearColorValue &clearColor,
MTLClearColor *colorOut)
{
*colorOut =
mtl::EmulatedAlphaClearColor(clearColor.toMTLClearColor(), texture->getColorWritableMask());
}
}
// FramebufferMtl implementation
FramebufferMtl::FramebufferMtl(const gl::FramebufferState &state,
bool flipY,
......@@ -41,6 +53,8 @@ void FramebufferMtl::reset()
rt = nullptr;
}
mDepthRenderTarget = mStencilRenderTarget = nullptr;
mRenderPassFirstColorAttachmentFormat = nullptr;
}
void FramebufferMtl::destroy(const gl::Context *context)
......@@ -67,9 +81,11 @@ angle::Result FramebufferMtl::invalidateSub(const gl::Context *context,
const GLenum *attachments,
const gl::Rectangle &area)
{
// NOTE(hqle): ES 3.0 feature.
UNIMPLEMENTED();
return angle::Result::Stop;
if (area.encloses(getCompleteRenderArea()))
{
return invalidateImpl(mtl::GetImpl(context), count, attachments);
}
return angle::Result::Continue;
}
angle::Result FramebufferMtl::clear(const gl::Context *context, GLbitfield mask)
......@@ -105,27 +121,53 @@ angle::Result FramebufferMtl::clearBufferfv(const gl::Context *context,
GLint drawbuffer,
const GLfloat *values)
{
// NOTE(hqle): ES 3.0 feature.
UNIMPLEMENTED();
return angle::Result::Stop;
mtl::ClearRectParams clearOpts;
gl::DrawBufferMask clearColorBuffers;
if (buffer == GL_DEPTH)
{
clearOpts.clearDepth = values[0];
}
else
{
clearColorBuffers.set(drawbuffer);
clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]);
}
return clearImpl(context, clearColorBuffers, &clearOpts);
}
angle::Result FramebufferMtl::clearBufferuiv(const gl::Context *context,
GLenum buffer,
GLint drawbuffer,
const GLuint *values)
{
// NOTE(hqle): ES 3.0 feature.
UNIMPLEMENTED();
return angle::Result::Stop;
gl::DrawBufferMask clearColorBuffers;
clearColorBuffers.set(drawbuffer);
mtl::ClearRectParams clearOpts;
clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]);
return clearImpl(context, clearColorBuffers, &clearOpts);
}
angle::Result FramebufferMtl::clearBufferiv(const gl::Context *context,
GLenum buffer,
GLint drawbuffer,
const GLint *values)
{
// NOTE(hqle): ES 3.0 feature.
UNIMPLEMENTED();
return angle::Result::Stop;
mtl::ClearRectParams clearOpts;
gl::DrawBufferMask clearColorBuffers;
if (buffer == GL_STENCIL)
{
clearOpts.clearStencil = values[0] & mtl::kStencilMaskAll;
}
else
{
clearColorBuffers.set(drawbuffer);
clearOpts.clearColor = mtl::ClearColorValue(values[0], values[1], values[2], values[3]);
}
return clearImpl(context, clearColorBuffers, &clearOpts);
}
angle::Result FramebufferMtl::clearBufferfi(const gl::Context *context,
GLenum buffer,
......@@ -133,9 +175,11 @@ angle::Result FramebufferMtl::clearBufferfi(const gl::Context *context,
GLfloat depth,
GLint stencil)
{
// NOTE(hqle): ES 3.0 feature.
UNIMPLEMENTED();
return angle::Result::Stop;
mtl::ClearRectParams clearOpts;
clearOpts.clearDepth = depth;
clearOpts.clearStencil = stencil & mtl::kStencilMaskAll;
return clearImpl(context, gl::DrawBufferMask(), &clearOpts);
}
const gl::InternalFormat &FramebufferMtl::getImplementationColorReadFormat(
......@@ -188,6 +232,13 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
PackPixelsParams params(flippedArea, angleFormat, outputPitch, pack.reverseRowOrder, packBuffer,
0);
if (params.packBuffer)
{
// If PBO is active, pixels is treated as offset.
params.offset = reinterpret_cast<ptrdiff_t>(pixels);
}
if (mFlipY)
{
params.reverseRowOrder = !params.reverseRowOrder;
......@@ -410,8 +461,8 @@ angle::Result FramebufferMtl::blitWithDraw(const gl::Context *context,
colorBlitParams.filter = filter;
colorBlitParams.dstLuminance = srcColorRt->getFormat()->actualAngleFormat().isLUMA();
ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(context, renderEncoder,
colorBlitParams));
ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(
context, renderEncoder, srcColorRt->getFormat()->actualAngleFormat(), colorBlitParams));
}
return angle::Result::Continue;
......@@ -431,19 +482,50 @@ bool FramebufferMtl::checkStatus(const gl::Context *context) const
return false;
}
if (mState.getDepthAttachment() && mState.getDepthAttachment()->getFormat().info->depthBits &&
mState.getDepthAttachment()->getFormat().info->stencilBits)
{
return checkPackedDepthStencilAttachment();
}
if (mState.getStencilAttachment() &&
mState.getStencilAttachment()->getFormat().info->depthBits &&
mState.getStencilAttachment()->getFormat().info->stencilBits &&
mState.hasSeparateDepthAndStencilAttachments())
mState.getStencilAttachment()->getFormat().info->stencilBits)
{
// If stencil attachment has depth & stencil bits, it must refer to the same texture
// as depth attachment.
return false;
return checkPackedDepthStencilAttachment();
}
return true;
}
bool FramebufferMtl::checkPackedDepthStencilAttachment() const
{
if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.0, 12.0))
{
// If depth/stencil attachment has depth & stencil bits, then depth & stencil must not have
// separate attachment. i.e. They must be the same texture or one of them has no
// attachment.
if (mState.hasSeparateDepthAndStencilAttachments())
{
WARN() << "Packed depth stencil texture/buffer must not be mixed with other "
"texture/buffer.";
return false;
}
}
else
{
// Metal 2.0 and below doesn't allow packed depth stencil texture to be attached only as
// depth or stencil buffer. i.e. None of the depth & stencil attachment can be null.
if (!mState.getDepthStencilAttachment())
{
WARN() << "Packed depth stencil texture/buffer must be bound to both depth & stencil "
"attachment point.";
return false;
}
}
return true;
}
angle::Result FramebufferMtl::syncState(const gl::Context *context,
GLenum binding,
const gl::Framebuffer::DirtyBits &dirtyBits,
......@@ -735,6 +817,8 @@ angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
{
mtl::RenderPassDesc &desc = *pDescOut;
mRenderPassFirstColorAttachmentFormat = nullptr;
mRenderPassAttachmentsSameColorType = true;
uint32_t maxColorAttachments = static_cast<uint32_t>(mState.getColorAttachments().size());
desc.numColorAttachments = 0;
desc.sampleCount = 1;
......@@ -751,6 +835,21 @@ angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
desc.numColorAttachments = std::max(desc.numColorAttachments, colorIndexGL + 1);
desc.sampleCount = std::max(desc.sampleCount, colorRenderTarget->getRenderSamples());
if (!mRenderPassFirstColorAttachmentFormat)
{
mRenderPassFirstColorAttachmentFormat = colorRenderTarget->getFormat();
}
else if (colorRenderTarget->getFormat())
{
if (mRenderPassFirstColorAttachmentFormat->actualAngleFormat().isSint() !=
colorRenderTarget->getFormat()->actualAngleFormat().isSint() ||
mRenderPassFirstColorAttachmentFormat->actualAngleFormat().isUint() !=
colorRenderTarget->getFormat()->actualAngleFormat().isUint())
{
mRenderPassAttachmentsSameColorType = false;
}
}
}
else
{
......@@ -781,14 +880,6 @@ angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
return angle::Result::Continue;
}
// Override clear color based on texture's write mask
void FramebufferMtl::overrideClearColor(const mtl::TextureRef &texture,
MTLClearColor clearColor,
MTLClearColor *colorOut)
{
*colorOut = mtl::EmulatedAlphaClearColor(clearColor, texture->getColorWritableMask());
}
angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts)
......@@ -837,7 +928,8 @@ angle::Result FramebufferMtl::clearWithLoadOpRenderPassNotStarted(
if (clearColorBuffers.test(colorIndexGL))
{
colorAttachment.loadAction = MTLLoadActionClear;
overrideClearColor(texture, clearOpts.clearColor.value(), &colorAttachment.clearColor);
OverrideMTLClearColor(texture, clearOpts.clearColor.value(),
&colorAttachment.clearColor);
}
}
......@@ -879,7 +971,7 @@ angle::Result FramebufferMtl::clearWithLoadOpRenderPassStarted(
if (clearColorBuffers.test(colorIndexGL))
{
MTLClearColor clearVal;
overrideClearColor(texture, clearOpts.clearColor.value(), &clearVal);
OverrideMTLClearColor(texture, clearOpts.clearColor.value(), &clearVal);
encoder->setColorLoadAction(MTLLoadActionClear, clearVal, colorIndexGL);
}
......@@ -905,10 +997,56 @@ angle::Result FramebufferMtl::clearWithDraw(const gl::Context *context,
ContextMtl *contextMtl = mtl::GetImpl(context);
DisplayMtl *display = contextMtl->getDisplay();
// Start new render encoder if not already.
mtl::RenderCommandEncoder *encoder = ensureRenderPassStarted(context, mRenderPassDesc);
if (mRenderPassAttachmentsSameColorType)
{
// Start new render encoder if not already.
mtl::RenderCommandEncoder *encoder = ensureRenderPassStarted(context, mRenderPassDesc);
return display->getUtils().clearWithDraw(context, encoder, clearOpts);
}
// Not all attachments have the same color type.
mtl::ClearRectParams overrideClearOps = clearOpts;
overrideClearOps.enabledBuffers.reset();
// First clear depth/stencil without color attachment
if (clearOpts.clearDepth.valid() || clearOpts.clearStencil.valid())
{
mtl::RenderPassDesc dsOnlyDesc = mRenderPassDesc;
dsOnlyDesc.numColorAttachments = 0;
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(dsOnlyDesc);
ANGLE_TRY(display->getUtils().clearWithDraw(context, encoder, overrideClearOps));
}
// Clear the color attachment one by one.
overrideClearOps.enabledBuffers.set(0);
for (size_t drawbuffer : clearColorBuffers)
{
if (drawbuffer >= mRenderPassDesc.numColorAttachments)
{
continue;
}
RenderTargetMtl *renderTarget = mColorRenderTargets[drawbuffer];
if (!renderTarget || !renderTarget->getTexture())
{
continue;
}
const mtl::Format &format = *renderTarget->getFormat();
mtl::PixelType clearColorType = overrideClearOps.clearColor.value().getType();
if ((clearColorType == mtl::PixelType::Int && !format.actualAngleFormat().isSint()) ||
(clearColorType == mtl::PixelType::UInt && !format.actualAngleFormat().isUint()) ||
(clearColorType == mtl::PixelType::Float && format.actualAngleFormat().isInt()))
{
continue;
}
return display->getUtils().clearWithDraw(context, encoder, clearOpts);
mtl::RenderCommandEncoder *encoder =
contextMtl->getRenderTargetCommandEncoder(*renderTarget);
ANGLE_TRY(display->getUtils().clearWithDraw(context, encoder, overrideClearOps));
}
return angle::Result::Continue;
}
angle::Result FramebufferMtl::clearImpl(const gl::Context *context,
......@@ -928,6 +1066,7 @@ angle::Result FramebufferMtl::clearImpl(const gl::Context *context,
const gl::Rectangle renderArea(0, 0, mState.getDimensions().width,
mState.getDimensions().height);
clearOpts.colorFormat = mRenderPassFirstColorAttachmentFormat;
clearOpts.dstTextureSize = mState.getExtents();
clearOpts.clearArea = ClipRectToScissor(contextMtl->getState(), renderArea, false);
clearOpts.flipY = mFlipY;
......
......@@ -77,5 +77,6 @@ void RenderTargetMtl::toRenderPassAttachmentDesc(mtl::RenderPassAttachmentDesc *
rpaDescOut->implicitMSTexture = mImplicitMSTexture.lock();
rpaDescOut->level = mLevelIndex;
rpaDescOut->sliceOrDepth = mLayerIndex;
rpaDescOut->blendable = mFormat ? mFormat->getCaps().blendable : false;
}
}
......@@ -546,8 +546,8 @@ angle::Result SurfaceMtl::resolveColorTextureIfNeeded(const gl::Context *context
mColorFormat);
mtl::RenderCommandEncoder *encoder =
contextMtl->getRenderTargetCommandEncoder(mColorManualResolveRenderTarget);
ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(context, encoder,
mMSColorTexture));
ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(
context, encoder, mColorFormat.actualAngleFormat(), mMSColorTexture));
contextMtl->endEncoding(true);
mColorManualResolveRenderTarget.reset();
}
......
......@@ -1780,7 +1780,8 @@ angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
blitParams.srcYFlipped = framebufferMtl->flipY();
blitParams.dstLuminance = internalFormat.isLUMA();
return displayMtl->getUtils().blitColorWithDraw(context, cmdEncoder, blitParams);
return displayMtl->getUtils().blitColorWithDraw(
context, cmdEncoder, colorReadRT->getFormat()->actualAngleFormat(), blitParams);
}
angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
......@@ -1937,7 +1938,8 @@ angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
blitParams.unpackPremultiplyAlpha = unpackPremultiplyAlpha;
blitParams.unpackUnmultiplyAlpha = unpackUnmultiplyAlpha;
return displayMtl->getUtils().blitColorWithDraw(context, cmdEncoder, blitParams);
return displayMtl->getUtils().copyTextureWithDraw(context, cmdEncoder, sourceAngleFormat,
mFormat.actualAngleFormat(), blitParams);
}
angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context,
......
......@@ -156,7 +156,7 @@ constexpr uint32_t kStencilMaskAll = 0xff; // Only 8 bits stencil is supported
constexpr MTLVertexStepFunction kVertexStepFunctionInvalid =
static_cast<MTLVertexStepFunction>(0xff);
constexpr float kEmulatedAlphaValue = 1.0f;
constexpr int kEmulatedAlphaValue = 1;
constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t);
......@@ -404,11 +404,59 @@ class ImageNativeIndexIterator final
gl::ImageIndexIterator mNativeIndexIte;
};
struct ClearOptions
using ClearColorValueBytes = std::array<uint8_t, 4 * sizeof(float)>;
class ClearColorValue
{
Optional<MTLClearColor> clearColor;
Optional<float> clearDepth;
Optional<uint32_t> clearStencil;
public:
constexpr ClearColorValue()
: mType(PixelType::Float), mRedF(0), mGreenF(0), mBlueF(0), mAlphaF(0)
{}
constexpr ClearColorValue(float r, float g, float b, float a)
: mType(PixelType::Float), mRedF(r), mGreenF(g), mBlueF(b), mAlphaF(a)
{}
constexpr ClearColorValue(int32_t r, int32_t g, int32_t b, int32_t a)
: mType(PixelType::Int), mRedI(r), mGreenI(g), mBlueI(b), mAlphaI(a)
{}
constexpr ClearColorValue(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
: mType(PixelType::UInt), mRedU(r), mGreenU(g), mBlueU(b), mAlphaU(a)
{}
constexpr ClearColorValue(const ClearColorValue &src)
: mType(src.mType), mValueBytes(src.mValueBytes)
{}
MTLClearColor toMTLClearColor() const;
PixelType getType() const { return mType; }
const ClearColorValueBytes &getValueBytes() const { return mValueBytes; }
ClearColorValue &operator=(const ClearColorValue &src);
void setAsFloat(float r, float g, float b, float a);
void setAsInt(int32_t r, int32_t g, int32_t b, int32_t a);
void setAsUInt(uint32_t r, uint32_t g, uint32_t b, uint32_t a);
private:
PixelType mType;
union
{
struct
{
float mRedF, mGreenF, mBlueF, mAlphaF;
};
struct
{
int32_t mRedI, mGreenI, mBlueI, mAlphaI;
};
struct
{
uint32_t mRedU, mGreenU, mBlueU, mAlphaU;
};
ClearColorValueBytes mValueBytes;
};
};
class CommandQueue;
......
......@@ -21,6 +21,58 @@ namespace rx
namespace mtl
{
// ClearColorValue implementation
ClearColorValue &ClearColorValue::operator=(const ClearColorValue &src)
{
mType = src.mType;
mValueBytes = src.mValueBytes;
return *this;
}
void ClearColorValue::setAsFloat(float r, float g, float b, float a)
{
mType = PixelType::Float;
mRedF = r;
mGreenF = g;
mBlueF = b;
mAlphaF = a;
}
void ClearColorValue::setAsInt(int32_t r, int32_t g, int32_t b, int32_t a)
{
mType = PixelType::Int;
mRedI = r;
mGreenI = g;
mBlueI = b;
mAlphaI = a;
}
void ClearColorValue::setAsUInt(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
{
mType = PixelType::UInt;
mRedU = r;
mGreenU = g;
mBlueU = b;
mAlphaU = a;
}
MTLClearColor ClearColorValue::toMTLClearColor() const
{
switch (mType)
{
case PixelType::Int:
return MTLClearColorMake(mRedI, mGreenI, mBlueI, mAlphaI);
case PixelType::UInt:
return MTLClearColorMake(mRedU, mGreenU, mBlueU, mAlphaU);
case PixelType::Float:
return MTLClearColorMake(mRedF, mGreenF, mBlueF, mAlphaF);
default:
UNREACHABLE();
return MTLClearColorMake(0, 0, 0, 0);
}
}
// ImageNativeIndex implementation
ImageNativeIndexIterator ImageNativeIndex::getLayerIterator(GLint layerCount) const
{
......
......@@ -29,10 +29,16 @@ class VisibilityBufferOffsetsMtl;
namespace mtl
{
struct ClearRectParams : public ClearOptions
struct ClearRectParams
{
Optional<ClearColorValue> clearColor;
Optional<float> clearDepth;
Optional<uint32_t> clearStencil;
MTLColorWriteMask clearColorMask = MTLColorWriteMaskAll;
const mtl::Format *colorFormat = nullptr;
gl::Extents dstTextureSize;
// Only clear enabled buffers
......@@ -159,7 +165,9 @@ struct CopyPixelsToBufferParams : CopyPixelsCommonParams
class ClearUtils final : angle::NonCopyable
{
public:
ClearUtils();
ClearUtils() = delete;
ClearUtils(const std::string &fragmentShaderName);
ClearUtils(const ClearUtils &src);
void onDestroy();
......@@ -180,6 +188,8 @@ class ClearUtils final : angle::NonCopyable
RenderCommandEncoder *cmdEncoder,
const ClearRectParams &params);
const std::string mFragmentShaderName;
// Render pipeline cache for clear with draw:
std::array<RenderPipelineCache, kMaxRenderTargets + 1> mClearRenderPipelineCache;
};
......@@ -187,7 +197,9 @@ class ClearUtils final : angle::NonCopyable
class ColorBlitUtils final : angle::NonCopyable
{
public:
ColorBlitUtils();
ColorBlitUtils() = delete;
ColorBlitUtils(const std::string &fragmentShaderName);
ColorBlitUtils(const ColorBlitUtils &src);
void onDestroy();
......@@ -211,6 +223,8 @@ class ColorBlitUtils final : angle::NonCopyable
RenderCommandEncoder *cmdEncoder,
const ColorBlitParams &params);
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)
......@@ -230,6 +244,7 @@ class DepthStencilBlitUtils final : angle::NonCopyable
angle::Result blitDepthStencilWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const DepthStencilBlitParams &params);
// 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.
......@@ -431,12 +446,19 @@ class RenderUtils : public Context, angle::NonCopyable
// Blit texture data to current framebuffer
angle::Result blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const angle::Format &srcAngleFormat,
const ColorBlitParams &params);
// 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 &params);
angle::Result blitDepthStencilWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
......@@ -490,8 +512,11 @@ class RenderUtils : public Context, angle::NonCopyable
const char *function,
unsigned int line) override;
ClearUtils mClearUtils;
ColorBlitUtils mColorBlitUtils;
std::array<ClearUtils, angle::EnumSize<PixelType>()> mClearUtils;
std::array<ColorBlitUtils, angle::EnumSize<PixelType>()> mColorBlitUtils;
ColorBlitUtils mCopyTextureFloatToUIntUtils;
DepthStencilBlitUtils mDepthStencilBlitUtils;
IndexGeneratorUtils mIndexUtils;
VisibilityResultUtils mVisibilityResultUtils;
......
......@@ -535,6 +535,11 @@ StencilBlitViaBufferParams::StencilBlitViaBufferParams(const DepthStencilBlitPar
// RenderUtils implementation
RenderUtils::RenderUtils(DisplayMtl *display)
: Context(display),
mClearUtils(
{ClearUtils("clearIntFS"), ClearUtils("clearUIntFS"), ClearUtils("clearFloatFS")}),
mColorBlitUtils({ColorBlitUtils("blitIntFS"), ColorBlitUtils("blitUIntFS"),
ColorBlitUtils("blitFloatFS")}),
mCopyTextureFloatToUIntUtils("copyTextureFloatToUIntFS"),
mCopyPixelsUtils(
{CopyPixelsUtils("readFromBufferToIntTexture", "writeFromIntTextureToBuffer"),
CopyPixelsUtils("readFromBufferToUIntTexture", "writeFromUIntTextureToBuffer"),
......@@ -550,11 +555,20 @@ angle::Result RenderUtils::initialize()
void RenderUtils::onDestroy()
{
mIndexUtils.onDestroy();
mClearUtils.onDestroy();
mColorBlitUtils.onDestroy();
mDepthStencilBlitUtils.onDestroy();
mIndexUtils.onDestroy();
mVisibilityResultUtils.onDestroy();
mMipmapUtils.onDestroy();
mCopyTextureFloatToUIntUtils.onDestroy();
for (ClearUtils &util : mClearUtils)
{
util.onDestroy();
}
for (ColorBlitUtils &util : mColorBlitUtils)
{
util.onDestroy();
}
for (CopyPixelsUtils &util : mCopyPixelsUtils)
{
util.onDestroy();
......@@ -590,19 +604,31 @@ angle::Result RenderUtils::clearWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ClearRectParams &params)
{
return mClearUtils.clearWithDraw(context, cmdEncoder, params);
int index = 0;
if (params.clearColor.valid())
{
index = static_cast<int>(params.clearColor.value().getType());
}
else if (params.colorFormat)
{
index = GetPixelTypeIndex(params.colorFormat->actualAngleFormat());
}
return mClearUtils[index].clearWithDraw(context, cmdEncoder, params);
}
// Blit texture data to current framebuffer
angle::Result RenderUtils::blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const angle::Format &srcAngleFormat,
const ColorBlitParams &params)
{
return mColorBlitUtils.blitColorWithDraw(context, cmdEncoder, params);
int index = GetPixelTypeIndex(srcAngleFormat);
return mColorBlitUtils[index].blitColorWithDraw(context, cmdEncoder, params);
}
angle::Result RenderUtils::blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const angle::Format &srcAngleFormat,
const TextureRef &srcTexture)
{
if (!srcTexture)
......@@ -618,7 +644,22 @@ angle::Result RenderUtils::blitColorWithDraw(const gl::Context *context,
params.dstRect = params.dstScissorRect = params.srcRect =
gl::Rectangle(0, 0, params.dstTextureSize.width, params.dstTextureSize.height);
return blitColorWithDraw(context, cmdEncoder, params);
return blitColorWithDraw(context, cmdEncoder, srcAngleFormat, params);
}
angle::Result RenderUtils::copyTextureWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const angle::Format &srcAngleFormat,
const angle::Format &dstAngleFormat,
const ColorBlitParams &params)
{
if (!srcAngleFormat.isInt() && dstAngleFormat.isUint())
{
return mCopyTextureFloatToUIntUtils.blitColorWithDraw(context, cmdEncoder, params);
}
ASSERT(srcAngleFormat.isSint() == dstAngleFormat.isSint() &&
srcAngleFormat.isUint() == dstAngleFormat.isUint());
return blitColorWithDraw(context, cmdEncoder, srcAngleFormat, params);
}
angle::Result RenderUtils::blitDepthStencilWithDraw(const gl::Context *context,
......@@ -705,7 +746,11 @@ angle::Result RenderUtils::packPixelsFromTextureToBuffer(ContextMtl *contextMtl,
}
// ClearUtils implementation
ClearUtils::ClearUtils() = default;
ClearUtils::ClearUtils(const std::string &fragmentShaderName)
: mFragmentShaderName(fragmentShaderName)
{}
ClearUtils::ClearUtils(const ClearUtils &src) : ClearUtils(src.mFragmentShaderName) {}
void ClearUtils::onDestroy()
{
......@@ -736,9 +781,10 @@ void ClearUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx, uint
type:MTLDataTypeUInt
withName:NUM_COLOR_OUTPUTS_CONSTANT_NAME];
id<MTLFunction> fragmentShader =
[[shaderLib newFunctionWithName:@"clearFloatFS" constantValues:funcConstants
error:&err] ANGLE_MTL_AUTORELEASE];
id<MTLFunction> fragmentShader = [[shaderLib
newFunctionWithName:[NSString stringWithUTF8String:mFragmentShaderName.c_str()]
constantValues:funcConstants
error:&err] ANGLE_MTL_AUTORELEASE];
ASSERT(fragmentShader);
cache.setVertexShader(ctx, vertexShader);
......@@ -840,11 +886,15 @@ void ClearUtils::setupClearWithDraw(const gl::Context *context,
// 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();
const ClearColorValue &clearValue = params.clearColor.value();
// ClearColorValue is an int, uint, float union so it's safe to use only floats.
// The Shader will do the bit cast based on appropriate format type.
// See shaders/clear.metal (3 variants ClearFloatFS, ClearIntFS and ClearUIntFS each does the
// appropriate bit cast)
ASSERT(sizeof(uniformParams.clearColor) == clearValue.getValueBytes().size());
std::memcpy(uniformParams.clearColor, clearValue.getValueBytes().data(),
clearValue.getValueBytes().size());
uniformParams.clearDepth = params.clearDepth.value();
cmdEncoder->setVertexData(uniformParams, 0);
cmdEncoder->setFragmentData(uniformParams, 0);
......@@ -893,7 +943,12 @@ angle::Result ClearUtils::clearWithDraw(const gl::Context *context,
}
// ColorBlitUtils implementation
ColorBlitUtils::ColorBlitUtils() = default;
ColorBlitUtils::ColorBlitUtils(const std::string &fragmentShaderName)
: mFragmentShaderName(fragmentShaderName)
{}
ColorBlitUtils::ColorBlitUtils(const ColorBlitUtils &src) : ColorBlitUtils(src.mFragmentShaderName)
{}
void ColorBlitUtils::onDestroy()
{
......@@ -952,9 +1007,10 @@ void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
type:MTLDataTypeInt
withName:SOURCE_TEXTURE_TYPE_CONSTANT_NAME];
id<MTLFunction> fragmentShader =
[[shaderLib newFunctionWithName:@"blitFloatFS" constantValues:funcConstants
error:&err] ANGLE_MTL_AUTORELEASE];
id<MTLFunction> fragmentShader = [[shaderLib
newFunctionWithName:[NSString stringWithUTF8String:mFragmentShaderName.c_str()]
constantValues:funcConstants
error:&err] ANGLE_MTL_AUTORELEASE];
ASSERT(vertexShader);
ASSERT(fragmentShader);
......
......@@ -303,6 +303,9 @@ struct RenderPassAttachmentDesc
MipmapNativeLevel level;
uint32_t sliceOrDepth;
// This attachment is blendable or not.
bool blendable;
MTLLoadAction loadAction;
MTLStoreAction storeAction;
MTLStoreActionOptions storeActionOptions;
......
......@@ -693,6 +693,7 @@ void RenderPassAttachmentDesc::reset()
implicitMSTexture.reset();
level = mtl::kZeroNativeMipLevel;
sliceOrDepth = 0;
blendable = false;
loadAction = MTLLoadActionLoad;
storeAction = MTLStoreActionStore;
storeActionOptions = MTLStoreActionOptionNone;
......@@ -746,6 +747,11 @@ void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendStat
{
// Copy parameters from blend state
outputDescriptor.colorAttachments[i].update(blendState);
if (!renderPassColorAttachment.blendable)
{
// Disable blending if the attachment's render target doesn't support blending.
outputDescriptor.colorAttachments[i].blendingEnabled = false;
}
outputDescriptor.colorAttachments[i].pixelFormat = texture->pixelFormat();
......
......@@ -210,17 +210,18 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context,
RenderTargetMtl tempRtt;
tempRtt.set(texture, index.getNativeLevel(), sliceOrDepth, textureObjFormat);
MTLClearColor blackColor = {};
int clearAlpha = 0;
if (!textureObjFormat.intendedAngleFormat().alphaBits)
{
// if intended format doesn't have alpha, set it to 1.0.
blackColor.alpha = kEmulatedAlphaValue;
clearAlpha = kEmulatedAlphaValue;
}
RenderCommandEncoder *encoder;
if (channelsToInit == MTLColorWriteMaskAll)
{
// If all channels will be initialized, use clear loadOp.
Optional<MTLClearColor> blackColor = MTLClearColorMake(0, 0, 0, clearAlpha);
encoder = contextMtl->getRenderTargetCommandEncoderWithClear(tempRtt, blackColor);
}
else
......@@ -233,8 +234,23 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context,
// If there are some channels don't need to be initialized, we must use clearWithDraw.
encoder = contextMtl->getRenderTargetCommandEncoder(tempRtt);
const angle::Format &angleFormat = textureObjFormat.actualAngleFormat();
ClearRectParams clearParams;
clearParams.clearColor = blackColor;
ClearColorValue clearColor;
if (angleFormat.isSint())
{
clearColor.setAsInt(0, 0, 0, clearAlpha);
}
else if (angleFormat.isUint())
{
clearColor.setAsUInt(0, 0, 0, clearAlpha);
}
else
{
clearColor.setAsFloat(0, 0, 0, clearAlpha);
}
clearParams.clearColor = clearColor;
clearParams.dstTextureSize = texture->sizeAt0();
clearParams.enabledBuffers.set(0);
clearParams.clearArea = gl::Rectangle(0, 0, texture->widthAt0(), texture->heightAt0());
......
......@@ -1100,6 +1100,142 @@ TEST_P(ClearTestES3, MaskedClearHeterogeneousAttachments)
verifyStencil(kStencilClearValue, kSize);
}
// Test that clearing multiple attachments of different nature (float, int and uint) in the
// presence of a scissor test works correctly. In the Vulkan backend, this exercises clearWithDraw
// and the relevant internal shaders.
TEST_P(ClearTestES3, ScissoredClearHeterogeneousAttachments)
{
// http://anglebug.com/4855
ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan());
// http://anglebug.com/5116
ANGLE_SKIP_TEST_IF(IsWindows() && (IsOpenGL() || IsD3D11()) && IsAMD());
constexpr uint32_t kSize = 16;
constexpr uint32_t kHalfSize = kSize / 2;
constexpr uint32_t kAttachmentCount = 3;
constexpr float kDepthClearValue = 0.256f;
constexpr int32_t kStencilClearValue = 0x1D;
constexpr GLenum kAttachmentFormats[kAttachmentCount] = {
GL_RGBA8,
GL_RGBA8I,
GL_RGBA8UI,
};
constexpr GLenum kDataFormats[kAttachmentCount] = {
GL_RGBA,
GL_RGBA_INTEGER,
GL_RGBA_INTEGER,
};
constexpr GLenum kDataTypes[kAttachmentCount] = {
GL_UNSIGNED_BYTE,
GL_BYTE,
GL_UNSIGNED_BYTE,
};
std::vector<unsigned char> pixelData(kSize * kSize * 4, 0);
glBindFramebuffer(GL_FRAMEBUFFER, mFBOs[0]);
GLTexture textures[kAttachmentCount];
GLRenderbuffer depthStencil;
GLenum drawBuffers[kAttachmentCount];
for (uint32_t i = 0; i < kAttachmentCount; ++i)
{
glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, kAttachmentFormats[i], kSize, kSize, 0, kDataFormats[i],
kDataTypes[i], pixelData.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i],
0);
drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
}
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kSize, kSize);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
glDrawBuffers(kAttachmentCount, drawBuffers);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(0, 0, 0, 0, 0, 0);
// Enable scissor test
glScissor(0, 0, kHalfSize, kHalfSize);
glEnable(GL_SCISSOR_TEST);
GLColor clearValuef = {25, 50, 75, 100};
angle::Vector4 clearValuefv = clearValuef.toNormalizedVector();
glClearColor(clearValuefv.x(), clearValuefv.y(), clearValuefv.z(), clearValuefv.w());
glClearDepthf(kDepthClearValue);
// clear stencil.
glClearBufferiv(GL_STENCIL, 0, &kStencilClearValue);
// clear float color attachment & depth together
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// clear integer attachment.
int clearValuei[4] = {10, -20, 30, -40};
glClearBufferiv(GL_COLOR, 1, clearValuei);
// clear unsigned integer attachment
uint32_t clearValueui[4] = {50, 60, 70, 80};
glClearBufferuiv(GL_COLOR, 2, clearValueui);
ASSERT_GL_NO_ERROR();
{
glReadBuffer(GL_COLOR_ATTACHMENT0);
ASSERT_GL_NO_ERROR();
GLColor expect = clearValuef;
EXPECT_PIXEL_COLOR_EQ(0, 0, expect);
EXPECT_PIXEL_COLOR_EQ(0, kHalfSize - 1, expect);
EXPECT_PIXEL_COLOR_EQ(kHalfSize - 1, 0, expect);
EXPECT_PIXEL_COLOR_EQ(kHalfSize - 1, kHalfSize - 1, expect);
EXPECT_PIXEL_EQ(kHalfSize + 1, kHalfSize + 1, 0, 0, 0, 0);
}
{
glReadBuffer(GL_COLOR_ATTACHMENT1);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_8I(0, 0, clearValuei[0], clearValuei[1], clearValuei[2], clearValuei[3]);
EXPECT_PIXEL_8I(0, kHalfSize - 1, clearValuei[0], clearValuei[1], clearValuei[2],
clearValuei[3]);
EXPECT_PIXEL_8I(kHalfSize - 1, 0, clearValuei[0], clearValuei[1], clearValuei[2],
clearValuei[3]);
EXPECT_PIXEL_8I(kHalfSize - 1, kHalfSize - 1, clearValuei[0], clearValuei[1],
clearValuei[2], clearValuei[3]);
EXPECT_PIXEL_8I(kHalfSize + 1, kHalfSize + 1, 0, 0, 0, 0);
}
{
glReadBuffer(GL_COLOR_ATTACHMENT2);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_8UI(0, 0, clearValueui[0], clearValueui[1], clearValueui[2], clearValueui[3]);
EXPECT_PIXEL_8UI(0, kHalfSize - 1, clearValueui[0], clearValueui[1], clearValueui[2],
clearValueui[3]);
EXPECT_PIXEL_8UI(kHalfSize - 1, 0, clearValueui[0], clearValueui[1], clearValueui[2],
clearValueui[3]);
EXPECT_PIXEL_8UI(kHalfSize - 1, kHalfSize - 1, clearValueui[0], clearValueui[1],
clearValueui[2], clearValueui[3]);
EXPECT_PIXEL_8UI(kHalfSize + 1, kHalfSize + 1, 0, 0, 0, 0);
}
glReadBuffer(GL_COLOR_ATTACHMENT0);
for (uint32_t i = 1; i < kAttachmentCount; ++i)
drawBuffers[i] = GL_NONE;
glDrawBuffers(kAttachmentCount, drawBuffers);
verifyDepth(kDepthClearValue, kHalfSize);
verifyStencil(kStencilClearValue, kHalfSize);
}
// This tests a bug where in a masked clear when calling "ClearBuffer", we would
// mistakenly clear every channel (including the masked-out ones)
TEST_P(ClearTestES3, MaskedClearBufferBug)
......@@ -2140,7 +2276,7 @@ TEST_P(ClearTestES3, ClearBufferfiStencilMask)
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ClearTest);
ANGLE_INSTANTIATE_TEST_ES3(ClearTestES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(ClearTestES3, ES3_METAL());
ANGLE_INSTANTIATE_TEST_COMBINE_4(MaskedScissoredClearTest,
MaskedScissoredClearVariationsTestPrint,
testing::Range(0, 3),
......@@ -2155,7 +2291,9 @@ ANGLE_INSTANTIATE_TEST_COMBINE_4(MaskedScissoredClearTest,
ES2_OPENGLES(),
ES3_OPENGLES(),
ES2_VULKAN(),
ES3_VULKAN());
ES3_VULKAN(),
ES2_METAL(),
ES3_METAL());
ANGLE_INSTANTIATE_TEST_COMBINE_4(VulkanClearTest,
MaskedScissoredClearVariationsTestPrint,
testing::Range(0, 3),
......@@ -2166,6 +2304,12 @@ ANGLE_INSTANTIATE_TEST_COMBINE_4(VulkanClearTest,
ES3_VULKAN());
// Not all ANGLE backends support RGB backbuffers
ANGLE_INSTANTIATE_TEST(ClearTestRGB, ES2_D3D11(), ES3_D3D11(), ES2_VULKAN(), ES3_VULKAN());
ANGLE_INSTANTIATE_TEST(ClearTestRGB,
ES2_D3D11(),
ES3_D3D11(),
ES2_VULKAN(),
ES3_VULKAN(),
ES2_METAL(),
ES3_METAL());
} // anonymous namespace
......@@ -2541,6 +2541,6 @@ ANGLE_INSTANTIATE_TEST(CopyTextureTestDest,
ES2_OPENGLES(),
ES2_VULKAN(),
ES2_METAL());
ANGLE_INSTANTIATE_TEST_ES3(CopyTextureTestES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(CopyTextureTestES3, ES3_METAL());
} // namespace angle
......@@ -301,6 +301,9 @@ TEST_P(DrawBuffersTest, BlendWithGaps)
// Fails on Intel Ubuntu 19.04 Mesa 19.0.2 Vulkan. http://anglebug.com/3616
ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
// http://anglebug.com/5154
ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsDesktopOpenGL());
glBindTexture(GL_TEXTURE_2D, mTextures[0]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, mTextures[0], 0);
......
......@@ -8116,12 +8116,12 @@ ANGLE_INSTANTIATE_TEST(Texture2DRGTest,
ANGLE_INSTANTIATE_TEST_ES3(Texture2DFloatTestES3);
ANGLE_INSTANTIATE_TEST_ES2(Texture2DFloatTestES2);
ANGLE_INSTANTIATE_TEST_ES3(TextureCubeTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES3(TextureCubeIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES3(TextureCubeIntegerEdgeTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DIntegerProjectiveOffsetTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DArrayIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture3DIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DIntegerTestES3, ES3_METAL());
ANGLE_INSTANTIATE_TEST_ES3_AND(TextureCubeIntegerTestES3, ES3_METAL());
ANGLE_INSTANTIATE_TEST_ES3_AND(TextureCubeIntegerEdgeTestES3, ES3_METAL());
ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DIntegerProjectiveOffsetTestES3, ES3_METAL());
ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DArrayIntegerTestES3, ES3_METAL());
ANGLE_INSTANTIATE_TEST_ES3_AND(Texture3DIntegerTestES3, ES3_METAL());
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(Texture2DDepthTest);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(PBOCompressedTextureTest);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(ETC1CompressedTextureTest);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment