Commit 73881112 authored by Laszlo Agocs's avatar Laszlo Agocs

Fix up mrt on Metal and finish the example

parent 2f4c4b7a
......@@ -50,6 +50,12 @@
#include "../shared/examplefw.h"
// Exercises MRT, by attaching 4 textures to a single QRhiTextureRenderTarget.
// The fragment shader outputs to all four targets.
// The textures are then used to render 4 quads, so their contents can be confirmed.
const int ATTCOUNT = 4;
static float quadVertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
......@@ -77,12 +83,15 @@ struct {
QRhiBuffer *ubuf = nullptr;
QRhiTextureRenderTarget *rt = nullptr;
QRhiRenderPassDescriptor *rtRp = nullptr;
QRhiTexture *tex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 winProj;
struct PerColorBuffer {
QRhiTexture *tex = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
};
PerColorBuffer colData[ATTCOUNT];
QRhiBuffer *triUbuf = nullptr;
QRhiShaderResourceBindings *triSrb = nullptr;
......@@ -105,34 +114,46 @@ void Window::customInit()
d.ibuf->build();
d.releasePool << d.ibuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
const int oneRoundedUniformBlockSize = m_r->ubufAligned(68);
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, oneRoundedUniformBlockSize * ATTCOUNT);
d.ubuf->build();
d.releasePool << d.ubuf;
d.tex = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget);
d.releasePool << d.tex;
d.tex->build();
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->build();
for (int i = 0; i < ATTCOUNT; ++i) {
QRhiTexture *tex = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget);
d.releasePool << tex;
tex->build();
QRhiShaderResourceBindings *srb = m_r->newShaderResourceBindings();
d.releasePool << srb;
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage,
d.ubuf, i * oneRoundedUniformBlockSize, 68),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, tex, d.sampler)
});
srb->build();
d.colData[i].tex = tex;
d.colData[i].srb = srb;
}
d.rt = m_r->newTextureRenderTarget({ d.tex });
QRhiTextureRenderTargetDescription rtDesc;
QVector<QRhiColorAttachment> att;
for (int i = 0; i < ATTCOUNT; ++i)
att.append(QRhiColorAttachment(d.colData[i].tex));
rtDesc.setColorAttachments(att);
d.rt = m_r->newTextureRenderTarget(rtDesc);
d.releasePool << d.rt;
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
d.releasePool << d.rtRp;
d.rt->setRenderPassDescriptor(d.rtRp);
d.rt->build();
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->build();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->build();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
......@@ -148,7 +169,7 @@ void Window::customInit()
{ 0, 1, QRhiVertexInputAttribute::Float2, 2 * sizeof(float) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setShaderResourceBindings(d.colData[0].srb); // all of them are layout-compatible
d.ps->setRenderPassDescriptor(m_rp);
d.ps->build();
......@@ -158,7 +179,8 @@ void Window::customInit()
d.initialUpdates->uploadStaticBuffer(d.ibuf, quadIndexData);
qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0;
d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
for (int i = 0; i < ATTCOUNT; ++i)
d.initialUpdates->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize + 64, 4, &flip);
// triangle
d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
......@@ -178,6 +200,12 @@ void Window::customInit()
{ QRhiGraphicsShaderStage::Vertex, getShader(QLatin1String(":/mrt.vert.qsb")) },
{ QRhiGraphicsShaderStage::Fragment, getShader(QLatin1String(":/mrt.frag.qsb")) }
});
QVector<QRhiGraphicsPipeline::TargetBlend> blends;
for (int i = 0; i < ATTCOUNT; ++i) {
QRhiGraphicsPipeline::TargetBlend blend;
blends.append(blend);
}
d.triPs->setTargetBlends(blends);
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
......@@ -227,19 +255,40 @@ void Window::customRender()
cb->draw(3);
cb->endPass();
u = m_r->nextResourceUpdateBatch();
if (d.winProj != m_proj) {
d.winProj = m_proj;
QMatrix4x4 mvp = m_proj;
mvp.translate(-2, 0, 0);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
const int oneRoundedUniformBlockSize = m_r->ubufAligned(68);
for (int i = 0; i < ATTCOUNT; ++i) {
QMatrix4x4 mvp = m_proj;
switch (i) {
case 0:
mvp.translate(-2.0f, 0, 0);
break;
case 1:
mvp.translate(-1.2f, 0, 0);
case 2:
mvp.translate(0.4f, 0, 0);
break;
case 3:
mvp.translate(1.6f, 0, 0);
break;
default:
Q_UNREACHABLE();
break;
}
u->updateDynamicBuffer(d.ubuf, i * oneRoundedUniformBlockSize, 64, mvp.constData());
}
}
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), { 0.4f, 0.7f, 0.0f, 1.0f }, { 1.0f, 0 });
cb->beginPass(m_sc->currentFrameRenderTarget(), { 0.4f, 0.7f, 0.0f, 1.0f }, { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
cb->setVertexInput(0, { { d.vbuf, 0 } }, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
for (int i = 0; i < ATTCOUNT; ++i) {
cb->setShaderResources(d.colData[i].srb);
cb->drawIndexed(6);
}
cb->endPass();
}
......@@ -15,8 +15,8 @@ layout(std140, binding = 0) uniform buf {
void main()
{
vec4 c = vec4(v_color * ubuf.opacity, ubuf.opacity);
c0 = c;
c1 = c;
c2 = c;
c0 = c * 0.25;
c1 = c * 0.5;
c2 = c * 0.75;
c3 = c;
}
......@@ -127,7 +127,8 @@ struct QRhiMetalData
MTLRenderPassDescriptor *createDefaultRenderPass(bool hasDepthStencil,
const QRhiColorClearValue &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue);
const QRhiDepthStencilClearValue &depthStencilClearValue,
int colorAttCount);
id<MTLLibrary> createMetalLib(const QBakedShader &shader, QBakedShaderKey::ShaderVariant shaderVariant,
QString *error, QByteArray *entryPoint);
id<MTLFunction> createMSLShaderFunction(id<MTLLibrary> lib, const QByteArray &entryPoint);
......@@ -767,6 +768,7 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, int startBinding, const QV
{
Q_ASSERT(inPass);
QMetalCommandBuffer *cbD = QRHI_RES(QMetalCommandBuffer, cb);
Q_ASSERT(cbD->currentPipeline);
QRhiBatchedBindings<id<MTLBuffer> > buffers;
QRhiBatchedBindings<NSUInteger> offsets;
......@@ -782,7 +784,13 @@ void QRhiMetal::setVertexInput(QRhiCommandBuffer *cb, int startBinding, const QV
offsets.finish();
// same binding space for vertex and constant buffers - work it around
const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, cbD->currentSrb)->maxBinding + 1;
QRhiShaderResourceBindings *srb = cbD->currentSrb;
// There's nothing guaranteeing setShaderResources() was called before
// setVertexInput()... but whatever srb will get bound will have to be
// layout-compatible anyways so maxBinding is the same.
if (!srb)
srb = cbD->currentPipeline->shaderResourceBindings();
const int firstVertexBinding = QRHI_RES(QMetalShaderResourceBindings, srb)->maxBinding + 1;
if (firstVertexBinding != cbD->d->currentFirstVertexBinding
|| buffers != cbD->d->currentVertexInputsBuffers
......@@ -1131,15 +1139,18 @@ QRhi::FrameOpResult QRhiMetal::finish()
MTLRenderPassDescriptor *QRhiMetalData::createDefaultRenderPass(bool hasDepthStencil,
const QRhiColorClearValue &colorClearValue,
const QRhiDepthStencilClearValue &depthStencilClearValue)
const QRhiDepthStencilClearValue &depthStencilClearValue,
int colorAttCount)
{
MTLRenderPassDescriptor *rp = [MTLRenderPassDescriptor renderPassDescriptor];
rp.colorAttachments[0].loadAction = MTLLoadActionClear;
rp.colorAttachments[0].storeAction = MTLStoreActionStore;
const QVector4D rgba = colorClearValue.rgba();
MTLClearColor c = MTLClearColorMake(rgba.x(), rgba.y(), rgba.z(), rgba.w());
rp.colorAttachments[0].clearColor = c;
for (int i = 0; i < colorAttCount; ++i) {
rp.colorAttachments[i].loadAction = MTLLoadActionClear;
rp.colorAttachments[i].storeAction = MTLStoreActionStore;
rp.colorAttachments[i].clearColor = c;
}
if (hasDepthStencil) {
rp.depthAttachment.loadAction = MTLLoadActionClear;
......@@ -1487,7 +1498,7 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
switch (rt->type()) {
case QRhiRenderTarget::RtRef:
rtD = QRHI_RES(QMetalReferenceRenderTarget, rt)->d;
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue);
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
if (rtD->colorAttCount) {
QMetalRenderTargetData::ColorAtt &color0(rtD->fb.colorAtt[0]);
if (color0.needsDrawableForTex || color0.needsDrawableForResolveTex) {
......@@ -1513,7 +1524,7 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
{
QMetalTextureRenderTarget *rtTex = QRHI_RES(QMetalTextureRenderTarget, rt);
rtD = rtTex->d;
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue);
cbD->d->currentPassRpDesc = d->createDefaultRenderPass(rtD->dsAttCount, colorClearValue, depthStencilClearValue, rtD->colorAttCount);
if (rtTex->m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents)) {
for (int i = 0; i < rtD->colorAttCount; ++i)
cbD->d->currentPassRpDesc.colorAttachments[i].loadAction = MTLLoadActionLoad;
......@@ -2812,6 +2823,9 @@ bool QMetalGraphicsPipeline::build()
rpDesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
rpDesc.colorAttachments[0].blendingEnabled = false;
Q_ASSERT(m_targetBlends.count() == rpD->colorAttachmentCount
|| (m_targetBlends.isEmpty() && rpD->colorAttachmentCount == 1));
for (int i = 0, ie = m_targetBlends.count(); i != ie; ++i) {
const QRhiGraphicsPipeline::TargetBlend &b(m_targetBlends[i]);
rpDesc.colorAttachments[i].pixelFormat = MTLPixelFormat(rpD->colorFormat[i]);
......
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