diff --git a/src/video_core/renderer_vulkan/vk_buffer.cpp b/src/video_core/renderer_vulkan/vk_buffer.cpp index 35e68bade..c7dff59e6 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer.cpp @@ -11,49 +11,58 @@ namespace Vulkan { -VKBuffer::~VKBuffer() -{ +VKBuffer::~VKBuffer() { if (memory != nullptr) { - g_vk_instace->GetDevice().unmapMemory(buffer_memory.get()); + g_vk_instace->GetDevice().unmapMemory(buffer_memory); } + + auto deleter = [this]() { + if (buffer) { + auto& device = g_vk_instace->GetDevice(); + device.destroyBuffer(buffer); + device.freeMemory(buffer_memory); + device.destroyBufferView(buffer_view); + } + }; + + g_vk_task_scheduler->Schedule(deleter); } -void VKBuffer::Create(uint32_t byte_count, vk::MemoryPropertyFlags properties, vk::BufferUsageFlags usage, vk::Format view_format) -{ +void VKBuffer::Create(u32 byte_count, vk::MemoryPropertyFlags properties, vk::BufferUsageFlags usage, + vk::Format view_format) { auto& device = g_vk_instace->GetDevice(); size = byte_count; vk::BufferCreateInfo bufferInfo({}, byte_count, usage); - buffer = device.createBufferUnique(bufferInfo); + buffer = device.createBuffer(bufferInfo); - auto mem_requirements = device.getBufferMemoryRequirements(buffer.get()); + auto mem_requirements = device.getBufferMemoryRequirements(buffer); auto memory_type_index = FindMemoryType(mem_requirements.memoryTypeBits, properties); vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index); - buffer_memory = device.allocateMemoryUnique(alloc_info); - device.bindBufferMemory(buffer.get(), buffer_memory.get(), 0); + buffer_memory = device.allocateMemory(alloc_info); + device.bindBufferMemory(buffer, buffer_memory, 0); // Optionally map the buffer to CPU memory - if (properties & vk::MemoryPropertyFlagBits::eHostVisible) - memory = device.mapMemory(buffer_memory.get(), 0, byte_count); + if (properties & vk::MemoryPropertyFlagBits::eHostVisible) { + memory = device.mapMemory(buffer_memory, 0, byte_count); + } // Create buffer view for texel buffers - if (usage & vk::BufferUsageFlagBits::eStorageTexelBuffer || usage & vk::BufferUsageFlagBits::eUniformTexelBuffer) - { - vk::BufferViewCreateInfo view_info({}, buffer.get(), view_format, 0, byte_count); - buffer_view = device.createBufferViewUnique(view_info); + if (usage & vk::BufferUsageFlagBits::eStorageTexelBuffer || + usage & vk::BufferUsageFlagBits::eUniformTexelBuffer) { + vk::BufferViewCreateInfo view_info({}, buffer, view_format, 0, byte_count); + buffer_view = device.createBufferView(view_info); } } -void VKBuffer::CopyBuffer(VKBuffer& src_buffer, VKBuffer& dst_buffer, const vk::BufferCopy& region) -{ +void VKBuffer::CopyBuffer(VKBuffer& src_buffer, VKBuffer& dst_buffer, const vk::BufferCopy& region) { auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); - command_buffer.copyBuffer(src_buffer.buffer.get(), dst_buffer.buffer.get(), region); + command_buffer.copyBuffer(src_buffer.buffer, dst_buffer.buffer, region); } -uint32_t VKBuffer::FindMemoryType(uint32_t type_filter, vk::MemoryPropertyFlags properties) -{ +u32 VKBuffer::FindMemoryType(u32 type_filter, vk::MemoryPropertyFlags properties) { vk::PhysicalDeviceMemoryProperties mem_properties = g_vk_instace->GetPhysicalDevice().getMemoryProperties(); for (uint32_t i = 0; i < mem_properties.memoryTypeCount; i++) @@ -67,4 +76,19 @@ uint32_t VKBuffer::FindMemoryType(uint32_t type_filter, vk::MemoryPropertyFlags UNREACHABLE(); } +void StagingBuffer::Create(u32 size) { + buffer.Create(size, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, + vk::BufferUsageFlagBits::eTransferSrc); +} + +u8* StagingBuffer::Request(u32 bytes) { + // Check if there is enough space left + if (buffer.GetSize() - end_offset >= bytes) { + u8* ptr = buffer.GetHostPointer() + end_offset; + end_offset += bytes; + + // Schedule the memory to be freed + } +} + } diff --git a/src/video_core/renderer_vulkan/vk_buffer.h b/src/video_core/renderer_vulkan/vk_buffer.h index 1d2a00d19..d166c6c01 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.h +++ b/src/video_core/renderer_vulkan/vk_buffer.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "common/common_types.h" @@ -19,21 +20,23 @@ public: ~VKBuffer(); /// 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(u32 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 u32 FindMemoryType(u32 type_filter, vk::MemoryPropertyFlags properties); static void CopyBuffer(VKBuffer& src_buffer, VKBuffer& dst_buffer, const vk::BufferCopy& region); /// 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(); } + vk::Buffer& GetBuffer() { return buffer; } + u32 GetSize() const { return size; } private: void* memory = nullptr; - vk::UniqueBuffer buffer; - vk::UniqueDeviceMemory buffer_memory; - vk::UniqueBufferView buffer_view; + vk::Buffer buffer; + vk::DeviceMemory buffer_memory; + vk::BufferView buffer_view; uint32_t size = 0; }; diff --git a/src/video_core/renderer_vulkan/vk_resource_cache.h b/src/video_core/renderer_vulkan/vk_resource_cache.h index 390eeaa6d..d2c65dc0b 100644 --- a/src/video_core/renderer_vulkan/vk_resource_cache.h +++ b/src/video_core/renderer_vulkan/vk_resource_cache.h @@ -31,16 +31,11 @@ public: void Shutdown(); // Public interface. - VKBuffer& GetTextureUploadBuffer() { return texture_upload_buffer; } vk::Sampler GetSampler(const SamplerInfo& info); vk::RenderPass GetRenderPass(vk::Format color_format, vk::Format depth_format, u32 multisamples, vk::AttachmentLoadOp load_op); vk::PipelineCache GetPipelineCache() const { return pipeline_cache.get(); } private: - // Dummy image for samplers that are unbound - VKTexture dummy_texture; - VKBuffer texture_upload_buffer; - // Descriptor sets std::array descriptor_layouts; vk::UniquePipelineLayout pipeline_layout; diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp index 80acf0f97..1863bea2f 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp @@ -98,8 +98,9 @@ void VKTaskScheduler::Submit(bool present, bool wait_completion) { // When the task completes the timeline will increment to the task id vk::TimelineSemaphoreSubmitInfo timeline_info({}, task.task_id); + std::array signal_semaphores = { timeline.get(), present_semaphore.get() }; vk::PipelineStageFlags wait_stage = vk::PipelineStageFlagBits::eColorAttachmentOutput; - vk::SubmitInfo submit_info({}, wait_stage, task.command_buffer, present_semaphore.get(), &timeline_info); + vk::SubmitInfo submit_info({}, wait_stage, task.command_buffer, signal_semaphores, &timeline_info); // Wait for new swapchain image if (present) { @@ -108,7 +109,6 @@ void VKTaskScheduler::Submit(bool present, bool wait_completion) { } // Submit the command buffer - submit_info.setSignalSemaphores(timeline.get()); g_vk_instace->GetGraphicsQueue().submit(submit_info); // Present the image when rendering has finished @@ -125,14 +125,12 @@ void VKTaskScheduler::Submit(bool present, bool wait_completion) { BeginTask(); } -void VKTaskScheduler::ScheduleDestroy(auto object) { - auto& resources = tasks[current_task]; - auto deleter = [object]() { g_vk_instace->GetDevice().destroy(object); }; - resources.cleanups.push_back(deleter); +void VKTaskScheduler::Schedule(std::function func) { + auto& task = tasks[current_task]; + task.cleanups.push_back(func); } -void VKTaskScheduler::BeginTask() -{ +void VKTaskScheduler::BeginTask() { // Move to the next command buffer. u32 next_task_index = (current_task + 1) % CONCURRENT_TASK_COUNT; auto& task = tasks[next_task_index]; @@ -151,6 +149,7 @@ void VKTaskScheduler::BeginTask() // Reset upload command buffer state current_task = next_task_index; + task.task_id = current_task_id++; } std::unique_ptr g_vk_task_scheduler; diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.h b/src/video_core/renderer_vulkan/vk_task_scheduler.h index b326a44be..67ff2da2d 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.h @@ -42,7 +42,7 @@ public: vk::CommandBuffer GetCommandBuffer(); /// Returns the task id that the CPU is recording - u64 GetCPUTick() const { return tasks[current_task].task_id; } + u64 GetCPUTick() const { return current_task_id; } /// Returns the last known task id to have completed execution in the GPU u64 GetGPUTick() const { return g_vk_instace->GetDevice().getSemaphoreCounterValue(timeline.get()); } @@ -52,7 +52,7 @@ public: void SyncToGPU(u64 task_index); /// Schedule a vulkan object for destruction when the GPU no longer uses it - void ScheduleDestroy(auto object); + void Schedule(std::function func); /// Submit the current work batch and move to the next frame void Submit(bool present = true, bool wait_completion = false); @@ -69,7 +69,7 @@ private: vk::UniqueSemaphore timeline; vk::UniqueCommandPool command_pool; - u64 last_completed_task_id = 0; + u64 current_task_id = 1; // Each task contains unique resources std::array tasks; diff --git a/src/video_core/renderer_vulkan/vk_texture.cpp b/src/video_core/renderer_vulkan/vk_texture.cpp index 9a753ee64..2dbfcec44 100644 --- a/src/video_core/renderer_vulkan/vk_texture.cpp +++ b/src/video_core/renderer_vulkan/vk_texture.cpp @@ -15,26 +15,25 @@ VKTexture::~VKTexture() { // Make sure to unbind the texture before destroying it g_vk_state->UnbindTexture(this); - if (cleanup_image && texture) { - g_vk_task_scheduler->ScheduleDestroy(texture); - } + auto deleter = [this]() { + auto& device = g_vk_instace->GetDevice(); + + if (texture) { + if (cleanup_image) { + device.destroyImage(texture); + } + + device.destroyImageView(texture_view); + device.freeMemory(texture_memory); + } + }; // Schedule deletion of the texture after it's no longer used // by the GPU - if (texture_view) { - g_vk_task_scheduler->ScheduleDestroy(texture_view); - } - - if (texture_memory) { - g_vk_task_scheduler->ScheduleDestroy(texture_memory); - } - - if (texture_view) { - g_vk_task_scheduler->ScheduleDestroy(texture_view); - } + g_vk_task_scheduler->Schedule(deleter); } -void VKTexture::Create(const Info& info) { +void VKTexture::Create(const Info& info, bool make_staging) { auto& device = g_vk_instace->GetDevice(); texture_info = info; @@ -87,6 +86,12 @@ void VKTexture::Create(const Info& info) { vk::ImageViewCreateInfo view_info({}, texture, info.view_type, texture_info.format, {}, vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)); texture_view = device.createImageView(view_info); + + // Create staging buffer + if (make_staging) { + staging.Create(image_size, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, + vk::BufferUsageFlagBits::eTransferSrc); + } } void VKTexture::Adopt(vk::Image image, vk::ImageViewCreateInfo view_info) { @@ -181,10 +186,14 @@ void VKTexture::TransitionLayout(vk::ImageLayout new_layout, vk::CommandBuffer& } void VKTexture::CopyPixels(std::span new_pixels) { + if (!staging.GetHostPointer()) { + LOG_ERROR(Render_Vulkan, "Cannot copy pixels without staging buffer!"); + } + auto command_buffer = g_vk_task_scheduler->GetCommandBuffer(); // Copy pixels to staging buffer - std::memcpy(g_vk_res_cache->GetTextureUploadBuffer().GetHostPointer(), + std::memcpy(staging.GetHostPointer(), new_pixels.data(), new_pixels.size() * channels); vk::BufferImageCopy region(0, 0, 0, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), 0, @@ -194,7 +203,6 @@ void VKTexture::CopyPixels(std::span new_pixels) { // Transition image to transfer format TransitionLayout(vk::ImageLayout::eTransferDstOptimal, command_buffer); - auto& staging = g_vk_res_cache->GetTextureUploadBuffer(); command_buffer.copyBufferToImage(staging.GetBuffer(), texture, vk::ImageLayout::eTransferDstOptimal, regions); // Prepare for shader reads @@ -250,9 +258,14 @@ void VKTexture::Fill(Common::Rectangle region, glm::vec2 depth_stencil) { } VKFramebuffer::~VKFramebuffer() { - if (framebuffer) { - g_vk_task_scheduler->ScheduleDestroy(framebuffer); - } + auto deleter = [this]() { + if (framebuffer) { + auto& device = g_vk_instace->GetDevice(); + device.destroyFramebuffer(framebuffer); + } + }; + + g_vk_task_scheduler->Schedule(deleter); } void VKFramebuffer::Create(const Info& info) { diff --git a/src/video_core/renderer_vulkan/vk_texture.h b/src/video_core/renderer_vulkan/vk_texture.h index 87e854063..f2b0732c1 100644 --- a/src/video_core/renderer_vulkan/vk_texture.h +++ b/src/video_core/renderer_vulkan/vk_texture.h @@ -42,7 +42,7 @@ public: ~VKTexture(); /// Create a new Vulkan texture object - void Create(const Info& info); + void Create(const Info& info, bool staging = false); /// Create a non-owning texture object, usefull for image object /// from the swapchain that are managed by another object @@ -79,6 +79,9 @@ private: vk::ImageView texture_view; vk::DeviceMemory texture_memory; u32 channels; + + // TODO: Make a global staging buffer + VKBuffer staging; }; enum Attachments {