Implement MSAA for Metal (#897)
This commit is contained in:
@@ -26,7 +26,7 @@ namespace filament {
|
||||
namespace driver {
|
||||
namespace metal {
|
||||
|
||||
struct MetalRenderTarget;
|
||||
class MetalRenderTarget;
|
||||
struct MetalSamplerBuffer;
|
||||
struct MetalSwapChain;
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user