Commit 4f247baf by Le Hoang Quyen Committed by Commit Bot

Metal: Implement EXT_draw_buffers & ANGLE_framebuffer_blit

Bug: angleproject:2634 Change-Id: I769ca7e113e660870e9b31dafb706c313db8ac24 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2332146 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com>
parent 1d331c91
......@@ -30,6 +30,12 @@ struct FeaturesMtl : FeatureSetBase
Feature hasNonUniformDispatch = {
"has_non_uniform_dispatch", FeatureCategory::MetalFeatures,
"The renderer supports non uniform compute shader dispatch's group size", &members};
// fragment stencil output support
Feature hasStencilOutput = {"has_shader_stencil_output", FeatureCategory::MetalFeatures,
"The renderer supports stencil output from fragment shader",
&members};
// Texture swizzle support:
Feature hasTextureSwizzle = {"has_texture_swizzle", FeatureCategory::MetalFeatures,
"The renderer supports texture swizzle", &members};
......
......@@ -134,6 +134,9 @@ uint64_t GetGpuIDFromDisplayID(uint32_t displayID);
// Helper to get the active GPU ID from an OpenGL display mask.
uint64_t GetGpuIDFromOpenGLDisplayMask(uint32_t displayMask);
// Get VendorID from metal device's registry ID
VendorID GetVendorIDFromMetalDeviceRegistryID(uint64_t registryID);
#endif
} // namespace angle
......
......@@ -232,6 +232,55 @@ uint64_t GetGpuIDFromOpenGLDisplayMask(uint32_t displayMask)
return 0;
}
// Get VendorID from metal device's registry ID
VendorID GetVendorIDFromMetalDeviceRegistryID(uint64_t registryID)
{
# if defined(ANGLE_PLATFORM_MACOS)
// On macOS, the following code is only supported since 10.13.
if (@available(macOS 10.13, *))
# endif
{
// Get a matching dictionary for the IOGraphicsAccelerator2
CFMutableDictionaryRef matchingDict = IORegistryEntryIDMatching(registryID);
if (matchingDict == nullptr)
{
return 0;
}
// IOServiceGetMatchingService will consume the reference on the matching dictionary,
// so we don't need to release the dictionary.
io_registry_entry_t acceleratorEntry =
IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict);
if (acceleratorEntry == IO_OBJECT_NULL)
{
return 0;
}
// Get the parent entry that will be the IOPCIDevice
io_registry_entry_t deviceEntry = IO_OBJECT_NULL;
if (IORegistryEntryGetParentEntry(acceleratorEntry, kIOServicePlane, &deviceEntry) !=
kIOReturnSuccess ||
deviceEntry == IO_OBJECT_NULL)
{
IOObjectRelease(acceleratorEntry);
return 0;
}
IOObjectRelease(acceleratorEntry);
uint32_t vendorId;
if (!GetEntryProperty(deviceEntry, CFSTR("vendor-id"), &vendorId))
{
vendorId = 0;
}
IOObjectRelease(deviceEntry);
return vendorId;
}
return 0;
}
bool GetSystemInfo(SystemInfo *info)
{
{
......
......@@ -54,6 +54,12 @@ _metal_backend_sources = [
"mtl_state_cache.mm",
"mtl_utils.h",
"mtl_utils.mm",
"shaders/compiled/compiled_default_metallib_2_1_debug_ios_autogen.inc",
"shaders/compiled/compiled_default_metallib_2_1_debug_ios_sim_autogen.inc",
"shaders/compiled/compiled_default_metallib_2_1_debug_mac_autogen.inc",
"shaders/compiled/compiled_default_metallib_2_1_ios_autogen.inc",
"shaders/compiled/compiled_default_metallib_2_1_ios_sim_autogen.inc",
"shaders/compiled/compiled_default_metallib_2_1_mac_autogen.inc",
"shaders/compiled/compiled_default_metallib_debug_ios_autogen.inc",
"shaders/compiled/compiled_default_metallib_debug_ios_sim_autogen.inc",
"shaders/compiled/compiled_default_metallib_debug_mac_autogen.inc",
......@@ -86,6 +92,7 @@ angle_source_set("angle_metal_backend") {
public_deps = [
"${angle_root}:angle_glslang_wrapper",
"${angle_root}:angle_gpu_info_util",
"${angle_root}:angle_image_util",
"${angle_root}:libANGLE_headers",
]
......
......@@ -27,6 +27,7 @@ class DisplayMtl;
class FramebufferMtl;
class VertexArrayMtl;
class ProgramMtl;
class RenderTargetMtl;
class WindowSurfaceMtl;
class ContextMtl : public ContextImpl, public mtl::Context
......@@ -266,7 +267,9 @@ class ContextMtl : public ContextImpl, public mtl::Context
void invalidateRenderPipeline();
// Call this to notify ContextMtl whenever FramebufferMtl's state changed
void onDrawFrameBufferChange(const gl::Context *context, FramebufferMtl *framebuffer);
void onDrawFrameBufferChangedState(const gl::Context *context,
FramebufferMtl *framebuffer,
bool renderPassChanged);
void onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer);
const MTLClearColor &getClearColorValue() const;
......@@ -297,7 +300,8 @@ class ContextMtl : public ContextImpl, public mtl::Context
void present(const gl::Context *context, id<CAMetalDrawable> presentationDrawable);
angle::Result finishCommandBuffer();
// Check whether compatible render pass has been started.
// Check whether compatible render pass has been started. Compatible render pass is a render
// pass having the same attachments, and possibly having different load/store options.
bool hasStartedRenderPass(const mtl::RenderPassDesc &desc);
// Get current render encoder. May be nullptr if no render pass has been started.
......@@ -305,16 +309,20 @@ class ContextMtl : public ContextImpl, public mtl::Context
// Will end current command encoder if it is valid, then start new encoder.
// Unless hasStartedRenderPass(desc) returns true.
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::RenderPassDesc &desc);
// Note: passing a compatible render pass with different load/store options won't end the
// current render pass. If a new render pass is desired, call endEncoding() prior to this.
mtl::RenderCommandEncoder *getRenderPassCommandEncoder(const mtl::RenderPassDesc &desc);
// Utilities to quickly create render command enconder to a specific texture:
// Utilities to quickly create render command encoder to a specific texture:
// The previous content of texture will be loaded
mtl::RenderCommandEncoder *getTextureRenderCommandEncoder(const mtl::TextureRef &textureTarget,
const gl::ImageIndex &index);
// The previous content of texture will be loaded if clearColor is not provided
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::TextureRef &textureTarget,
const gl::ImageIndex &index,
const Optional<MTLClearColor> &clearColor);
mtl::RenderCommandEncoder *getRenderTargetCommandEncoderWithClear(
const RenderTargetMtl &renderTarget,
const Optional<MTLClearColor> &clearColor);
// The previous content of texture will be loaded
mtl::RenderCommandEncoder *getRenderCommandEncoder(const mtl::TextureRef &textureTarget,
const gl::ImageIndex &index);
mtl::RenderCommandEncoder *getRenderTargetCommandEncoder(const RenderTargetMtl &renderTarget);
// Will end current command encoder and start new blit command encoder. Unless a blit comamnd
// encoder is already started.
......
......@@ -18,6 +18,7 @@
#include "libANGLE/renderer/metal/FrameBufferMtl.h"
#include "libANGLE/renderer/metal/ProgramMtl.h"
#include "libANGLE/renderer/metal/RenderBufferMtl.h"
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
#include "libANGLE/renderer/metal/ShaderMtl.h"
#include "libANGLE/renderer/metal/TextureMtl.h"
#include "libANGLE/renderer/metal/VertexArrayMtl.h"
......@@ -1212,7 +1213,7 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder()
return &mRenderEncoder;
}
mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::RenderPassDesc &desc)
mtl::RenderCommandEncoder *ContextMtl::getRenderPassCommandEncoder(const mtl::RenderPassDesc &desc)
{
if (hasStartedRenderPass(desc))
{
......@@ -1229,12 +1230,11 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::Render
return &mRenderEncoder.restart(desc);
}
// Utilities to quickly create render command enconder to a specific texture:
// The previous content of texture will be loaded if clearColor is not provided
mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(
// Utilities to quickly create render command encoder to a specific texture:
// The previous content of texture will be loaded
mtl::RenderCommandEncoder *ContextMtl::getTextureRenderCommandEncoder(
const mtl::TextureRef &textureTarget,
const gl::ImageIndex &index,
const Optional<MTLClearColor> &clearColor)
const gl::ImageIndex &index)
{
ASSERT(textureTarget && textureTarget->valid());
......@@ -1244,21 +1244,39 @@ mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(
rpDesc.colorAttachments[0].level = index.getLevelIndex();
rpDesc.colorAttachments[0].sliceOrDepth = index.hasLayer() ? index.getLayerIndex() : 0;
rpDesc.numColorAttachments = 1;
rpDesc.sampleCount = textureTarget->samples();
return getRenderPassCommandEncoder(rpDesc);
}
// The previous content of texture will be loaded if clearColor is not provided
mtl::RenderCommandEncoder *ContextMtl::getRenderTargetCommandEncoderWithClear(
const RenderTargetMtl &renderTarget,
const Optional<MTLClearColor> &clearColor)
{
ASSERT(renderTarget.getTexture());
mtl::RenderPassDesc rpDesc;
renderTarget.toRenderPassAttachmentDesc(&rpDesc.colorAttachments[0]);
rpDesc.numColorAttachments = 1;
rpDesc.sampleCount = renderTarget.getRenderSamples();
if (clearColor.valid())
{
rpDesc.colorAttachments[0].loadAction = MTLLoadActionClear;
rpDesc.colorAttachments[0].clearColor =
mtl::EmulatedAlphaClearColor(clearColor.value(), textureTarget->getColorWritableMask());
rpDesc.colorAttachments[0].clearColor = mtl::EmulatedAlphaClearColor(
clearColor.value(), renderTarget.getTexture()->getColorWritableMask());
endEncoding(true);
}
return getRenderCommandEncoder(rpDesc);
return getRenderPassCommandEncoder(rpDesc);
}
// The previous content of texture will be loaded
mtl::RenderCommandEncoder *ContextMtl::getRenderCommandEncoder(const mtl::TextureRef &textureTarget,
const gl::ImageIndex &index)
mtl::RenderCommandEncoder *ContextMtl::getRenderTargetCommandEncoder(
const RenderTargetMtl &renderTarget)
{
return getRenderCommandEncoder(textureTarget, index, Optional<MTLClearColor>());
return getRenderTargetCommandEncoderWithClear(renderTarget, Optional<MTLClearColor>());
}
mtl::BlitCommandEncoder *ContextMtl::getBlitCommandEncoder()
......@@ -1403,25 +1421,33 @@ void ContextMtl::updateDrawFrameBufferBinding(const gl::Context *context)
mDrawFramebuffer->onStartedDrawingToFrameBuffer(context);
onDrawFrameBufferChange(context, mDrawFramebuffer);
onDrawFrameBufferChangedState(context, mDrawFramebuffer, true);
}
void ContextMtl::onDrawFrameBufferChange(const gl::Context *context, FramebufferMtl *framebuffer)
void ContextMtl::onDrawFrameBufferChangedState(const gl::Context *context,
FramebufferMtl *framebuffer,
bool renderPassChanged)
{
const gl::State &glState = getState();
ASSERT(framebuffer == mtl::GetImpl(glState.getDrawFramebuffer()));
mDirtyBits.set(DIRTY_BIT_DRAW_FRAMEBUFFER);
updateViewport(framebuffer, glState.getViewport(), glState.getNearPlane(),
glState.getFarPlane());
updateFrontFace(glState);
updateScissor(glState);
// End any render encoding using the old render pass.
endEncoding(false);
// Need to re-apply state to RenderCommandEncoder
invalidateState(context);
if (renderPassChanged)
{
// End any render encoding using the old render pass.
endEncoding(false);
// Need to re-apply state to RenderCommandEncoder
invalidateState(context);
}
else
{
// Invalidate current pipeline only.
invalidateRenderPipeline();
}
}
void ContextMtl::onBackbufferResized(const gl::Context *context, WindowSurfaceMtl *backbuffer)
......@@ -1798,6 +1824,9 @@ angle::Result ContextMtl::checkIfPipelineChanged(
mRenderPipelineDesc.alphaToCoverageEnabled = mState.isSampleAlphaToCoverageEnabled();
mRenderPipelineDesc.emulateCoverageMask = mState.isSampleCoverageEnabled();
mRenderPipelineDesc.outputDescriptor.updateEnabledDrawBuffers(
mDrawFramebuffer->getState().getEnabledDrawBuffers());
*changedPipelineDesc = mRenderPipelineDesc;
}
......
......@@ -111,6 +111,9 @@ class DisplayMtl : public DisplayImpl
bool supportsEitherGPUFamily(uint8_t iOSFamily, uint8_t macFamily) const;
bool supportsIOSGPUFamily(uint8_t iOSFamily) const;
bool supportsMacGPUFamily(uint8_t macFamily) const;
bool isAMD() const;
bool isIntel() const;
bool isNVIDIA() const;
id<MTLDevice> getMetalDevice() const { return mMetalDevice; }
......@@ -160,6 +163,7 @@ class DisplayMtl : public DisplayImpl
angle::Result initializeShaderLibrary();
mtl::AutoObjCPtr<id<MTLDevice>> mMetalDevice = nil;
uint32_t mMetalDeviceVendorId = 0;
mtl::CommandQueue mCmdQueue;
......
......@@ -8,6 +8,7 @@
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "gpu_info_util/SystemInfo.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/Surface.h"
......@@ -70,6 +71,8 @@ angle::Result DisplayMtl::initializeImpl(egl::Display *display)
return angle::Result::Stop;
}
mMetalDeviceVendorId = mtl::GetDeviceVendorId(mMetalDevice);
mCmdQueue.set([[mMetalDevice.get() newCommandQueue] ANGLE_MTL_AUTORELEASE]);
mCapsInitialized = false;
......@@ -100,6 +103,8 @@ void DisplayMtl::terminate()
mMetalDevice = nil;
mCapsInitialized = false;
mMetalDeviceVendorId = 0;
if (mGlslangInitialized)
{
GlslangRelease();
......@@ -585,9 +590,9 @@ void DisplayMtl::initializeExtensions() const
mNativeExtensions.mapBufferOES = true;
mNativeExtensions.mapBufferRange = false;
mNativeExtensions.textureStorage = true;
mNativeExtensions.drawBuffers = false;
mNativeExtensions.drawBuffers = true;
mNativeExtensions.fragDepth = true;
mNativeExtensions.framebufferBlit = false;
mNativeExtensions.framebufferBlit = true;
mNativeExtensions.framebufferMultisample = false;
mNativeExtensions.copyTexture = true;
mNativeExtensions.copyCompressedTexture = false;
......@@ -662,7 +667,13 @@ void DisplayMtl::initializeTextureCaps() const
void DisplayMtl::initializeFeatures()
{
bool isMetal2_1 = false;
bool isMetal2_2 = false;
if (ANGLE_APPLE_AVAILABLE_XCI(10.14, 13.0, 12.0))
{
isMetal2_1 = true;
}
if (ANGLE_APPLE_AVAILABLE_XCI(10.15, 13.0, 13.0))
{
isMetal2_2 = true;
......@@ -672,6 +683,7 @@ void DisplayMtl::initializeFeatures()
mFeatures.hasBaseVertexInstancedDraw.enabled = true;
mFeatures.hasDepthTextureFiltering.enabled = false;
mFeatures.hasNonUniformDispatch.enabled = true;
mFeatures.hasStencilOutput.enabled = false;
mFeatures.hasTextureSwizzle.enabled = false;
mFeatures.allowSeparatedDepthStencilBuffers.enabled = false;
......@@ -682,6 +694,11 @@ void DisplayMtl::initializeFeatures()
ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve,
supportsEitherGPUFamily(3, 1));
// http://anglebug.com/4919
// Stencil blit shader is not compiled on Intel & NVIDIA, need investigation.
ANGLE_FEATURE_CONDITION((&mFeatures), hasStencilOutput,
isMetal2_1 && !isIntel() && !isNVIDIA());
ANGLE_FEATURE_CONDITION((&mFeatures), hasTextureSwizzle,
isMetal2_2 && supportsEitherGPUFamily(1, 2));
......@@ -710,11 +727,27 @@ angle::Result DisplayMtl::initializeShaderLibrary()
size_t compiled_shader_binary_len;
#if !defined(NDEBUG)
compiled_shader_binary = compiled_default_metallib_debug;
compiled_shader_binary_len = compiled_default_metallib_debug_len;
if (getFeatures().hasStencilOutput.enabled)
{
compiled_shader_binary = compiled_default_metallib_2_1_debug;
compiled_shader_binary_len = compiled_default_metallib_2_1_debug_len;
}
else
{
compiled_shader_binary = compiled_default_metallib_debug;
compiled_shader_binary_len = compiled_default_metallib_debug_len;
}
#else
compiled_shader_binary = compiled_default_metallib;
compiled_shader_binary_len = compiled_default_metallib_len;
if (getFeatures().hasStencilOutput.enabled)
{
compiled_shader_binary = compiled_default_metallib_2_1;
compiled_shader_binary_len = compiled_default_metallib_2_1_len;
}
else
{
compiled_shader_binary = compiled_default_metallib;
compiled_shader_binary_len = compiled_default_metallib_len;
}
#endif
mDefaultShaders = CreateShaderLibraryFromBinary(getMetalDevice(), compiled_shader_binary,
......@@ -879,4 +912,19 @@ bool DisplayMtl::supportsEitherGPUFamily(uint8_t iOSFamily, uint8_t macFamily) c
return supportsIOSGPUFamily(iOSFamily) || supportsMacGPUFamily(macFamily);
}
bool DisplayMtl::isAMD() const
{
return angle::IsAMD(mMetalDeviceVendorId);
}
bool DisplayMtl::isIntel() const
{
return angle::IsIntel(mMetalDeviceVendorId);
}
bool DisplayMtl::isNVIDIA() const
{
return angle::IsNVIDIA(mMetalDeviceVendorId);
}
} // namespace rx
......@@ -121,6 +121,13 @@ class FramebufferMtl : public FramebufferImpl
private:
void reset();
angle::Result invalidateImpl(ContextMtl *contextMtl, size_t count, const GLenum *attachments);
angle::Result blitWithDraw(const gl::Context *context,
FramebufferMtl *srcFrameBuffer,
bool blitColorBuffer,
bool blitDepthBuffer,
bool blitStencilBuffer,
GLenum filter,
const mtl::BlitParams &baseParams);
angle::Result clearImpl(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
mtl::ClearRectParams *clearOpts);
......@@ -129,6 +136,15 @@ class FramebufferMtl : public FramebufferImpl
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts);
angle::Result clearWithLoadOpRenderPassNotStarted(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts);
angle::Result clearWithLoadOpRenderPassStarted(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts,
mtl::RenderCommandEncoder *encoder);
angle::Result clearWithDraw(const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts);
......
......@@ -14,6 +14,7 @@
#include "common/MemoryBuffer.h"
#include "common/angleutils.h"
#include "common/debug.h"
#include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/FrameBufferMtl.h"
#include "libANGLE/renderer/metal/SurfaceMtl.h"
......@@ -199,14 +200,221 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
}
angle::Result FramebufferMtl::blit(const gl::Context *context,
const gl::Rectangle &sourceArea,
const gl::Rectangle &destArea,
const gl::Rectangle &sourceAreaIn,
const gl::Rectangle &destAreaIn,
GLbitfield mask,
GLenum filter)
{
// NOTE(hqle): MSAA feature.
UNIMPLEMENTED();
return angle::Result::Stop;
bool blitColorBuffer = (mask & GL_COLOR_BUFFER_BIT) != 0;
bool blitDepthBuffer = (mask & GL_DEPTH_BUFFER_BIT) != 0;
bool blitStencilBuffer = (mask & GL_STENCIL_BUFFER_BIT) != 0;
const gl::State &glState = context->getState();
const gl::Framebuffer *glSrcFramebuffer = glState.getReadFramebuffer();
FramebufferMtl *srcFrameBuffer = mtl::GetImpl(glSrcFramebuffer);
blitColorBuffer =
blitColorBuffer && srcFrameBuffer->getColorReadRenderTarget(context) != nullptr;
blitDepthBuffer = blitDepthBuffer && srcFrameBuffer->getDepthRenderTarget() != nullptr;
blitStencilBuffer = blitStencilBuffer && srcFrameBuffer->getStencilRenderTarget() != nullptr;
if (!blitColorBuffer && !blitDepthBuffer && !blitStencilBuffer)
{
// No-op
return angle::Result::Continue;
}
gl::Rectangle sourceArea = sourceAreaIn;
gl::Rectangle destArea = destAreaIn;
const gl::Rectangle srcFramebufferDimensions = srcFrameBuffer->getCompleteRenderArea();
// If the destination is flipped in either direction, we will flip the source instead so that
// the destination area is always unflipped.
sourceArea = sourceArea.flip(destArea.isReversedX(), destArea.isReversedY());
destArea = destArea.removeReversal();
// Calculate the stretch factor prior to any clipping, as it needs to remain constant.
const float stretch[2] = {
std::abs(sourceArea.width / static_cast<float>(destArea.width)),
std::abs(sourceArea.height / static_cast<float>(destArea.height)),
};
// First, clip the source area to framebuffer. That requires transforming the dest area to
// match the clipped source.
gl::Rectangle absSourceArea = sourceArea.removeReversal();
gl::Rectangle clippedSourceArea;
if (!gl::ClipRectangle(srcFramebufferDimensions, absSourceArea, &clippedSourceArea))
{
return angle::Result::Continue;
}
// Resize the destination area based on the new size of source. Note again that stretch is
// calculated as SrcDimension/DestDimension.
gl::Rectangle srcClippedDestArea;
if (clippedSourceArea == absSourceArea)
{
// If there was no clipping, keep dest area as is.
srcClippedDestArea = destArea;
}
else
{
// Shift dest area's x0,y0,x1,y1 by as much as the source area's got shifted (taking
// stretching into account)
float x0Shift = std::round((clippedSourceArea.x - absSourceArea.x) / stretch[0]);
float y0Shift = std::round((clippedSourceArea.y - absSourceArea.y) / stretch[1]);
float x1Shift = std::round((absSourceArea.x1() - clippedSourceArea.x1()) / stretch[0]);
float y1Shift = std::round((absSourceArea.y1() - clippedSourceArea.y1()) / stretch[1]);
// If the source area was reversed in any direction, the shift should be applied in the
// opposite direction as well.
if (sourceArea.isReversedX())
{
std::swap(x0Shift, x1Shift);
}
if (sourceArea.isReversedY())
{
std::swap(y0Shift, y1Shift);
}
srcClippedDestArea.x = destArea.x0() + static_cast<int>(x0Shift);
srcClippedDestArea.y = destArea.y0() + static_cast<int>(y0Shift);
int x1 = destArea.x1() - static_cast<int>(x1Shift);
int y1 = destArea.y1() - static_cast<int>(y1Shift);
srcClippedDestArea.width = x1 - srcClippedDestArea.x;
srcClippedDestArea.height = y1 - srcClippedDestArea.y;
}
// Flip source area if necessary
clippedSourceArea = srcFrameBuffer->getCorrectFlippedReadArea(context, clippedSourceArea);
bool unpackFlipX = sourceArea.isReversedX();
bool unpackFlipY = sourceArea.isReversedY();
if (srcFrameBuffer->flipY())
{
// The rectangle already flipped by calling getCorrectFlippedReadArea(). So reverse the
// unpackFlipY flag.
unpackFlipY = !unpackFlipY;
}
ASSERT(!destArea.isReversedX() && !destArea.isReversedY());
// Clip the destination area to the framebuffer size and scissor.
gl::Rectangle scissoredDestArea;
if (!gl::ClipRectangle(ClipRectToScissor(glState, this->getCompleteRenderArea(), false),
srcClippedDestArea, &scissoredDestArea))
{
return angle::Result::Continue;
}
// Use blit with draw
mtl::BlitParams baseParams;
baseParams.dstTextureSize = mState.getExtents();
baseParams.dstRect = srcClippedDestArea;
baseParams.dstScissorRect = scissoredDestArea;
baseParams.dstFlipY = this->flipY();
baseParams.srcRect = clippedSourceArea;
// This flag is for auto flipping the rect inside RenderUtils. Since we already flip it using
// getCorrectFlippedReadArea(). This flag is not needed.
baseParams.srcYFlipped = false;
baseParams.unpackFlipX = unpackFlipX;
baseParams.unpackFlipY = unpackFlipY;
return blitWithDraw(context, srcFrameBuffer, blitColorBuffer, blitDepthBuffer,
blitStencilBuffer, filter, baseParams);
}
angle::Result FramebufferMtl::blitWithDraw(const gl::Context *context,
FramebufferMtl *srcFrameBuffer,
bool blitColorBuffer,
bool blitDepthBuffer,
bool blitStencilBuffer,
GLenum filter,
const mtl::BlitParams &baseParams)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// Use blit with draw
mtl::RenderCommandEncoder *renderEncoder = nullptr;
// Blit Depth & stencil
if (blitDepthBuffer || blitStencilBuffer)
{
mtl::DepthStencilBlitParams dsBlitParams;
memcpy(&dsBlitParams, &baseParams, sizeof(baseParams));
RenderTargetMtl *srcDepthRt = srcFrameBuffer->getDepthRenderTarget();
RenderTargetMtl *srcStencilRt = srcFrameBuffer->getStencilRenderTarget();
if (blitDepthBuffer)
{
dsBlitParams.src = srcDepthRt->getTexture();
dsBlitParams.srcLevel = srcDepthRt->getLevelIndex();
dsBlitParams.srcLayer = srcDepthRt->getLayerIndex();
}
if (blitStencilBuffer && srcStencilRt->getTexture())
{
dsBlitParams.srcStencil = srcStencilRt->getTexture()->getStencilView();
dsBlitParams.srcLevel = srcStencilRt->getLevelIndex();
dsBlitParams.srcLayer = srcStencilRt->getLayerIndex();
if (!contextMtl->getDisplay()->getFeatures().hasStencilOutput.enabled &&
mStencilRenderTarget)
{
// Directly writing to stencil in shader is not supported, use temporary copy buffer
// work around. This is a compute pass.
mtl::StencilBlitViaBufferParams stencilOnlyBlitParams = dsBlitParams;
stencilOnlyBlitParams.dstStencil = mStencilRenderTarget->getTexture();
stencilOnlyBlitParams.dstStencilLayer = mStencilRenderTarget->getLayerIndex();
stencilOnlyBlitParams.dstStencilLevel = mStencilRenderTarget->getLevelIndex();
stencilOnlyBlitParams.dstPackedDepthStencilFormat =
mStencilRenderTarget->getFormat()->hasDepthAndStencilBits();
ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitStencilViaCopyBuffer(
context, stencilOnlyBlitParams));
// Prevent the stencil to be blitted with draw again
dsBlitParams.srcStencil = nullptr;
}
}
// The actual blitting of depth and/or stencil
renderEncoder = ensureRenderPassStarted(context);
ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitDepthStencilWithDraw(
context, renderEncoder, dsBlitParams));
} // if (blitDepthBuffer || blitStencilBuffer)
else
{
renderEncoder = ensureRenderPassStarted(context);
}
// Blit color
if (blitColorBuffer)
{
mtl::ColorBlitParams colorBlitParams;
memcpy(&colorBlitParams, &baseParams, sizeof(baseParams));
RenderTargetMtl *srcColorRt = srcFrameBuffer->getColorReadRenderTarget(context);
ASSERT(srcColorRt);
colorBlitParams.src = srcColorRt->getTexture();
colorBlitParams.srcLevel = srcColorRt->getLevelIndex();
colorBlitParams.srcLayer = srcColorRt->getLayerIndex();
colorBlitParams.blitColorMask = contextMtl->getColorMask();
colorBlitParams.enabledBuffers = getState().getEnabledDrawBuffers();
colorBlitParams.filter = filter;
colorBlitParams.dstLuminance = srcColorRt->getFormat()->actualAngleFormat().isLUMA();
ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(context, renderEncoder,
colorBlitParams));
}
return angle::Result::Continue;
}
bool FramebufferMtl::checkStatus(const gl::Context *context) const
......@@ -223,6 +431,16 @@ bool FramebufferMtl::checkStatus(const gl::Context *context) const
return false;
}
if (mState.getStencilAttachment() &&
mState.getStencilAttachment()->getFormat().info->depthBits &&
mState.getStencilAttachment()->getFormat().info->stencilBits &&
mState.hasSeparateDepthAndStencilAttachments())
{
// If stencil attachment has depth & stencil bits, it must refer to the same texture
// as depth attachment.
return false;
}
return true;
}
......@@ -232,7 +450,7 @@ angle::Result FramebufferMtl::syncState(const gl::Context *context,
gl::Command command)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
ASSERT(dirtyBits.any());
bool mustNotifyContext = false;
for (size_t dirtyBit : dirtyBits)
{
switch (dirtyBit)
......@@ -248,6 +466,8 @@ angle::Result FramebufferMtl::syncState(const gl::Context *context,
// NOTE(hqle): What are we supposed to do?
break;
case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
mustNotifyContext = true;
break;
case gl::Framebuffer::DIRTY_BIT_READ_BUFFER:
case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
......@@ -277,14 +497,15 @@ angle::Result FramebufferMtl::syncState(const gl::Context *context,
auto oldRenderPassDesc = mRenderPassDesc;
ANGLE_TRY(prepareRenderPass(context, &mRenderPassDesc));
bool renderPassChanged = !oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc);
if (!oldRenderPassDesc.equalIgnoreLoadStoreOptions(mRenderPassDesc))
if (mustNotifyContext || renderPassChanged)
{
FramebufferMtl *currentDrawFramebuffer =
mtl::GetImpl(context->getState().getDrawFramebuffer());
if (currentDrawFramebuffer == this)
{
contextMtl->onDrawFrameBufferChange(context, this);
contextMtl->onDrawFrameBufferChangedState(context, this, renderPassChanged);
}
}
......@@ -358,11 +579,11 @@ mtl::RenderCommandEncoder *FramebufferMtl::ensureRenderPassStarted(const gl::Con
}
}
// Only support ensureRenderPassStarted() with different load & store options.
// The texture, level, slice must be the same.
// Only support ensureRenderPassStarted() with different load & store options only. The texture,
// level, slice must be the same.
ASSERT(desc.equalIgnoreLoadStoreOptions(mRenderPassDesc));
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderCommandEncoder(desc);
mtl::RenderCommandEncoder *encoder = contextMtl->getRenderPassCommandEncoder(desc);
if (mRenderPassCleanStart)
{
......@@ -503,8 +724,6 @@ angle::Result FramebufferMtl::updateCachedRenderTarget(const gl::Context *contex
angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
mtl::RenderPassDesc *pDescOut)
{
gl::DrawBufferMask enabledBuffer = mState.getEnabledDrawBuffers();
mtl::RenderPassDesc &desc = *pDescOut;
uint32_t maxColorAttachments = static_cast<uint32_t>(mState.getColorAttachments().size());
......@@ -517,7 +736,7 @@ angle::Result FramebufferMtl::prepareRenderPass(const gl::Context *context,
mtl::RenderPassColorAttachmentDesc &colorAttachment = desc.colorAttachments[colorIndexGL];
const RenderTargetMtl *colorRenderTarget = mColorRenderTargets[colorIndexGL];
if (colorRenderTarget && enabledBuffer.test(colorIndexGL))
if (colorRenderTarget)
{
colorRenderTarget->toRenderPassAttachmentDesc(&colorAttachment);
......@@ -568,82 +787,104 @@ angle::Result FramebufferMtl::clearWithLoadOp(const gl::Context *context,
ContextMtl *contextMtl = mtl::GetImpl(context);
bool startedRenderPass = contextMtl->hasStartedRenderPass(mRenderPassDesc);
mtl::RenderCommandEncoder *encoder = nullptr;
mtl::RenderPassDesc tempDesc = mRenderPassDesc;
if (startedRenderPass)
{
encoder = contextMtl->getRenderCommandEncoder();
encoder = ensureRenderPassStarted(context);
if (encoder->hasDrawCalls())
{
// Render pass already has draw calls recorded, it is better to use clear with draw
// operation.
return clearWithDraw(context, clearColorBuffers, clearOpts);
}
else
{
// If render pass has started but there is no draw call yet. It is OK to change the
// loadOp.
return clearWithLoadOpRenderPassStarted(context, clearColorBuffers, clearOpts, encoder);
}
}
else
{
return clearWithLoadOpRenderPassNotStarted(context, clearColorBuffers, clearOpts);
}
}
size_t attachmentCount = 0;
for (size_t colorIndexGL : mState.getEnabledDrawBuffers())
angle::Result FramebufferMtl::clearWithLoadOpRenderPassNotStarted(
const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts)
{
mtl::RenderPassDesc tempDesc = mRenderPassDesc;
for (uint32_t colorIndexGL = 0; colorIndexGL < tempDesc.numColorAttachments; ++colorIndexGL)
{
ASSERT(colorIndexGL < mtl::kMaxRenderTargets);
uint32_t attachmentIdx = static_cast<uint32_t>(attachmentCount++);
mtl::RenderPassColorAttachmentDesc &colorAttachment =
tempDesc.colorAttachments[attachmentIdx];
tempDesc.colorAttachments[colorIndexGL];
const mtl::TextureRef &texture = colorAttachment.texture;
if (clearColorBuffers.test(colorIndexGL))
{
if (startedRenderPass)
{
// Render pass already started, and we want to clear this buffer,
// then discard its content before clearing.
encoder->setColorStoreAction(MTLStoreActionDontCare, attachmentIdx);
}
colorAttachment.loadAction = MTLLoadActionClear;
overrideClearColor(texture, clearOpts.clearColor.value(), &colorAttachment.clearColor);
}
else if (startedRenderPass)
{
// If render pass already started and we don't want to clear this buffer,
// then store it with current render encoder and load it before clearing step
encoder->setColorStoreAction(MTLStoreActionStore, attachmentIdx);
colorAttachment.loadAction = MTLLoadActionLoad;
}
}
MTLStoreAction preClearDethpStoreAction = MTLStoreActionStore,
preClearStencilStoreAction = MTLStoreActionStore;
if (clearOpts.clearDepth.valid())
{
preClearDethpStoreAction = MTLStoreActionDontCare;
tempDesc.depthAttachment.loadAction = MTLLoadActionClear;
tempDesc.depthAttachment.clearDepth = clearOpts.clearDepth.value();
}
else if (startedRenderPass)
{
// If render pass already started and we don't want to clear this buffer,
// then store it with current render encoder and load it before clearing step
preClearDethpStoreAction = MTLStoreActionStore;
tempDesc.depthAttachment.loadAction = MTLLoadActionLoad;
}
if (clearOpts.clearStencil.valid())
{
preClearStencilStoreAction = MTLStoreActionDontCare;
tempDesc.stencilAttachment.loadAction = MTLLoadActionClear;
tempDesc.stencilAttachment.clearStencil = clearOpts.clearStencil.value();
}
else if (startedRenderPass)
// Start new render encoder with loadOp=Clear
ensureRenderPassStarted(context, tempDesc);
return angle::Result::Continue;
}
angle::Result FramebufferMtl::clearWithLoadOpRenderPassStarted(
const gl::Context *context,
gl::DrawBufferMask clearColorBuffers,
const mtl::ClearRectParams &clearOpts,
mtl::RenderCommandEncoder *encoder)
{
ASSERT(!encoder->hasDrawCalls());
for (uint32_t colorIndexGL = 0; colorIndexGL < mRenderPassDesc.numColorAttachments;
++colorIndexGL)
{
// If render pass already started and we don't want to clear this buffer,
// then store it with current render encoder and load it before clearing step
preClearStencilStoreAction = MTLStoreActionStore;
tempDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
ASSERT(colorIndexGL < mtl::kMaxRenderTargets);
mtl::RenderPassColorAttachmentDesc &colorAttachment =
mRenderPassDesc.colorAttachments[colorIndexGL];
const mtl::TextureRef &texture = colorAttachment.texture;
if (clearColorBuffers.test(colorIndexGL))
{
MTLClearColor clearVal;
overrideClearColor(texture, clearOpts.clearColor.value(), &clearVal);
encoder->setColorLoadAction(MTLLoadActionClear, clearVal, colorIndexGL);
}
}
// End current render encoder.
if (startedRenderPass)
if (clearOpts.clearDepth.valid())
{
encoder->setDepthStencilStoreAction(preClearDethpStoreAction, preClearStencilStoreAction);
contextMtl->endEncoding(encoder);
encoder->setDepthLoadAction(MTLLoadActionClear, clearOpts.clearDepth.value());
}
// Start new render encoder with loadOp=Clear
ensureRenderPassStarted(context, tempDesc);
if (clearOpts.clearStencil.valid())
{
encoder->setStencilLoadAction(MTLLoadActionClear, clearOpts.clearStencil.value());
}
return angle::Result::Continue;
}
......@@ -678,8 +919,9 @@ angle::Result FramebufferMtl::clearImpl(const gl::Context *context,
const gl::Rectangle renderArea(0, 0, mState.getDimensions().width,
mState.getDimensions().height);
clearOpts.clearArea = ClipRectToScissor(contextMtl->getState(), renderArea, false);
clearOpts.flipY = mFlipY;
clearOpts.dstTextureSize = mState.getExtents();
clearOpts.clearArea = ClipRectToScissor(contextMtl->getState(), renderArea, false);
clearOpts.flipY = mFlipY;
// Discard clear altogether if scissor has 0 width or height.
if (clearOpts.clearArea.width == 0 || clearOpts.clearArea.height == 0)
......@@ -695,6 +937,9 @@ angle::Result FramebufferMtl::clearImpl(const gl::Context *context,
clearOpts.clearDepth.reset();
}
// Only clear enabled buffers
clearOpts.enabledBuffers = clearColorBuffers;
if (clearOpts.clearArea == renderArea &&
(!clearOpts.clearColor.valid() || colorMask == MTLColorWriteMaskAll) &&
(!clearOpts.clearStencil.valid() ||
......@@ -786,13 +1031,21 @@ angle::Result FramebufferMtl::invalidateImpl(ContextMtl *contextMtl,
gl::Rectangle FramebufferMtl::getCorrectFlippedReadArea(const gl::Context *context,
const gl::Rectangle &glArea) const
{
RenderTargetMtl *colorReadRT = getColorReadRenderTarget(context);
ASSERT(colorReadRT);
RenderTargetMtl *readRT = getColorReadRenderTarget(context);
if (!readRT)
{
readRT = mDepthRenderTarget;
}
if (!readRT)
{
readRT = mStencilRenderTarget;
}
ASSERT(readRT);
gl::Rectangle flippedArea = glArea;
if (mFlipY)
{
flippedArea.y =
colorReadRT->getTexture()->height(static_cast<uint32_t>(colorReadRT->getLevelIndex())) -
readRT->getTexture()->height(static_cast<uint32_t>(readRT->getLevelIndex())) -
flippedArea.y - flippedArea.height;
}
......
......@@ -60,8 +60,8 @@ angle::Result RenderbufferMtl::setStorageImpl(const gl::Context *context,
if ((mTexture == nullptr || !mTexture->valid()) && (width != 0 && height != 0))
{
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mFormat, static_cast<uint32_t>(width),
static_cast<uint32_t>(height), 1, false, false,
&mTexture));
static_cast<uint32_t>(height), 1, false,
mFormat.hasDepthAndStencilBits(), &mTexture));
mRenderTarget.set(mTexture, 0, 0, mFormat);
......
......@@ -102,6 +102,7 @@ class SurfaceMtl : public SurfaceImpl
int mSamples = 0;
RenderTargetMtl mColorRenderTarget;
RenderTargetMtl mColorManualResolveRenderTarget;
RenderTargetMtl mDepthRenderTarget;
RenderTargetMtl mStencilRenderTarget;
};
......
......@@ -89,17 +89,19 @@ angle::Result CreateTexture(const gl::Context *context,
mtl::TextureRef *textureOut)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
bool allowFormatView = format.hasDepthAndStencilBits();
if (samples > 1)
{
ANGLE_TRY(mtl::Texture::Make2DMSTexture(contextMtl, format, width, height, samples,
/** renderTargetOnly */ renderTargetOnly,
/** allowFormatView */ false, textureOut));
/** allowFormatView */ allowFormatView,
textureOut));
}
else
{
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, format, width, height, 1,
/** renderTargetOnly */ renderTargetOnly,
/** allowFormatView */ false, textureOut));
/** allowFormatView */ allowFormatView, textureOut));
}
return angle::Result::Continue;
}
......@@ -291,6 +293,7 @@ void SurfaceMtl::destroy(const egl::Display *display)
mMSColorTexture = nullptr;
mColorRenderTarget.reset();
mColorManualResolveRenderTarget.reset();
mDepthRenderTarget.reset();
mStencilRenderTarget.reset();
}
......@@ -474,7 +477,7 @@ angle::Result SurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *
if (mDepthFormat.valid() && (!mDepthTexture || mDepthTexture->size() != size))
{
ANGLE_TRY(CreateTexture(context, mDepthFormat, size.width, size.height, mSamples,
/** renderTargetOnly */ true, &mDepthTexture));
/** renderTargetOnly */ false, &mDepthTexture));
mDepthRenderTarget.set(mDepthTexture, 0, 0, mDepthFormat);
}
......@@ -488,7 +491,7 @@ angle::Result SurfaceMtl::ensureCompanionTexturesSizeCorrect(const gl::Context *
else
{
ANGLE_TRY(CreateTexture(context, mStencilFormat, size.width, size.height, mSamples,
/** renderTargetOnly */ true, &mStencilTexture));
/** renderTargetOnly */ false, &mStencilTexture));
}
mStencilRenderTarget.set(mStencilTexture, 0, 0, mStencilFormat);
......@@ -505,11 +508,13 @@ angle::Result SurfaceMtl::resolveColorTextureIfNeeded(const gl::Context *context
// Manually resolve texture
ContextMtl *contextMtl = mtl::GetImpl(context);
mColorManualResolveRenderTarget.set(mColorTexture, 0, 0, mColorFormat);
mtl::RenderCommandEncoder *encoder =
contextMtl->getRenderCommandEncoder(mColorTexture, gl::ImageIndex::Make2D(0));
ANGLE_TRY(
contextMtl->getDisplay()->getUtils().blitWithDraw(context, encoder, mMSColorTexture));
contextMtl->getRenderTargetCommandEncoder(mColorManualResolveRenderTarget);
ANGLE_TRY(contextMtl->getDisplay()->getUtils().blitColorWithDraw(context, encoder,
mMSColorTexture));
contextMtl->endEncoding(true);
mColorManualResolveRenderTarget.reset();
}
return angle::Result::Continue;
}
......@@ -969,8 +974,9 @@ angle::Result IOSurfaceSurfaceMtl::ensureColorTextureCreated(const gl::Context *
if (kIOSurfaceFormats[mIOSurfaceFormatIdx].internalFormat == GL_RGB)
{
// This format has emulated alpha channel. Initialize texture's alpha channel to 1.0.
ANGLE_TRY(mtl::InitializeTextureContentsGPU(
context, mColorTexture, gl::ImageIndex::Make2D(0), MTLColorWriteMaskAlpha));
ANGLE_TRY(mtl::InitializeTextureContentsGPU(context, mColorTexture, mColorFormat,
gl::ImageIndex::Make2D(0),
MTLColorWriteMaskAlpha));
// Disable subsequent rendering to alpha channel.
mColorTexture->setColorWritableMask(MTLColorWriteMaskAll & (~MTLColorWriteMaskAlpha));
......
......@@ -333,9 +333,9 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
{
case gl::TextureType::_2D:
layers = 1;
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mFormat, desc.size.width,
desc.size.height, mips, false, true,
&mNativeTexture));
ANGLE_TRY(mtl::Texture::Make2DTexture(
contextMtl, mFormat, desc.size.width, desc.size.height, mips, false,
mFormat.hasDepthAndStencilBits(), &mNativeTexture));
mLayeredRenderTargets.resize(1);
mLayeredRenderTargets[0].set(mNativeTexture, 0, 0, mFormat);
mLayeredTextureViews.resize(1);
......@@ -344,7 +344,8 @@ angle::Result TextureMtl::ensureTextureCreated(const gl::Context *context)
case gl::TextureType::CubeMap:
layers = 6;
ANGLE_TRY(mtl::Texture::MakeCubeTexture(contextMtl, mFormat, desc.size.width, mips,
false, true, &mNativeTexture));
false, mFormat.hasDepthAndStencilBits(),
&mNativeTexture));
mLayeredRenderTargets.resize(gl::kCubeFaceCount);
mLayeredTextureViews.resize(gl::kCubeFaceCount);
for (uint32_t f = 0; f < gl::kCubeFaceCount; ++f)
......@@ -894,7 +895,8 @@ angle::Result TextureMtl::redefineImage(const gl::Context *context,
case gl::TextureType::_2D:
case gl::TextureType::CubeMap:
ANGLE_TRY(mtl::Texture::Make2DTexture(contextMtl, mtlFormat, size.width,
size.height, 1, false, false, &image));
size.height, 1, false,
mFormat.hasDepthAndStencilBits(), &image));
break;
default:
UNREACHABLE();
......@@ -1204,20 +1206,26 @@ angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
mtl::TextureRef &image = mTexImages[GetImageLayerIndex(index)][index.getLevelIndex()];
ASSERT(image && image->valid());
mtl::RenderCommandEncoder *cmdEncoder =
contextMtl->getRenderCommandEncoder(image, GetImageBaseLevelIndex(image));
mtl::BlitParams blitParams;
RenderTargetMtl writeRtt;
writeRtt.set(image, 0, 0, mFormat);
mtl::RenderCommandEncoder *cmdEncoder = contextMtl->getRenderTargetCommandEncoder(writeRtt);
mtl::ColorBlitParams blitParams;
blitParams.dstTextureSize = image->size(0);
blitParams.dstRect = gl::Rectangle(modifiedDestOffset.x, modifiedDestOffset.y,
clippedSourceArea.width, clippedSourceArea.height);
blitParams.dstScissorRect = blitParams.dstRect;
blitParams.dstOffset = modifiedDestOffset;
blitParams.dstColorMask = image->getColorWritableMask();
blitParams.enabledBuffers.set(0);
blitParams.src = colorReadRT->getTexture();
blitParams.srcLevel = static_cast<uint32_t>(colorReadRT->getLevelIndex());
blitParams.srcLevel = colorReadRT->getLevelIndex();
blitParams.srcLayer = colorReadRT->getLayerIndex();
blitParams.srcRect = clippedSourceArea;
blitParams.srcYFlipped = framebufferMtl->flipY();
blitParams.dstLuminance = internalFormat.isLUMA();
return displayMtl->getUtils().blitWithDraw(context, cmdEncoder, blitParams);
return displayMtl->getUtils().blitColorWithDraw(context, cmdEncoder, blitParams);
}
angle::Result TextureMtl::copySubImageCPU(const gl::Context *context,
......@@ -1336,14 +1344,19 @@ angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
}
mtl::RenderCommandEncoder *cmdEncoder =
contextMtl->getRenderCommandEncoder(image, GetImageBaseLevelIndex(image));
mtl::BlitParams blitParams;
contextMtl->getTextureRenderCommandEncoder(image, GetImageBaseLevelIndex(image));
mtl::ColorBlitParams blitParams;
blitParams.dstTextureSize = image->size();
blitParams.dstRect =
gl::Rectangle(destOffset.x, destOffset.y, sourceBox.width, sourceBox.height);
blitParams.dstScissorRect = blitParams.dstRect;
blitParams.dstOffset = destOffset;
blitParams.dstColorMask = image->getColorWritableMask();
blitParams.enabledBuffers.set(0);
blitParams.src = sourceTexture;
blitParams.srcLevel = sourceNativeLevel;
blitParams.srcLayer = 0;
blitParams.srcRect = gl::Rectangle(sourceBox.x, sourceBox.y, sourceBox.width, sourceBox.height);
blitParams.srcYFlipped = false;
blitParams.dstLuminance = internalFormat.isLUMA();
......@@ -1351,7 +1364,7 @@ angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
blitParams.unpackPremultiplyAlpha = unpackPremultiplyAlpha;
blitParams.unpackUnmultiplyAlpha = unpackUnmultiplyAlpha;
return displayMtl->getUtils().blitWithDraw(context, cmdEncoder, blitParams);
return displayMtl->getUtils().blitColorWithDraw(context, cmdEncoder, blitParams);
}
angle::Result TextureMtl::copySubTextureCPU(const gl::Context *context,
......
......@@ -97,7 +97,7 @@ namespace mtl
// NOTE(hqle): support variable max number of vertex attributes
constexpr uint32_t kMaxVertexAttribs = gl::MAX_VERTEX_ATTRIBS;
// NOTE(hqle): support variable max number of render targets
constexpr uint32_t kMaxRenderTargets = 1;
constexpr uint32_t kMaxRenderTargets = 4;
constexpr size_t kDefaultAttributeSize = 4 * sizeof(float);
......
......@@ -4,7 +4,8 @@
// found in the LICENSE file.
//
// mtl_render_utils.h:
// Defines the class interface for RenderUtils.
// 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_
......@@ -13,6 +14,7 @@
#import <Metal/Metal.h>
#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"
......@@ -28,6 +30,10 @@ namespace mtl
{
struct ClearRectParams : public ClearOptions
{
gl::Extents dstTextureSize;
// Only clear enabled buffers
gl::DrawBufferMask enabledBuffers;
gl::Rectangle clearArea;
bool flipY = false;
......@@ -35,25 +41,60 @@ struct ClearRectParams : public ClearOptions
struct BlitParams
{
gl::Offset dstOffset;
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(), and dstFlipY controls whether the final viewport needs to be
// flipped when drawing to destination texture.
// 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;
MTLColorWriteMask dstColorMask = MTLColorWriteMaskAll;
bool dstFlipX = false;
TextureRef src;
uint32_t srcLevel = 0;
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 unpackFlipY = false; // flip texture data copying process in Y direction
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
{
MTLColorWriteMask blitColorMask = MTLColorWriteMaskAll;
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;
uint32_t dstStencilLevel = 0;
uint32_t dstStencilLayer = 0;
bool dstPackedDepthStencilFormat = false;
};
struct TriFanFromArrayParams
{
uint32_t firstVertex;
......@@ -98,8 +139,7 @@ class ClearUtils
const ClearRectParams &params);
private:
// Defer loading of render pipeline state cache.
void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx);
void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx, uint32_t numColorAttachments);
void setupClearWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
......@@ -111,7 +151,7 @@ class ClearUtils
const ClearRectParams &params);
// Render pipeline cache for clear with draw:
RenderPipelineCache mClearRenderPipelineCache;
std::array<RenderPipelineCache, kMaxRenderTargets + 1> mClearRenderPipelineCache;
};
class ColorBlitUtils
......@@ -122,34 +162,85 @@ class ColorBlitUtils
void onDestroy();
// Blit texture data to current framebuffer
angle::Result blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
angle::Result blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ColorBlitParams &params);
private:
// Defer loading of render pipeline state cache.
void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
uint32_t numColorAttachments,
int alphaPremultiplyType,
int textureType,
int sourceTextureType,
RenderPipelineCache *cacheOut);
void setupBlitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
void setupColorBlitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ColorBlitParams &params);
id<MTLRenderPipelineState> getBlitRenderPipelineState(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
id<MTLRenderPipelineState> getColorBlitRenderPipelineState(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ColorBlitParams &params);
// Blit with draw pipeline caches:
// - array dimension: source texture type (2d, ms, array, 3d, etc)
// First array dimension: number of outputs.
// Second array dimension: source texture type (2d, ms, array, 3d, etc)
using ColorBlitRenderPipelineCacheArray =
std::array<RenderPipelineCache, mtl_shader::kTextureTypeCount>;
std::array<std::array<RenderPipelineCache, mtl_shader::kTextureTypeCount>,
kMaxRenderTargets>;
ColorBlitRenderPipelineCacheArray mBlitRenderPipelineCache;
ColorBlitRenderPipelineCacheArray mBlitPremultiplyAlphaRenderPipelineCache;
ColorBlitRenderPipelineCacheArray mBlitUnmultiplyAlphaRenderPipelineCache;
};
class DepthStencilBlitUtils
{
public:
void onDestroy();
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.
// 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 &params);
private:
void ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
int sourceDepthTextureType,
int sourceStencilTextureType,
RenderPipelineCache *cacheOut);
void setupDepthStencilBlitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const DepthStencilBlitParams &params);
id<MTLRenderPipelineState> getDepthStencilBlitRenderPipelineState(
const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const DepthStencilBlitParams &params);
id<MTLComputePipelineState> getStencilToBufferComputePipelineState(
ContextMtl *ctx,
const StencilBlitViaBufferParams &params);
std::array<RenderPipelineCache, mtl_shader::kTextureTypeCount> mDepthBlitRenderPipelineCache;
std::array<RenderPipelineCache, mtl_shader::kTextureTypeCount> mStencilBlitRenderPipelineCache;
std::array<std::array<RenderPipelineCache, mtl_shader::kTextureTypeCount>,
mtl_shader::kTextureTypeCount>
mDepthStencilBlitRenderPipelineCache;
std::array<AutoObjCPtr<id<MTLComputePipelineState>>, 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
{
......@@ -234,14 +325,21 @@ class RenderUtils : public Context, angle::NonCopyable
RenderCommandEncoder *cmdEncoder,
const ClearRectParams &params);
// Blit texture data to current framebuffer
angle::Result blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params);
angle::Result blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
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 blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const TextureRef &srcTexture);
angle::Result blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const TextureRef &srcTexture);
angle::Result blitDepthStencilWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const DepthStencilBlitParams &params);
// See DepthStencilBlitUtils::blitStencilViaCopyBuffer()
angle::Result blitStencilViaCopyBuffer(const gl::Context *context,
const StencilBlitViaBufferParams &params);
// See IndexGeneratorUtils
angle::Result convertIndexBufferGPU(ContextMtl *contextMtl,
......@@ -271,6 +369,7 @@ class RenderUtils : public Context, angle::NonCopyable
ClearUtils mClearUtils;
ColorBlitUtils mColorBlitUtils;
DepthStencilBlitUtils mDepthStencilBlitUtils;
IndexGeneratorUtils mIndexUtils;
};
......
......@@ -33,6 +33,7 @@ namespace
#define PREMULTIPLY_ALPHA_CONSTANT_NAME @"kPremultiplyAlpha"
#define UNMULTIPLY_ALPHA_CONSTANT_NAME @"kUnmultiplyAlpha"
#define SOURCE_TEXTURE_TYPE_CONSTANT_NAME @"kSourceTextureType"
#define SOURCE_TEXTURE2_TYPE_CONSTANT_NAME @"kSourceTexture2Type"
// See libANGLE/renderer/metal/shaders/clear.metal
struct ClearParamsUniform
......@@ -52,8 +53,21 @@ struct BlitParamsUniform
uint8_t dstFlipX = 0;
uint8_t dstFlipY = 0;
uint8_t dstLuminance = 0; // dest texture is luminace
uint8_t padding1;
float padding2[3];
uint8_t padding[13];
};
struct BlitStencilToBufferParamsUniform
{
float srcStartTexCoords[2];
float srcTexCoordSteps[2];
uint32_t srcLevel;
uint32_t srcLayer;
uint32_t dstSize[2];
uint32_t dstBufferRowPitch;
uint8_t resolveMS;
uint8_t padding[11];
};
struct IndexConversionUniform
......@@ -67,6 +81,7 @@ void GetBlitTexCoords(uint32_t srcWidth,
uint32_t srcHeight,
const gl::Rectangle &srcRect,
bool srcYFlipped,
bool unpackFlipX,
bool unpackFlipY,
float *u0,
float *v0,
......@@ -86,6 +101,11 @@ void GetBlitTexCoords(uint32_t srcWidth,
std::swap(y0, y1);
}
if (unpackFlipX)
{
std::swap(x0, x1);
}
if (unpackFlipY)
{
std::swap(y0, y1);
......@@ -248,6 +268,15 @@ void ClearRenderPipelineCacheArray(T *pipelineCacheArray)
}
template <typename T>
void ClearRenderPipelineCache2DArray(T *pipelineCache2DArray)
{
for (auto &level1Array : *pipelineCache2DArray)
{
ClearRenderPipelineCacheArray(&level1Array);
}
}
template <typename T>
void ClearPipelineStateArray(T *pipelineCacheArray)
{
for (auto &pipeline : *pipelineCacheArray)
......@@ -265,6 +294,28 @@ void ClearPipelineState2DArray(T *pipelineCache2DArray)
}
}
// Dispatch compute using 3D grid
void DispatchCompute(ContextMtl *contextMtl,
ComputeCommandEncoder *encoder,
bool allowNonUniform,
const MTLSize &numThreads,
const MTLSize &threadsPerThreadgroup)
{
if (allowNonUniform && contextMtl->getDisplay()->getFeatures().hasNonUniformDispatch.enabled)
{
encoder->dispatchNonUniform(numThreads, threadsPerThreadgroup);
}
else
{
MTLSize groups = MTLSizeMake(
(numThreads.width + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
(numThreads.height + threadsPerThreadgroup.height - 1) / threadsPerThreadgroup.height,
(numThreads.depth + threadsPerThreadgroup.depth - 1) / threadsPerThreadgroup.depth);
encoder->dispatch(groups, threadsPerThreadgroup);
}
}
// Dispatch compute using 1D grid
void DispatchCompute(ContextMtl *contextMtl,
ComputeCommandEncoder *cmdEncoder,
id<MTLComputePipelineState> pipelineState,
......@@ -285,20 +336,28 @@ void DispatchCompute(ContextMtl *contextMtl,
}
}
void SetupFullscreenDrawCommonStates(RenderCommandEncoder *cmdEncoder)
void SetupFullscreenQuadDrawCommonStates(RenderCommandEncoder *cmdEncoder)
{
cmdEncoder->setCullMode(MTLCullModeNone);
cmdEncoder->setTriangleFillMode(MTLTriangleFillModeFill);
cmdEncoder->setDepthBias(0, 0, 0);
}
void SetupBlitWithDrawUniformData(RenderCommandEncoder *cmdEncoder, const BlitParams &params)
void SetupBlitWithDrawUniformData(RenderCommandEncoder *cmdEncoder,
const BlitParams &params,
bool isColorBlit)
{
BlitParamsUniform uniformParams;
uniformParams.dstFlipY = params.dstFlipY ? 1 : 0;
uniformParams.srcLevel = params.srcLevel;
uniformParams.dstLuminance = params.dstLuminance ? 1 : 0;
uniformParams.dstFlipX = params.dstFlipX ? 1 : 0;
uniformParams.dstFlipY = params.dstFlipY ? 1 : 0;
uniformParams.srcLevel = params.srcLevel;
uniformParams.srcLayer = params.srcLayer;
if (isColorBlit)
{
const ColorBlitParams *colorParams = static_cast<const ColorBlitParams *>(&params);
uniformParams.dstLuminance = colorParams->dstLuminance ? 1 : 0;
}
// Compute source texCoords
uint32_t srcWidth = 0, srcHeight = 0;
......@@ -307,14 +366,21 @@ void SetupBlitWithDrawUniformData(RenderCommandEncoder *cmdEncoder, const BlitPa
srcWidth = params.src->width(params.srcLevel);
srcHeight = params.src->height(params.srcLevel);
}
else if (!isColorBlit)
{
const DepthStencilBlitParams *dsParams =
static_cast<const DepthStencilBlitParams *>(&params);
srcWidth = dsParams->srcStencil->width(dsParams->srcLevel);
srcHeight = dsParams->srcStencil->height(dsParams->srcLevel);
}
else
{
UNREACHABLE();
}
float u0, v0, u1, v1;
GetBlitTexCoords(srcWidth, srcHeight, params.srcRect, params.srcYFlipped, params.unpackFlipY,
&u0, &v0, &u1, &v1);
GetBlitTexCoords(srcWidth, srcHeight, params.srcRect, params.srcYFlipped, params.unpackFlipX,
params.unpackFlipY, &u0, &v0, &u1, &v1);
float du = u1 - u0;
float dv = v1 - v0;
......@@ -334,8 +400,52 @@ void SetupBlitWithDrawUniformData(RenderCommandEncoder *cmdEncoder, const BlitPa
cmdEncoder->setVertexData(uniformParams, 0);
cmdEncoder->setFragmentData(uniformParams, 0);
}
void SetupCommonBlitWithDrawStates(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params,
bool isColorBlit)
{
// Setup states
SetupFullscreenQuadDrawCommonStates(cmdEncoder);
// Viewport
MTLViewport viewportMtl =
GetViewport(params.dstRect, params.dstTextureSize.height, params.dstFlipY);
MTLScissorRect scissorRectMtl =
GetScissorRect(params.dstScissorRect, params.dstTextureSize.height, params.dstFlipY);
cmdEncoder->setViewport(viewportMtl);
cmdEncoder->setScissorRect(scissorRectMtl);
if (params.src)
{
cmdEncoder->setFragmentTexture(params.src, 0);
}
// Uniform
SetupBlitWithDrawUniformData(cmdEncoder, params, isColorBlit);
}
} // namespace
// StencilBlitViaBufferParams implementation
StencilBlitViaBufferParams::StencilBlitViaBufferParams() {}
StencilBlitViaBufferParams::StencilBlitViaBufferParams(const DepthStencilBlitParams &srcIn)
{
dstTextureSize = srcIn.dstTextureSize;
dstRect = srcIn.dstRect;
dstScissorRect = srcIn.dstScissorRect;
dstFlipY = srcIn.dstFlipY;
dstFlipX = srcIn.dstFlipX;
srcRect = srcIn.srcRect;
srcYFlipped = srcIn.srcYFlipped;
unpackFlipX = srcIn.unpackFlipX;
unpackFlipY = srcIn.unpackFlipY;
srcStencil = srcIn.srcStencil;
}
// RenderUtils implementation
RenderUtils::RenderUtils(DisplayMtl *display) : Context(display) {}
......@@ -351,6 +461,7 @@ void RenderUtils::onDestroy()
mIndexUtils.onDestroy();
mClearUtils.onDestroy();
mColorBlitUtils.onDestroy();
mDepthStencilBlitUtils.onDestroy();
}
// override ErrorHandler
......@@ -386,25 +497,44 @@ angle::Result RenderUtils::clearWithDraw(const gl::Context *context,
}
// Blit texture data to current framebuffer
angle::Result RenderUtils::blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params)
angle::Result RenderUtils::blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ColorBlitParams &params)
{
return mColorBlitUtils.blitWithDraw(context, cmdEncoder, params);
return mColorBlitUtils.blitColorWithDraw(context, cmdEncoder, params);
}
angle::Result RenderUtils::blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const TextureRef &srcTexture)
angle::Result RenderUtils::blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const TextureRef &srcTexture)
{
if (!srcTexture)
{
return angle::Result::Continue;
}
BlitParams params;
params.src = srcTexture;
params.srcRect = gl::Rectangle(0, 0, srcTexture->width(), srcTexture->height());
return blitWithDraw(context, cmdEncoder, params);
ColorBlitParams params;
params.enabledBuffers.set(0);
params.src = srcTexture;
params.dstTextureSize =
gl::Extents(static_cast<int>(srcTexture->width()), static_cast<int>(srcTexture->height()),
static_cast<int>(srcTexture->depth()));
params.dstRect = params.dstScissorRect = params.srcRect =
gl::Rectangle(0, 0, params.dstTextureSize.width, params.dstTextureSize.height);
return blitColorWithDraw(context, cmdEncoder, params);
}
angle::Result RenderUtils::blitDepthStencilWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const DepthStencilBlitParams &params)
{
return mDepthStencilBlitUtils.blitDepthStencilWithDraw(context, cmdEncoder, params);
}
angle::Result RenderUtils::blitStencilViaCopyBuffer(const gl::Context *context,
const StencilBlitViaBufferParams &params)
{
return mDepthStencilBlitUtils.blitStencilViaCopyBuffer(context, params);
}
angle::Result RenderUtils::convertIndexBufferGPU(ContextMtl *contextMtl,
......@@ -445,11 +575,18 @@ ClearUtils::ClearUtils() = default;
void ClearUtils::onDestroy()
{
mClearRenderPipelineCache.clear();
ClearRenderPipelineCacheArray(&mClearRenderPipelineCache);
}
void ClearUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx)
void ClearUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx, uint32_t numOutputs)
{
RenderPipelineCache &cache = mClearRenderPipelineCache[numOutputs];
if (cache.getVertexShader() && cache.getFragmentShader())
{
// Already initialized.
return;
}
ANGLE_MTL_OBJC_SCOPE
{
NSError *err = nil;
......@@ -459,8 +596,8 @@ void ClearUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx)
MTLFunctionConstantValues *funcConstants =
[[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE];
// Use 1 color outputs for now.
uint32_t numOutputs = 1;
// Create clear shader pipeline cache for each number of color outputs.
// So clear k color outputs will use mClearRenderPipelineCache[k] for example:
[funcConstants setConstantValue:&numOutputs
type:MTLDataTypeUInt
withName:NUM_COLOR_OUTPUTS_CONSTANT_NAME];
......@@ -470,8 +607,8 @@ void ClearUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx)
error:&err] ANGLE_MTL_AUTORELEASE];
ASSERT(fragmentShader);
mClearRenderPipelineCache.setVertexShader(ctx, vertexShader);
mClearRenderPipelineCache.setFragmentShader(ctx, fragmentShader);
cache.setVertexShader(ctx, vertexShader);
cache.setFragmentShader(ctx, fragmentShader);
}
}
......@@ -516,23 +653,29 @@ id<MTLRenderPipelineState> ClearUtils::getClearRenderPipelineState(const gl::Con
RenderCommandEncoder *cmdEncoder,
const ClearRectParams &params)
{
ContextMtl *contextMtl = GetImpl(context);
MTLColorWriteMask colorMask = contextMtl->getColorMask();
ContextMtl *contextMtl = GetImpl(context);
// The color mask to be applied to every color attachment:
MTLColorWriteMask globalColorMask = contextMtl->getColorMask();
if (!params.clearColor.valid())
{
colorMask = MTLColorWriteMaskNone;
globalColorMask = MTLColorWriteMaskNone;
}
RenderPipelineDesc pipelineDesc;
const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
renderPassDesc.populateRenderPipelineOutputDesc(colorMask, &pipelineDesc.outputDescriptor);
renderPassDesc.populateRenderPipelineOutputDesc(globalColorMask,
&pipelineDesc.outputDescriptor);
// Disable clear for some outputs that are not enabled
pipelineDesc.outputDescriptor.updateEnabledDrawBuffers(params.enabledBuffers);
pipelineDesc.inputPrimitiveTopology = kPrimitiveTopologyClassTriangle;
ensureRenderPipelineStateCacheInitialized(contextMtl);
ensureRenderPipelineStateCacheInitialized(contextMtl, renderPassDesc.numColorAttachments);
RenderPipelineCache &cache = mClearRenderPipelineCache[renderPassDesc.numColorAttachments];
return mClearRenderPipelineCache.getRenderPipelineState(contextMtl, pipelineDesc);
return cache.getRenderPipelineState(contextMtl, pipelineDesc);
}
void ClearUtils::setupClearWithDraw(const gl::Context *context,
......@@ -544,41 +687,19 @@ void ClearUtils::setupClearWithDraw(const gl::Context *context,
getClearRenderPipelineState(context, cmdEncoder, params);
ASSERT(renderPipelineState);
// Setup states
SetupFullscreenDrawCommonStates(cmdEncoder);
SetupFullscreenQuadDrawCommonStates(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;
}
TextureRef texture = renderPassAttachment.texture;
viewport =
GetViewport(params.clearArea, texture->height(renderPassAttachment.level), params.flipY);
viewport = GetViewport(params.clearArea, params.dstTextureSize.height, params.flipY);
scissorRect =
GetScissorRect(params.clearArea, texture->height(renderPassAttachment.level), params.flipY);
scissorRect = GetScissorRect(params.clearArea, params.dstTextureSize.height, params.flipY);
cmdEncoder->setViewport(viewport);
cmdEncoder->setScissorRect(scissorRect);
......@@ -638,12 +759,13 @@ ColorBlitUtils::ColorBlitUtils() = default;
void ColorBlitUtils::onDestroy()
{
ClearRenderPipelineCacheArray(&mBlitRenderPipelineCache);
ClearRenderPipelineCacheArray(&mBlitPremultiplyAlphaRenderPipelineCache);
ClearRenderPipelineCacheArray(&mBlitUnmultiplyAlphaRenderPipelineCache);
ClearRenderPipelineCache2DArray(&mBlitRenderPipelineCache);
ClearRenderPipelineCache2DArray(&mBlitPremultiplyAlphaRenderPipelineCache);
ClearRenderPipelineCache2DArray(&mBlitUnmultiplyAlphaRenderPipelineCache);
}
void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
uint32_t numOutputs,
int alphaPremultiplyType,
int textureType,
RenderPipelineCache *cacheOut)
......@@ -681,8 +803,8 @@ void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
type:MTLDataTypeBool
withName:UNMULTIPLY_ALPHA_CONSTANT_NAME];
// Use 1 color outputs for now.
uint32_t numOutputs = 1;
// We create blit shader pipeline cache for each number of color outputs.
// So blit k color outputs will use mBlitRenderPipelineCache[k-1] for example:
[funcConstants setConstantValue:&numOutputs
type:MTLDataTypeUInt
withName:NUM_COLOR_OUTPUTS_CONSTANT_NAME];
......@@ -703,104 +825,305 @@ void ColorBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
}
}
id<MTLRenderPipelineState> ColorBlitUtils::getBlitRenderPipelineState(
id<MTLRenderPipelineState> ColorBlitUtils::getColorBlitRenderPipelineState(
const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params)
const ColorBlitParams &params)
{
ContextMtl *contextMtl = GetImpl(context);
RenderPipelineDesc pipelineDesc;
const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
renderPassDesc.populateRenderPipelineOutputDesc(params.dstColorMask,
renderPassDesc.populateRenderPipelineOutputDesc(params.blitColorMask,
&pipelineDesc.outputDescriptor);
// Disable blit for some outputs that are not enabled
pipelineDesc.outputDescriptor.updateEnabledDrawBuffers(params.enabledBuffers);
pipelineDesc.inputPrimitiveTopology = kPrimitiveTopologyClassTriangle;
RenderPipelineCache *pipelineCache;
int alphaPremultiplyType;
int textureType = GetShaderTextureType(params.src);
uint32_t nOutputIndex = renderPassDesc.numColorAttachments - 1;
int textureType = GetShaderTextureType(params.src);
if (params.unpackPremultiplyAlpha == params.unpackUnmultiplyAlpha)
{
alphaPremultiplyType = 0;
pipelineCache = &mBlitRenderPipelineCache[textureType];
pipelineCache = &mBlitRenderPipelineCache[nOutputIndex][textureType];
}
else if (params.unpackPremultiplyAlpha)
{
alphaPremultiplyType = 1;
pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache[textureType];
pipelineCache = &mBlitPremultiplyAlphaRenderPipelineCache[nOutputIndex][textureType];
}
else
{
alphaPremultiplyType = 2;
pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache[textureType];
pipelineCache = &mBlitUnmultiplyAlphaRenderPipelineCache[nOutputIndex][textureType];
}
ensureRenderPipelineStateCacheInitialized(contextMtl, alphaPremultiplyType, textureType,
pipelineCache);
ensureRenderPipelineStateCacheInitialized(contextMtl, renderPassDesc.numColorAttachments,
alphaPremultiplyType, textureType, pipelineCache);
return pipelineCache->getRenderPipelineState(contextMtl, pipelineDesc);
}
void ColorBlitUtils::setupBlitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params)
void ColorBlitUtils::setupColorBlitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ColorBlitParams &params)
{
ASSERT(cmdEncoder->renderPassDesc().numColorAttachments == 1 && params.src);
ASSERT(cmdEncoder->renderPassDesc().numColorAttachments >= 1 && params.src);
ContextMtl *contextMtl = mtl::GetImpl(context);
// Generate render pipeline state
id<MTLRenderPipelineState> renderPipelineState =
getBlitRenderPipelineState(context, cmdEncoder, params);
getColorBlitRenderPipelineState(context, cmdEncoder, params);
ASSERT(renderPipelineState);
// Setup states
SetupFullscreenDrawCommonStates(cmdEncoder);
cmdEncoder->setRenderPipelineState(renderPipelineState);
cmdEncoder->setDepthStencilState(
contextMtl->getDisplay()->getStateCache().getNullDepthStencilState(contextMtl));
// Viewport
const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
const RenderPassColorAttachmentDesc &renderPassColorAttachment =
renderPassDesc.colorAttachments[0];
mtl::TextureRef 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);
SetupCommonBlitWithDrawStates(context, cmdEncoder, params, true);
// Set default sampler state with linear filtering.
// NOTE(hqle): Support configurable filtering (required by glBitFramebuffer).
// Set sampler state
SamplerDesc samplerDesc;
samplerDesc.reset();
samplerDesc.minFilter = samplerDesc.magFilter = MTLSamplerMinMagFilterLinear;
samplerDesc.minFilter = samplerDesc.magFilter = GetFilter(params.filter);
cmdEncoder->setFragmentSamplerState(contextMtl->getDisplay()->getStateCache().getSamplerState(
contextMtl->getMetalDevice(), samplerDesc),
0, FLT_MAX, 0);
// Uniform
SetupBlitWithDrawUniformData(cmdEncoder, params);
}
angle::Result ColorBlitUtils::blitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const BlitParams &params)
angle::Result ColorBlitUtils::blitColorWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const ColorBlitParams &params)
{
if (!params.src)
{
return angle::Result::Continue;
}
ContextMtl *contextMtl = GetImpl(context);
setupBlitWithDraw(context, cmdEncoder, params);
setupColorBlitWithDraw(context, cmdEncoder, params);
// Draw the screen aligned triangle
cmdEncoder->draw(MTLPrimitiveTypeTriangle, 0, 3);
// Invalidate current context's state
contextMtl->invalidateState(context);
return angle::Result::Continue;
}
// DepthStencilBlitUtils implementation
void DepthStencilBlitUtils::onDestroy()
{
ClearRenderPipelineCacheArray(&mDepthBlitRenderPipelineCache);
ClearRenderPipelineCacheArray(&mStencilBlitRenderPipelineCache);
ClearRenderPipelineCache2DArray(&mDepthStencilBlitRenderPipelineCache);
ClearPipelineStateArray(&mStencilBlitToBufferComPipelineCache);
mStencilCopyBuffer = nullptr;
}
void DepthStencilBlitUtils::ensureRenderPipelineStateCacheInitialized(ContextMtl *ctx,
int sourceDepthTextureType,
int sourceStencilTextureType,
RenderPipelineCache *cacheOut)
{
RenderPipelineCache &cache = *cacheOut;
if (cache.getVertexShader() && cache.getFragmentShader())
{
// Already initialized.
return;
}
ANGLE_MTL_OBJC_SCOPE
{
NSError *err = nil;
id<MTLLibrary> shaderLib = ctx->getDisplay()->getDefaultShadersLib();
id<MTLFunction> vertexShader =
[[shaderLib newFunctionWithName:@"blitVS"] ANGLE_MTL_AUTORELEASE];
MTLFunctionConstantValues *funcConstants =
[[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE];
NSString *shaderName;
if (sourceDepthTextureType != -1 && sourceStencilTextureType != -1)
{
shaderName = @"blitDepthStencilFS";
}
else if (sourceDepthTextureType != -1)
{
shaderName = @"blitDepthFS";
}
else
{
shaderName = @"blitStencilFS";
}
if (sourceDepthTextureType != -1)
{
[funcConstants setConstantValue:&sourceDepthTextureType
type:MTLDataTypeInt
withName:SOURCE_TEXTURE_TYPE_CONSTANT_NAME];
}
if (sourceStencilTextureType != -1)
{
[funcConstants setConstantValue:&sourceStencilTextureType
type:MTLDataTypeInt
withName:SOURCE_TEXTURE2_TYPE_CONSTANT_NAME];
}
id<MTLFunction> fragmentShader =
[[shaderLib newFunctionWithName:shaderName constantValues:funcConstants
error:&err] ANGLE_MTL_AUTORELEASE];
ASSERT(fragmentShader);
cache.setVertexShader(ctx, vertexShader);
cache.setFragmentShader(ctx, fragmentShader);
}
}
id<MTLComputePipelineState> DepthStencilBlitUtils::getStencilToBufferComputePipelineState(
ContextMtl *contextMtl,
const StencilBlitViaBufferParams &params)
{
int sourceStencilTextureType = GetShaderTextureType(params.srcStencil);
AutoObjCPtr<id<MTLComputePipelineState>> &cache =
mStencilBlitToBufferComPipelineCache[sourceStencilTextureType];
if (cache)
{
return cache;
}
ANGLE_MTL_OBJC_SCOPE
{
auto funcConstants = [[[MTLFunctionConstantValues alloc] init] ANGLE_MTL_AUTORELEASE];
[funcConstants setConstantValue:&sourceStencilTextureType
type:MTLDataTypeInt
withName:SOURCE_TEXTURE2_TYPE_CONSTANT_NAME];
EnsureSpecializedComputePipelineInitialized(
contextMtl->getDisplay(), @"blitStencilToBufferCS", funcConstants, &cache);
}
return cache;
}
id<MTLRenderPipelineState> DepthStencilBlitUtils::getDepthStencilBlitRenderPipelineState(
const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const DepthStencilBlitParams &params)
{
ContextMtl *contextMtl = GetImpl(context);
RenderPipelineDesc pipelineDesc;
const RenderPassDesc &renderPassDesc = cmdEncoder->renderPassDesc();
renderPassDesc.populateRenderPipelineOutputDesc(&pipelineDesc.outputDescriptor);
// Disable all color outputs
pipelineDesc.outputDescriptor.updateEnabledDrawBuffers(gl::DrawBufferMask());
pipelineDesc.inputPrimitiveTopology = kPrimitiveTopologyClassTriangle;
RenderPipelineCache *pipelineCache;
int depthTextureType = GetShaderTextureType(params.src);
int stencilTextureType = GetShaderTextureType(params.srcStencil);
if (params.src && params.srcStencil)
{
pipelineCache = &mDepthStencilBlitRenderPipelineCache[depthTextureType][stencilTextureType];
}
else if (params.src)
{
// Only depth blit
pipelineCache = &mDepthBlitRenderPipelineCache[depthTextureType];
}
else
{
// Only stencil blit
pipelineCache = &mStencilBlitRenderPipelineCache[stencilTextureType];
}
ensureRenderPipelineStateCacheInitialized(contextMtl, depthTextureType, stencilTextureType,
pipelineCache);
return pipelineCache->getRenderPipelineState(contextMtl, pipelineDesc);
}
void DepthStencilBlitUtils::setupDepthStencilBlitWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const DepthStencilBlitParams &params)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
ASSERT(params.src || params.srcStencil);
SetupCommonBlitWithDrawStates(context, cmdEncoder, params, false);
// Generate render pipeline state
id<MTLRenderPipelineState> renderPipelineState =
getDepthStencilBlitRenderPipelineState(context, cmdEncoder, params);
ASSERT(renderPipelineState);
// Setup states
cmdEncoder->setRenderPipelineState(renderPipelineState);
// Depth stencil state
mtl::DepthStencilDesc dsStateDesc;
dsStateDesc.reset();
dsStateDesc.depthCompareFunction = MTLCompareFunctionAlways;
if (params.src)
{
// Enable depth write
dsStateDesc.depthWriteEnabled = true;
}
else
{
// Disable depth write
dsStateDesc.depthWriteEnabled = false;
}
if (params.srcStencil)
{
cmdEncoder->setFragmentTexture(params.srcStencil, 1);
if (!contextMtl->getDisplay()->getFeatures().hasStencilOutput.enabled)
{
// Hardware must support stencil writing directly in shader.
UNREACHABLE();
}
// Enable stencil write to framebuffer
dsStateDesc.frontFaceStencil.stencilCompareFunction = MTLCompareFunctionAlways;
dsStateDesc.backFaceStencil.stencilCompareFunction = MTLCompareFunctionAlways;
dsStateDesc.frontFaceStencil.depthStencilPassOperation = MTLStencilOperationReplace;
dsStateDesc.backFaceStencil.depthStencilPassOperation = MTLStencilOperationReplace;
dsStateDesc.frontFaceStencil.writeMask = kStencilMaskAll;
dsStateDesc.backFaceStencil.writeMask = kStencilMaskAll;
}
cmdEncoder->setDepthStencilState(contextMtl->getDisplay()->getStateCache().getDepthStencilState(
contextMtl->getMetalDevice(), dsStateDesc));
}
angle::Result DepthStencilBlitUtils::blitDepthStencilWithDraw(const gl::Context *context,
RenderCommandEncoder *cmdEncoder,
const DepthStencilBlitParams &params)
{
if (!params.src && !params.srcStencil)
{
return angle::Result::Continue;
}
ContextMtl *contextMtl = GetImpl(context);
setupDepthStencilBlitWithDraw(context, cmdEncoder, params);
// Draw the screen aligned triangle
cmdEncoder->draw(MTLPrimitiveTypeTriangle, 0, 3);
......@@ -811,6 +1134,104 @@ angle::Result ColorBlitUtils::blitWithDraw(const gl::Context *context,
return angle::Result::Continue;
}
angle::Result DepthStencilBlitUtils::blitStencilViaCopyBuffer(
const gl::Context *context,
const StencilBlitViaBufferParams &params)
{
// Depth texture must be omitted.
ASSERT(!params.src);
if (!params.srcStencil || !params.dstStencil)
{
return angle::Result::Continue;
}
ContextMtl *contextMtl = GetImpl(context);
// Create intermediate buffer.
uint32_t bufferRequiredRowPitch =
static_cast<uint32_t>(params.dstRect.width) * params.dstStencil->samples();
uint32_t bufferRequiredSize =
bufferRequiredRowPitch * static_cast<uint32_t>(params.dstRect.height);
if (!mStencilCopyBuffer || mStencilCopyBuffer->size() < bufferRequiredSize)
{
ANGLE_TRY(Buffer::MakeBuffer(contextMtl, bufferRequiredSize, nullptr, &mStencilCopyBuffer));
}
// Copy stencil data to buffer via compute shader. We cannot use blit command since blit command
// doesn't support multisample resolve and scaling.
ComputeCommandEncoder *cmdEncoder = contextMtl->getComputeCommandEncoder();
ASSERT(cmdEncoder);
id<MTLComputePipelineState> pipeline =
getStencilToBufferComputePipelineState(contextMtl, params);
cmdEncoder->setComputePipelineState(pipeline);
uint32_t srcWidth = params.srcStencil->width(params.srcLevel);
uint32_t srcHeight = params.srcStencil->height(params.srcLevel);
float u0, v0, u1, v1;
bool unpackFlipX = params.unpackFlipX;
bool unpackFlipY = params.unpackFlipY;
if (params.dstFlipX)
{
unpackFlipX = !unpackFlipX;
}
if (params.dstFlipY)
{
unpackFlipY = !unpackFlipY;
}
GetBlitTexCoords(srcWidth, srcHeight, params.srcRect, params.srcYFlipped, unpackFlipX,
unpackFlipY, &u0, &v0, &u1, &v1);
BlitStencilToBufferParamsUniform uniform;
uniform.srcTexCoordSteps[0] = (u1 - u0) / params.dstRect.width;
uniform.srcTexCoordSteps[1] = (v1 - v0) / params.dstRect.height;
uniform.srcStartTexCoords[0] = u0 + uniform.srcTexCoordSteps[0] * 0.5f;
uniform.srcStartTexCoords[1] = v0 + uniform.srcTexCoordSteps[1] * 0.5f;
uniform.srcLevel = params.srcLevel;
uniform.srcLayer = params.srcLayer;
uniform.dstSize[0] = params.dstRect.width;
uniform.dstSize[1] = params.dstRect.height;
uniform.dstBufferRowPitch = bufferRequiredRowPitch;
uniform.resolveMS = params.dstStencil->samples() == 1;
cmdEncoder->setTexture(params.srcStencil, 1);
cmdEncoder->setData(uniform, 0);
cmdEncoder->setBufferForWrite(mStencilCopyBuffer, 0, 1);
NSUInteger w = pipeline.threadExecutionWidth;
MTLSize threadsPerThreadgroup = MTLSizeMake(w, 1, 1);
DispatchCompute(contextMtl, cmdEncoder, /** allowNonUniform */ true,
MTLSizeMake(params.dstRect.width, params.dstRect.height, 1),
threadsPerThreadgroup);
// Copy buffer to real destination texture
ASSERT(params.dstStencil->textureType() != MTLTextureType3D);
mtl::BlitCommandEncoder *blitEncoder = contextMtl->getBlitCommandEncoder();
// Only copy the scissored area of the buffer.
MTLScissorRect viewportRectMtl =
GetScissorRect(params.dstRect, params.dstTextureSize.height, params.dstFlipY);
MTLScissorRect scissorRectMtl =
GetScissorRect(params.dstScissorRect, params.dstTextureSize.height, params.dstFlipY);
uint32_t dx = static_cast<uint32_t>(scissorRectMtl.x - viewportRectMtl.x);
uint32_t dy = static_cast<uint32_t>(scissorRectMtl.y - viewportRectMtl.y);
uint32_t bufferStartReadableOffset = dx + bufferRequiredRowPitch * dy;
blitEncoder->copyBufferToTexture(
mStencilCopyBuffer, bufferStartReadableOffset, bufferRequiredRowPitch, 0,
MTLSizeMake(scissorRectMtl.width, scissorRectMtl.height, 1), params.dstStencil,
params.dstStencilLayer, params.dstStencilLevel,
MTLOriginMake(scissorRectMtl.x, scissorRectMtl.y, 0),
params.dstPackedDepthStencilFormat ? MTLBlitOptionStencilFromDepthStencil
: MTLBlitOptionNone);
return angle::Result::Continue;
}
// IndexGeneratorUtils implementation
void IndexGeneratorUtils::onDestroy()
{
......
......@@ -158,6 +158,7 @@ class Texture final : public Resource,
uint32_t width(uint32_t level = 0) const;
uint32_t height(uint32_t level = 0) const;
uint32_t depth(uint32_t level = 0) const;
gl::Extents size(uint32_t level = 0) const;
gl::Extents size(const gl::ImageIndex &index) const;
......@@ -171,6 +172,9 @@ class Texture final : public Resource,
// Get linear color space view. Only usable for sRGB textures.
TextureRef getLinearColorView();
// Get stencil view
TextureRef getStencilView();
// Change the wrapped metal object. Special case for swapchain image
void set(id<MTLTexture> metalTexture);
......@@ -206,6 +210,8 @@ class Texture final : public Resource,
// Linear view of sRGB texture
TextureRef mLinearColorView;
TextureRef mStencilView;
};
class Buffer final : public Resource, public WrappedObject<id<MTLBuffer>>
......
......@@ -425,13 +425,18 @@ uint32_t Texture::height(uint32_t level) const
return static_cast<uint32_t>(GetMipSize(get().height, level));
}
uint32_t Texture::depth(uint32_t level) const
{
return static_cast<uint32_t>(GetMipSize(get().depth, level));
}
gl::Extents Texture::size(uint32_t level) const
{
gl::Extents re;
re.width = width(level);
re.height = height(level);
re.depth = static_cast<uint32_t>(GetMipSize(get().depth, level));
re.depth = depth(level);
return re;
}
......@@ -472,10 +477,41 @@ TextureRef Texture::getLinearColorView()
return mLinearColorView;
}
TextureRef Texture::getStencilView()
{
if (mStencilView)
{
return mStencilView;
}
switch (pixelFormat())
{
case MTLPixelFormatStencil8:
case MTLPixelFormatX32_Stencil8:
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
case MTLPixelFormatX24_Stencil8:
#endif
return mStencilView = shared_from_this();
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
case MTLPixelFormatDepth24Unorm_Stencil8:
mStencilView = createViewWithDifferentFormat(MTLPixelFormatX24_Stencil8);
break;
#endif
case MTLPixelFormatDepth32Float_Stencil8:
mStencilView = createViewWithDifferentFormat(MTLPixelFormatX32_Stencil8);
break;
default:
UNREACHABLE();
}
return mStencilView;
}
void Texture::set(id<MTLTexture> metalTexture)
{
ParentClass::set(metalTexture);
// Reset stencil view
mStencilView = nullptr;
mLinearColorView = nullptr;
}
......
......@@ -206,6 +206,8 @@ struct RenderPipelineOutputDesc
{
bool operator==(const RenderPipelineOutputDesc &rhs) const;
void updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers);
RenderPipelineColorAttachmentDesc colorAttachments[kMaxRenderTargets];
// Use uint16_t instead of MTLPixelFormat to compact space
......
......@@ -618,6 +618,17 @@ bool RenderPipelineOutputDesc::operator==(const RenderPipelineOutputDesc &rhs) c
ANGLE_PROP_EQ(*this, rhs, stencilAttachmentPixelFormat);
}
void RenderPipelineOutputDesc::updateEnabledDrawBuffers(gl::DrawBufferMask enabledBuffers)
{
for (uint32_t colorIndex = 0; colorIndex < this->numColorAttachments; ++colorIndex)
{
if (!enabledBuffers.test(colorIndex))
{
this->colorAttachments[colorIndex].writeMask = MTLColorWriteMaskNone;
}
}
}
// RenderPipelineDesc implementation
RenderPipelineDesc::RenderPipelineDesc()
{
......@@ -716,11 +727,10 @@ void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendStat
auto &renderPassColorAttachment = this->colorAttachments[i];
auto texture = renderPassColorAttachment.texture;
// Copy parameters from blend state
outputDescriptor.colorAttachments[i].update(blendState);
if (texture)
{
// Copy parameters from blend state
outputDescriptor.colorAttachments[i].update(blendState);
outputDescriptor.colorAttachments[i].pixelFormat = texture->pixelFormat();
......@@ -731,7 +741,9 @@ void RenderPassDesc::populateRenderPipelineOutputDesc(const BlendDesc &blendStat
}
else
{
outputDescriptor.colorAttachments[i].pixelFormat = MTLPixelFormatInvalid;
outputDescriptor.colorAttachments[i].blendingEnabled = false;
outputDescriptor.colorAttachments[i].pixelFormat = MTLPixelFormatInvalid;
}
}
......
......@@ -37,6 +37,7 @@ angle::Result InitializeTextureContents(const gl::Context *context,
// - channelsToInit parameter controls which channels will get their content initialized.
angle::Result InitializeTextureContentsGPU(const gl::Context *context,
const TextureRef &texture,
const Format &textureObjFormat,
const gl::ImageIndex &index,
MTLColorWriteMask channelsToInit);
......@@ -54,6 +55,8 @@ MTLScissorRect GetScissorRect(const gl::Rectangle &rect,
NSUInteger screenHeight = 0,
bool flipY = false);
uint32_t GetDeviceVendorId(id<MTLDevice> metalDevice);
AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary(id<MTLDevice> metalDevice,
const std::string &source,
AutoObjCPtr<NSError *> *error);
......
......@@ -13,8 +13,10 @@
#include <TargetConditionals.h>
#include "common/MemoryBuffer.h"
#include "gpu_info_util/SystemInfo.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/RenderTargetMtl.h"
#include "libANGLE/renderer/metal/mtl_render_utils.h"
namespace rx
......@@ -22,6 +24,48 @@ namespace rx
namespace mtl
{
namespace
{
uint32_t GetDeviceVendorIdFromName(id<MTLDevice> metalDevice)
{
struct Vendor
{
NSString *const trademark;
uint32_t vendorId;
};
constexpr Vendor kVendors[] = {{@"AMD", angle::kVendorID_AMD},
{@"Radeon", angle::kVendorID_AMD},
{@"Intel", angle::kVendorID_Intel},
{@"Geforce", angle::kVendorID_NVIDIA},
{@"Quadro", angle::kVendorID_NVIDIA}};
ANGLE_MTL_OBJC_SCOPE
{
if (metalDevice)
{
for (const Vendor &it : kVendors)
{
if ([metalDevice.name rangeOfString:it.trademark].location != NSNotFound)
{
return it.vendorId;
}
}
}
return 0;
}
}
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
uint32_t GetDeviceVendorIdFromIOKit(id<MTLDevice> device)
{
return angle::GetVendorIDFromMetalDeviceRegistryID(device.registryID);
}
#endif
}
angle::Result InitializeTextureContents(const gl::Context *context,
const TextureRef &texture,
const Format &textureObjFormat,
......@@ -89,7 +133,8 @@ angle::Result InitializeTextureContents(const gl::Context *context,
}
else
{
ANGLE_TRY(InitializeTextureContentsGPU(context, texture, index, MTLColorWriteMaskAll));
ANGLE_TRY(InitializeTextureContentsGPU(context, texture, textureObjFormat, index,
MTLColorWriteMaskAll));
}
return angle::Result::Continue;
......@@ -97,11 +142,17 @@ angle::Result InitializeTextureContents(const gl::Context *context,
angle::Result InitializeTextureContentsGPU(const gl::Context *context,
const TextureRef &texture,
const Format &textureObjFormat,
const gl::ImageIndex &index,
MTLColorWriteMask channelsToInit)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// NOTE(hqle): Support layered textures
ASSERT(!index.hasLayer());
// Use clear render command
RenderTargetMtl tempRtt;
tempRtt.set(texture, index.getLevelIndex(), 0, textureObjFormat);
// temporarily enable color channels requested via channelsToInit. Some emulated format has some
// channels write mask disabled when the texture is created.
......@@ -113,16 +164,18 @@ angle::Result InitializeTextureContentsGPU(const gl::Context *context,
{
// If all channels will be initialized, use clear loadOp.
Optional<MTLClearColor> blackColor = MTLClearColorMake(0, 0, 0, 1);
encoder = contextMtl->getRenderCommandEncoder(texture, index, blackColor);
encoder = contextMtl->getRenderTargetCommandEncoderWithClear(tempRtt, blackColor);
}
else
{
// If there are some channels don't need to be initialized, we must use clearWithDraw.
encoder = contextMtl->getRenderCommandEncoder(texture, index);
encoder = contextMtl->getRenderTargetCommandEncoder(tempRtt);
ClearRectParams clearParams;
clearParams.clearColor = {.alpha = 1};
clearParams.clearArea = gl::Rectangle(0, 0, texture->width(), texture->height());
clearParams.clearColor = {.alpha = 1};
clearParams.dstTextureSize = texture->size();
clearParams.enabledBuffers.set(0);
clearParams.clearArea = gl::Rectangle(0, 0, texture->width(), texture->height());
ANGLE_TRY(
contextMtl->getDisplay()->getUtils().clearWithDraw(context, encoder, clearParams));
......@@ -193,6 +246,23 @@ MTLScissorRect GetScissorRect(const gl::Rectangle &rect, NSUInteger screenHeight
return re;
}
uint32_t GetDeviceVendorId(id<MTLDevice> metalDevice)
{
uint32_t vendorId = 0;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
if (ANGLE_APPLE_AVAILABLE_XC(10.13, 13.0))
{
vendorId = GetDeviceVendorIdFromIOKit(metalDevice);
}
#endif
if (!vendorId)
{
vendorId = GetDeviceVendorIdFromName(metalDevice);
}
return vendorId;
}
AutoObjCPtr<id<MTLLibrary>> CreateShaderLibrary(id<MTLDevice> metalDevice,
const std::string &source,
AutoObjCPtr<NSError *> *error)
......
......@@ -834,6 +834,9 @@ TEST_P(BlitFramebufferANGLETest, BlitStencil)
// http://anglebug.com/2205
ANGLE_SKIP_TEST_IF(IsIntel() && IsD3D9());
// http://anglebug.com/4919
ANGLE_SKIP_TEST_IF(IsIntel() && IsMetal());
glBindFramebuffer(GL_FRAMEBUFFER, mUserFBO);
glClearColor(0.0, 1.0, 0.0, 1.0);
......@@ -2006,6 +2009,8 @@ ANGLE_INSTANTIATE_TEST(BlitFramebufferANGLETest,
ES2_OPENGL(),
ES3_OPENGL(),
ES2_VULKAN(),
ES3_VULKAN());
ES3_VULKAN(),
ES2_METAL(),
WithNoShaderStencilOutput(ES2_METAL()));
ANGLE_INSTANTIATE_TEST_ES3(BlitFramebufferTest);
......@@ -209,6 +209,11 @@ std::ostream &operator<<(std::ostream &stream, const PlatformParameters &pp)
stream << "_EmulateCopyTexImage2DFromRenderbuffers";
}
if (pp.eglParameters.shaderStencilOutputFeature == EGL_FALSE)
{
stream << "_NoStencilOutput";
}
return stream;
}
......
......@@ -227,6 +227,13 @@ inline PlatformParameters WithEmulateCopyTexImage2DFromRenderbuffers(
return p;
}
inline PlatformParameters WithNoShaderStencilOutput(const PlatformParameters &params)
{
PlatformParameters re = params;
re.eglParameters.shaderStencilOutputFeature = EGL_FALSE;
return re;
}
inline PlatformParameters WithRobustness(const PlatformParameters &params)
{
PlatformParameters withRobustness = params;
......
......@@ -61,7 +61,7 @@ struct EGLPlatformParameters
return std::tie(renderer, majorVersion, minorVersion, deviceType, presentPath,
debugLayersEnabled, contextVirtualization, transformFeedbackFeature,
allocateNonZeroMemoryFeature, emulateCopyTexImage2DFromRenderbuffers,
platformMethods, robustness);
shaderStencilOutputFeature, platformMethods, robustness);
}
EGLint renderer = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
......@@ -75,6 +75,7 @@ struct EGLPlatformParameters
EGLint transformFeedbackFeature = EGL_DONT_CARE;
EGLint allocateNonZeroMemoryFeature = EGL_DONT_CARE;
EGLint emulateCopyTexImage2DFromRenderbuffers = EGL_DONT_CARE;
EGLint shaderStencilOutputFeature = EGL_DONT_CARE;
angle::PlatformMethods *platformMethods = nullptr;
};
......
......@@ -196,6 +196,11 @@ bool EGLWindow::initializeDisplay(OSWindow *osWindow,
enabledFeatureOverrides.push_back("emulate_copyteximage2d_from_renderbuffers");
}
if (params.shaderStencilOutputFeature == EGL_FALSE)
{
disabledFeatureOverrides.push_back("has_shader_stencil_output");
}
if (!disabledFeatureOverrides.empty())
{
if (strstr(extensionString, "EGL_ANGLE_feature_control") == nullptr)
......
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