renderer_vulkan: Improve texture interface and add framebuffer support
This commit is contained in:
@@ -21,10 +21,15 @@ public:
|
|||||||
/// Create a generic Vulkan buffer object
|
/// Create a generic Vulkan buffer object
|
||||||
void Create(uint32_t size, vk::MemoryPropertyFlags properties, vk::BufferUsageFlags usage, vk::Format view_format = vk::Format::eUndefined);
|
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 uint32_t FindMemoryType(uint32_t type_filter, vk::MemoryPropertyFlags properties);
|
||||||
static void CopyBuffer(VKBuffer& src_buffer, VKBuffer& dst_buffer, const vk::BufferCopy& region);
|
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;
|
void* memory = nullptr;
|
||||||
vk::UniqueBuffer buffer;
|
vk::UniqueBuffer buffer;
|
||||||
vk::UniqueDeviceMemory buffer_memory;
|
vk::UniqueDeviceMemory buffer_memory;
|
||||||
|
@@ -54,9 +54,6 @@ bool VKResourceCache::Initialize()
|
|||||||
vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts);
|
vk::PipelineLayoutCreateInfo layout_info({}, descriptor_layouts);
|
||||||
pipeline_layout = g_vk_instace->GetDevice().createPipelineLayoutUnique(layout_info);
|
pipeline_layout = g_vk_instace->GetDevice().createPipelineLayoutUnique(layout_info);
|
||||||
|
|
||||||
if (!CreateStaticSamplers())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Create global texture staging buffer
|
// Create global texture staging buffer
|
||||||
texture_upload_buffer.Create(MAX_TEXTURE_UPLOAD_BUFFER_SIZE,
|
texture_upload_buffer.Create(MAX_TEXTURE_UPLOAD_BUFFER_SIZE,
|
||||||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
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,
|
vk::RenderPass VKResourceCache::GetRenderPass(vk::Format color_format, vk::Format depth_format,
|
||||||
u32 multisamples, vk::AttachmentLoadOp load_op)
|
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 key = std::tie(color_format, depth_format, multisamples, load_op);
|
||||||
auto it = render_pass_cache.find(key);
|
auto it = render_pass_cache.find(key);
|
||||||
if (it != render_pass_cache.end()) {
|
if (it != render_pass_cache.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Otherwise create a new one with the parameters provided
|
||||||
vk::SubpassDescription subpass({}, vk::PipelineBindPoint::eGraphics);
|
vk::SubpassDescription subpass({}, vk::PipelineBindPoint::eGraphics);
|
||||||
std::array<vk::AttachmentDescription, 2> attachments;
|
std::array<vk::AttachmentDescription, 2> attachments;
|
||||||
std::array<vk::AttachmentReference, 2> references;
|
std::array<vk::AttachmentReference, 2> references;
|
||||||
|
@@ -53,6 +53,6 @@ private:
|
|||||||
std::string pipeline_cache_filename;
|
std::string pipeline_cache_filename;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::unique_ptr<VKResourceCache> g_object_cache;
|
extern std::unique_ptr<VKResourceCache> g_vk_res_cache;
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
@@ -6,17 +6,16 @@
|
|||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "video_core/renderer_vulkan/vk_texture.h"
|
#include "video_core/renderer_vulkan/vk_texture.h"
|
||||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_resource_cache.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
|
|
||||||
void VKTexture::Create(const Info& info)
|
void VKTexture::Create(const Info& info)
|
||||||
{
|
{
|
||||||
auto& device = g_vk_instace->GetDevice();
|
auto& device = g_vk_instace->GetDevice();
|
||||||
format = info.format;
|
texture_info = info;
|
||||||
width = info.width;
|
|
||||||
height = info.height;
|
|
||||||
|
|
||||||
switch (format)
|
switch (texture_info.format)
|
||||||
{
|
{
|
||||||
case vk::Format::eR8G8B8A8Uint:
|
case vk::Format::eR8G8B8A8Uint:
|
||||||
case vk::Format::eR8G8B8A8Srgb:
|
case vk::Format::eR8G8B8A8Srgb:
|
||||||
@@ -27,24 +26,26 @@ void VKTexture::Create(const Info& info)
|
|||||||
channels = 3;
|
channels = 3;
|
||||||
break;
|
break;
|
||||||
default:
|
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
|
// Make sure the texture size doesn't exceed the global staging buffer size
|
||||||
u32 image_size = width * height * channels;
|
u32 image_size = texture_info.width * texture_info.height * channels;
|
||||||
staging.Create(image_size, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
|
assert(image_size <= MAX_TEXTURE_UPLOAD_BUFFER_SIZE);
|
||||||
vk::BufferUsageFlagBits::eTransferSrc);
|
|
||||||
pixels = staging.memory;
|
|
||||||
|
|
||||||
// Create the texture
|
// 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
|
vk::ImageCreateInfo image_info
|
||||||
(
|
(
|
||||||
flags,
|
flags,
|
||||||
info.type,
|
info.type,
|
||||||
format,
|
texture_info.format,
|
||||||
{ width, height, 1 }, info.mipmap_levels, info.array_layers,
|
{ texture_info.width, texture_info.height, 1 }, info.mipmap_levels, info.array_layers,
|
||||||
vk::SampleCountFlagBits::e1,
|
static_cast<vk::SampleCountFlagBits>(info.multisamples),
|
||||||
vk::ImageTiling::eOptimal,
|
vk::ImageTiling::eOptimal,
|
||||||
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled
|
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled
|
||||||
);
|
);
|
||||||
@@ -60,31 +61,9 @@ void VKTexture::Create(const Info& info)
|
|||||||
device.bindImageMemory(texture.get(), texture_memory.get(), 0);
|
device.bindImageMemory(texture.get(), texture_memory.get(), 0);
|
||||||
|
|
||||||
// Create texture view
|
// 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));
|
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
|
||||||
texture_view = device.createImageViewUnique(view_info);
|
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)
|
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);
|
TransitionLayout(vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
|
||||||
|
|
||||||
// Copy pixels to staging buffer
|
// 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
|
// Copy the staging buffer to the image
|
||||||
vk::CommandBufferAllocateInfo alloc_info(g_vk_instace->command_pool.get(), vk::CommandBufferLevel::ePrimary, 1);
|
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});
|
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 };
|
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();
|
command_buffer.end();
|
||||||
|
|
||||||
vk::SubmitInfo submit_info({}, {}, {}, 1, &command_buffer);
|
vk::SubmitInfo submit_info({}, {}, {}, 1, &command_buffer);
|
||||||
queue.submit(submit_info, nullptr);
|
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);
|
device.freeCommandBuffers(g_vk_instace->command_pool.get(), command_buffer);
|
||||||
|
|
||||||
// Prepare for shader reads
|
// Prepare for shader reads
|
||||||
TransitionLayout(vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <functional>
|
||||||
#include "video_core/renderer_vulkan/vk_buffer.h"
|
#include "video_core/renderer_vulkan/vk_buffer.h"
|
||||||
|
|
||||||
namespace Vulkan {
|
namespace Vulkan {
|
||||||
@@ -19,6 +20,7 @@ struct SamplerInfo {
|
|||||||
|
|
||||||
/// Vulkan texture object
|
/// Vulkan texture object
|
||||||
class VKTexture final : public NonCopyable {
|
class VKTexture final : public NonCopyable {
|
||||||
|
friend class VKFramebuffer;
|
||||||
public:
|
public:
|
||||||
/// Information for the creation of the target texture
|
/// Information for the creation of the target texture
|
||||||
struct Info {
|
struct Info {
|
||||||
@@ -28,6 +30,7 @@ public:
|
|||||||
vk::ImageViewType view_type;
|
vk::ImageViewType view_type;
|
||||||
u32 mipmap_levels = 1;
|
u32 mipmap_levels = 1;
|
||||||
u32 array_layers = 1;
|
u32 array_layers = 1;
|
||||||
|
u32 multisamples = 1;
|
||||||
SamplerInfo sampler_info = {};
|
SamplerInfo sampler_info = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,41 +44,52 @@ public:
|
|||||||
/// Copies CPU side pixel data to the GPU texture buffer
|
/// Copies CPU side pixel data to the GPU texture buffer
|
||||||
void CopyPixels(std::span<u32> pixels);
|
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:
|
private:
|
||||||
/// Used to transition the image to an optimal layout during transfers
|
/// Used to transition the image to an optimal layout during transfers
|
||||||
void TransitionLayout(vk::ImageLayout old_layout, vk::ImageLayout new_layout);
|
void TransitionLayout(vk::ImageLayout old_layout, vk::ImageLayout new_layout);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Texture buffer
|
Info texture_info;
|
||||||
void* pixels = nullptr;
|
|
||||||
uint32_t width = 0, height = 0, channels = 0;
|
|
||||||
VKBuffer staging;
|
|
||||||
|
|
||||||
// Texture objects
|
|
||||||
vk::UniqueImage texture;
|
vk::UniqueImage texture;
|
||||||
vk::UniqueImageView texture_view;
|
vk::UniqueImageView texture_view;
|
||||||
vk::UniqueDeviceMemory texture_memory;
|
vk::UniqueDeviceMemory texture_memory;
|
||||||
vk::UniqueSampler texture_sampler;
|
u32 channels;
|
||||||
vk::Format format;
|
};
|
||||||
|
|
||||||
|
enum Attachments {
|
||||||
|
Color = 0,
|
||||||
|
DepthStencil = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Vulkan framebuffer object similar to an FBO in OpenGL
|
/// Vulkan framebuffer object similar to an FBO in OpenGL
|
||||||
class VKFramebuffer final : public NonCopyable {
|
class VKFramebuffer final : public NonCopyable {
|
||||||
public:
|
public:
|
||||||
|
struct Info {
|
||||||
|
VKTexture* color;
|
||||||
|
VKTexture* depth_stencil;
|
||||||
|
};
|
||||||
|
|
||||||
VKFramebuffer() = default;
|
VKFramebuffer() = default;
|
||||||
~VKFramebuffer() = default;
|
~VKFramebuffer() = default;
|
||||||
|
|
||||||
// Create Vulkan framebuffer object
|
/// Create Vulkan framebuffer object
|
||||||
void Create(u32 width, u32 height, u32 layers, u32 samples);
|
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:
|
private:
|
||||||
u32 width, height;
|
u32 width, height;
|
||||||
vk::UniqueFramebuffer framebuffer;
|
vk::UniqueFramebuffer framebuffer;
|
||||||
vk::RenderPass load_renderpass;
|
std::array<VKTexture*, 2> attachments;
|
||||||
vk::RenderPass discard_renderpass;
|
|
||||||
vk::RenderPass clear_renderpass;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user