Metal: implement MSAA stencil buffers (#5834)
This commit is contained in:
@@ -850,9 +850,26 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
|
||||
if (stencilAttachment) {
|
||||
stencil = stencilAttachment;
|
||||
|
||||
ASSERT_PRECONDITION(stencil.getSampleCount() <= samples,
|
||||
"MetalRenderTarget was initialized with a MSAA STENCIL texture, but sample count is %d.",
|
||||
samples);
|
||||
|
||||
auto t = stencil.metalTexture;
|
||||
const auto twidth = std::max(1u, t->width >> stencil.level);
|
||||
const auto theight = std::max(1u, t->height >> stencil.level);
|
||||
tmin = { math::min(tmin.x, twidth), math::min(tmin.y, theight) };
|
||||
tmax = { math::max(tmax.x, twidth), math::max(tmax.y, theight) };
|
||||
attachmentCount++;
|
||||
|
||||
// TODO: stencil MSAA
|
||||
// If we were given a single-sampled texture but the samples parameter is > 1, we create
|
||||
// a multisampled sidecar texture and do a resolve automatically.
|
||||
if (samples > 1 && stencil.getSampleCount() == 1) {
|
||||
auto& sidecar = stencil.metalTexture->msaaSidecar;
|
||||
if (!sidecar) {
|
||||
sidecar = createMultisampledTexture(context->device, stencil.getPixelFormat(),
|
||||
stencil.metalTexture->width, stencil.metalTexture->height, samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that all attachments have the same non-zero dimensions.
|
||||
@@ -921,8 +938,8 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
|
||||
descriptor.depthAttachment.clearDepth = params.clearDepth;
|
||||
}
|
||||
|
||||
const bool automaticResolve = samples > 1 && depthAttachment.getSampleCount() == 1;
|
||||
if (automaticResolve) {
|
||||
const bool depthAutomaticResolve = samples > 1 && depthAttachment.getSampleCount() == 1;
|
||||
if (depthAutomaticResolve) {
|
||||
// We're rendering into our temporary MSAA texture and doing an automatic resolve.
|
||||
// We should not be attempting to load anything into the MSAA texture.
|
||||
assert_invariant(descriptor.depthAttachment.loadAction != MTLLoadActionLoad);
|
||||
@@ -952,6 +969,28 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
|
||||
descriptor.stencilAttachment.storeAction = getStoreAction(params, TargetBufferFlags::STENCIL);
|
||||
descriptor.stencilAttachment.clearStencil = params.clearStencil;
|
||||
}
|
||||
|
||||
const bool stencilAutomaticResolve = samples > 1 && stencilAttachment.getSampleCount() == 1;
|
||||
if (stencilAutomaticResolve) {
|
||||
// We're rendering into our temporary MSAA texture and doing an automatic resolve.
|
||||
// We should not be attempting to load anything into the MSAA texture.
|
||||
assert_invariant(descriptor.stencilAttachment.loadAction != MTLLoadActionLoad);
|
||||
assert_invariant(!defaultRenderTarget);
|
||||
|
||||
id<MTLTexture> sidecar = stencilAttachment.getMSAASidecarTexture();
|
||||
assert_invariant(sidecar);
|
||||
|
||||
descriptor.stencilAttachment.texture = sidecar;
|
||||
descriptor.stencilAttachment.level = 0;
|
||||
descriptor.stencilAttachment.slice = 0;
|
||||
const bool discard = any(discardFlags & TargetBufferFlags::STENCIL);
|
||||
if (!discard) {
|
||||
descriptor.stencilAttachment.resolveTexture = stencilAttachment.getTexture();
|
||||
descriptor.stencilAttachment.resolveLevel = stencilAttachment.level;
|
||||
descriptor.stencilAttachment.resolveSlice = stencilAttachment.layer;
|
||||
descriptor.stencilAttachment.storeAction = MTLStoreActionMultisampleResolve;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MetalRenderTarget::Attachment MetalRenderTarget::getDrawColorAttachment(size_t index) {
|
||||
|
||||
@@ -189,4 +189,88 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
api.destroyRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
// Create two textures: a single-sampled color and a MSAA stencil texture.
|
||||
// We also create two RenderTargets, one for each pass:
|
||||
// Pass 0: Render a triangle only into the MSAA stencil buffer.
|
||||
// Pass 1: Render a triangle into (an auto-created) MSAA color buffer using the stencil test.
|
||||
// Performs an auto-resolve on the color.
|
||||
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE);
|
||||
auto depthStencilTextureMSAA = api.createTexture(SamplerType::SAMPLER_2D, 1,
|
||||
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
|
||||
auto renderTarget0 = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4,
|
||||
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
|
||||
auto renderTarget1 = getDriverApi().createRenderTarget(
|
||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4,
|
||||
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
|
||||
|
||||
api.startCapture(0);
|
||||
|
||||
// We'll be using a triangle as geometry.
|
||||
TrianglePrimitive smallTriangle(api);
|
||||
static filament::math::float2 vertices[3] = {
|
||||
{ -0.5, -0.5 },
|
||||
{ 0.5, -0.5 },
|
||||
{ -0.5, 0.5 }
|
||||
};
|
||||
smallTriangle.updateVertices(vertices);
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
// Step 1: Clear the stencil buffer to all zeroes.
|
||||
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
||||
RenderPassParams params = {};
|
||||
params.flags.clear = TargetBufferFlags::STENCIL;
|
||||
params.viewport = {0, 0, 512, 512};
|
||||
params.clearStencil = 0u;
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
|
||||
PipelineState ps = {};
|
||||
ps.program = program;
|
||||
ps.rasterState.colorWrite = false;
|
||||
ps.rasterState.depthWrite = false;
|
||||
ps.rasterState.stencilWrite = true;
|
||||
ps.rasterState.stencilOpDepthStencilPass = StencilOperation::INCR;
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget0, params);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
// Step 2: Render a larger triangle with the stencil test enabled.
|
||||
params.flags.clear = TargetBufferFlags::COLOR0;
|
||||
params.flags.discardStart = TargetBufferFlags::COLOR0;
|
||||
params.flags.discardEnd = TargetBufferFlags::STENCIL;
|
||||
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.rasterState.stencilWrite = false;
|
||||
ps.rasterState.stencilOpDepthStencilPass = StencilOperation::KEEP;
|
||||
ps.rasterState.stencilFunc = RasterState::StencilFunction::E;
|
||||
ps.rasterState.stencilRef = 0u;
|
||||
|
||||
api.beginRenderPass(renderTarget1, params);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
api.stopCapture(0);
|
||||
api.endFrame(0);
|
||||
|
||||
readPixelsAndAssertHash("StencilBufferAutoResolve", 512, 512, renderTarget1, 0xC7E34C43, true);
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
|
||||
api.destroyTexture(colorTexture);
|
||||
api.destroyTexture(depthStencilTextureMSAA);
|
||||
api.destroyRenderTarget(renderTarget0);
|
||||
api.destroyRenderTarget(renderTarget1);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
Reference in New Issue
Block a user