blob: 5cacac2af5b2628d2ce0228244a3536129fc76ba [file] [log] [blame]
/*
* Copyright 2024 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/RenderPassDesc.h"
#include "include/gpu/graphite/TextureInfo.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/TextureInfoPriv.h"
namespace skgpu::graphite {
namespace {
const char* to_str(LoadOp op) {
switch (op) {
case LoadOp::kLoad: return "load";
case LoadOp::kClear: return "clear";
case LoadOp::kDiscard: return "discard";
}
SkUNREACHABLE;
}
const char* to_str(StoreOp op) {
switch (op) {
case StoreOp::kStore: return "store";
case StoreOp::kDiscard: return "discard";
}
SkUNREACHABLE;
}
} // anonymous namespace
RenderPassDesc RenderPassDesc::Make(const Caps* caps,
const TextureInfo& targetInfo,
LoadOp loadOp,
StoreOp storeOp,
SkEnumBitMask<DepthStencilFlags> depthStencilFlags,
const std::array<float, 4>& clearColor,
bool requiresMSAA,
Swizzle writeSwizzle,
const DstReadStrategy dstReadStrategy) {
// It doesn't make sense to have a storeOp for our main target not be store. Why are we doing
// this DrawPass then
SkASSERT(storeOp == StoreOp::kStore);
RenderPassDesc desc;
desc.fClearColor = clearColor;
desc.fClearDepth = 0.f; // Depth and stencil is currently always cleared to 0 if it's used
desc.fClearStencil = 0;
desc.fWriteSwizzle = writeSwizzle;
desc.fDstReadStrategy = dstReadStrategy;
TextureFormat colorFormat = TextureInfoPriv::ViewFormat(targetInfo);
// The render pass's overall sample count will either be the target's sample count
// (when single-sampling or already multisampled), or the default sample count (which will then
// be either the implicit sample count for msaa-render-to-single-sample or the explicit sample
// count of a separate color attachment).
//
// Higher-level logic should ensure the default MSAA sample count is supported if using either
// msaa-render-to-single-sample or with separate attachments, and select non-MSAA techniques if
// they weren't supported. Downgrade to single-sampled if we get here somehow anyways.
const uint8_t defaultSamples = caps->defaultMSAASamplesCount();
const bool canUseDefaultMSAA = caps->msaaRenderToSingleSampledSupport() ||
caps->isSampleCountSupported(colorFormat, defaultSamples);
desc.fSampleCount = requiresMSAA && targetInfo.numSamples() == 1
? (canUseDefaultMSAA ? defaultSamples : 1)
: targetInfo.numSamples();
// We need to handle MSAA with an extra color attachment if:
const bool needsMSAAColorAttachment =
desc.fSampleCount > 1 && // using MSAA for the render pass,
targetInfo.numSamples() == 1 && // the target isn't already MSAA'ed,
!caps->msaaRenderToSingleSampledSupport(); // can't use an MSAA->single extension.
if (needsMSAAColorAttachment) {
// We set the color and resolve attachments up the same regardless of if the backend ends up
// using msaaRenderToSingleSampledSupport() to skip explicitly creating the MSAA attachment.
// The color attachment (and any depth/stencil attachment) will use `sampleCount` and the
// resolve attachment will be single-sampled.
desc.fColorAttachment = {colorFormat,
loadOp != LoadOp::kClear ? LoadOp::kDiscard : LoadOp::kClear,
StoreOp::kDiscard,
desc.fSampleCount};
desc.fColorResolveAttachment = {colorFormat,
loadOp != LoadOp::kLoad ? LoadOp::kDiscard : LoadOp::kLoad,
storeOp,
/*sampleCount=*/1};
} else {
// The target will be the color attachment and skip configuring the resolve attachment.
SkASSERT(desc.fColorResolveAttachment.fFormat == TextureFormat::kUnsupported);
desc.fColorAttachment = {colorFormat,
loadOp,
storeOp,
SkTo<uint8_t>(targetInfo.numSamples())};
}
if (depthStencilFlags != DepthStencilFlags::kNone) {
TextureFormat dsFormat = caps->getDepthStencilFormat(depthStencilFlags);
SkASSERT(dsFormat != TextureFormat::kUnsupported);
// Depth and stencil values are currently always cleared and don't need to persist.
// The sample count should always matche the color attachment.
desc.fDepthStencilAttachment = {dsFormat,
LoadOp::kClear,
StoreOp::kDiscard,
desc.fColorAttachment.fSampleCount};
} else {
SkASSERT(desc.fDepthStencilAttachment.fFormat == TextureFormat::kUnsupported);
}
return desc;
}
SkString RenderPassDesc::toString() const {
return SkStringPrintf("RP(color: %s, resolve: %s, ds: %s, samples: %u, swizzle: %s, "
"clear: c(%f,%f,%f,%f), d(%f), s(0x%02x), dst read: %u)",
fColorAttachment.toString().c_str(),
fColorResolveAttachment.toString().c_str(),
fDepthStencilAttachment.toString().c_str(),
fSampleCount,
fWriteSwizzle.asString().c_str(),
fClearColor[0], fClearColor[1], fClearColor[2], fClearColor[3],
fClearDepth,
fClearStencil,
(unsigned)fDstReadStrategy);
}
SkString RenderPassDesc::toPipelineLabel() const {
// Given current policies, these assumptions should hold and mean the conciseness in the label
// is still unambiguous.
SkASSERT(fColorAttachment.fFormat != TextureFormat::kUnsupported);
SkASSERT(fColorResolveAttachment.fFormat == TextureFormat::kUnsupported ||
fColorResolveAttachment.fFormat == fColorAttachment.fFormat);
SkASSERT(fDepthStencilAttachment.fFormat == TextureFormat::kUnsupported ||
fDepthStencilAttachment.fSampleCount == fColorAttachment.fSampleCount);
SkASSERT(fColorResolveAttachment.fFormat == TextureFormat::kUnsupported ||
fColorResolveAttachment.fSampleCount == 1);
SkASSERT(fColorAttachment.fSampleCount == fSampleCount ||
(fColorAttachment.fSampleCount == 1 && fSampleCount > 1));
const char* colorFormatStr = TextureFormatName(fColorAttachment.fFormat);
const char* dsFormatStr = "{}";
if (fDepthStencilAttachment.fFormat != TextureFormat::kUnsupported) {
dsFormatStr = TextureFormatName(fDepthStencilAttachment.fFormat);
}
// This intentionally only includes the fixed state that impacts pipeline compilation.
// We include the load op of the color attachment when there is a resolve attachment because
// the load may trigger a different renderpass description.
const char* colorLoadStr = "";
const bool loadMsaaFromResolve =
fColorResolveAttachment.fFormat != TextureFormat::kUnsupported &&
fColorResolveAttachment.fLoadOp == LoadOp::kLoad;
// This should, technically, check Caps::loadOpAffectsMSAAPipelines before adding the extra
// string. Only the Metal backend doesn't set that flag, however, so we just assume it is set
// to reduce plumbing. Since the Metal backend doesn't differentiate its UniqueKeys wrt
// resolve-loads, this can lead to instances where two Metal Pipeline labels will map to the
// same UniqueKey (i.e., one with "w/ msaa load" and one without it).
if (loadMsaaFromResolve /* && Caps::loadOpAffectsMSAAPipelines() */) {
colorLoadStr = " w/ msaa load";
}
// There are three supported ways of achieving MSAA rendering that we distinguish compactly.
// 1. Direct sampling w/ N samples (includes single sample)
// 2. MSAA render to single-sampled extensions
// 3. Explicit MSAA color attachment w/ resolve
// Since we don't expect to be mixing case 2 and 3 on the same device, treating them the same
// in the pipeline labels makes it more convenient when writing test expectations.
SkString sampleCountStr;
if (fColorResolveAttachment.fFormat == TextureFormat::kUnsupported &&
fSampleCount == fColorAttachment.fSampleCount) {
// Case 1: "xN"
sampleCountStr = SkStringPrintf("x%u", fSampleCount);
} else {
// Case 2 and 3: "xN->1"
sampleCountStr = SkStringPrintf("x%u->1", fSampleCount);
}
// NOTE: This label does not differentiate between explicitly resolved MSAA color attachments
// and MSAA-render-to-single-sample renderpasses. For a given set of Caps, we currently only
// expect to generate one or the other variety.
return SkStringPrintf("RP((%s+%s %s).%s%s)",
colorFormatStr,
dsFormatStr,
sampleCountStr.c_str(),
fWriteSwizzle.asString().c_str(),
colorLoadStr);
}
SkString AttachmentDesc::toString() const {
if (fFormat == TextureFormat::kUnsupported) {
return SkString("{}");
} else {
return SkStringPrintf("{f: %s x%u, ops: %s->%s}",
TextureFormatName(fFormat),
fSampleCount,
to_str(fLoadOp),
to_str(fStoreOp));
}
}
bool AttachmentDesc::isCompatible(const TextureInfo& texInfo) const {
return fFormat == TextureInfoPriv::ViewFormat(texInfo) &&
fSampleCount == texInfo.numSamples();
}
} // namespace skgpu::graphite