Implement MSAA for Metal (#897)

This commit is contained in:
Ben Doherty
2019-02-28 12:27:20 -08:00
committed by GitHub
parent 4d90b9d0f0
commit c8a6a4dd62
6 changed files with 124 additions and 30 deletions

View File

@@ -26,7 +26,7 @@ namespace filament {
namespace driver {
namespace metal {
struct MetalRenderTarget;
class MetalRenderTarget;
struct MetalSamplerBuffer;
struct MetalSwapChain;

View File

@@ -124,25 +124,27 @@ void MetalDriver::createProgramR(Driver::ProgramHandle rph, Program&& program) {
}
void MetalDriver::createDefaultRenderTargetR(Driver::RenderTargetHandle rth, int dummy) {
construct_handle<MetalRenderTarget>(mHandleMap, rth);
construct_handle<MetalRenderTarget>(mHandleMap, rth, mContext);
}
void MetalDriver::createRenderTargetR(Driver::RenderTargetHandle rth,
Driver::TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, Driver::TextureFormat format, Driver::TargetBufferInfo color,
Driver::TargetBufferInfo depth, Driver::TargetBufferInfo stencil) {
auto renderTarget = construct_handle<MetalRenderTarget>(mHandleMap, rth, width, height);
id<MTLTexture> mtlColor = nil;
id<MTLTexture> mtlDepth = nil;
if (color.handle) {
auto colorTexture = handle_cast<MetalTexture>(mHandleMap, color.handle);
renderTarget->color = [colorTexture->texture retain];
mtlColor = colorTexture->texture;
} else if (targetBufferFlags & TargetBufferFlags::COLOR) {
ASSERT_POSTCONDITION(false, "A color buffer is required for a render target.");
}
if (depth.handle) {
auto depthTexture = handle_cast<MetalTexture>(mHandleMap, depth.handle);
renderTarget->depth = [depthTexture->texture retain];
mtlDepth = depthTexture->texture;
} else if (targetBufferFlags & TargetBufferFlags::DEPTH) {
MTLTextureDescriptor* depthTextureDesc =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatDepth32Float
@@ -151,9 +153,12 @@ void MetalDriver::createRenderTargetR(Driver::RenderTargetHandle rth,
mipmapped:NO];
depthTextureDesc.usage = MTLTextureUsageRenderTarget;
depthTextureDesc.resourceOptions = MTLResourceStorageModePrivate;
renderTarget->depth = [mContext->device newTextureWithDescriptor:depthTextureDesc];
mtlDepth = [mContext->device newTextureWithDescriptor:depthTextureDesc];
}
construct_handle<MetalRenderTarget>(mHandleMap, rth, mContext, width, height, samples, format,
mtlColor, mtlDepth);
ASSERT_POSTCONDITION(
!stencil.handle && !(targetBufferFlags & TargetBufferFlags::STENCIL),
"Stencil buffer not supported.");
@@ -402,13 +407,9 @@ void MetalDriver::beginRenderPass(Driver::RenderTargetHandle rth,
// Color
if (renderTarget->isDefaultRenderTarget) {
descriptor.colorAttachments[0].texture = acquireDrawable(mContext).texture;
mContext->currentSurfacePixelFormat = mContext->currentDrawable.texture.pixelFormat;
} else {
descriptor.colorAttachments[0].texture = renderTarget->color;
mContext->currentSurfacePixelFormat = renderTarget->color.pixelFormat;
}
descriptor.colorAttachments[0].texture = renderTarget->getColor();
descriptor.colorAttachments[0].resolveTexture = renderTarget->getColorResolve();
mContext->currentSurfacePixelFormat = descriptor.colorAttachments[0].texture.pixelFormat;
// Metal clears the entire attachment without respect to viewport or scissor.
// TODO: Might need to clear the scissor area manually via a draw if we need that functionality.
@@ -424,20 +425,17 @@ void MetalDriver::beginRenderPass(Driver::RenderTargetHandle rth,
// Depth
if (renderTarget->isDefaultRenderTarget) {
descriptor.depthAttachment.texture = mContext->currentSurface->depthTexture;
mContext->currentDepthPixelFormat = mContext->currentSurface->depthTexture.pixelFormat;
} else {
descriptor.depthAttachment.texture = renderTarget->depth;
if (renderTarget->depth) {
mContext->currentDepthPixelFormat = renderTarget->depth.pixelFormat;
} else {
mContext->currentDepthPixelFormat = MTLPixelFormatInvalid;
}
}
descriptor.depthAttachment.texture = renderTarget->getDepth();
descriptor.depthAttachment.resolveTexture = renderTarget->getDepthResolve();
descriptor.depthAttachment.loadAction = clearDepth ? MTLLoadActionClear : MTLLoadActionDontCare;
descriptor.depthAttachment.clearDepth = params.clearDepth;
mContext->currentDepthPixelFormat = descriptor.depthAttachment.texture.pixelFormat;
if (renderTarget->isMultisampled()) {
descriptor.colorAttachments[0].storeAction = MTLStoreActionMultisampleResolve;
// TODO: We don't need to resolve the depth texture if we don't need it.
descriptor.depthAttachment.storeAction = MTLStoreActionMultisampleResolve;
}
mContext->currentCommandEncoder =
[mContext->currentCommandBuffer renderCommandEncoderWithDescriptor:descriptor];
@@ -520,7 +518,7 @@ void MetalDriver::viewport(ssize_t left, ssize_t bottom, size_t width, size_t he
ASSERT_PRECONDITION(mContext->currentCommandEncoder != nullptr, "currentCommandEncoder is null");
// Flip the viewport, because Metal's screen space is vertically flipped that of Filament's.
NSInteger renderTargetHeight =
mContext->currentRenderTarget->isDefaultRenderTarget ?
mContext->currentRenderTarget->isDefaultRenderTarget() ?
mContext->currentSurface->surfaceHeight : mContext->currentRenderTarget->height;
MTLViewport metalViewport {
.originX = static_cast<double>(left),
@@ -581,6 +579,7 @@ void MetalDriver::readStreamPixels(Driver::StreamHandle sh, uint32_t x, uint32_t
void MetalDriver::blit(Driver::TargetBufferFlags buffers,
Driver::RenderTargetHandle dst, driver::Viewport dstRect,
Driver::RenderTargetHandle src, driver::Viewport srcRect) {
}
void MetalDriver::draw(Driver::PipelineState ps, Driver::RenderPrimitiveHandle rph) {
@@ -597,6 +596,7 @@ void MetalDriver::draw(Driver::PipelineState ps, Driver::RenderPrimitiveHandle r
.vertexDescription = primitive->vertexDescription,
.colorAttachmentPixelFormat = mContext->currentSurfacePixelFormat,
.depthAttachmentPixelFormat = mContext->currentDepthPixelFormat,
.sampleCount = mContext->currentRenderTarget->getSamples(),
.blendState = BlendState {
.blendingEnabled = rs.hasBlending(),
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),

View File

@@ -120,14 +120,37 @@ struct MetalSamplerBuffer : public HwSamplerBuffer {
explicit MetalSamplerBuffer(size_t size) : HwSamplerBuffer(size) {}
};
struct MetalRenderTarget : public HwRenderTarget {
MetalRenderTarget(uint32_t width, uint32_t height) : HwRenderTarget(width, height) {}
MetalRenderTarget() : HwRenderTarget(0, 0), isDefaultRenderTarget(true) {}
class MetalRenderTarget : public HwRenderTarget {
public:
MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height, uint8_t samples,
TextureFormat format, id<MTLTexture> color, id<MTLTexture> depth);
explicit MetalRenderTarget(MetalContext* context)
: HwRenderTarget(0, 0), context(context), defaultRenderTarget(true) {}
~MetalRenderTarget();
bool isDefaultRenderTarget = false;
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
bool isMultisampled() const { return samples > 1; }
uint8_t getSamples() const { return samples; }
id<MTLTexture> getColor();
id<MTLTexture> getColorResolve();
id<MTLTexture> getDepth();
id<MTLTexture> getDepthResolve();
private:
static id<MTLTexture> createMultisampledTexture(id<MTLDevice> device, TextureFormat format,
uint32_t width, uint32_t height, uint8_t samples);
MetalContext* context;
id<MTLTexture> color = nil;
id<MTLTexture> depth = nil;
bool defaultRenderTarget = false;
uint8_t samples = 1;
// These textures are only used if this render target is multisampled.
id<MTLTexture> multisampledColor = nil;
id<MTLTexture> multisampledDepth = nil;
};
} // namespace metal

View File

@@ -21,6 +21,7 @@
#include <details/Texture.h> // for FTexture::getFormatSize
#include <utils/Panic.h>
#include <utils/trap.h>
namespace filament {
namespace driver {
@@ -252,6 +253,7 @@ MetalTexture::MetalTexture(id<MTLDevice> device, driver::SamplerType target, uin
height:height
mipmapped:mipmapped];
descriptor.mipmapLevelCount = levels;
descriptor.textureType = MTLTextureType2D;
} else if (target == driver::SamplerType::SAMPLER_CUBEMAP) {
ASSERT_POSTCONDITION(width == height, "Cubemap faces must be square.");
descriptor = [MTLTextureDescriptor textureCubeDescriptorWithPixelFormat:pixelFormat
@@ -315,9 +317,73 @@ void MetalTexture::loadCubeImage(const PixelBufferDescriptor& data, const FaceOf
}
}
MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height,
uint8_t samples, TextureFormat format, id<MTLTexture> color, id<MTLTexture> depth)
: HwRenderTarget(width, height), context(context), color(color), depth(depth),
samples(samples) {
[color retain];
[depth retain];
if (samples > 1) {
multisampledColor =
createMultisampledTexture(context->device, format, width, height, samples);
if (depth != nil) {
multisampledDepth = createMultisampledTexture(context->device, TextureFormat::DEPTH32F,
width, height, samples);
}
}
}
id<MTLTexture> MetalRenderTarget::getColor() {
if (defaultRenderTarget) {
return acquireDrawable(context).texture;
}
return isMultisampled() ? multisampledColor : color;
}
id<MTLTexture> MetalRenderTarget::getColorResolve() {
return isMultisampled() ? color : nil;
}
id<MTLTexture> MetalRenderTarget::getDepthResolve() {
return isMultisampled() ? depth : nil;
}
id<MTLTexture> MetalRenderTarget::getDepth() {
if (defaultRenderTarget) {
return context->currentSurface->depthTexture;
}
return isMultisampled() ? multisampledDepth : depth;
}
MetalRenderTarget::~MetalRenderTarget() {
[color release];
[depth release];
[multisampledColor release];
[multisampledDepth release];
}
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device,
TextureFormat format, uint32_t width, uint32_t height, uint8_t samples) {
MTLPixelFormat metalFormat = getMetalFormat(format);
ASSERT_POSTCONDITION(metalFormat != MTLPixelFormatInvalid, "Pixel format not supported.");
MTLTextureDescriptor* descriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:metalFormat
width:width
height:height
mipmapped:NO];
descriptor.textureType = MTLTextureType2DMultisample;
descriptor.sampleCount = samples;
descriptor.usage = MTLTextureUsageRenderTarget;
#if defined(IOS)
descriptor.resourceOptions = MTLResourceStorageModeMemoryless;
#else
descriptor.resourceOptions = MTLResourceStorageModePrivate;
#endif
return [device newTextureWithDescriptor:descriptor];
}
} // namespace metal

View File

@@ -207,6 +207,7 @@ struct PipelineState {
VertexDescription vertexDescription;
MTLPixelFormat colorAttachmentPixelFormat = MTLPixelFormatInvalid;
MTLPixelFormat depthAttachmentPixelFormat = MTLPixelFormatInvalid;
NSUInteger sampleCount = 1;
BlendState blendState;
bool operator==(const PipelineState& rhs) const noexcept {
@@ -216,6 +217,7 @@ struct PipelineState {
this->vertexDescription == rhs.vertexDescription &&
this->colorAttachmentPixelFormat == rhs.colorAttachmentPixelFormat &&
this->depthAttachmentPixelFormat == rhs.depthAttachmentPixelFormat &&
this->sampleCount == rhs.sampleCount &&
this->blendState == rhs.blendState
);
}

View File

@@ -73,6 +73,9 @@ id<MTLRenderPipelineState> PipelineStateCreator::operator()(id<MTLDevice> device
// Depth attachment
descriptor.depthAttachmentPixelFormat = state.depthAttachmentPixelFormat;
// MSAA
descriptor.rasterSampleCount = state.sampleCount;
NSError* error = nullptr;
id<MTLRenderPipelineState> pipeline = [device newRenderPipelineStateWithDescriptor:descriptor
error:&error];