renderer_vulkan: Improve texture interface and add framebuffer support

This commit is contained in:
GPUCode
2022-04-28 19:22:32 +03:00
parent 84f97fc77e
commit 98332af610
5 changed files with 108 additions and 60 deletions

View File

@@ -21,10 +21,15 @@ public:
/// Create a generic Vulkan buffer object
void Create(uint32_t size, vk::MemoryPropertyFlags properties, vk::BufferUsageFlags usage, vk::Format view_format = vk::Format::eUndefined);
/// Global utility functions used by other objects
static uint32_t FindMemoryType(uint32_t type_filter, vk::MemoryPropertyFlags properties);
static void CopyBuffer(VKBuffer& src_buffer, VKBuffer& dst_buffer, const vk::BufferCopy& region);
public:
/// Return a pointer to the mapped memory if the buffer is host mapped
u8* GetHostPointer() { return reinterpret_cast<u8*>(memory); }
vk::Buffer& GetBuffer() { return buffer.get(); }
private:
void* memory = nullptr;
vk::UniqueBuffer buffer;
vk::UniqueDeviceMemory buffer_memory;

View File

@@ -54,9 +54,6 @@ bool VKResourceCache::Initialize()
vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts);
pipeline_layout = g_vk_instace->GetDevice().createPipelineLayoutUnique(layout_info);
if (!CreateStaticSamplers())
return false;
// Create global texture staging buffer
texture_upload_buffer.Create(MAX_TEXTURE_UPLOAD_BUFFER_SIZE,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
@@ -104,12 +101,14 @@ vk::Sampler VKResourceCache::GetSampler(const SamplerInfo& info)
vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Format depth_format,
u32 multisamples, vk::AttachmentLoadOp load_op)
{
// Search the cache if we can reuse an already created renderpass
auto key = std::tie(color_format, depth_format, multisamples, load_op);
auto it = render_pass_cache.find(key);
if (it != render_pass_cache.end()) {
return it->second;
}
// Otherwise create a new one with the parameters provided
vk::SubpassDescription subpass({}, vk::PipelineBindPoint::eGraphics);
std::array<vk::AttachmentDescription, 2> attachments;
std::array<vk::AttachmentReference, 2> references;

View File

@@ -53,6 +53,6 @@ private:
std::string pipeline_cache_filename;
};
extern std::unique_ptr<VKResourceCache> g_object_cache;
extern std::unique_ptr<VKResourceCache> g_vk_res_cache;
} // namespace Vulkan

View File

