video_core: Add image support

This commit is contained in:
raphaelthegreat 2024-05-27 01:07:46 +03:00
parent 729e166cd3
commit d59b102b6f
48 changed files with 1264 additions and 259 deletions

View file

@ -44,6 +44,22 @@ using Libraries::VideoOut::TilingMode;
return usage;
}
[[nodiscard]] vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept {
switch (type) {
case AmdGpu::ImageType::Color1D:
return vk::ImageType::e1D;
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Color1DArray:
case AmdGpu::ImageType::Cube:
return vk::ImageType::e2D;
case AmdGpu::ImageType::Color3D:
case AmdGpu::ImageType::Color2DArray:
return vk::ImageType::e3D;
default:
UNREACHABLE();
}
}
ImageInfo::ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group) noexcept {
const auto& attrib = group.attrib;
is_tiled = attrib.tiling_mode == TilingMode::Tile;
@ -72,10 +88,23 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer) noexcept {
type = vk::ImageType::e2D;
size.width = buffer.Pitch();
size.height = buffer.Height();
size.depth = 1;
pitch = size.width;
guest_size_bytes = buffer.slice.tile_max * (buffer.view.slice_max + 1);
}
ImageInfo::ImageInfo(const AmdGpu::Image& image) noexcept {
is_tiled = false;
pixel_format = LiverpoolToVK::SurfaceFormat(image.GetDataFmt(), image.GetNumberFmt());
type = ConvertImageType(image.type);
size.width = image.width + 1;
size.height = image.height + 1;
size.depth = 1;
// TODO: Derive this properly from tiling params
pitch = size.width;
guest_size_bytes = size.width * size.height * 4;
}
UniqueImage::UniqueImage(vk::Device device_, VmaAllocator allocator_)
: device{device_}, allocator{allocator_} {}
@ -109,7 +138,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
: instance{&instance_}, scheduler{&scheduler_}, info{info_},
image{instance->GetDevice(), instance->GetAllocator()}, cpu_addr{cpu_addr},
cpu_addr_end{cpu_addr + info.guest_size_bytes} {
vk::ImageCreateFlags flags{};
vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat};
if (info.type == vk::ImageType::e2D && info.resources.layers >= 6 &&
info.size.width == info.size.height) {
flags |= vk::ImageCreateFlagBits::eCubeCompatible;

View file

@ -7,6 +7,7 @@
#include "common/types.h"
#include "core/libraries/videoout/buffer.h"
#include "video_core/amdgpu/liverpool.h"
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_common.h"
#include "video_core/texture_cache/image_view.h"
#include "video_core/texture_cache/types.h"
@ -34,6 +35,7 @@ struct ImageInfo {
ImageInfo() = default;
explicit ImageInfo(const Libraries::VideoOut::BufferAttributeGroup& group) noexcept;
explicit ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer) noexcept;
explicit ImageInfo(const AmdGpu::Image& image) noexcept;
bool is_tiled = false;
vk::Format pixel_format = vk::Format::eUndefined;

View file

@ -1,11 +1,62 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/texture_cache/image_view.h"
namespace VideoCore {
vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) {
switch (type) {
case AmdGpu::ImageType::Color1D:
return vk::ImageViewType::e1D;
case AmdGpu::ImageType::Color1DArray:
return vk::ImageViewType::e1DArray;
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Cube:
return vk::ImageViewType::e2D;
case AmdGpu::ImageType::Color2DArray:
return vk::ImageViewType::e2DArray;
case AmdGpu::ImageType::Color3D:
return vk::ImageViewType::e3D;
default:
UNREACHABLE();
}
}
vk::ComponentSwizzle ConvertComponentSwizzle(u32 dst_sel) {
switch (dst_sel) {
case 0:
return vk::ComponentSwizzle::eZero;
case 1:
return vk::ComponentSwizzle::eOne;
case 4:
return vk::ComponentSwizzle::eR;
case 5:
return vk::ComponentSwizzle::eG;
case 6:
return vk::ComponentSwizzle::eB;
case 7:
return vk::ComponentSwizzle::eA;
default:
UNREACHABLE();
}
}
ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image) noexcept {
type = ConvertImageViewType(image.type);
format = Vulkan::LiverpoolToVK::SurfaceFormat(image.GetDataFmt(), image.GetNumberFmt());
range.base.level = image.base_level;
range.base.layer = 0;
range.extent.levels = 1;
range.extent.layers = 1;
mapping.r = ConvertComponentSwizzle(image.dst_sel_x);
mapping.g = ConvertComponentSwizzle(image.dst_sel_y);
mapping.b = ConvertComponentSwizzle(image.dst_sel_z);
mapping.a = ConvertComponentSwizzle(image.dst_sel_w);
}
ImageView::ImageView(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler,
const ImageViewInfo& info_, vk::Image image)
: info{info_} {

View file

@ -3,6 +3,7 @@
#pragma once
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_common.h"
#include "video_core/texture_cache/types.h"
@ -14,6 +15,9 @@ class Scheduler;
namespace VideoCore {
struct ImageViewInfo {
explicit ImageViewInfo() = default;
explicit ImageViewInfo(const AmdGpu::Image& image) noexcept;
vk::ImageViewType type = vk::ImageViewType::e2D;
vk::Format format = vk::Format::eR8G8B8A8Unorm;
SubresourceRange range;

View file

@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/texture_cache/sampler.h"
namespace VideoCore {
Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler) {
using namespace Vulkan;
const vk::SamplerCreateInfo sampler_ci = {
.magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter),
.minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter),
.mipmapMode = LiverpoolToVK::MipFilter(sampler.mip_filter),
.addressModeU = LiverpoolToVK::ClampMode(sampler.clamp_x),
.addressModeV = LiverpoolToVK::ClampMode(sampler.clamp_y),
.addressModeW = LiverpoolToVK::ClampMode(sampler.clamp_z),
.mipLodBias = sampler.LodBias(),
.compareEnable = sampler.depth_compare_func != AmdGpu::DepthCompare::Never,
.compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func),
.minLod = sampler.MinLod(),
.maxLod = sampler.MaxLod(),
.borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type),
.unnormalizedCoordinates = bool(sampler.force_unnormalized),
};
handle = instance.GetDevice().createSamplerUnique(sampler_ci);
}
Sampler::~Sampler() = default;
} // namespace VideoCore

View file

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "video_core/amdgpu/resource.h"
#include "video_core/renderer_vulkan/vk_common.h"
namespace Vulkan {
class Instance;
}
namespace VideoCore {
class Sampler {
public:
explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler);
~Sampler();
Sampler(const Sampler&) = delete;
Sampler& operator=(const Sampler&) = delete;
Sampler(Sampler&&) = default;
Sampler& operator=(Sampler&&) = default;
vk::Sampler Handle() const noexcept {
return *handle;
}
private:
vk::UniqueSampler handle;
};
} // namespace VideoCore

View file

@ -1,10 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <limits>
#include <xxhash.h>
#include "common/assert.h"
#include "common/config.h"
#include "core/libraries/videoout/buffer.h"
#include "core/virtual_memory.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/texture_cache/texture_cache.h"
@ -137,6 +136,21 @@ Image& TextureCache::FindImage(const ImageInfo& info, VAddr cpu_address) {
return image;
}
ImageView& TextureCache::FindImageView(const AmdGpu::Image& desc) {
Image& image = FindImage(ImageInfo{desc}, desc.Address());
const ImageViewInfo view_info{desc};
if (const ImageViewId view_id = image.FindView(view_info); view_id) {
return slot_image_views[view_id];
}
const ImageViewId view_id =
slot_image_views.insert(instance, scheduler, view_info, image.image);
image.image_view_infos.emplace_back(view_info);
image.image_view_ids.emplace_back(view_id);
return slot_image_views[view_id];
}
ImageView& TextureCache::RenderTarget(const AmdGpu::Liverpool::ColorBuffer& buffer) {
const ImageInfo info{buffer};
auto& image = FindImage(info, buffer.Address());
@ -159,7 +173,7 @@ void TextureCache::RefreshImage(Image& image) {
image.flags &= ~ImageFlagBits::CpuModified;
// Upload data to the staging buffer.
const auto [data, offset, _] = staging.Map(image.info.guest_size_bytes, 0);
const auto [data, offset, _] = staging.Map(image.info.guest_size_bytes, 4);
const u8* image_data = reinterpret_cast<const u8*>(image.cpu_addr);
if (image.info.is_tiled) {
ConvertTileToLinear(data, image_data, image.info.size.width, image.info.size.height,
@ -202,6 +216,12 @@ void TextureCache::RefreshImage(Image& image) {
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferRead);
}
vk::Sampler TextureCache::GetSampler(const AmdGpu::Sampler& sampler) {
const u64 hash = XXH3_64bits(&sampler, sizeof(sampler));
const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler);
return it->second.Handle();
}
void TextureCache::RegisterImage(ImageId image_id) {
Image& image = slot_images[image_id];
ASSERT_MSG(False(image.flags & ImageFlagBits::Registered),

View file

@ -7,9 +7,11 @@
#include <boost/icl/interval_map.hpp>
#include <tsl/robin_map.h>
#include "video_core/amdgpu/resource.h"
#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/sampler.h"
#include "video_core/texture_cache/slot_vector.h"
namespace Core::Libraries::VideoOut {
@ -36,12 +38,18 @@ public:
/// Retrieves the image handle of the image with the provided attributes and address.
Image& FindImage(const ImageInfo& info, VAddr cpu_address);
/// Retrieves an image view with the properties of the specified image descriptor.
ImageView& FindImageView(const AmdGpu::Image& image);
/// Retrieves the render target with specified properties
ImageView& RenderTarget(const AmdGpu::Liverpool::ColorBuffer& buffer);
/// Reuploads image contents.
void RefreshImage(Image& image);
/// Retrieves the sampler that matches the provided S# descriptor.
vk::Sampler GetSampler(const AmdGpu::Sampler& sampler);
private:
/// Iterate over all page indices in a range
template <typename Func>
@ -121,6 +129,7 @@ private:
Vulkan::StreamBuffer staging;
SlotVector<Image> slot_images;
SlotVector<ImageView> slot_image_views;
tsl::robin_map<u64, Sampler> samplers;
tsl::robin_pg_map<u64, std::vector<ImageId>> page_table;
boost::icl::interval_map<VAddr, s32> cached_pages;
#ifdef _WIN64

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/assert.h"
#include "video_core/texture_cache/tile_manager.h"
namespace VideoCore {