vk_compute_pipeline: Initial implementation
This abstraction represents a Vulkan compute pipeline.
This commit is contained in:
parent
b392a5986e
commit
dc96a59fa0
|
@ -155,6 +155,8 @@ if (ENABLE_VULKAN)
|
||||||
renderer_vulkan/maxwell_to_vk.h
|
renderer_vulkan/maxwell_to_vk.h
|
||||||
renderer_vulkan/vk_buffer_cache.cpp
|
renderer_vulkan/vk_buffer_cache.cpp
|
||||||
renderer_vulkan/vk_buffer_cache.h
|
renderer_vulkan/vk_buffer_cache.h
|
||||||
|
renderer_vulkan/vk_compute_pipeline.cpp
|
||||||
|
renderer_vulkan/vk_compute_pipeline.h
|
||||||
renderer_vulkan/vk_descriptor_pool.cpp
|
renderer_vulkan/vk_descriptor_pool.cpp
|
||||||
renderer_vulkan/vk_descriptor_pool.h
|
renderer_vulkan/vk_descriptor_pool.h
|
||||||
renderer_vulkan/vk_device.cpp
|
renderer_vulkan/vk_device.cpp
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "video_core/renderer_vulkan/declarations.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_compute_pipeline.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_device.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
VKComputePipeline::VKComputePipeline(const VKDevice& device, VKScheduler& scheduler,
|
||||||
|
VKDescriptorPool& descriptor_pool,
|
||||||
|
VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||||
|
const SPIRVShader& shader)
|
||||||
|
: device{device}, scheduler{scheduler}, entries{shader.entries},
|
||||||
|
descriptor_set_layout{CreateDescriptorSetLayout()},
|
||||||
|
descriptor_allocator{descriptor_pool, *descriptor_set_layout},
|
||||||
|
update_descriptor_queue{update_descriptor_queue}, layout{CreatePipelineLayout()},
|
||||||
|
descriptor_template{CreateDescriptorUpdateTemplate()},
|
||||||
|
shader_module{CreateShaderModule(shader.code)}, pipeline{CreatePipeline()} {}
|
||||||
|
|
||||||
|
VKComputePipeline::~VKComputePipeline() = default;
|
||||||
|
|
||||||
|
vk::DescriptorSet VKComputePipeline::CommitDescriptorSet() {
|
||||||
|
if (!descriptor_template) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto set = descriptor_allocator.Commit(scheduler.GetFence());
|
||||||
|
update_descriptor_queue.Send(*descriptor_template, set);
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueDescriptorSetLayout VKComputePipeline::CreateDescriptorSetLayout() const {
|
||||||
|
std::vector<vk::DescriptorSetLayoutBinding> bindings;
|
||||||
|
u32 binding = 0;
|
||||||
|
const auto AddBindings = [&](vk::DescriptorType descriptor_type, std::size_t num_entries) {
|
||||||
|
// TODO(Rodrigo): Maybe make individual bindings here?
|
||||||
|
for (u32 bindpoint = 0; bindpoint < static_cast<u32>(num_entries); ++bindpoint) {
|
||||||
|
bindings.emplace_back(binding++, descriptor_type, 1, vk::ShaderStageFlagBits::eCompute,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AddBindings(vk::DescriptorType::eUniformBuffer, entries.const_buffers.size());
|
||||||
|
AddBindings(vk::DescriptorType::eStorageBuffer, entries.global_buffers.size());
|
||||||
|
AddBindings(vk::DescriptorType::eUniformTexelBuffer, entries.texel_buffers.size());
|
||||||
|
AddBindings(vk::DescriptorType::eCombinedImageSampler, entries.samplers.size());
|
||||||
|
AddBindings(vk::DescriptorType::eStorageImage, entries.images.size());
|
||||||
|
|
||||||
|
const vk::DescriptorSetLayoutCreateInfo descriptor_set_layout_ci(
|
||||||
|
{}, static_cast<u32>(bindings.size()), bindings.data());
|
||||||
|
|
||||||
|
const auto dev = device.GetLogical();
|
||||||
|
const auto& dld = device.GetDispatchLoader();
|
||||||
|
return dev.createDescriptorSetLayoutUnique(descriptor_set_layout_ci, nullptr, dld);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniquePipelineLayout VKComputePipeline::CreatePipelineLayout() const {
|
||||||
|
const vk::PipelineLayoutCreateInfo layout_ci({}, 1, &*descriptor_set_layout, 0, nullptr);
|
||||||
|
const auto dev = device.GetLogical();
|
||||||
|
return dev.createPipelineLayoutUnique(layout_ci, nullptr, device.GetDispatchLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueDescriptorUpdateTemplate VKComputePipeline::CreateDescriptorUpdateTemplate() const {
|
||||||
|
std::vector<vk::DescriptorUpdateTemplateEntry> template_entries;
|
||||||
|
u32 binding = 0;
|
||||||
|
u32 offset = 0;
|
||||||
|
FillDescriptorUpdateTemplateEntries(device, entries, binding, offset, template_entries);
|
||||||
|
if (template_entries.empty()) {
|
||||||
|
// If the shader doesn't use descriptor sets, skip template creation.
|
||||||
|
return UniqueDescriptorUpdateTemplate{};
|
||||||
|
}
|
||||||
|
|
||||||
|
const vk::DescriptorUpdateTemplateCreateInfo template_ci(
|
||||||
|
{}, static_cast<u32>(template_entries.size()), template_entries.data(),
|
||||||
|
vk::DescriptorUpdateTemplateType::eDescriptorSet, *descriptor_set_layout,
|
||||||
|
vk::PipelineBindPoint::eGraphics, *layout, DESCRIPTOR_SET);
|
||||||
|
|
||||||
|
const auto dev = device.GetLogical();
|
||||||
|
const auto& dld = device.GetDispatchLoader();
|
||||||
|
return dev.createDescriptorUpdateTemplateUnique(template_ci, nullptr, dld);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueShaderModule VKComputePipeline::CreateShaderModule(const std::vector<u32>& code) const {
|
||||||
|
const vk::ShaderModuleCreateInfo module_ci({}, code.size() * sizeof(u32), code.data());
|
||||||
|
const auto dev = device.GetLogical();
|
||||||
|
return dev.createShaderModuleUnique(module_ci, nullptr, device.GetDispatchLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
UniquePipeline VKComputePipeline::CreatePipeline() const {
|
||||||
|
vk::PipelineShaderStageCreateInfo shader_stage_ci({}, vk::ShaderStageFlagBits::eCompute,
|
||||||
|
*shader_module, "main", nullptr);
|
||||||
|
vk::PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci;
|
||||||
|
subgroup_size_ci.requiredSubgroupSize = GuestWarpSize;
|
||||||
|
if (entries.uses_warps && device.IsGuestWarpSizeSupported(vk::ShaderStageFlagBits::eCompute)) {
|
||||||
|
shader_stage_ci.pNext = &subgroup_size_ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vk::ComputePipelineCreateInfo create_info({}, shader_stage_ci, *layout, {}, 0);
|
||||||
|
const auto dev = device.GetLogical();
|
||||||
|
return dev.createComputePipelineUnique({}, create_info, nullptr, device.GetDispatchLoader());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/renderer_vulkan/declarations.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
class VKDevice;
|
||||||
|
class VKScheduler;
|
||||||
|
class VKUpdateDescriptorQueue;
|
||||||
|
|
||||||
|
class VKComputePipeline final {
|
||||||
|
public:
|
||||||
|
explicit VKComputePipeline(const VKDevice& device, VKScheduler& scheduler,
|
||||||
|
VKDescriptorPool& descriptor_pool,
|
||||||
|
VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||||
|
const SPIRVShader& shader);
|
||||||
|
~VKComputePipeline();
|
||||||
|
|
||||||
|
vk::DescriptorSet CommitDescriptorSet();
|
||||||
|
|
||||||
|
vk::Pipeline GetHandle() const {
|
||||||
|
return *pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::PipelineLayout GetLayout() const {
|
||||||
|
return *layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShaderEntries& GetEntries() {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
UniqueDescriptorSetLayout CreateDescriptorSetLayout() const;
|
||||||
|
|
||||||
|
UniquePipelineLayout CreatePipelineLayout() const;
|
||||||
|
|
||||||
|
UniqueDescriptorUpdateTemplate CreateDescriptorUpdateTemplate() const;
|
||||||
|
|
||||||
|
UniqueShaderModule CreateShaderModule(const std::vector<u32>& code) const;
|
||||||
|
|
||||||
|
UniquePipeline CreatePipeline() const;
|
||||||
|
|
||||||
|
const VKDevice& device;
|
||||||
|
VKScheduler& scheduler;
|
||||||
|
ShaderEntries entries;
|
||||||
|
|
||||||
|
UniqueDescriptorSetLayout descriptor_set_layout;
|
||||||
|
DescriptorAllocator descriptor_allocator;
|
||||||
|
VKUpdateDescriptorQueue& update_descriptor_queue;
|
||||||
|
UniquePipelineLayout layout;
|
||||||
|
UniqueDescriptorUpdateTemplate descriptor_template;
|
||||||
|
UniqueShaderModule shader_module;
|
||||||
|
UniquePipeline pipeline;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
|
@ -4,9 +4,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/renderer_vulkan/declarations.h"
|
#include "video_core/renderer_vulkan/declarations.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
||||||
#include "video_core/shader/shader_ir.h"
|
#include "video_core/shader/shader_ir.h"
|
||||||
|
@ -15,6 +18,42 @@ namespace Vulkan {
|
||||||
|
|
||||||
class VKDevice;
|
class VKDevice;
|
||||||
|
|
||||||
|
struct ComputePipelineCacheKey {
|
||||||
|
GPUVAddr shader{};
|
||||||
|
u32 shared_memory_size{};
|
||||||
|
std::array<u32, 3> workgroup_size{};
|
||||||
|
|
||||||
|
std::size_t Hash() const noexcept {
|
||||||
|
return static_cast<std::size_t>(shader) ^
|
||||||
|
((static_cast<std::size_t>(shared_memory_size) >> 7) << 40) ^
|
||||||
|
static_cast<std::size_t>(workgroup_size[0]) ^
|
||||||
|
(static_cast<std::size_t>(workgroup_size[1]) << 16) ^
|
||||||
|
(static_cast<std::size_t>(workgroup_size[2]) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const ComputePipelineCacheKey& rhs) const noexcept {
|
||||||
|
return std::tie(shader, shared_memory_size, workgroup_size) ==
|
||||||
|
std::tie(rhs.shader, rhs.shared_memory_size, rhs.workgroup_size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<Vulkan::ComputePipelineCacheKey> {
|
||||||
|
std::size_t operator()(const Vulkan::ComputePipelineCacheKey& k) const noexcept {
|
||||||
|
return k.Hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
class VKDevice;
|
||||||
|
|
||||||
void FillDescriptorUpdateTemplateEntries(
|
void FillDescriptorUpdateTemplateEntries(
|
||||||
const VKDevice& device, const ShaderEntries& entries, u32& binding, u32& offset,
|
const VKDevice& device, const ShaderEntries& entries, u32& binding, u32& offset,
|
||||||
std::vector<vk::DescriptorUpdateTemplateEntry>& template_entries);
|
std::vector<vk::DescriptorUpdateTemplateEntry>& template_entries);
|
||||||
|
|
Reference in New Issue