video: Import new shader recompiler + display a triangle (#142)

This commit is contained in:
TheTurtle 2024-05-22 01:35:12 +03:00 committed by GitHub
parent 8cf64a33b2
commit 8730968385
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
103 changed files with 17793 additions and 729 deletions

View file

@ -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: {

View file

@ -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{};

View 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

View 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

View file

@ -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();

View file

@ -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;

View 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

View 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

View file

@ -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,

View 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

View 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

View 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

View 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

View file

@ -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;
}

View file

@ -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;

View file

@ -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{};

View file

@ -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) {

View file

@ -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;

View file

@ -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

View file

@ -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 {