blob: 6aa2dbde6d2e3cf85b4c7c7b6ec3a3a096415496 [file]
/*
* Copyright 2023 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/vk/VulkanRenderPass.h"
#include "src/gpu/graphite/Texture.h"
#include "src/gpu/graphite/vk/VulkanCommandBuffer.h"
#include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h"
#include "src/gpu/graphite/vk/VulkanSharedContext.h"
#include "src/gpu/graphite/vk/VulkanTexture.h"
namespace skgpu::graphite {
namespace { // anonymous namespace
void add_attachment_description_info_to_key(GraphiteResourceKey::Builder& builder,
const TextureInfo& textureInfo,
int& builderIdx,
LoadOp loadOp,
StoreOp storeOp) {
VulkanTextureInfo vkTexInfo;
if (textureInfo.isValid() && textureInfo.getVulkanTextureInfo(&vkTexInfo)) {
builder[builderIdx++] = vkTexInfo.fFormat;
builder[builderIdx++] = vkTexInfo.fSampleCount;
SkASSERT(sizeof(loadOp) < (1u << 8));
SkASSERT(sizeof(storeOp) < (1u << 8));
builder[builderIdx++] = static_cast<uint8_t>(loadOp) << 8 | static_cast<uint8_t>(storeOp);
}
// We only count attachments that are valid textures when calculating the total number of
// render pass attachments, so if a texture is invalid, simply skip it rather than using
// VK_ATTACHMENT_UNUSED and incrementing the builderIdx. Attachments can be differentiated from
// one another by their sample count and format (i.e. depth/stencil attachments will have a
// depth/stencil format).
}
} // anonymous namespace
GraphiteResourceKey VulkanRenderPass::MakeRenderPassKey(
const RenderPassDesc& renderPassDesc, bool compatibleOnly) {
static constexpr uint32_t kKeyValueUnused = UINT32_MAX;
bool hasColorAttachment = renderPassDesc.fColorAttachment.fTextureInfo.isValid();
bool hasColorResolveAttachment = renderPassDesc.fColorResolveAttachment.fTextureInfo.isValid();
bool hasDepthStencilAttachment = renderPassDesc.fDepthStencilAttachment.fTextureInfo.isValid();
bool hasInputAttachment = false; // TODO: Determine this through RenderPassDesc
// TODO: Query for more attachments once the RenderPassDesc struct contains that information.
// For now, we only ever expect to see 0 or 1 of each attachment type (color, resolve, and
// depth/stencil), so the count of each of those can simply be determined with a bool.
const int numColorAttachments = hasColorAttachment ? 1 : 0;
const int numResolveAttachments = hasColorResolveAttachment ? 1 : 0;
const int numDepthStencilAttachments = hasDepthStencilAttachment ? 1 : 0;
const int numInputAttachments = hasInputAttachment ? 1 : 0;
const int totalRenderPassAttachments = numColorAttachments + numResolveAttachments +
numDepthStencilAttachments + numInputAttachments;
// Accumulate attachments into a container to mimic future structure in RenderPassDesc
skia_private::TArray<const AttachmentDesc*> rpAttachments(totalRenderPassAttachments);
if (hasColorAttachment) {
rpAttachments.push_back(&renderPassDesc.fColorAttachment);
}
if (hasColorResolveAttachment) {
rpAttachments.push_back(&renderPassDesc.fColorResolveAttachment);
}
if (hasDepthStencilAttachment) {
rpAttachments.push_back(&renderPassDesc.fDepthStencilAttachment);
}
// TODO: Add input attachment from RenderPassDesc if it has one
// Calculate how many int32s we need to capture all relevant information.
int num32DataCnt = 0;
// Each renderpass key will start with the total number of attachments associated with it.
// This will use one uint32.
num32DataCnt++;
// The key will then contain key information for each attachment. This includes format, sample
// count, and load/store operation information.
num32DataCnt += 3 * totalRenderPassAttachments;
// Then, subpass information will be added in the form of attachment reference indices. Reserve
// one int32 for each possible attachment reference type, of which there are 4.
// TODO: Reference RenderPassDesc to determine number and makeup of subpasses. For now, we only
// ever expect 1 (in most cases) or 2 (when loading MSAA from resolve).
int subpassCount = 1;
num32DataCnt += 4 * subpassCount;
// Finally, subpass dependency information will be recorded. Each dependency will be allotted 7
// int32s to store all its pertinent information. Relevant once we support multiple subpasses.
int subpassDependencyCount = 0;
num32DataCnt += 7 * subpassDependencyCount;
static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
GraphiteResourceKey key;
GraphiteResourceKey::Builder builder(&key, kType, num32DataCnt, Shareable::kYes);
// Now we can actually populate the key.
int builderIdx = 0;
builder[builderIdx++] = totalRenderPassAttachments;
// Iterate through each renderpass attachment to add its information
for (int i = 0; i < totalRenderPassAttachments; i++) {
add_attachment_description_info_to_key(
builder,
rpAttachments[i]->fTextureInfo,
builderIdx,
// Assign LoadOp::kLoad and StoreOp::kStore as default load/store operations for
// compatible render passes where load/store ops don't need to match.
compatibleOnly ? LoadOp::kLoad : rpAttachments[i]->fLoadOp,
compatibleOnly ? StoreOp::kStore : rpAttachments[i]->fStoreOp);
}
// Add subpass information to the key
for (int j = 0; j < subpassCount; j++) {
// The following assumes that we only have up to one reference of each type per subpass and
// that attachments are indexed in order of color, resolve, depth/stencil, then input
// attachments. TODO: Fetch actual attachment reference and index information for each
// subpass from RenderPassDesc. For now, we only support 1 subpass, so we can infer the
// subpass simply references the attachments of the renderpass itself.
int attachmentIdx = 0;
builder[builderIdx++] = hasColorAttachment ? attachmentIdx++ : VK_ATTACHMENT_UNUSED;
builder[builderIdx++] = hasColorResolveAttachment ? attachmentIdx++ : VK_ATTACHMENT_UNUSED;
builder[builderIdx++] = hasDepthStencilAttachment ? attachmentIdx++ : VK_ATTACHMENT_UNUSED;
builder[builderIdx++] = hasInputAttachment ? attachmentIdx++ : VK_ATTACHMENT_UNUSED;
}
// TODO: Query RenderPassDesc for subpass dependency information & populate the key accordingly
for (int i = 0; i < subpassDependencyCount; i++) {
builder[builderIdx++] = kKeyValueUnused; // srcSubpass index
builder[builderIdx++] = kKeyValueUnused; // dstSubpass index
builder[builderIdx++] = kKeyValueUnused; // srcStageMask
builder[builderIdx++] = kKeyValueUnused; // dstStageMask
builder[builderIdx++] = kKeyValueUnused; // srcAccessMask
builder[builderIdx++] = kKeyValueUnused; // dstAccessMask
builder[builderIdx++] = kKeyValueUnused; // dependencyFlags
}
builder.finish();
return key;
}
namespace { // anonymous namespace
void setup_vk_attachment_description(VkAttachmentDescription* outAttachment,
const VulkanTextureInfo& textureInfo,
const AttachmentDesc& desc,
const LoadOp loadOp,
const StoreOp storeOp,
const VkImageLayout initialLayout,
const VkImageLayout finalLayout) {
static_assert((int)LoadOp::kLoad == 0);
static_assert((int)LoadOp::kClear == 1);
static_assert((int)LoadOp::kDiscard == 2);
static_assert(std::size(vkLoadOp) == kLoadOpCount);
static_assert((int)StoreOp::kStore == 0);
static_assert((int)StoreOp::kDiscard == 1);
static_assert(std::size(vkStoreOp) == kStoreOpCount);
outAttachment->flags = 0;
outAttachment->format = textureInfo.fFormat;
VkSampleCountFlagBits sampleCount;
SkAssertResult(
skgpu::SampleCountToVkSampleCount(textureInfo.fSampleCount, &sampleCount));
outAttachment->samples = sampleCount;
switch (initialLayout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
outAttachment->loadOp = vkLoadOp[static_cast<int>(loadOp)];
outAttachment->storeOp = vkStoreOp[static_cast<int>(storeOp)];
outAttachment->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
outAttachment->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// The loadOp and storeOp refer to the depth part of the attachment and the stencil*Ops
// refer to the stencil bits in the attachment.
outAttachment->loadOp = vkLoadOp[static_cast<int>(loadOp)];
outAttachment->storeOp = vkStoreOp[static_cast<int>(storeOp)];
outAttachment->stencilLoadOp = vkLoadOp[static_cast<int>(loadOp)];
outAttachment->stencilStoreOp = vkStoreOp[static_cast<int>(storeOp)];
break;
default:
SK_ABORT("Unexpected attachment layout");
}
outAttachment->initialLayout = initialLayout;
outAttachment->finalLayout = finalLayout == VK_IMAGE_LAYOUT_UNDEFINED ? initialLayout
: finalLayout;
}
} // anonymous namespace
sk_sp<VulkanRenderPass> VulkanRenderPass::MakeRenderPass(const VulkanSharedContext* context,
const RenderPassDesc& renderPassDesc,
bool compatibleOnly) {
VkRenderPass renderPass;
renderPass = VK_NULL_HANDLE;
auto& colorAttachmentTextureInfo = renderPassDesc.fColorAttachment.fTextureInfo;
auto& colorResolveAttachmentTextureInfo = renderPassDesc.fColorResolveAttachment.fTextureInfo;
auto& depthStencilAttachmentTextureInfo = renderPassDesc.fDepthStencilAttachment.fTextureInfo;
bool hasColorAttachment = colorAttachmentTextureInfo.isValid();
bool hasColorResolveAttachment = colorResolveAttachmentTextureInfo.isValid();
bool hasDepthStencilAttachment = depthStencilAttachmentTextureInfo.isValid();
skia_private::TArray<VkAttachmentDescription> attachmentDescs;
// Create and track attachment references for the subpass.
VkAttachmentReference colorRef;
VkAttachmentReference resolveRef;
VkAttachmentReference depthStencilRef;
if (hasColorAttachment) {
VulkanTextureInfo colorAttachTexInfo;
colorAttachmentTextureInfo.getVulkanTextureInfo(&colorAttachTexInfo);
auto& colorAttachDesc = renderPassDesc.fColorAttachment;
colorRef.attachment = attachmentDescs.size();
VkAttachmentDescription& vkColorAttachDesc = attachmentDescs.push_back();
memset(&vkColorAttachDesc, 0, sizeof(VkAttachmentDescription));
setup_vk_attachment_description(
&vkColorAttachDesc,
colorAttachTexInfo,
colorAttachDesc,
compatibleOnly ? LoadOp::kDiscard : colorAttachDesc.fLoadOp,
compatibleOnly ? StoreOp::kDiscard : colorAttachDesc.fStoreOp,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
colorRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
if (hasColorResolveAttachment) {
SkASSERT(renderPassDesc.fColorResolveAttachment.fStoreOp == StoreOp::kStore);
VulkanTextureInfo resolveAttachTexInfo;
colorResolveAttachmentTextureInfo.getVulkanTextureInfo(&resolveAttachTexInfo);
auto& resolveAttachDesc = renderPassDesc.fColorResolveAttachment;
resolveRef.attachment = attachmentDescs.size();
VkAttachmentDescription& vkResolveAttachDesc = attachmentDescs.push_back();
memset(&vkResolveAttachDesc, 0, sizeof(VkAttachmentDescription));
setup_vk_attachment_description(
&vkResolveAttachDesc,
resolveAttachTexInfo,
resolveAttachDesc,
compatibleOnly ? LoadOp::kDiscard : resolveAttachDesc.fLoadOp,
compatibleOnly ? StoreOp::kDiscard : resolveAttachDesc.fStoreOp,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
resolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
} else {
resolveRef.attachment = VK_ATTACHMENT_UNUSED;
resolveRef.layout = VK_IMAGE_LAYOUT_UNDEFINED;
}
} else {
SkASSERT(false);
colorRef.attachment = VK_ATTACHMENT_UNUSED;
colorRef.layout = VK_IMAGE_LAYOUT_UNDEFINED;
resolveRef.attachment = VK_ATTACHMENT_UNUSED;
resolveRef.layout = VK_IMAGE_LAYOUT_UNDEFINED;
}
if (hasDepthStencilAttachment) {
VulkanTextureInfo depthStencilTexInfo;
depthStencilAttachmentTextureInfo.getVulkanTextureInfo(&depthStencilTexInfo);
auto& depthStencilAttachDesc = renderPassDesc.fDepthStencilAttachment;
depthStencilRef.attachment = attachmentDescs.size();
VkAttachmentDescription& vkDepthStencilAttachDesc = attachmentDescs.push_back();
setup_vk_attachment_description(
&vkDepthStencilAttachDesc,
depthStencilTexInfo,
depthStencilAttachDesc,
compatibleOnly ? LoadOp::kDiscard : depthStencilAttachDesc.fLoadOp,
compatibleOnly ? StoreOp::kDiscard : depthStencilAttachDesc.fStoreOp,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
depthStencilRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
} else {
depthStencilRef.attachment = VK_ATTACHMENT_UNUSED;
depthStencilRef.layout = VK_IMAGE_LAYOUT_UNDEFINED;
}
// Create VkRenderPass
VkRenderPassCreateInfo renderPassInfo;
memset(&renderPassInfo, 0, sizeof(VkRenderPassCreateInfo));
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.pNext = nullptr;
renderPassInfo.flags = 0;
// TODO: Support multiple subpasses. 2 are needed for loading MSAA from resolve.
renderPassInfo.subpassCount = 1;
VkSubpassDescription subpassDesc;
memset(&subpassDesc, 0, sizeof(VkSubpassDescription));
subpassDesc.flags = 0;
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
// TODO: Add support for input attachments
subpassDesc.inputAttachmentCount = 0;
subpassDesc.pInputAttachments = nullptr;
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = &colorRef;
subpassDesc.pResolveAttachments = &resolveRef;
subpassDesc.pDepthStencilAttachment = &depthStencilRef;
subpassDesc.preserveAttachmentCount = 0;
subpassDesc.pPreserveAttachments = nullptr;
renderPassInfo.pSubpasses = &subpassDesc;
renderPassInfo.dependencyCount = 0;
renderPassInfo.pDependencies = VK_NULL_HANDLE;
renderPassInfo.attachmentCount = attachmentDescs.size();
renderPassInfo.pAttachments = attachmentDescs.begin();
VkResult result;
VULKAN_CALL_RESULT(context->interface(), result, CreateRenderPass(context->device(),
&renderPassInfo,
nullptr,
&renderPass));
if (result != VK_SUCCESS) {
return nullptr;
}
VkExtent2D granularity;
VULKAN_CALL(context->interface(), GetRenderAreaGranularity(context->device(),
renderPass,
&granularity));
return sk_sp<VulkanRenderPass>(new VulkanRenderPass(context, renderPass, granularity));
}
VulkanRenderPass::VulkanRenderPass(const VulkanSharedContext* context,
VkRenderPass renderPass,
VkExtent2D granularity)
: Resource(context,
Ownership::kOwned,
skgpu::Budgeted::kYes,
/*gpuMemorySize=*/0,
/*label=*/"VulkanRenderPass")
, fSharedContext(context)
, fRenderPass (renderPass)
, fGranularity (granularity) {
}
void VulkanRenderPass::freeGpuData() {
VULKAN_CALL(fSharedContext->interface(),
DestroyRenderPass(fSharedContext->device(), fRenderPass, nullptr));
}
} // namespace skgpu::graphite