@@ -6,17 +6,16 @@
#include "common/logging/log.h"
#include "video_core/renderer_vulkan/vk_texture.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_resource_cache.h"
namespace Vulkan {
void VKTexture::Create(const Info& info)
{
auto& device = g_vk_instace->GetDevice();
format = info.format;
width = info.width;
height = info.height;
texture_info = info;
switch (format)
switch (texture_info.format)
{
case vk::Format::eR8G8B8A8Uint:
case vk::Format::eR8G8B8A8Srgb:
@@ -27,24 +26,26 @@ void VKTexture::Create(const Info& info)
channels = 3;
break;
default:
LOG_CRITICAL(Render_Vulkan, "Unknown texture format {}", format);
LOG_CRITICAL(Render_Vulkan, "Unknown texture format {}", texture_info.format);
}
// Create staging memory buffer for pixel transfers
u32 image_size = width * height * channels;
staging.Create(image_size, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
vk::BufferUsageFlagBits::eTransferSrc);
pixels = staging.memory;
// Make sure the texture size doesn't exceed the global staging buffer size
u32 image_size = texture_info.width * texture_info.height * channels;
assert(image_size <= MAX_TEXTURE_UPLOAD_BUFFER_SIZE);
// Create the texture
vk::ImageCreateFlags flags = info.view_type == vk::ImageViewType::eCube ? vk::ImageCreateFlagBits::eCubeCompatible : {};
vk::ImageCreateFlags flags;
if (info.view_type == vk::ImageViewType::eCube) {
flags = vk::ImageCreateFlagBits::eCubeCompatible;
}
vk::ImageCreateInfo image_info
(
flags,
info.type,
format,
{ width, height, 1 }, info.mipmap_levels, info.array_layers,
vk::SampleCountFlagBits::e1,
texture_info.format,
{ texture_info.width, texture_info.height, 1 }, info.mipmap_levels, info.array_layers,
static_cast<vk::SampleCountFlagBits>(info.multisamples),
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled
);
@@ -60,31 +61,9 @@ void VKTexture::Create(const Info& info)
device.bindImageMemory(texture.get(), texture_memory.get(), 0);
// Create texture view
vk::ImageViewCreateInfo view_info({}, texture.get(), info.view_type, format, {},
vk::ImageViewCreateInfo view_info({}, texture.get(), info.view_type, texture_info.format, {},
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
texture_view = device.createImageViewUnique(view_info);
// Create texture sampler
auto properties = g_vk_instace->GetPhysicalDevice().getProperties();
vk::SamplerCreateInfo sampler_info
(
{},
info.sampler_info.mag_filter,
info.sampler_info.min_filter,
info.sampler_info.mipmap_mode,
info.sampler_info.wrapping[0], info.sampler_info.wrapping[1], info.sampler_info.wrapping[2],
{},
true,
properties.limits.maxSamplerAnisotropy,
false,
vk::CompareOp::eAlways,
{},
{},
vk::BorderColor::eIntOpaqueBlack,
false
);
texture_sampler = device.createSamplerUnique(sampler_info);
}
void VKTexture::TransitionLayout(vk::ImageLayout old_layout, vk::ImageLayout new_layout)
@@ -140,7 +119,8 @@ void VKTexture::CopyPixels(std::span<u32> new_pixels)
TransitionLayout(vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
// Copy pixels to staging buffer
std::memcpy(pixels, new_pixels.data(), new_pixels.size() * channels);
std::memcpy(g_vk_res_cache->GetTextureUploadBuffer().GetHostPointer(),
new_pixels.data(), new_pixels.size() * channels);
// Copy the staging buffer to the image
vk::CommandBufferAllocateInfo alloc_info(g_vk_instace->command_pool.get(), vk::CommandBufferLevel::ePrimary, 1);
@@ -148,20 +128,70 @@ void VKTexture::CopyPixels(std::span<u32> new_pixels)
command_buffer.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
vk::BufferImageCopy region(0, 0, 0, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), {0}, {width,height,1});
vk::BufferImageCopy region(0, 0, 0, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), 0,
{ texture_info.width, texture_info.height, 1 });
std::array<vk::BufferImageCopy, 1> regions = { region };
command_buffer.copyBufferToImage(staging.buffer.get(), texture.get(), vk::ImageLayout::eTransferDstOptimal, regions);
auto& staging = g_vk_res_cache->GetTextureUploadBuffer();
command_buffer.copyBufferToImage(staging.GetBuffer(), texture.get(), vk::ImageLayout::eTransferDstOptimal, regions);
command_buffer.end();
vk::SubmitInfo submit_info({}, {}, {}, 1, &command_buffer);
queue.submit(submit_info, nullptr);
queue.waitIdle();
/// NOTE: Remove this when the renderer starts working, otherwise it will be very slow
queue.waitIdle();
device.freeCommandBuffers(g_vk_instace->command_pool.get(), command_buffer);
// Prepare for shader reads
TransitionLayout(vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
}
void VKFramebuffer::Create(const Info& info)
{
// Make sure that either attachment is valid
assert(info.color || info.depth_stencil);
attachments = { info.color, info.depth_stencil };
auto rect = info.color ? info.color->GetRect() : info.depth_stencil->GetRect();
auto color_format = info.color ? info.color->GetFormat() : vk::Format::eUndefined;
auto depth_format = info.depth_stencil ? info.depth_stencil->GetFormat() : vk::Format::eUndefined;
vk::FramebufferCreateInfo framebuffer_info
(
{},
g_vk_res_cache->GetRenderPass(color_format, depth_format, 1, vk::AttachmentLoadOp::eLoad),
{},
rect.extent.width,
rect.extent.height,
1
);
if (info.color && info.depth_stencil) {
std::array<vk::ImageView, 2> views = { info.color->GetView(), info.depth_stencil->GetView() };
framebuffer_info.setAttachments(views);
}
else {
auto valid = info.color ? info.color : info.depth_stencil;
std::array<vk::ImageView, 1> view = { valid->GetView() };
framebuffer_info.setAttachments(view);
}
framebuffer = g_vk_instace->GetDevice().createFramebufferUnique(framebuffer_info);
}
void VKFramebuffer::Prepare()
{
// Transition attachments to their optimal formats for rendering
if (attachments[Attachments::Color]) {
attachments[Attachments::Color]->TransitionLayout(vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal);
}
if (attachments[Attachments::DepthStencil]) {
attachments[Attachments::DepthStencil]->TransitionLayout(vk::ImageLayout::eUndefined,
vk::ImageLayout::eDepthStencilAttachmentOptimal);
}
}
}

View File

@@ -6,6 +6,7 @@
#include <memory>
#include <span>
#include <functional>
#include "video_core/renderer_vulkan/vk_buffer.h"
namespace Vulkan {
@@ -19,6 +20,7 @@ struct SamplerInfo {
/// Vulkan texture object
class VKTexture final : public NonCopyable {
friend class VKFramebuffer;
public:
/// Information for the creation of the target texture
struct Info {
@@ -28,6 +30,7 @@ public:
vk::ImageViewType view_type;
u32 mipmap_levels = 1;
u32 array_layers = 1;
u32 multisamples = 1;
SamplerInfo sampler_info = {};
};
@@ -41,41 +44,52 @@ public:
/// Copies CPU side pixel data to the GPU texture buffer
void CopyPixels(std::span<u32> pixels);
/// Get Vulkan objects
vk::ImageView& GetView() { return texture_view.get(); }
vk::Format GetFormat() const { return texture_info.format; }
vk::Rect2D GetRect() const { return vk::Rect2D({}, { texture_info.width, texture_info.height }); }
u32 GetSamples() const { return texture_info.multisamples; }
private:
/// Used to transition the image to an optimal layout during transfers
void TransitionLayout(vk::ImageLayout old_layout, vk::ImageLayout new_layout);
private:
// Texture buffer
void* pixels = nullptr;
uint32_t width = 0, height = 0, channels = 0;
VKBuffer staging;
// Texture objects
Info texture_info;
vk::UniqueImage texture;
vk::UniqueImageView texture_view;
vk::UniqueDeviceMemory texture_memory;
vk::UniqueSampler texture_sampler;
vk::Format format;
u32 channels;
};
enum Attachments {
Color = 0,
DepthStencil = 1
};
/// Vulkan framebuffer object similar to an FBO in OpenGL
class VKFramebuffer final : public NonCopyable {
public:
struct Info {
VKTexture* color;
VKTexture* depth_stencil;
};
VKFramebuffer() = default;
~VKFramebuffer() = default;
// Create Vulkan framebuffer object
void Create(u32 width, u32 height, u32 layers, u32 samples);
/// Create Vulkan framebuffer object
void Create(const Info& info);
VkRect2D GetRect() const { return VkRect2D{{0, 0}, {width, height}}; }
/// Configure frambuffer for rendering
void Prepare();
vk::Rect2D GetRect() const { return vk::Rect2D({}, { width, height }); }
private:
u32 width, height;
vk::UniqueFramebuffer framebuffer;
vk::RenderPass load_renderpass;
vk::RenderPass discard_renderpass;
vk::RenderPass clear_renderpass;
std::array<VKTexture*, 2> attachments;
};
}