diff --git a/src/video_core/renderer_vulkan/vk_buffer.h b/src/video_core/renderer_vulkan/vk_buffer.h index 359da7a0e..1d2a00d19 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.h +++ b/src/video_core/renderer_vulkan/vk_buffer.h @@ -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(memory); } + vk::Buffer& GetBuffer() { return buffer.get(); } + +private: void* memory = nullptr; vk::UniqueBuffer buffer; vk::UniqueDeviceMemory buffer_memory; diff --git a/src/video_core/renderer_vulkan/vk_resource_cache.cpp b/src/video_core/renderer_vulkan/vk_resource_cache.cpp index 07b8035f1..262ad2816 100644 --- a/src/video_core/renderer_vulkan/vk_resource_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_resource_cache.cpp @@ -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 attachments; std::array references; diff --git a/src/video_core/renderer_vulkan/vk_resource_cache.h b/src/video_core/renderer_vulkan/vk_resource_cache.h index 5cf8e08fc..2dfb5a8c1 100644 --- a/src/video_core/renderer_vulkan/vk_resource_cache.h +++ b/src/video_core/renderer_vulkan/vk_resource_cache.h @@ -53,6 +53,6 @@ private: std::string pipeline_cache_filename; }; -extern std::unique_ptr g_object_cache; +extern std::unique_ptr g_vk_res_cache; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_texture.cpp b/src/video_core/renderer_vulkan/vk_texture.cpp index f9512ab8a..7e0ee6300 100644 --- a/src/video_core/renderer_vulkan/vk_texture.cpp +++ b/src/video_core/renderer_vulkan/vk_texture.cpp @@ -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(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 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 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 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 views = { info.color->GetView(), info.depth_stencil->GetView() }; + framebuffer_info.setAttachments(views); + } + else { + auto valid = info.color ? info.color : info.depth_stencil; + std::array 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); + } +} + } diff --git a/src/video_core/renderer_vulkan/vk_texture.h b/src/video_core/renderer_vulkan/vk_texture.h index c91c31966..aa08f53c9 100644 --- a/src/video_core/renderer_vulkan/vk_texture.h +++ b/src/video_core/renderer_vulkan/vk_texture.h @@ -6,6 +6,7 @@ #include #include +#include #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 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 attachments; }; }