mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-05-31 15:53:17 +00:00
video: Import new shader recompiler + display a triangle (#142)
This commit is contained in:
parent
8cf64a33b2
commit
8730968385
103 changed files with 17793 additions and 729 deletions
|
@ -6,6 +6,7 @@
|
|||
#include "common/thread.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/amdgpu/pm4_cmds.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
|
||||
|
@ -107,7 +108,7 @@ void Liverpool::ProcessCmdList(const u32* cmdbuf, u32 size_in_bytes) {
|
|||
regs.index_base_address.base_addr_hi.Assign(draw_index->index_base_hi);
|
||||
regs.num_indices = draw_index->index_count;
|
||||
regs.draw_initiator = draw_index->draw_initiator;
|
||||
// rasterizer->DrawIndex();
|
||||
rasterizer->DrawIndex();
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::DrawIndexAuto: {
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
#include <thread>
|
||||
#include <queue>
|
||||
|
||||
namespace Vulkan {
|
||||
class Rasterizer;
|
||||
}
|
||||
|
||||
namespace AmdGpu {
|
||||
|
||||
#define GFX6_3D_REG_INDEX(field_name) (offsetof(AmdGpu::Liverpool::Regs, field_name) / sizeof(u32))
|
||||
|
@ -46,9 +50,10 @@ struct Liverpool {
|
|||
} settings;
|
||||
UserData user_data;
|
||||
|
||||
const u8* Address() const {
|
||||
template <typename T = u8>
|
||||
const T* Address() const {
|
||||
const uintptr_t addr = uintptr_t(address_hi) << 40 | uintptr_t(address_lo) << 8;
|
||||
return reinterpret_cast<const u8*>(addr);
|
||||
return reinterpret_cast<const T*>(addr);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -631,10 +636,15 @@ public:
|
|||
|
||||
void WaitGpuIdle();
|
||||
|
||||
void BindRasterizer(Vulkan::Rasterizer* rasterizer_) {
|
||||
rasterizer = rasterizer_;
|
||||
}
|
||||
|
||||
private:
|
||||
void ProcessCmdList(const u32* cmdbuf, u32 size_in_bytes);
|
||||
void Process(std::stop_token stoken);
|
||||
|
||||
Vulkan::Rasterizer* rasterizer;
|
||||
std::jthread process_thread{};
|
||||
std::queue<std::span<const u32>> gfx_ring{};
|
||||
std::condition_variable_any cv_submit{};
|
||||
|
|
113
src/video_core/renderer_vulkan/liverpool_to_vk.cpp
Normal file
113
src/video_core/renderer_vulkan/liverpool_to_vk.cpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
|
||||
|
||||
namespace Vulkan::LiverpoolToVK {
|
||||
|
||||
vk::StencilOp StencilOp(Liverpool::StencilFunc op) {
|
||||
switch (op) {
|
||||
case Liverpool::StencilFunc::Keep:
|
||||
return vk::StencilOp::eKeep;
|
||||
case Liverpool::StencilFunc::Zero:
|
||||
return vk::StencilOp::eZero;
|
||||
case Liverpool::StencilFunc::AddClamp:
|
||||
return vk::StencilOp::eIncrementAndClamp;
|
||||
case Liverpool::StencilFunc::SubClamp:
|
||||
return vk::StencilOp::eDecrementAndClamp;
|
||||
case Liverpool::StencilFunc::Invert:
|
||||
return vk::StencilOp::eInvert;
|
||||
case Liverpool::StencilFunc::AddWrap:
|
||||
return vk::StencilOp::eIncrementAndWrap;
|
||||
case Liverpool::StencilFunc::SubWrap:
|
||||
return vk::StencilOp::eDecrementAndWrap;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return vk::StencilOp::eKeep;
|
||||
}
|
||||
}
|
||||
|
||||
vk::CompareOp CompareOp(Liverpool::CompareFunc func) {
|
||||
switch (func) {
|
||||
case Liverpool::CompareFunc::Always:
|
||||
return vk::CompareOp::eAlways;
|
||||
case Liverpool::CompareFunc::Equal:
|
||||
return vk::CompareOp::eEqual;
|
||||
case Liverpool::CompareFunc::GreaterEqual:
|
||||
return vk::CompareOp::eGreaterOrEqual;
|
||||
case Liverpool::CompareFunc::Greater:
|
||||
return vk::CompareOp::eGreater;
|
||||
case Liverpool::CompareFunc::LessEqual:
|
||||
return vk::CompareOp::eLessOrEqual;
|
||||
case Liverpool::CompareFunc::Less:
|
||||
return vk::CompareOp::eLess;
|
||||
case Liverpool::CompareFunc::NotEqual:
|
||||
return vk::CompareOp::eNotEqual;
|
||||
case Liverpool::CompareFunc::Never:
|
||||
return vk::CompareOp::eNever;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return vk::CompareOp::eAlways;
|
||||
}
|
||||
}
|
||||
|
||||
vk::PrimitiveTopology PrimitiveType(Liverpool::PrimitiveType type) {
|
||||
switch (type) {
|
||||
case Liverpool::PrimitiveType::PointList:
|
||||
return vk::PrimitiveTopology::ePointList;
|
||||
case Liverpool::PrimitiveType::LineList:
|
||||
return vk::PrimitiveTopology::eLineList;
|
||||
case Liverpool::PrimitiveType::LineStrip:
|
||||
return vk::PrimitiveTopology::eLineStrip;
|
||||
case Liverpool::PrimitiveType::TriangleList:
|
||||
return vk::PrimitiveTopology::eTriangleList;
|
||||
case Liverpool::PrimitiveType::TriangleFan:
|
||||
return vk::PrimitiveTopology::eTriangleFan;
|
||||
case Liverpool::PrimitiveType::TriangleStrip:
|
||||
return vk::PrimitiveTopology::eTriangleStrip;
|
||||
case Liverpool::PrimitiveType::AdjLineList:
|
||||
return vk::PrimitiveTopology::eLineListWithAdjacency;
|
||||
case Liverpool::PrimitiveType::AdjLineStrip:
|
||||
return vk::PrimitiveTopology::eLineStripWithAdjacency;
|
||||
case Liverpool::PrimitiveType::AdjTriangleList:
|
||||
return vk::PrimitiveTopology::eTriangleListWithAdjacency;
|
||||
case Liverpool::PrimitiveType::AdjTriangleStrip:
|
||||
return vk::PrimitiveTopology::eTriangleStripWithAdjacency;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return vk::PrimitiveTopology::eTriangleList;
|
||||
}
|
||||
}
|
||||
|
||||
vk::PolygonMode PolygonMode(Liverpool::PolygonMode mode) {
|
||||
switch (mode) {
|
||||
case Liverpool::PolygonMode::Point:
|
||||
return vk::PolygonMode::ePoint;
|
||||
case Liverpool::PolygonMode::Line:
|
||||
return vk::PolygonMode::eLine;
|
||||
case Liverpool::PolygonMode::Fill:
|
||||
return vk::PolygonMode::eFill;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return vk::PolygonMode::eFill;
|
||||
}
|
||||
}
|
||||
|
||||
vk::CullModeFlags CullMode(Liverpool::CullMode mode) {
|
||||
switch (mode) {
|
||||
case Liverpool::CullMode::None:
|
||||
return vk::CullModeFlagBits::eNone;
|
||||
case Liverpool::CullMode::Front:
|
||||
return vk::CullModeFlagBits::eFront;
|
||||
case Liverpool::CullMode::Back:
|
||||
return vk::CullModeFlagBits::eBack;
|
||||
case Liverpool::CullMode::FrontAndBack:
|
||||
return vk::CullModeFlagBits::eFrontAndBack;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return vk::CullModeFlagBits::eNone;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Vulkan::LiverpoolToVK
|
23
src/video_core/renderer_vulkan/liverpool_to_vk.h
Normal file
23
src/video_core/renderer_vulkan/liverpool_to_vk.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace Vulkan::LiverpoolToVK {
|
||||
|
||||
using Liverpool = AmdGpu::Liverpool;
|
||||
|
||||
vk::StencilOp StencilOp(Liverpool::StencilFunc op);
|
||||
|
||||
vk::CompareOp CompareOp(Liverpool::CompareFunc func);
|
||||
|
||||
vk::PrimitiveTopology PrimitiveType(Liverpool::PrimitiveType type);
|
||||
|
||||
vk::PolygonMode PolygonMode(Liverpool::PolygonMode mode);
|
||||
|
||||
vk::CullModeFlags CullMode(Liverpool::CullMode mode);
|
||||
|
||||
} // namespace Vulkan::LiverpoolToVK
|
|
@ -7,6 +7,7 @@
|
|||
#include "core/libraries/system/systemservice.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
|
@ -60,9 +61,10 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format for
|
|||
};
|
||||
}
|
||||
|
||||
RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_)
|
||||
RendererVulkan::RendererVulkan(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool)
|
||||
: window{window_}, instance{window, Config::getGpuId()}, scheduler{instance},
|
||||
swapchain{instance, window}, texture_cache{instance, scheduler} {
|
||||
rasterizer = std::make_unique<Rasterizer>(instance, scheduler, texture_cache, liverpool);
|
||||
const u32 num_images = swapchain.GetImageCount();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
|
||||
|
|
|
@ -13,6 +13,10 @@ namespace Frontend {
|
|||
class WindowSDL;
|
||||
}
|
||||
|
||||
namespace AmdGpu {
|
||||
struct Liverpool;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
struct Frame {
|
||||
|
@ -26,9 +30,11 @@ struct Frame {
|
|||
vk::CommandBuffer cmdbuf;
|
||||
};
|
||||
|
||||
class Rasterizer;
|
||||
|
||||
class RendererVulkan {
|
||||
public:
|
||||
explicit RendererVulkan(Frontend::WindowSDL& window);
|
||||
explicit RendererVulkan(Frontend::WindowSDL& window, AmdGpu::Liverpool* liverpool);
|
||||
~RendererVulkan();
|
||||
|
||||
Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute,
|
||||
|
@ -47,6 +53,7 @@ private:
|
|||
Instance instance;
|
||||
Scheduler scheduler;
|
||||
Swapchain swapchain;
|
||||
std::unique_ptr<Rasterizer> rasterizer;
|
||||
VideoCore::TextureCache texture_cache;
|
||||
vk::UniqueCommandPool command_pool;
|
||||
std::vector<Frame> present_frames;
|
||||
|
|
162
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
Normal file
162
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
GraphicsPipeline::GraphicsPipeline(const Instance& instance_, const PipelineKey& key_,
|
||||
vk::PipelineCache pipeline_cache_, vk::PipelineLayout layout_,
|
||||
std::array<vk::ShaderModule, MaxShaderStages> modules)
|
||||
: instance{instance_}, pipeline_layout{layout_}, pipeline_cache{pipeline_cache_}, key{key_} {
|
||||
const vk::Device device = instance.GetDevice();
|
||||
|
||||
const vk::PipelineVertexInputStateCreateInfo vertex_input_info = {
|
||||
.vertexBindingDescriptionCount = 0U,
|
||||
.pVertexBindingDescriptions = nullptr,
|
||||
.vertexAttributeDescriptionCount = 0U,
|
||||
.pVertexAttributeDescriptions = nullptr,
|
||||
};
|
||||
|
||||
const vk::PipelineInputAssemblyStateCreateInfo input_assembly = {
|
||||
.topology = LiverpoolToVK::PrimitiveType(key.prim_type),
|
||||
.primitiveRestartEnable = false,
|
||||
};
|
||||
|
||||
const vk::PipelineRasterizationStateCreateInfo raster_state = {
|
||||
.depthClampEnable = false,
|
||||
.rasterizerDiscardEnable = false,
|
||||
.polygonMode = LiverpoolToVK::PolygonMode(key.polygon_mode),
|
||||
.cullMode = LiverpoolToVK::CullMode(key.cull_mode),
|
||||
.frontFace = vk::FrontFace::eClockwise,
|
||||
.depthBiasEnable = false,
|
||||
.lineWidth = 1.0f,
|
||||
};
|
||||
|
||||
const vk::PipelineMultisampleStateCreateInfo multisampling = {
|
||||
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
||||
.sampleShadingEnable = false,
|
||||
};
|
||||
|
||||
const vk::PipelineColorBlendAttachmentState colorblend_attachment = {
|
||||
.blendEnable = false,
|
||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
||||
};
|
||||
|
||||
const vk::PipelineColorBlendStateCreateInfo color_blending = {
|
||||
.logicOpEnable = false,
|
||||
.logicOp = vk::LogicOp::eCopy,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &colorblend_attachment,
|
||||
.blendConstants = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
||||
};
|
||||
|
||||
const vk::Viewport viewport = {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = 1.0f,
|
||||
.height = 1.0f,
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
|
||||
const vk::Rect2D scissor = {
|
||||
.offset = {0, 0},
|
||||
.extent = {1, 1},
|
||||
};
|
||||
|
||||
const vk::PipelineViewportStateCreateInfo viewport_info = {
|
||||
.viewportCount = 1,
|
||||
.pViewports = &viewport,
|
||||
.scissorCount = 1,
|
||||
.pScissors = &scissor,
|
||||
};
|
||||
|
||||
boost::container::static_vector<vk::DynamicState, 14> dynamic_states = {
|
||||
vk::DynamicState::eViewport,
|
||||
vk::DynamicState::eScissor,
|
||||
};
|
||||
|
||||
const vk::PipelineDynamicStateCreateInfo dynamic_info = {
|
||||
.dynamicStateCount = static_cast<u32>(dynamic_states.size()),
|
||||
.pDynamicStates = dynamic_states.data(),
|
||||
};
|
||||
|
||||
const vk::PipelineDepthStencilStateCreateInfo depth_info = {
|
||||
.depthTestEnable = key.depth.depth_enable,
|
||||
.depthWriteEnable = key.depth.depth_write_enable,
|
||||
.depthCompareOp = LiverpoolToVK::CompareOp(key.depth.depth_func),
|
||||
.depthBoundsTestEnable = key.depth.depth_bounds_enable,
|
||||
.stencilTestEnable = key.depth.stencil_enable,
|
||||
.front{
|
||||
.failOp = LiverpoolToVK::StencilOp(key.stencil.stencil_fail_front),
|
||||
.passOp = LiverpoolToVK::StencilOp(key.stencil.stencil_zpass_front),
|
||||
.depthFailOp = LiverpoolToVK::StencilOp(key.stencil.stencil_zfail_front),
|
||||
.compareOp = LiverpoolToVK::CompareOp(key.depth.stencil_ref_func),
|
||||
.compareMask = key.stencil_ref_front.stencil_mask,
|
||||
.writeMask = key.stencil_ref_front.stencil_write_mask,
|
||||
.reference = key.stencil_ref_front.stencil_test_val,
|
||||
},
|
||||
.back{
|
||||
.failOp = LiverpoolToVK::StencilOp(key.stencil.stencil_fail_back),
|
||||
.passOp = LiverpoolToVK::StencilOp(key.stencil.stencil_zpass_back),
|
||||
.depthFailOp = LiverpoolToVK::StencilOp(key.stencil.stencil_zfail_back),
|
||||
.compareOp = LiverpoolToVK::CompareOp(key.depth.stencil_bf_func),
|
||||
.compareMask = key.stencil_ref_back.stencil_mask,
|
||||
.writeMask = key.stencil_ref_back.stencil_write_mask,
|
||||
.reference = key.stencil_ref_back.stencil_test_val,
|
||||
},
|
||||
};
|
||||
|
||||
u32 shader_count = 2;
|
||||
std::array<vk::PipelineShaderStageCreateInfo, MaxShaderStages> shader_stages;
|
||||
shader_stages[0] = vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||
.module = modules[0],
|
||||
.pName = "main",
|
||||
};
|
||||
shader_stages[1] = vk::PipelineShaderStageCreateInfo{
|
||||
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||
.module = modules[4],
|
||||
.pName = "main",
|
||||
};
|
||||
|
||||
const vk::Format color_format = vk::Format::eB8G8R8A8Srgb;
|
||||
const vk::PipelineRenderingCreateInfoKHR pipeline_rendering_ci = {
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachmentFormats = &color_format,
|
||||
.depthAttachmentFormat = vk::Format::eUndefined,
|
||||
.stencilAttachmentFormat = vk::Format::eUndefined,
|
||||
};
|
||||
|
||||
const vk::GraphicsPipelineCreateInfo pipeline_info = {
|
||||
.pNext = &pipeline_rendering_ci,
|
||||
.stageCount = shader_count,
|
||||
.pStages = shader_stages.data(),
|
||||
.pVertexInputState = &vertex_input_info,
|
||||
.pInputAssemblyState = &input_assembly,
|
||||
.pViewportState = &viewport_info,
|
||||
.pRasterizationState = &raster_state,
|
||||
.pMultisampleState = &multisampling,
|
||||
.pDepthStencilState = &depth_info,
|
||||
.pColorBlendState = &color_blending,
|
||||
.pDynamicState = &dynamic_info,
|
||||
.layout = pipeline_layout,
|
||||
};
|
||||
|
||||
auto result = device.createGraphicsPipelineUnique(pipeline_cache, pipeline_info);
|
||||
if (result.result == vk::Result::eSuccess) {
|
||||
pipeline = std::move(result.value);
|
||||
} else {
|
||||
UNREACHABLE_MSG("Graphics pipeline creation failed!");
|
||||
}
|
||||
}
|
||||
|
||||
GraphicsPipeline::~GraphicsPipeline() = default;
|
||||
|
||||
} // namespace Vulkan
|
46
src/video_core/renderer_vulkan/vk_graphics_pipeline.h
Normal file
46
src/video_core/renderer_vulkan/vk_graphics_pipeline.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/types.h"
|
||||
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
static constexpr u32 MaxShaderStages = 5;
|
||||
|
||||
class Instance;
|
||||
|
||||
using Liverpool = AmdGpu::Liverpool;
|
||||
|
||||
struct PipelineKey {
|
||||
Liverpool::DepthControl depth;
|
||||
Liverpool::StencilControl stencil;
|
||||
Liverpool::StencilRefMask stencil_ref_front;
|
||||
Liverpool::StencilRefMask stencil_ref_back;
|
||||
Liverpool::PrimitiveType prim_type;
|
||||
Liverpool::PolygonMode polygon_mode;
|
||||
Liverpool::CullMode cull_mode;
|
||||
};
|
||||
static_assert(std::has_unique_object_representations_v<PipelineKey>);
|
||||
|
||||
class GraphicsPipeline {
|
||||
public:
|
||||
explicit GraphicsPipeline(const Instance& instance, const PipelineKey& key,
|
||||
vk::PipelineCache pipeline_cache, vk::PipelineLayout layout,
|
||||
std::array<vk::ShaderModule, MaxShaderStages> modules);
|
||||
~GraphicsPipeline();
|
||||
|
||||
[[nodiscard]] vk::Pipeline Handle() const noexcept {
|
||||
return *pipeline;
|
||||
}
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
vk::UniquePipeline pipeline;
|
||||
vk::PipelineLayout pipeline_layout;
|
||||
vk::PipelineCache pipeline_cache;
|
||||
PipelineKey key;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
|
@ -196,9 +196,15 @@ bool Instance::CreateDevice() {
|
|||
.shaderClipDistance = features.shaderClipDistance,
|
||||
},
|
||||
},
|
||||
vk::PhysicalDeviceVulkan11Features{
|
||||
.shaderDrawParameters = true,
|
||||
},
|
||||
vk::PhysicalDeviceVulkan12Features{
|
||||
.timelineSemaphore = true,
|
||||
},
|
||||
vk::PhysicalDeviceVulkan13Features{
|
||||
.dynamicRendering = true,
|
||||
},
|
||||
vk::PhysicalDeviceCustomBorderColorFeaturesEXT{
|
||||
.customBorderColors = true,
|
||||
.customBorderColorWithoutFormat = true,
|
||||
|
|
70
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
Normal file
70
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/scope_exit.h"
|
||||
#include "shader_recompiler/recompiler.h"
|
||||
#include "shader_recompiler/runtime_info.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
|
||||
AmdGpu::Liverpool* liverpool_)
|
||||
: instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, inst_pool{4096},
|
||||
block_pool{512} {
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {
|
||||
.setLayoutCount = 0U,
|
||||
.pSetLayouts = nullptr,
|
||||
.pushConstantRangeCount = 0,
|
||||
.pPushConstantRanges = nullptr,
|
||||
};
|
||||
pipeline_layout = instance.GetDevice().createPipelineLayoutUnique(layout_info);
|
||||
pipeline_cache = instance.GetDevice().createPipelineCacheUnique({});
|
||||
}
|
||||
|
||||
void PipelineCache::BindPipeline() {
|
||||
SCOPE_EXIT {
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle());
|
||||
};
|
||||
|
||||
if (pipeline) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto get_program = [&](const AmdGpu::Liverpool::ShaderProgram& pgm, Shader::Stage stage) {
|
||||
const u32* token = pgm.Address<u32>();
|
||||
|
||||
// Retrieve shader header.
|
||||
Shader::BinaryInfo bininfo;
|
||||
std::memcpy(&bininfo, token + (token[1] + 1) * 2, sizeof(bininfo));
|
||||
|
||||
// Lookup if the shader already exists.
|
||||
const auto it = module_map.find(bininfo.shader_hash);
|
||||
if (it != module_map.end()) {
|
||||
return *it->second;
|
||||
}
|
||||
|
||||
// Compile and cache shader.
|
||||
const auto data = std::span{token, bininfo.length / sizeof(u32)};
|
||||
const auto program = Shader::TranslateProgram(inst_pool, block_pool, stage, data);
|
||||
return CompileSPV(program, instance.GetDevice());
|
||||
};
|
||||
|
||||
// Retrieve shader stage modules.
|
||||
// TODO: Only do this when program address is changed.
|
||||
stages[0] = get_program(liverpool->regs.vs_program, Shader::Stage::Vertex);
|
||||
stages[4] = get_program(liverpool->regs.ps_program, Shader::Stage::Fragment);
|
||||
|
||||
// Bind pipeline.
|
||||
// TODO: Read entire key based on reg state.
|
||||
graphics_key.prim_type = liverpool->regs.primitive_type;
|
||||
graphics_key.polygon_mode = liverpool->regs.polygon_control.PolyMode();
|
||||
pipeline = std::make_unique<GraphicsPipeline>(instance, graphics_key, *pipeline_cache,
|
||||
*pipeline_layout, stages);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
40
src/video_core/renderer_vulkan/vk_pipeline_cache.h
Normal file
40
src/video_core/renderer_vulkan/vk_pipeline_cache.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include "shader_recompiler/ir/basic_block.h"
|
||||
#include "shader_recompiler/object_pool.h"
|
||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class Instance;
|
||||
class Scheduler;
|
||||
|
||||
class PipelineCache {
|
||||
static constexpr size_t MaxShaderStages = 5;
|
||||
|
||||
public:
|
||||
explicit PipelineCache(const Instance& instance, Scheduler& scheduler,
|
||||
AmdGpu::Liverpool* liverpool);
|
||||
~PipelineCache() = default;
|
||||
|
||||
void BindPipeline();
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
Scheduler& scheduler;
|
||||
AmdGpu::Liverpool* liverpool;
|
||||
vk::UniquePipelineCache pipeline_cache;
|
||||
vk::UniquePipelineLayout pipeline_layout;
|
||||
tsl::robin_map<size_t, vk::UniqueShaderModule> module_map;
|
||||
std::array<vk::ShaderModule, MaxShaderStages> stages{};
|
||||
std::unique_ptr<GraphicsPipeline> pipeline;
|
||||
PipelineKey graphics_key{};
|
||||
Shader::ObjectPool<Shader::IR::Inst> inst_pool;
|
||||
Shader::ObjectPool<Shader::IR::Block> block_pool;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
99
src/video_core/renderer_vulkan/vk_rasterizer.cpp
Normal file
99
src/video_core/renderer_vulkan/vk_rasterizer.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/texture_cache/image_view.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
static constexpr vk::BufferUsageFlags VertexIndexFlags = vk::BufferUsageFlagBits::eVertexBuffer |
|
||||
vk::BufferUsageFlagBits::eIndexBuffer |
|
||||
vk::BufferUsageFlagBits::eTransferDst;
|
||||
|
||||
Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_,
|
||||
VideoCore::TextureCache& texture_cache_, AmdGpu::Liverpool* liverpool_)
|
||||
: instance{instance_}, scheduler{scheduler_}, texture_cache{texture_cache_},
|
||||
liverpool{liverpool_}, pipeline_cache{instance, scheduler, liverpool},
|
||||
vertex_index_buffer{instance, scheduler, VertexIndexFlags, 64_MB} {
|
||||
liverpool->BindRasterizer(this);
|
||||
}
|
||||
|
||||
Rasterizer::~Rasterizer() = default;
|
||||
|
||||
void Rasterizer::DrawIndex() {
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
auto& regs = liverpool->regs;
|
||||
|
||||
static bool first_time = true;
|
||||
if (first_time) {
|
||||
first_time = false;
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateDynamicState();
|
||||
|
||||
pipeline_cache.BindPipeline();
|
||||
|
||||
const u32 pitch = regs.color_buffers[0].Pitch();
|
||||
const u32 height = regs.color_buffers[0].Height();
|
||||
const u32 tile_max = regs.color_buffers[0].slice.tile_max;
|
||||
auto& image_view = texture_cache.RenderTarget(regs.color_buffers[0].Address(), pitch);
|
||||
|
||||
const vk::RenderingAttachmentInfo color_info = {
|
||||
.imageView = *image_view.image_view,
|
||||
.imageLayout = vk::ImageLayout::eGeneral,
|
||||
.loadOp = vk::AttachmentLoadOp::eLoad,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
};
|
||||
|
||||
// TODO: Don't restart renderpass every draw
|
||||
const vk::RenderingInfo rendering_info = {
|
||||
.renderArea = {.offset = {0, 0}, .extent = {1920, 1080}},
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachments = &color_info,
|
||||
};
|
||||
|
||||
cmdbuf.beginRendering(rendering_info);
|
||||
cmdbuf.bindIndexBuffer(vertex_index_buffer.Handle(), 0, vk::IndexType::eUint32);
|
||||
cmdbuf.bindVertexBuffers(0, vertex_index_buffer.Handle(), vk::DeviceSize(0));
|
||||
cmdbuf.draw(regs.num_indices, regs.num_instances.NumInstances(), 0, 0);
|
||||
cmdbuf.endRendering();
|
||||
}
|
||||
|
||||
void Rasterizer::UpdateDynamicState() {
|
||||
UpdateViewportScissorState();
|
||||
}
|
||||
|
||||
void Rasterizer::UpdateViewportScissorState() {
|
||||
auto& regs = liverpool->regs;
|
||||
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
const vk::Viewport viewport{
|
||||
.x = regs.viewports[0].xoffset - regs.viewports[0].xscale,
|
||||
.y = regs.viewports[0].yoffset - regs.viewports[0].yscale,
|
||||
.width = regs.viewports[0].xscale * 2.0f,
|
||||
.height = regs.viewports[0].yscale * 2.0f,
|
||||
.minDepth = regs.viewports[0].zoffset - regs.viewports[0].zscale,
|
||||
.maxDepth = regs.viewports[0].zscale + regs.viewports[0].zoffset,
|
||||
};
|
||||
const vk::Rect2D scissor{
|
||||
.offset = {regs.screen_scissor.top_left_x, regs.screen_scissor.top_left_y},
|
||||
.extent = {regs.screen_scissor.GetWidth(), regs.screen_scissor.GetHeight()},
|
||||
};
|
||||
cmdbuf.setViewport(0, viewport);
|
||||
cmdbuf.setScissor(0, scissor);
|
||||
}
|
||||
|
||||
void Rasterizer::UpdateDepthStencilState() {
|
||||
auto& depth = liverpool->regs.depth_control;
|
||||
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
cmdbuf.setDepthBoundsTestEnable(depth.depth_bounds_enable);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
51
src/video_core/renderer_vulkan/vk_rasterizer.h
Normal file
51
src/video_core/renderer_vulkan/vk_rasterizer.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
struct Liverpool;
|
||||
}
|
||||
|
||||
namespace VideoCore {
|
||||
class TextureCache;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
class Scheduler;
|
||||
class GraphicsPipeline;
|
||||
|
||||
class Rasterizer {
|
||||
public:
|
||||
explicit Rasterizer(const Instance& instance, Scheduler& scheduler,
|
||||
VideoCore::TextureCache& texture_cache, AmdGpu::Liverpool* liverpool);
|
||||
~Rasterizer();
|
||||
|
||||
/// Performs a draw call with an index buffer.
|
||||
void DrawIndex();
|
||||
|
||||
/// Updates graphics state that is not part of the bound pipeline.
|
||||
void UpdateDynamicState();
|
||||
|
||||
private:
|
||||
/// Updates viewport and scissor from liverpool registers.
|
||||
void UpdateViewportScissorState();
|
||||
|
||||
/// Updates depth and stencil pipeline state from liverpool registers.
|
||||
void UpdateDepthStencilState();
|
||||
|
||||
private:
|
||||
const Instance& instance;
|
||||
Scheduler& scheduler;
|
||||
VideoCore::TextureCache& texture_cache;
|
||||
AmdGpu::Liverpool* liverpool;
|
||||
PipelineCache pipeline_cache;
|
||||
StreamBuffer vertex_index_buffer;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
|
@ -38,7 +38,7 @@ using Libraries::VideoOut::TilingMode;
|
|||
if (false /*&& IsDepthStencilFormat(format)*/) {
|
||||
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
} else {
|
||||
// usage |= vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage;
|
||||
usage |= vk::ImageUsageFlagBits::eColorAttachment;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "common/types.h"
|
||||
#include "core/libraries/videoout/buffer.h"
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
#include "video_core/texture_cache/image_view.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
|
||||
namespace Vulkan {
|
||||
|
@ -41,23 +42,6 @@ struct ImageInfo {
|
|||
u32 guest_size_bytes = 0;
|
||||
};
|
||||
|
||||
struct Handle {
|
||||
VmaAllocation allocation;
|
||||
VkImage image;
|
||||
|
||||
Handle() = default;
|
||||
|
||||
Handle(Handle&& other)
|
||||
: image{std::exchange(other.image, VK_NULL_HANDLE)},
|
||||
allocation{std::exchange(other.allocation, VK_NULL_HANDLE)} {}
|
||||
|
||||
Handle& operator=(Handle&& other) {
|
||||
image = std::exchange(other.image, VK_NULL_HANDLE);
|
||||
allocation = std::exchange(other.allocation, VK_NULL_HANDLE);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct UniqueImage {
|
||||
explicit UniqueImage(vk::Device device, VmaAllocator allocator);
|
||||
~UniqueImage();
|
||||
|
@ -100,6 +84,14 @@ struct Image {
|
|||
return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end;
|
||||
}
|
||||
|
||||
ImageViewId FindView(const ImageViewInfo& info) const {
|
||||
const auto it = std::ranges::find(image_view_infos, info);
|
||||
if (it == image_view_infos.end()) {
|
||||
return {};
|
||||
}
|
||||
return image_view_ids[std::distance(it, image_view_infos.begin())];
|
||||
}
|
||||
|
||||
void Transit(vk::ImageLayout dst_layout, vk::Flags<vk::AccessFlagBits> dst_mask);
|
||||
|
||||
const Vulkan::Instance* instance;
|
||||
|
@ -110,6 +102,8 @@ struct Image {
|
|||
ImageFlagBits flags = ImageFlagBits::CpuModified;
|
||||
VAddr cpu_addr = 0;
|
||||
VAddr cpu_addr_end = 0;
|
||||
std::vector<ImageViewInfo> image_view_infos;
|
||||
std::vector<ImageViewId> image_view_ids;
|
||||
|
||||
// Resource state tracking
|
||||
vk::Flags<vk::PipelineStageFlagBits> pl_stage = vk::PipelineStageFlagBits::eAllCommands;
|
||||
|
|
|
@ -13,20 +13,9 @@ class Scheduler;
|
|||
|
||||
namespace VideoCore {
|
||||
|
||||
enum class ImageViewType : u32 {
|
||||
e1D,
|
||||
e2D,
|
||||
Cube,
|
||||
e3D,
|
||||
e1DArray,
|
||||
e2DArray,
|
||||
CubeArray,
|
||||
Buffer,
|
||||
};
|
||||
|
||||
struct ImageViewInfo {
|
||||
vk::ImageViewType type{};
|
||||
vk::Format format{};
|
||||
vk::ImageViewType type = vk::ImageViewType::e2D;
|
||||
vk::Format format = vk::Format::eR8G8B8A8Unorm;
|
||||
SubresourceRange range;
|
||||
vk::ComponentMapping mapping{};
|
||||
|
||||
|
|
|
@ -31,10 +31,6 @@ class SlotVector {
|
|||
constexpr static std::size_t InitialCapacity = 1024;
|
||||
|
||||
public:
|
||||
SlotVector() {
|
||||
Reserve(InitialCapacity);
|
||||
}
|
||||
|
||||
~SlotVector() noexcept {
|
||||
std::size_t index = 0;
|
||||
for (u64 bits : stored_bitset) {
|
||||
|
|
|
@ -83,6 +83,15 @@ TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler&
|
|||
ASSERT_MSG(veh_handle, "Failed to register an exception handler");
|
||||
#endif
|
||||
g_texture_cache = this;
|
||||
|
||||
ImageInfo info;
|
||||
info.pixel_format = vk::Format::eR8G8B8A8Unorm;
|
||||
info.type = vk::ImageType::e2D;
|
||||
const ImageId null_id = slot_images.insert(instance, scheduler, info, 0);
|
||||
ASSERT(null_id.index == 0);
|
||||
|
||||
ImageViewInfo view_info;
|
||||
void(slot_image_views.insert(instance, scheduler, view_info, slot_images[null_id].image));
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache() {
|
||||
|
@ -128,6 +137,29 @@ Image& TextureCache::FindImage(const ImageInfo& info, VAddr cpu_address) {
|
|||
return image;
|
||||
}
|
||||
|
||||
ImageView& TextureCache::RenderTarget(VAddr cpu_address, u32 pitch) {
|
||||
boost::container::small_vector<ImageId, 2> image_ids;
|
||||
ForEachImageInRegion(cpu_address, pitch * 4, [&](ImageId image_id, Image& image) {
|
||||
if (image.cpu_addr == cpu_address) {
|
||||
image_ids.push_back(image_id);
|
||||
}
|
||||
});
|
||||
|
||||
ASSERT_MSG(image_ids.size() <= 1, "Overlapping framebuffers not allowed!");
|
||||
auto* image = &slot_images[image_ids.empty() ? ImageId{0} : image_ids.back()];
|
||||
|
||||
ImageViewInfo info;
|
||||
info.format = vk::Format::eB8G8R8A8Srgb;
|
||||
if (const ImageViewId view_id = image->FindView(info); view_id) {
|
||||
return slot_image_views[view_id];
|
||||
}
|
||||
|
||||
const ImageViewId view_id = slot_image_views.insert(instance, scheduler, info, image->image);
|
||||
image->image_view_infos.emplace_back(info);
|
||||
image->image_view_ids.emplace_back(view_id);
|
||||
return slot_image_views[view_id];
|
||||
}
|
||||
|
||||
void TextureCache::RefreshImage(Image& image) {
|
||||
// Mark image as validated.
|
||||
image.flags &= ~ImageFlagBits::CpuModified;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
|
||||
#include "video_core/texture_cache/image.h"
|
||||
#include "video_core/texture_cache/image_view.h"
|
||||
#include "video_core/texture_cache/slot_vector.h"
|
||||
|
||||
namespace Core::Libraries::VideoOut {
|
||||
|
@ -35,6 +36,9 @@ public:
|
|||
/// Retrieves the image handle of the image with the provided attributes and address.
|
||||
Image& FindImage(const ImageInfo& info, VAddr cpu_address);
|
||||
|
||||
/// Retrieves the render target with specified properties
|
||||
ImageView& RenderTarget(VAddr cpu_address, u32 pitch);
|
||||
|
||||
/// Reuploads image contents.
|
||||
void RefreshImage(Image& image);
|
||||
|
||||
|
@ -116,6 +120,7 @@ private:
|
|||
Vulkan::Scheduler& scheduler;
|
||||
Vulkan::StreamBuffer staging;
|
||||
SlotVector<Image> slot_images;
|
||||
SlotVector<ImageView> slot_image_views;
|
||||
tsl::robin_pg_map<u64, std::vector<ImageId>> page_table;
|
||||
boost::icl::interval_map<VAddr, s32> cached_pages;
|
||||
#ifdef _WIN64
|
||||
|
|
|
@ -47,16 +47,22 @@ struct SubresourceLayers {
|
|||
struct SubresourceBase {
|
||||
s32 level = 0;
|
||||
s32 layer = 0;
|
||||
|
||||
auto operator<=>(const SubresourceBase&) const = default;
|
||||
};
|
||||
|
||||
struct SubresourceExtent {
|
||||
s32 levels = 1;
|
||||
s32 layers = 1;
|
||||
|
||||
auto operator<=>(const SubresourceExtent&) const = default;
|
||||
};
|
||||
|
||||
struct SubresourceRange {
|
||||
SubresourceBase base;
|
||||
SubresourceExtent extent;
|
||||
|
||||
auto operator<=>(const SubresourceRange&) const = default;
|
||||
};
|
||||
|
||||
struct ImageCopy {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue