mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-01-05 16:20:22 +01:00
475 lines
15 KiB
C++
475 lines
15 KiB
C++
#include "stdafx.h"
|
|
#include "VKHelpers.h"
|
|
#include "../GCM.h"
|
|
#include "../RSXThread.h"
|
|
#include "../RSXTexture.h"
|
|
#include "../rsx_utils.h"
|
|
#include "../Common/TextureUtils.h"
|
|
#include "VKFormats.h"
|
|
|
|
namespace vk
|
|
{
|
|
VkComponentMapping default_component_map()
|
|
{
|
|
VkComponentMapping result = {};
|
|
result.a = VK_COMPONENT_SWIZZLE_A;
|
|
result.r = VK_COMPONENT_SWIZZLE_R;
|
|
result.g = VK_COMPONENT_SWIZZLE_G;
|
|
result.b = VK_COMPONENT_SWIZZLE_B;
|
|
|
|
return result;
|
|
}
|
|
|
|
VkImageSubresource default_image_subresource()
|
|
{
|
|
VkImageSubresource subres = {};
|
|
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
subres.mipLevel = 0;
|
|
subres.arrayLayer = 0;
|
|
|
|
return subres;
|
|
}
|
|
|
|
VkImageSubresourceRange default_image_subresource_range()
|
|
{
|
|
VkImageSubresourceRange subres = {};
|
|
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
subres.baseArrayLayer = 0;
|
|
subres.baseMipLevel = 0;
|
|
subres.layerCount = 1;
|
|
subres.levelCount = 1;
|
|
|
|
return subres;
|
|
}
|
|
|
|
void copy_image(VkCommandBuffer cmd, VkImage &src, VkImage &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, u32 width, u32 height, u32 mipmaps, VkImageAspectFlagBits aspect)
|
|
{
|
|
VkImageSubresourceLayers a_src = {}, a_dst = {};
|
|
a_src.aspectMask = aspect;
|
|
a_src.baseArrayLayer = 0;
|
|
a_src.layerCount = 1;
|
|
a_src.mipLevel = 0;
|
|
|
|
a_dst = a_src;
|
|
|
|
VkImageCopy rgn = {};
|
|
rgn.extent.depth = 1;
|
|
rgn.extent.width = width;
|
|
rgn.extent.height = height;
|
|
rgn.dstOffset = { 0, 0, 0 };
|
|
rgn.srcOffset = { 0, 0, 0 };
|
|
rgn.srcSubresource = a_src;
|
|
rgn.dstSubresource = a_dst;
|
|
|
|
if (srcLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
|
|
change_image_layout(cmd, src, srcLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, aspect);
|
|
|
|
if (dstLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
change_image_layout(cmd, dst, dstLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect);
|
|
|
|
for (u32 mip_level = 0; mip_level < mipmaps; ++mip_level)
|
|
{
|
|
vkCmdCopyImage(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &rgn);
|
|
|
|
rgn.srcSubresource.mipLevel++;
|
|
rgn.dstSubresource.mipLevel++;
|
|
}
|
|
|
|
if (srcLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
|
|
change_image_layout(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcLayout, aspect);
|
|
|
|
if (dstLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
change_image_layout(cmd, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dstLayout, aspect);
|
|
}
|
|
|
|
void copy_scaled_image(VkCommandBuffer cmd, VkImage & src, VkImage & dst, VkImageLayout srcLayout, VkImageLayout dstLayout, u32 src_width, u32 src_height, u32 dst_width, u32 dst_height, u32 mipmaps, VkImageAspectFlagBits aspect)
|
|
{
|
|
VkImageSubresourceLayers a_src = {}, a_dst = {};
|
|
a_src.aspectMask = aspect;
|
|
a_src.baseArrayLayer = 0;
|
|
a_src.layerCount = 1;
|
|
a_src.mipLevel = 0;
|
|
|
|
a_dst = a_src;
|
|
|
|
VkImageBlit rgn = {};
|
|
rgn.srcOffsets[0] = { 0, 0, 0 };
|
|
rgn.srcOffsets[1] = { (int32_t)src_width, (int32_t)src_height, 1 };
|
|
rgn.dstOffsets[0] = { 0, 0, 0 };
|
|
rgn.dstOffsets[1] = { (int32_t)dst_width, (int32_t)dst_height, 1 };
|
|
rgn.dstSubresource = a_dst;
|
|
rgn.srcSubresource = a_src;
|
|
|
|
if (srcLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
|
|
change_image_layout(cmd, src, srcLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, aspect);
|
|
|
|
if (dstLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
change_image_layout(cmd, dst, dstLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect);
|
|
|
|
for (u32 mip_level = 0; mip_level < mipmaps; ++mip_level)
|
|
{
|
|
vkCmdBlitImage(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &rgn, VK_FILTER_LINEAR);
|
|
|
|
rgn.srcSubresource.mipLevel++;
|
|
rgn.dstSubresource.mipLevel++;
|
|
}
|
|
|
|
if (srcLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
|
|
change_image_layout(cmd, src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, srcLayout, aspect);
|
|
|
|
if (dstLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
change_image_layout(cmd, dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dstLayout, aspect);
|
|
}
|
|
|
|
void copy_texture(VkCommandBuffer cmd, texture &src, texture &dst, VkImageLayout srcLayout, VkImageLayout dstLayout, u32 width, u32 height, u32 mipmaps, VkImageAspectFlagBits aspect)
|
|
{
|
|
VkImage isrc = (VkImage)src;
|
|
VkImage idst = (VkImage)dst;
|
|
|
|
copy_image(cmd, isrc, idst, srcLayout, dstLayout, width, height, mipmaps, aspect);
|
|
}
|
|
|
|
texture::texture(vk::swap_chain_image &img)
|
|
{
|
|
m_image_contents = img;
|
|
m_view = img;
|
|
|
|
//We did not create this object, do not allow internal modification!
|
|
owner = nullptr;
|
|
}
|
|
|
|
void texture::create(vk::render_device &device, VkFormat format, VkImageType image_type, VkImageViewType view_type, VkImageCreateFlags image_flags, VkImageUsageFlags usage, VkImageTiling tiling, u32 width, u32 height, u32 mipmaps, bool gpu_only, VkComponentMapping swizzle)
|
|
{
|
|
owner = &device;
|
|
|
|
//First create the image
|
|
VkImageCreateInfo image_info = {};
|
|
|
|
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
image_info.imageType = image_type;
|
|
image_info.format = format;
|
|
image_info.extent = { width, height, 1 };
|
|
image_info.mipLevels = mipmaps;
|
|
image_info.arrayLayers = (image_flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)? 6: 1;
|
|
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
image_info.tiling = tiling;
|
|
image_info.usage = usage;
|
|
image_info.flags = image_flags;
|
|
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
CHECK_RESULT(vkCreateImage(device, &image_info, nullptr, &m_image_contents));
|
|
|
|
vkGetImageMemoryRequirements(device, m_image_contents, &m_memory_layout);
|
|
vram_allocation.allocate_from_pool(device, m_memory_layout.size, !gpu_only, m_memory_layout.memoryTypeBits);
|
|
|
|
CHECK_RESULT(vkBindImageMemory(device, m_image_contents, vram_allocation, 0));
|
|
|
|
VkImageViewCreateInfo view_info = {};
|
|
view_info.format = format;
|
|
view_info.image = m_image_contents;
|
|
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
view_info.viewType = view_type;
|
|
view_info.components = swizzle;
|
|
view_info.subresourceRange = default_image_subresource_range();
|
|
|
|
if (usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
|
|
{
|
|
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
m_image_aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
}
|
|
|
|
CHECK_RESULT(vkCreateImageView(device, &view_info, nullptr, &m_view));
|
|
|
|
m_width = width;
|
|
m_height = height;
|
|
m_mipmaps = mipmaps;
|
|
m_internal_format = format;
|
|
m_flags = usage;
|
|
m_view_type = view_type;
|
|
m_usage = usage;
|
|
m_tiling = tiling;
|
|
|
|
ready = true;
|
|
}
|
|
|
|
void texture::create(vk::render_device &device, VkFormat format, VkImageUsageFlags usage, VkImageTiling tiling, u32 width, u32 height, u32 mipmaps, bool gpu_only, VkComponentMapping swizzle)
|
|
{
|
|
create(device, format, VK_IMAGE_TYPE_2D, VK_IMAGE_VIEW_TYPE_2D, 0, usage, tiling, width, height, mipmaps, gpu_only, swizzle);
|
|
}
|
|
|
|
void texture::create(vk::render_device &device, VkFormat format, VkImageUsageFlags usage, u32 width, u32 height, u32 mipmaps, bool gpu_only, VkComponentMapping swizzle)
|
|
{
|
|
VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
|
|
/* The spec mandates checking against all usage bits for support in either linear or optimal tiling modes.
|
|
* Ideally, no assumptions should be made, but for simplification, we'll assume optimal mode suppoorts everything
|
|
*/
|
|
|
|
VkFormatProperties props;
|
|
vkGetPhysicalDeviceFormatProperties(device.gpu(), format, &props);
|
|
|
|
bool linear_is_supported = true;
|
|
|
|
if (!!(usage & VK_IMAGE_USAGE_SAMPLED_BIT))
|
|
{
|
|
if (!(props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
|
|
linear_is_supported = false;
|
|
}
|
|
|
|
if (linear_is_supported && !!(usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT))
|
|
{
|
|
if (!(props.linearTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
|
|
linear_is_supported = false;
|
|
}
|
|
|
|
if (linear_is_supported && !!(usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT))
|
|
{
|
|
if (!(props.linearTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
|
|
linear_is_supported = false;
|
|
}
|
|
|
|
if (linear_is_supported && !!(usage & VK_IMAGE_USAGE_STORAGE_BIT))
|
|
{
|
|
if (!(props.linearTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
|
|
linear_is_supported = false;
|
|
}
|
|
|
|
if (linear_is_supported)
|
|
tiling = VK_IMAGE_TILING_LINEAR;
|
|
else
|
|
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
|
|
create(device, format, usage, tiling, width, height, mipmaps, gpu_only, swizzle);
|
|
}
|
|
|
|
void texture::init(rsx::texture& tex, vk::command_buffer &cmd, bool ignore_checks)
|
|
{
|
|
VkImageViewType best_type = VK_IMAGE_VIEW_TYPE_2D;
|
|
|
|
if (tex.cubemap() && m_view_type != VK_IMAGE_VIEW_TYPE_CUBE)
|
|
{
|
|
vk::render_device &dev = (*owner);
|
|
VkFormat format = m_internal_format;
|
|
VkImageUsageFlags usage = m_usage;
|
|
VkImageTiling tiling = m_tiling;
|
|
|
|
destroy();
|
|
create(dev, format, VK_IMAGE_TYPE_2D, VK_IMAGE_VIEW_TYPE_CUBE, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, usage, tiling, tex.width(), tex.height(), tex.mipmap(), false, default_component_map());
|
|
}
|
|
|
|
if (!tex.cubemap() && tex.depth() > 1 && m_view_type != VK_IMAGE_VIEW_TYPE_3D)
|
|
{
|
|
best_type = VK_IMAGE_VIEW_TYPE_3D;
|
|
|
|
vk::render_device &dev = (*owner);
|
|
VkFormat format = m_internal_format;
|
|
VkImageUsageFlags usage = m_usage;
|
|
VkImageTiling tiling = m_tiling;
|
|
|
|
destroy();
|
|
create(dev, format, VK_IMAGE_TYPE_3D, VK_IMAGE_VIEW_TYPE_3D, 0, usage, tiling, tex.width(), tex.height(), tex.mipmap(), false, default_component_map());
|
|
}
|
|
|
|
VkImageSubresource subres = {};
|
|
subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
subres.mipLevel = 0;
|
|
subres.arrayLayer = 0;
|
|
|
|
u8 *data;
|
|
|
|
VkFormatProperties props;
|
|
vk::physical_device dev = owner->gpu();
|
|
vkGetPhysicalDeviceFormatProperties(dev, m_internal_format, &props);
|
|
|
|
if (ignore_checks || props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
|
|
{
|
|
std::vector<std::pair<u16, VkSubresourceLayout>> layout_alignment(tex.mipmap());
|
|
|
|
for (u32 i = 0; i < tex.mipmap(); ++i)
|
|
{
|
|
layout_alignment[i].first = 4096;
|
|
vkGetImageSubresourceLayout((*owner), m_image_contents, &subres, &layout_alignment[i].second);
|
|
|
|
if (m_view_type == VK_IMAGE_VIEW_TYPE_CUBE)
|
|
layout_alignment[i].second.size *= 6;
|
|
|
|
while (layout_alignment[i].first > 1)
|
|
{
|
|
//Test if is wholly divisible by alignment..
|
|
if (!(layout_alignment[i].second.rowPitch & (layout_alignment[i].first - 1)))
|
|
break;
|
|
|
|
layout_alignment[i].first >>= 1;
|
|
}
|
|
|
|
subres.mipLevel++;
|
|
}
|
|
|
|
if (tex.mipmap() == 1)
|
|
{
|
|
u64 buffer_size = get_placed_texture_storage_size(tex, layout_alignment[0].first, layout_alignment[0].first);
|
|
if (buffer_size != layout_alignment[0].second.size)
|
|
{
|
|
if (buffer_size > layout_alignment[0].second.size)
|
|
{
|
|
LOG_ERROR(RSX, "Layout->pitch = %d, size=%d, height=%d", layout_alignment[0].second.rowPitch, layout_alignment[0].second.size, tex.height());
|
|
LOG_ERROR(RSX, "Computed alignment would have been %d, which yielded a size of %d", layout_alignment[0].first, buffer_size);
|
|
LOG_ERROR(RSX, "Retrying...");
|
|
|
|
//layout_alignment[0].first >>= 1;
|
|
buffer_size = get_placed_texture_storage_size(tex, layout_alignment[0].first, layout_alignment[0].first);
|
|
|
|
if (buffer_size != layout_alignment[0].second.size)
|
|
throw EXCEPTION("Bad texture alignment computation!");
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR(RSX, "Bad texture alignment computation: expected size=%d bytes, computed=%d bytes, alignment=%d, hw pitch=%d",
|
|
layout_alignment[0].second.size, buffer_size, layout_alignment[0].first, layout_alignment[0].second.rowPitch);
|
|
}
|
|
}
|
|
|
|
CHECK_RESULT(vkMapMemory((*owner), vram_allocation, 0, m_memory_layout.size, 0, (void**)&data));
|
|
gsl::span<gsl::byte> mapped{ (gsl::byte*)(data + layout_alignment[0].second.offset), gsl::narrow<int>(layout_alignment[0].second.size) };
|
|
|
|
const std::vector<rsx_subresource_layout> &subresources_layout = get_subresources_layout(tex);
|
|
for (const rsx_subresource_layout &layout : subresources_layout)
|
|
{
|
|
upload_texture_subresource(mapped, layout, tex.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN), !(tex.format() & CELL_GCM_TEXTURE_LN), layout_alignment[0].first);
|
|
}
|
|
vkUnmapMemory((*owner), vram_allocation);
|
|
}
|
|
else
|
|
{
|
|
auto &layer_props = layout_alignment[layout_alignment.size() - 1].second;
|
|
u64 max_size = layer_props.offset + layer_props.size;
|
|
|
|
if (m_memory_layout.size < max_size)
|
|
{
|
|
throw EXCEPTION("Failed to upload texture. Invalid memory block size.");
|
|
}
|
|
|
|
int index= 0;
|
|
std::vector<std::pair<u64, u32>> layout_offset_info(tex.mipmap());
|
|
|
|
for (auto &mip_info : layout_offset_info)
|
|
{
|
|
auto &alignment = layout_alignment[index].first;
|
|
auto &layout = layout_alignment[index++].second;
|
|
|
|
mip_info = std::make_pair(layout.offset, (u32)layout.rowPitch);
|
|
}
|
|
|
|
CHECK_RESULT(vkMapMemory((*owner), vram_allocation, 0, m_memory_layout.size, 0, (void**)&data));
|
|
gsl::span<gsl::byte> mapped{ (gsl::byte*)(data), gsl::narrow<int>(m_memory_layout.size) };
|
|
|
|
const std::vector<rsx_subresource_layout> &subresources_layout = get_subresources_layout(tex);
|
|
size_t idx = 0;
|
|
for (const rsx_subresource_layout &layout : subresources_layout)
|
|
{
|
|
const auto &dst_layout = layout_offset_info[idx++];
|
|
upload_texture_subresource(mapped.subspan(dst_layout.first), layout, tex.format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN), !(tex.format() & CELL_GCM_TEXTURE_LN), dst_layout.second);
|
|
}
|
|
vkUnmapMemory((*owner), vram_allocation);
|
|
}
|
|
}
|
|
else if (!ignore_checks)
|
|
{
|
|
if (!staging_texture)
|
|
{
|
|
staging_texture = new texture();
|
|
staging_texture->create((*owner), m_internal_format, VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_TILING_LINEAR, m_width, m_height, tex.mipmap(), false, default_component_map());
|
|
}
|
|
|
|
staging_texture->init(tex, cmd, true);
|
|
staging_texture->change_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
|
|
ready = false;
|
|
}
|
|
}
|
|
|
|
void texture::flush(vk::command_buffer &cmd)
|
|
{
|
|
if (!ready)
|
|
{
|
|
vk::copy_texture(cmd, *staging_texture, *this, staging_texture->get_layout(), m_layout, m_width, m_height, m_mipmaps, m_image_aspect);
|
|
ready = true;
|
|
}
|
|
}
|
|
|
|
void texture::init_debug()
|
|
{
|
|
void *data;
|
|
CHECK_RESULT(vkMapMemory((*owner), vram_allocation, 0, m_memory_layout.size, 0, (void**)&data));
|
|
|
|
memset(data, 0xFF, m_memory_layout.size);
|
|
vkUnmapMemory((*owner), vram_allocation);
|
|
}
|
|
|
|
void texture::change_layout(vk::command_buffer &cmd, VkImageLayout new_layout)
|
|
{
|
|
if (m_layout == new_layout) return;
|
|
|
|
vk::change_image_layout(cmd, m_image_contents, m_layout, new_layout, m_image_aspect);
|
|
m_layout = new_layout;
|
|
}
|
|
|
|
VkImageLayout texture::get_layout()
|
|
{
|
|
return m_layout;
|
|
}
|
|
|
|
const u32 texture::width()
|
|
{
|
|
return m_width;
|
|
}
|
|
|
|
const u32 texture::height()
|
|
{
|
|
return m_height;
|
|
}
|
|
|
|
const u16 texture::mipmaps()
|
|
{
|
|
return m_mipmaps;
|
|
}
|
|
|
|
void texture::destroy()
|
|
{
|
|
if (!owner) return;
|
|
|
|
//Destroy all objects managed by this object
|
|
vkDestroyImageView((*owner), m_view, nullptr);
|
|
vkDestroyImage((*owner), m_image_contents, nullptr);
|
|
|
|
vram_allocation.destroy();
|
|
|
|
owner = nullptr;
|
|
m_view = nullptr;
|
|
m_image_contents = nullptr;
|
|
|
|
if (staging_texture)
|
|
{
|
|
staging_texture->destroy();
|
|
delete staging_texture;
|
|
staging_texture = nullptr;
|
|
}
|
|
}
|
|
|
|
const VkFormat texture::get_format()
|
|
{
|
|
return m_internal_format;
|
|
}
|
|
|
|
texture::operator VkImage()
|
|
{
|
|
return m_image_contents;
|
|
}
|
|
|
|
texture::operator VkImageView()
|
|
{
|
|
return m_view;
|
|
}
|
|
|
|
}
|