summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_vulkan
diff options
context:
space:
mode:
authorReinUsesLisp <reinuseslisp@airmail.cc>2020-01-07 01:29:13 +0100
committerReinUsesLisp <reinuseslisp@airmail.cc>2020-01-07 02:02:26 +0100
commit2effdeb9243b5ca80a696b9bc003fb0179e0b6bd (patch)
tree5a082ab5ed4b37d6eb9a4ad05f47d7fa7370ff17 /src/video_core/renderer_vulkan
parentvk_compute_pipeline: Initial implementation (diff)
downloadyuzu-2effdeb9243b5ca80a696b9bc003fb0179e0b6bd.tar
yuzu-2effdeb9243b5ca80a696b9bc003fb0179e0b6bd.tar.gz
yuzu-2effdeb9243b5ca80a696b9bc003fb0179e0b6bd.tar.bz2
yuzu-2effdeb9243b5ca80a696b9bc003fb0179e0b6bd.tar.lz
yuzu-2effdeb9243b5ca80a696b9bc003fb0179e0b6bd.tar.xz
yuzu-2effdeb9243b5ca80a696b9bc003fb0179e0b6bd.tar.zst
yuzu-2effdeb9243b5ca80a696b9bc003fb0179e0b6bd.zip
Diffstat (limited to 'src/video_core/renderer_vulkan')
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp271
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h90
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h32
3 files changed, 393 insertions, 0 deletions
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
new file mode 100644
index 000000000..2e0536bf6
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -0,0 +1,271 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <vector>
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/microprofile.h"
+#include "video_core/renderer_vulkan/declarations.h"
+#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
+#include "video_core/renderer_vulkan/maxwell_to_vk.h"
+#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
+#include "video_core/renderer_vulkan/vk_device.h"
+#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
+#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
+#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
+#include "video_core/renderer_vulkan/vk_scheduler.h"
+#include "video_core/renderer_vulkan/vk_update_descriptor.h"
+
+namespace Vulkan {
+
+MICROPROFILE_DECLARE(Vulkan_PipelineCache);
+
+namespace {
+
+vk::StencilOpState GetStencilFaceState(const FixedPipelineState::StencilFace& face) {
+ return vk::StencilOpState(MaxwellToVK::StencilOp(face.action_stencil_fail),
+ MaxwellToVK::StencilOp(face.action_depth_pass),
+ MaxwellToVK::StencilOp(face.action_depth_fail),
+ MaxwellToVK::ComparisonOp(face.test_func), 0, 0, 0);
+}
+
+bool SupportsPrimitiveRestart(vk::PrimitiveTopology topology) {
+ static constexpr std::array unsupported_topologies = {
+ vk::PrimitiveTopology::ePointList,
+ vk::PrimitiveTopology::eLineList,
+ vk::PrimitiveTopology::eTriangleList,
+ vk::PrimitiveTopology::eLineListWithAdjacency,
+ vk::PrimitiveTopology::eTriangleListWithAdjacency,
+ vk::PrimitiveTopology::ePatchList};
+ return std::find(std::begin(unsupported_topologies), std::end(unsupported_topologies),
+ topology) == std::end(unsupported_topologies);
+}
+
+} // Anonymous namespace
+
+VKGraphicsPipeline::VKGraphicsPipeline(const VKDevice& device, VKScheduler& scheduler,
+ VKDescriptorPool& descriptor_pool,
+ VKUpdateDescriptorQueue& update_descriptor_queue,
+ VKRenderPassCache& renderpass_cache,
+ const GraphicsPipelineCacheKey& key,
+ const std::vector<vk::DescriptorSetLayoutBinding>& bindings,
+ const SPIRVProgram& program)
+ : device{device}, scheduler{scheduler}, fixed_state{key.fixed_state}, hash{key.Hash()},
+ descriptor_set_layout{CreateDescriptorSetLayout(bindings)},
+ descriptor_allocator{descriptor_pool, *descriptor_set_layout},
+ update_descriptor_queue{update_descriptor_queue}, layout{CreatePipelineLayout()},
+ descriptor_template{CreateDescriptorUpdateTemplate(program)}, modules{CreateShaderModules(
+ program)},
+ renderpass{renderpass_cache.GetRenderPass(key.renderpass_params)}, pipeline{CreatePipeline(
+ key.renderpass_params,
+ program)} {}
+
+VKGraphicsPipeline::~VKGraphicsPipeline() = default;
+
+vk::DescriptorSet VKGraphicsPipeline::CommitDescriptorSet() {
+ if (!descriptor_template) {
+ return {};
+ }
+ const auto set = descriptor_allocator.Commit(scheduler.GetFence());
+ update_descriptor_queue.Send(*descriptor_template, set);
+ return set;
+}
+
+UniqueDescriptorSetLayout VKGraphicsPipeline::CreateDescriptorSetLayout(
+ const std::vector<vk::DescriptorSetLayoutBinding>& bindings) const {
+ 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 VKGraphicsPipeline::CreatePipelineLayout() const {
+ const vk::PipelineLayoutCreateInfo pipeline_layout_ci({}, 1, &*descriptor_set_layout, 0,
+ nullptr);
+ const auto dev = device.GetLogical();
+ const auto& dld = device.GetDispatchLoader();
+ return dev.createPipelineLayoutUnique(pipeline_layout_ci, nullptr, dld);
+}
+
+UniqueDescriptorUpdateTemplate VKGraphicsPipeline::CreateDescriptorUpdateTemplate(
+ const SPIRVProgram& program) const {
+ std::vector<vk::DescriptorUpdateTemplateEntry> template_entries;
+ u32 binding = 0;
+ u32 offset = 0;
+ for (const auto& stage : program) {
+ if (stage) {
+ FillDescriptorUpdateTemplateEntries(device, stage->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);
+}
+
+std::vector<UniqueShaderModule> VKGraphicsPipeline::CreateShaderModules(
+ const SPIRVProgram& program) const {
+ std::vector<UniqueShaderModule> modules;
+ const auto dev = device.GetLogical();
+ const auto& dld = device.GetDispatchLoader();
+ for (std::size_t i = 0; i < Maxwell::MaxShaderStage; ++i) {
+ const auto& stage = program[i];
+ if (!stage) {
+ continue;
+ }
+ const vk::ShaderModuleCreateInfo module_ci({}, stage->code.size() * sizeof(u32),
+ stage->code.data());
+ modules.emplace_back(dev.createShaderModuleUnique(module_ci, nullptr, dld));
+ }
+ return modules;
+}
+
+UniquePipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
+ const SPIRVProgram& program) const {
+ const auto& vi = fixed_state.vertex_input;
+ const auto& ia = fixed_state.input_assembly;
+ const auto& ds = fixed_state.depth_stencil;
+ const auto& cd = fixed_state.color_blending;
+ const auto& ts = fixed_state.tessellation;
+ const auto& rs = fixed_state.rasterizer;
+
+ std::vector<vk::VertexInputBindingDescription> vertex_bindings;
+ std::vector<vk::VertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
+ for (std::size_t i = 0; i < vi.num_bindings; ++i) {
+ const auto& binding = vi.bindings[i];
+ const bool instanced = binding.divisor != 0;
+ const auto rate = instanced ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex;
+ vertex_bindings.emplace_back(binding.index, binding.stride, rate);
+ if (instanced) {
+ vertex_binding_divisors.emplace_back(binding.index, binding.divisor);
+ }
+ }
+
+ std::vector<vk::VertexInputAttributeDescription> vertex_attributes;
+ const auto& input_attributes = program[0]->entries.attributes;
+ for (std::size_t i = 0; i < vi.num_attributes; ++i) {
+ const auto& attribute = vi.attributes[i];
+ if (input_attributes.find(attribute.index) == input_attributes.end()) {
+ // Skip attributes not used by the vertex shaders.
+ continue;
+ }
+ vertex_attributes.emplace_back(attribute.index, attribute.buffer,
+ MaxwellToVK::VertexFormat(attribute.type, attribute.size),
+ attribute.offset);
+ }
+
+ vk::PipelineVertexInputStateCreateInfo vertex_input_ci(
+ {}, static_cast<u32>(vertex_bindings.size()), vertex_bindings.data(),
+ static_cast<u32>(vertex_attributes.size()), vertex_attributes.data());
+
+ const vk::PipelineVertexInputDivisorStateCreateInfoEXT vertex_input_divisor_ci(
+ static_cast<u32>(vertex_binding_divisors.size()), vertex_binding_divisors.data());
+ if (!vertex_binding_divisors.empty()) {
+ vertex_input_ci.pNext = &vertex_input_divisor_ci;
+ }
+
+ const auto primitive_topology = MaxwellToVK::PrimitiveTopology(device, ia.topology);
+ const vk::PipelineInputAssemblyStateCreateInfo input_assembly_ci(
+ {}, primitive_topology,
+ ia.primitive_restart_enable && SupportsPrimitiveRestart(primitive_topology));
+
+ const vk::PipelineTessellationStateCreateInfo tessellation_ci({}, ts.patch_control_points);
+
+ const vk::PipelineViewportStateCreateInfo viewport_ci({}, Maxwell::NumViewports, nullptr,
+ Maxwell::NumViewports, nullptr);
+
+ // TODO(Rodrigo): Find out what's the default register value for front face
+ const vk::PipelineRasterizationStateCreateInfo rasterizer_ci(
+ {}, rs.depth_clamp_enable, false, vk::PolygonMode::eFill,
+ rs.cull_enable ? MaxwellToVK::CullFace(rs.cull_face) : vk::CullModeFlagBits::eNone,
+ rs.cull_enable ? MaxwellToVK::FrontFace(rs.front_face) : vk::FrontFace::eCounterClockwise,
+ rs.depth_bias_enable, 0.0f, 0.0f, 0.0f, 1.0f);
+
+ const vk::PipelineMultisampleStateCreateInfo multisampling_ci(
+ {}, vk::SampleCountFlagBits::e1, false, 0.0f, nullptr, false, false);
+
+ const vk::CompareOp depth_test_compare = ds.depth_test_enable
+ ? MaxwellToVK::ComparisonOp(ds.depth_test_function)
+ : vk::CompareOp::eAlways;
+
+ const vk::PipelineDepthStencilStateCreateInfo depth_stencil_ci(
+ {}, ds.depth_test_enable, ds.depth_write_enable, depth_test_compare, ds.depth_bounds_enable,
+ ds.stencil_enable, GetStencilFaceState(ds.front_stencil),
+ GetStencilFaceState(ds.back_stencil), 0.0f, 0.0f);
+
+ std::array<vk::PipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
+ const std::size_t num_attachments =
+ std::min(cd.attachments_count, renderpass_params.color_attachments.size());
+ for (std::size_t i = 0; i < num_attachments; ++i) {
+ constexpr std::array component_table{
+ vk::ColorComponentFlagBits::eR, vk::ColorComponentFlagBits::eG,
+ vk::ColorComponentFlagBits::eB, vk::ColorComponentFlagBits::eA};
+ const auto& blend = cd.attachments[i];
+
+ vk::ColorComponentFlags color_components{};
+ for (std::size_t j = 0; j < component_table.size(); ++j) {
+ if (blend.components[j])
+ color_components |= component_table[j];
+ }
+
+ cb_attachments[i] = vk::PipelineColorBlendAttachmentState(
+ blend.enable, MaxwellToVK::BlendFactor(blend.src_rgb_func),
+ MaxwellToVK::BlendFactor(blend.dst_rgb_func),
+ MaxwellToVK::BlendEquation(blend.rgb_equation),
+ MaxwellToVK::BlendFactor(blend.src_a_func), MaxwellToVK::BlendFactor(blend.dst_a_func),
+ MaxwellToVK::BlendEquation(blend.a_equation), color_components);
+ }
+ const vk::PipelineColorBlendStateCreateInfo color_blending_ci({}, false, vk::LogicOp::eCopy,
+ static_cast<u32>(num_attachments),
+ cb_attachments.data(), {});
+
+ constexpr std::array dynamic_states = {
+ vk::DynamicState::eViewport, vk::DynamicState::eScissor,
+ vk::DynamicState::eDepthBias, vk::DynamicState::eBlendConstants,
+ vk::DynamicState::eDepthBounds, vk::DynamicState::eStencilCompareMask,
+ vk::DynamicState::eStencilWriteMask, vk::DynamicState::eStencilReference};
+ const vk::PipelineDynamicStateCreateInfo dynamic_state_ci(
+ {}, static_cast<u32>(dynamic_states.size()), dynamic_states.data());
+
+ vk::PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci;
+ subgroup_size_ci.requiredSubgroupSize = GuestWarpSize;
+
+ std::vector<vk::PipelineShaderStageCreateInfo> shader_stages;
+ std::size_t module_index = 0;
+ for (std::size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) {
+ if (!program[stage]) {
+ continue;
+ }
+ const auto stage_enum = static_cast<Tegra::Engines::ShaderType>(stage);
+ const auto vk_stage = MaxwellToVK::ShaderStage(stage_enum);
+ auto& stage_ci = shader_stages.emplace_back(vk::PipelineShaderStageCreateFlags{}, vk_stage,
+ *modules[module_index++], "main", nullptr);
+ if (program[stage]->entries.uses_warps && device.IsGuestWarpSizeSupported(vk_stage)) {
+ stage_ci.pNext = &subgroup_size_ci;
+ }
+ }
+
+ const vk::GraphicsPipelineCreateInfo create_info(
+ {}, static_cast<u32>(shader_stages.size()), shader_stages.data(), &vertex_input_ci,
+ &input_assembly_ci, &tessellation_ci, &viewport_ci, &rasterizer_ci, &multisampling_ci,
+ &depth_stencil_ci, &color_blending_ci, &dynamic_state_ci, *layout, renderpass, 0, {}, 0);
+
+ const auto dev = device.GetLogical();
+ const auto& dld = device.GetDispatchLoader();
+ return dev.createGraphicsPipelineUnique(nullptr, create_info, nullptr, dld);
+}
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
new file mode 100644
index 000000000..4f5e4ea2d
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -0,0 +1,90 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/renderer_vulkan/declarations.h"
+#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
+#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
+#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
+#include "video_core/renderer_vulkan/vk_resource_manager.h"
+#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
+
+namespace Vulkan {
+
+using Maxwell = Tegra::Engines::Maxwell3D::Regs;
+
+struct GraphicsPipelineCacheKey;
+
+class VKDescriptorPool;
+class VKDevice;
+class VKRenderPassCache;
+class VKScheduler;
+class VKUpdateDescriptorQueue;
+
+using SPIRVProgram = std::array<std::optional<SPIRVShader>, Maxwell::MaxShaderStage>;
+
+class VKGraphicsPipeline final {
+public:
+ explicit VKGraphicsPipeline(const VKDevice& device, VKScheduler& scheduler,
+ VKDescriptorPool& descriptor_pool,
+ VKUpdateDescriptorQueue& update_descriptor_queue,
+ VKRenderPassCache& renderpass_cache,
+ const GraphicsPipelineCacheKey& key,
+ const std::vector<vk::DescriptorSetLayoutBinding>& bindings,
+ const SPIRVProgram& program);
+ ~VKGraphicsPipeline();
+
+ vk::DescriptorSet CommitDescriptorSet();
+
+ vk::Pipeline GetHandle() const {
+ return *pipeline;
+ }
+
+ vk::PipelineLayout GetLayout() const {
+ return *layout;
+ }
+
+ vk::RenderPass GetRenderPass() const {
+ return renderpass;
+ }
+
+private:
+ UniqueDescriptorSetLayout CreateDescriptorSetLayout(
+ const std::vector<vk::DescriptorSetLayoutBinding>& bindings) const;
+
+ UniquePipelineLayout CreatePipelineLayout() const;
+
+ UniqueDescriptorUpdateTemplate CreateDescriptorUpdateTemplate(
+ const SPIRVProgram& program) const;
+
+ std::vector<UniqueShaderModule> CreateShaderModules(const SPIRVProgram& program) const;
+
+ UniquePipeline CreatePipeline(const RenderPassParams& renderpass_params,
+ const SPIRVProgram& program) const;
+
+ const VKDevice& device;
+ VKScheduler& scheduler;
+ const FixedPipelineState fixed_state;
+ const u64 hash;
+
+ UniqueDescriptorSetLayout descriptor_set_layout;
+ DescriptorAllocator descriptor_allocator;
+ VKUpdateDescriptorQueue& update_descriptor_queue;
+ UniquePipelineLayout layout;
+ UniqueDescriptorUpdateTemplate descriptor_template;
+ std::vector<UniqueShaderModule> modules;
+
+ vk::RenderPass renderpass;
+ UniquePipeline pipeline;
+};
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 33b1a1d23..e49ed135d 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -8,9 +8,12 @@
#include <cstddef>
#include <vector>
+#include <boost/functional/hash.hpp>
+
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_vulkan/declarations.h"
+#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
#include "video_core/shader/shader_ir.h"
@@ -18,6 +21,28 @@ namespace Vulkan {
class VKDevice;
+using Maxwell = Tegra::Engines::Maxwell3D::Regs;
+
+struct GraphicsPipelineCacheKey {
+ FixedPipelineState fixed_state;
+ std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
+ RenderPassParams renderpass_params;
+
+ std::size_t Hash() const noexcept {
+ std::size_t hash = fixed_state.Hash();
+ for (const auto& shader : shaders) {
+ boost::hash_combine(hash, shader);
+ }
+ boost::hash_combine(hash, renderpass_params.Hash());
+ return hash;
+ }
+
+ bool operator==(const GraphicsPipelineCacheKey& rhs) const noexcept {
+ return std::tie(fixed_state, shaders, renderpass_params) ==
+ std::tie(rhs.fixed_state, rhs.shaders, rhs.renderpass_params);
+ }
+};
+
struct ComputePipelineCacheKey {
GPUVAddr shader{};
u32 shared_memory_size{};
@@ -42,6 +67,13 @@ struct ComputePipelineCacheKey {
namespace std {
template <>
+struct hash<Vulkan::GraphicsPipelineCacheKey> {
+ std::size_t operator()(const Vulkan::GraphicsPipelineCacheKey& k) const noexcept {
+ return k.Hash();
+ }
+};
+
+template <>
struct hash<Vulkan::ComputePipelineCacheKey> {
std::size_t operator()(const Vulkan::ComputePipelineCacheKey& k) const noexcept {
return k.Hash();