blob: 785b080c5d0665c27bd8d146a9f982f8bf58a7f1 [file] [log] [blame]
/*
* Copyright 2022 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/VulkanResourceProvider.h"
#include "include/core/SkSpan.h"
#include "include/gpu/ShaderErrorHandler.h"
#include "include/gpu/graphite/BackendTexture.h"
#include "src/core/SkSLTypeShared.h"
#include "src/gpu/graphite/Buffer.h"
#include "src/gpu/graphite/ComputePipeline.h"
#include "src/gpu/graphite/ContextUtils.h"
#include "src/gpu/graphite/GraphicsPipeline.h"
#include "src/gpu/graphite/PipelineUtils.h"
#include "src/gpu/graphite/RendererProvider.h"
#include "src/gpu/graphite/Sampler.h"
#include "src/gpu/graphite/Texture.h"
#include "src/gpu/graphite/vk/VulkanBuffer.h"
#include "src/gpu/graphite/vk/VulkanCommandBuffer.h"
#include "src/gpu/graphite/vk/VulkanDescriptorPool.h"
#include "src/gpu/graphite/vk/VulkanDescriptorSet.h"
#include "src/gpu/graphite/vk/VulkanGraphicsPipeline.h"
#include "src/gpu/graphite/vk/VulkanGraphiteUtilsPriv.h"
#include "src/gpu/graphite/vk/VulkanSampler.h"
#include "src/gpu/graphite/vk/VulkanSharedContext.h"
#include "src/gpu/graphite/vk/VulkanTexture.h"
#include "src/sksl/SkSLProgramKind.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/ir/SkSLProgram.h"
namespace skgpu::graphite {
VkDescriptorSetLayout VulkanResourceProvider::DescTypeAndCountToVkDescSetLayout(
const VulkanSharedContext* ctxt,
SkSpan<DescTypeAndCount> requestedDescriptors) {
VkDescriptorSetLayout layout;
skia_private::STArray<kDescriptorTypeCount, VkDescriptorSetLayoutBinding> bindingLayouts;
for (size_t i = 0, j = 0; i < requestedDescriptors.size(); i++) {
if (requestedDescriptors[i].count != 0) {
VkDescriptorSetLayoutBinding* layoutBinding = &bindingLayouts.at(j++);
memset(layoutBinding, 0, sizeof(VkDescriptorSetLayoutBinding));
layoutBinding->binding = 0;
layoutBinding->descriptorType =
VulkanDescriptorSet::DsTypeEnumToVkDs(requestedDescriptors[i].type);
layoutBinding->descriptorCount = requestedDescriptors[i].count;
// TODO: Obtain layout binding stage flags from visibility (vertex or shader)
layoutBinding->stageFlags = 0;
// TODO: Optionally set immutableSamplers here.
layoutBinding->pImmutableSamplers = nullptr;
}
}
VkDescriptorSetLayoutCreateInfo layoutCreateInfo;
memset(&layoutCreateInfo, 0, sizeof(VkDescriptorSetLayoutCreateInfo));
layoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutCreateInfo.pNext = nullptr;
layoutCreateInfo.flags = 0;
layoutCreateInfo.bindingCount = bindingLayouts.size();
layoutCreateInfo.pBindings = &bindingLayouts.front();
VkResult result;
VULKAN_CALL_RESULT(ctxt->interface(),
result,
CreateDescriptorSetLayout(ctxt->device(),
&layoutCreateInfo,
nullptr,
&layout));
if (result != VK_SUCCESS) {
SkDebugf("Failed to create VkDescriptorSetLayout\n");
layout = VK_NULL_HANDLE;
}
return layout;
}
VulkanResourceProvider::VulkanResourceProvider(SharedContext* sharedContext,
SingleOwner* singleOwner,
uint32_t recorderID)
: ResourceProvider(sharedContext, singleOwner, recorderID) {}
VulkanResourceProvider::~VulkanResourceProvider() {}
const VulkanSharedContext* VulkanResourceProvider::vulkanSharedContext() {
return static_cast<const VulkanSharedContext*>(fSharedContext);
}
sk_sp<Texture> VulkanResourceProvider::createWrappedTexture(const BackendTexture&) {
return nullptr;
}
sk_sp<GraphicsPipeline> VulkanResourceProvider::createGraphicsPipeline(
const RuntimeEffectDictionary* runtimeDict,
const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc) {
SkSL::Program::Interface vsInterface, fsInterface;
SkSL::ProgramSettings settings;
settings.fForceNoRTFlip = true; // TODO: Confirm
auto compiler = this->skslCompiler();
ShaderErrorHandler* errorHandler = fSharedContext->caps()->shaderErrorHandler();
const RenderStep* step =
fSharedContext->rendererProvider()->lookup(pipelineDesc.renderStepID());
bool useShadingSsboIndex =
fSharedContext->caps()->storageBufferPreferred() && step->performsShading();
const FragSkSLInfo fsSkSLInfo = GetSkSLFS(fSharedContext->caps(),
fSharedContext->shaderCodeDictionary(),
runtimeDict,
step,
pipelineDesc.paintParamsID(),
useShadingSsboIndex,
renderPassDesc.fWriteSwizzle);
const std::string& fsSkSL = fsSkSLInfo.fSkSL;
const bool localCoordsNeeded = fsSkSLInfo.fRequiresLocalCoords;
bool hasFragment = !fsSkSL.empty();
std::string vsSPIRV, fsSPIRV;
VkShaderModule fsModule = VK_NULL_HANDLE, vsModule = VK_NULL_HANDLE;
if (hasFragment) {
if (!SkSLToSPIRV(compiler,
fsSkSL,
SkSL::ProgramKind::kGraphiteFragment,
settings,
&fsSPIRV,
&fsInterface,
errorHandler)) {
return {};
}
fsModule = createVulkanShaderModule(this->vulkanSharedContext(),
fsSPIRV,
VK_SHADER_STAGE_FRAGMENT_BIT);
if (!fsModule) {
return {};
}
}
if (!SkSLToSPIRV(compiler,
GetSkSLVS(fSharedContext->caps()->resourceBindingRequirements(),
step,
useShadingSsboIndex,
localCoordsNeeded),
SkSL::ProgramKind::kGraphiteVertex,
settings,
&vsSPIRV,
&vsInterface,
errorHandler)) {
return {};
}
vsModule = createVulkanShaderModule(this->vulkanSharedContext(),
vsSPIRV,
VK_SHADER_STAGE_VERTEX_BIT);
if (!vsModule) {
return {};
}
// for now, clean up shader modules
VULKAN_CALL(this->vulkanSharedContext()->interface(),
DestroyShaderModule(this->vulkanSharedContext()->device(), vsModule, nullptr));
VULKAN_CALL(this->vulkanSharedContext()->interface(),
DestroyShaderModule(this->vulkanSharedContext()->device(), fsModule, nullptr));
// TODO: Generate depth-stencil state, blend info
return VulkanGraphicsPipeline::Make(this->vulkanSharedContext());
}
sk_sp<ComputePipeline> VulkanResourceProvider::createComputePipeline(const ComputePipelineDesc&) {
return nullptr;
}
sk_sp<Texture> VulkanResourceProvider::createTexture(SkISize size, const TextureInfo& info,
skgpu::Budgeted budgeted) {
return VulkanTexture::Make(this->vulkanSharedContext(), size, info, budgeted);
}
sk_sp<Buffer> VulkanResourceProvider::createBuffer(size_t size,
BufferType type,
AccessPattern accessPattern) {
return VulkanBuffer::Make(this->vulkanSharedContext(), size, type, accessPattern);
}
sk_sp<Sampler> VulkanResourceProvider::createSampler(const SkSamplingOptions& samplingOptions,
SkTileMode xTileMode,
SkTileMode yTileMode) {
return VulkanSampler::Make(this->vulkanSharedContext(), samplingOptions, xTileMode, yTileMode);
}
BackendTexture VulkanResourceProvider::onCreateBackendTexture(SkISize dimensions,
const TextureInfo&) {
return {};
}
VulkanDescriptorSet* VulkanResourceProvider::findOrCreateDescriptorSet(
SkSpan<DescTypeAndCount> requestedDescriptors) {
GraphiteResourceKey key;
// TODO(nicolettep): Optimize key structure. It is horrendously inefficient but functional.
// Fow now, have each descriptor type and quantity take up an entire uint32_t, with an
// additional uint32_t added to include a unique identifier for different descriptor sets that
// have the same set layout.
static const int kNum32DataCnt = (kDescriptorTypeCount * 2) + 1;
static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
Resource* descSet = nullptr;
// Search for available descriptor sets by assembling the last part of the key with a unique set
// ID (which ranges from 0 to kMaxNumSets - 1). Start the search at 0 and continue until an
// available set is found.
// TODO(nicolettep): Explore ways to optimize this traversal.
for (uint32_t i = 0; i < VulkanDescriptorPool::kMaxNumSets; i++) {
GraphiteResourceKey::Builder builder(&key, kType, kNum32DataCnt, Shareable::kNo);
// Assemble the base component of a descriptor set key which is determined by the type and
// quantity of requested descriptors.
for (size_t j = 0, k = 0; k < requestedDescriptors.size(); j = j + 2, k++) {
builder[j+1] = static_cast<uint32_t>(requestedDescriptors[k].type);
builder[j] = requestedDescriptors[k].count;
}
builder[kNum32DataCnt - 1] = i;
builder.finish();
if ((descSet = fResourceCache->findAndRefResource(key, skgpu::Budgeted::kNo))) {
// A non-null resource pointer indicates we have found an available descriptor set.
return static_cast<VulkanDescriptorSet*>(descSet);
}
key.reset();
}
// If we did not find an existing avilable desc set, allocate sets with the appropriate layout
// and add them to the cache.
auto pool = VulkanDescriptorPool::Make(this->vulkanSharedContext(), requestedDescriptors);
VkDescriptorSetLayout layout = DescTypeAndCountToVkDescSetLayout(
this->vulkanSharedContext(),
requestedDescriptors);
// Store the key of the first descriptor set so it can be easily accessed later.
GraphiteResourceKey firstSetKey;
// Allocate the maximum number of sets so they can be easily accessed as needed from the cache.
for (int i = 0; i < VulkanDescriptorPool::kMaxNumSets ; i++) {
GraphiteResourceKey::Builder builder(&key, kType, kNum32DataCnt, Shareable::kNo);
descSet = VulkanDescriptorSet::Make(this->vulkanSharedContext(), pool, &layout).get();
// Assemble the base component of a descriptor set key which is determined by the type and
// quantity of requested descriptors.
for (size_t j = 0, k = 0; k < requestedDescriptors.size(); j = j + 2, k++) {
builder[j+1] = static_cast<uint32_t>(requestedDescriptors[k].type);
builder[j] = requestedDescriptors[k].count;
}
builder[kNum32DataCnt - 1] = i;
builder.finish();
descSet->setKey(key);
fResourceCache->insertResource(descSet);
if (i == 0) {
firstSetKey = key;
}
key.reset();
}
descSet = fResourceCache->findAndRefResource(firstSetKey, skgpu::Budgeted::kNo);
return descSet ? static_cast<VulkanDescriptorSet*>(descSet) : nullptr;
}
} // namespace skgpu::graphite