diff --git a/src/common/common_types.h b/src/common/common_types.h index 88a124144..ee18eac81 100644 --- a/src/common/common_types.h +++ b/src/common/common_types.h @@ -56,10 +56,6 @@ protected: constexpr NonCopyable() = default; ~NonCopyable() = default; - // Enable std::move operations - NonCopyable(NonCopyable&&) = default; - NonCopyable& operator=(NonCopyable&&) = default; - NonCopyable(const NonCopyable&) = delete; NonCopyable& operator=(const NonCopyable&) = delete; }; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 5d0d6af6a..ce8545338 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -131,35 +131,6 @@ std::vector RequiredExtensions(Frontend::WindowSystemType window_ty return extensions; } -/** - * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left - * corner and (width, height) on the lower-bottom. - * - * The projection part of the matrix is trivial, hence these operations are represented - * by a 3x2 matrix. - * - * @param flipped Whether the frame should be flipped upside down. - */ -static glm::mat3x2 MakeOrthographicMatrix(const float width, const float height, bool flipped) { - glm::mat3x2 matrix; // Laid out in column-major order - - // Last matrix row is implicitly assumed to be [0, 0, 1]. - if (flipped) { - // clang-format off - matrix[0][0] = 2.f / width; matrix[1][0] = 0.f; matrix[2][0] = -1.f; - matrix[0][1] = 0.f; matrix[1][1] = 2.f / height; matrix[2][1] = -1.f; - // clang-format on - } - else { - // clang-format off - matrix[0][0] = 2.f / width; matrix[1][0] = 0.f; matrix[2][0] = -1.f; - matrix[1][1] = 0.f; matrix[1][1] = -2.f / height; matrix[2][1] = 1.f; - // clang-format on - } - - return matrix; -} - RendererVulkan::RendererVulkan(Frontend::EmuWindow& window) : RendererBase{window} { @@ -304,13 +275,13 @@ void RendererVulkan::ConfigureFramebufferTexture(ScreenInfo& screen, const GPU:: switch (screen.format) { case GPU::Regs::PixelFormat::RGBA8: - texture_info.format = vk::Format::eR8G8B8A8Srgb; + texture_info.format = vk::Format::eR8G8B8A8Unorm; break; case GPU::Regs::PixelFormat::RGB8: // Note that the texture will actually give us an RGBA8 image because next to no modern hardware supports RGB formats. // The pixels will be converted automatically by Upload() - texture_info.format = vk::Format::eR8G8B8Srgb; + texture_info.format = vk::Format::eR8G8B8Unorm; break; case GPU::Regs::PixelFormat::RGB565: @@ -521,7 +492,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f // Set projection matrix draw_info.modelview = glm::transpose(glm::ortho(0.f, static_cast(layout.width), static_cast(layout.height), 0.0f, - -1.f, 1.f)); + 0.f, 1.f)); const bool stereo_single_screen = false /* Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph || Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || @@ -535,7 +506,7 @@ void RendererVulkan::DrawScreens(const Layout::FramebufferLayout& layout, bool f auto& image = swapchain->GetCurrentImage(); auto& state = VulkanState::Get(); - state.BeginRendering(image, std::nullopt, false, clear_color, vk::AttachmentLoadOp::eClear); + state.BeginRendering(&image, nullptr, false, clear_color, vk::AttachmentLoadOp::eClear); state.SetPresentTextures(screen_infos[0].display_texture->GetView(), screen_infos[1].display_texture->GetView(), screen_infos[2].display_texture->GetView()); @@ -727,7 +698,7 @@ VideoCore::ResultStatus RendererVulkan::Init() { auto surface = CreateSurface(instance, render_window); g_vk_instace = std::make_unique(); g_vk_task_scheduler = std::make_unique(); - g_vk_instace->Create(instance, physical_devices[0], surface, true); + g_vk_instace->Create(instance, physical_devices[1], surface, true); g_vk_task_scheduler->Create(); //auto& layout = render_window.GetFramebufferLayout(); diff --git a/src/video_core/renderer_vulkan/vk_buffer.cpp b/src/video_core/renderer_vulkan/vk_buffer.cpp index 58b5dd9ed..7c4db7171 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer.cpp @@ -28,12 +28,12 @@ void VKBuffer::Create(const VKBuffer::Info& info) { auto memory_type_index = FindMemoryType(mem_requirements.memoryTypeBits, info.properties); vk::MemoryAllocateInfo alloc_info(mem_requirements.size, memory_type_index); - buffer_memory = device.allocateMemory(alloc_info); - device.bindBufferMemory(buffer, buffer_memory, 0); + memory = device.allocateMemory(alloc_info); + device.bindBufferMemory(buffer, memory, 0); // Optionally map the buffer to CPU memory if (info.properties & vk::MemoryPropertyFlagBits::eHostVisible) { - host_ptr = device.mapMemory(buffer_memory, 0, info.size); + host_ptr = device.mapMemory(memory, 0, info.size); } for (auto& format : info.view_formats) { @@ -51,13 +51,16 @@ void VKBuffer::Recreate() { void VKBuffer::Destroy() { if (buffer) { if (host_ptr != nullptr) { - g_vk_instace->GetDevice().unmapMemory(buffer_memory); + g_vk_instace->GetDevice().unmapMemory(memory); } - auto deleter = [this]() { + auto deleter = [buffer = buffer, + memory = memory, + view_count = view_count, + views = views]() { auto device = g_vk_instace->GetDevice(); device.destroyBuffer(buffer); - device.freeMemory(buffer_memory); + device.freeMemory(memory); for (u32 i = 0; i < view_count; i++) { device.destroyBufferView(views[i]); diff --git a/src/video_core/renderer_vulkan/vk_buffer.h b/src/video_core/renderer_vulkan/vk_buffer.h index 8a997e647..288354c4c 100644 --- a/src/video_core/renderer_vulkan/vk_buffer.h +++ b/src/video_core/renderer_vulkan/vk_buffer.h @@ -54,7 +54,7 @@ public: protected: Info buffer_info; vk::Buffer buffer; - vk::DeviceMemory buffer_memory; + vk::DeviceMemory memory; void* host_ptr = nullptr; std::array views; u32 view_count{}; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 2c76386c2..45a3ac918 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -7,22 +7,21 @@ #include "common/logging/log.h" #include "video_core/renderer_vulkan/vk_instance.h" -#if defined(VK_EXT_color_write_enable) -PFN_vkCmdSetColorWriteEnableEXT ptr_vkCmdSetColorWriteEnableEXT; -#endif /* defined(VK_EXT_color_write_enable) */ - namespace Vulkan { std::unique_ptr g_vk_instace; VKInstance::~VKInstance() { + device.waitIdle(); + device.destroy(); + instance.destroy(); } -bool VKInstance::Create(vk::Instance instance, vk::PhysicalDevice physical_device, +bool VKInstance::Create(vk::Instance new_instance, vk::PhysicalDevice gpu, vk::SurfaceKHR surface, bool enable_validation_layer) { - this->instance = instance; - this->physical_device = physical_device; + instance = new_instance; + physical_device = gpu; // Get physical device limits device_limits = physical_device.getProperties().limits; @@ -98,15 +97,11 @@ bool VKInstance::CreateDevice(vk::SurfaceKHR surface, bool validation_enabled) { } // Create logical device - device = physical_device.createDeviceUnique(device_info); - -#if defined(VK_EXT_color_write_enable) - ptr_vkCmdSetColorWriteEnableEXT = reinterpret_cast(device->getProcAddr("vkCmdSetColorWriteEnableEXT")); -#endif /* defined(VK_EXT_color_write_enable) */ + device = physical_device.createDevice(device_info); // Grab the graphics and present queues. - graphics_queue = device->getQueue(graphics_queue_family_index, 0); - present_queue = device->getQueue(present_queue_family_index, 0); + graphics_queue = device.getQueue(graphics_queue_family_index, 0); + present_queue = device.getQueue(present_queue_family_index, 0); return true; } @@ -195,7 +190,3 @@ bool VKInstance::FindExtensions() { } } // namespace Vulkan - -void vkCmdSetColorWriteEnableEXT(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkBool32* pColorWriteEnables) { - ptr_vkCmdSetColorWriteEnableEXT(commandBuffer, attachmentCount, pColorWriteEnables); -} diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 6e726fec1..209d59a4a 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -22,8 +22,9 @@ public: bool Create(vk::Instance instance, vk::PhysicalDevice gpu, vk::SurfaceKHR surface, bool enable_validation_layer); - vk::Device GetDevice() const { return device.get(); } + vk::Device GetDevice() const { return device; } vk::PhysicalDevice GetPhysicalDevice() const { return physical_device; } + vk::Instance GetInstance() const { return instance; } /// Retrieve queue information u32 GetGraphicsQueueFamilyIndex() const { return graphics_queue_family_index; } @@ -46,9 +47,9 @@ public: vk::Queue present_queue, graphics_queue; // Core vulkan objects - vk::Instance instance; vk::PhysicalDevice physical_device; - vk::UniqueDevice device; + vk::Instance instance; + vk::Device device; // Extensions and features std::vector extensions; @@ -67,7 +68,3 @@ public: extern std::unique_ptr g_vk_instace; } // namespace Vulkan - -#if defined(VK_EXT_color_write_enable) -extern PFN_vkCmdSetColorWriteEnableEXT ptr_vkCmdSetColorWriteEnableEXT; -#endif /* defined(VK_EXT_color_write_enable) */ diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a2ecf26f8..64e7bec51 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -367,7 +367,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { state.SetScissor(scissor); // Bind the framebuffer surfaces - state.BeginRendering(color_surface->texture, depth_surface->texture, true); + state.BeginRendering(color_surface != nullptr ? &color_surface->texture : nullptr, + depth_surface != nullptr ? &depth_surface->texture : nullptr, true); state.ApplyRenderState(Pica::g_state.regs); state.SetVertexBuffer(vertex_buffer, 0); @@ -399,8 +400,14 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) { } state.EndRendering(); - color_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); - depth_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); + + if (color_surface) { + color_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); + } + + if (depth_surface) { + depth_surface->texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); + } g_vk_task_scheduler->Submit(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer_cache.cpp b/src/video_core/renderer_vulkan/vk_rasterizer_cache.cpp index 8707667b1..0b9cccae1 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer_cache.cpp @@ -42,8 +42,8 @@ using SurfaceType = SurfaceParams::SurfaceType; using PixelFormat = SurfaceParams::PixelFormat; static constexpr std::array fb_format_tuples = {{ - vk::Format::eR8G8B8A8Srgb, // RGBA8 - vk::Format::eR8G8B8Srgb, // RGB8 + vk::Format::eR8G8B8A8Unorm, // RGBA8 + vk::Format::eR8G8B8Unorm, // RGB8 vk::Format::eR5G5B5A1UnormPack16, // RGB5A1 vk::Format::eR5G6B5UnormPack16, // RGB565 vk::Format::eR4G4B4A4UnormPack16, // RGBA4 @@ -66,7 +66,7 @@ vk::Format GetFormatTuple(PixelFormat pixel_format) { ASSERT(tuple_idx < depth_format_tuples.size()); return depth_format_tuples[tuple_idx]; } - return vk::Format::eR8G8B8A8Srgb; + return vk::Format::eR8G8B8A8Unorm; } template @@ -293,6 +293,7 @@ static bool FillSurface(const Surface& surface, std::array fill_buffer, if (surface->GetScaledRect() != rect) { // TODO: use vkCmdClearAttachments to clear subrects LOG_ERROR(Render_Vulkan, "Partial surface fills not implemented"); + return false; } vk::ImageSubresourceRange image_range{{}, 0, 1, 0, 1}; @@ -327,12 +328,36 @@ static bool FillSurface(const Surface& surface, std::array fill_buffer, cmdbuffer.clearColorImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal, color, image_range); + texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); + return true; + } + case SurfaceParams::SurfaceType::Depth: + case SurfaceParams::SurfaceType::DepthStencil: { + auto& texture = surface->texture; + texture.Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal); + u32 value_32bit = 0; + vk::ClearDepthStencilValue clear_value; + + if (surface->pixel_format == SurfaceParams::PixelFormat::D16) { + std::memcpy(&value_32bit, fill_buffer.data(), sizeof(u16)); + clear_value.depth = value_32bit / 65535.0f; // 2^16 - 1 + } else if (surface->pixel_format == SurfaceParams::PixelFormat::D24) { + std::memcpy(&value_32bit, fill_buffer.data(), 3); + clear_value.depth = value_32bit / 16777215.0f; // 2^24 - 1 + } else { + std::memcpy(&value_32bit, fill_buffer.data(), sizeof(u32)); + clear_value.depth = (value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 + clear_value.stencil = value_32bit >> 24; + } + + cmdbuffer.clearDepthStencilImage(texture.GetHandle(), vk::ImageLayout::eTransferDstOptimal, + clear_value, image_range); texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); return true; } default: - LOG_ERROR(Render_Vulkan, "non-color fills not implemented"); + LOG_CRITICAL(Render_Vulkan, "Unsupported fill operation requested!"); return false; } } @@ -344,14 +369,14 @@ static vk::Rect2D FromRect(Common::Rectangle rect) { } // Allocate an uninitialized texture of appropriate size and format for the surface -VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Format format, +void RasterizerCacheVulkan::AllocateTexture(VKTexture& target, SurfaceType type, vk::Format format, u32 width, u32 height) { // First check if the texture can be recycled auto recycled_tex = host_texture_recycler.find({format, width, height}); if (recycled_tex != host_texture_recycler.end()) { - VKTexture texture = std::move(recycled_tex->second); + target = std::move(recycled_tex->second); host_texture_recycler.erase(recycled_tex); - return texture; + return; } auto GetUsage = [](SurfaceType type) { @@ -378,7 +403,7 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo // Otherwise create a brand new texture u32 levels = std::log2(std::max(width, height)) + 1; - VKTexture::Info texture_info = { + VKTexture::Info texture_info{ .width = width, .height = height, .format = format, @@ -388,13 +413,11 @@ VKTexture RasterizerCacheVulkan::AllocateSurfaceTexture(SurfaceType type, vk::Fo .levels = levels }; - VKTexture texture; - texture.Create(texture_info); - auto cmdbuffer = g_vk_task_scheduler->GetUploadCommandBuffer(); - texture.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); - return texture; + target.Destroy(); + target.Create(texture_info); + target.Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); } CachedSurface::~CachedSurface() { @@ -1431,8 +1454,8 @@ Surface RasterizerCacheVulkan::CreateSurface(const SurfaceParams& params) { static_cast(*surface) = params; surface->invalid_regions.insert(surface->GetInterval()); - surface->texture = AllocateSurfaceTexture(params.type, GetFormatTuple(surface->pixel_format), - surface->GetScaledWidth(), surface->GetScaledHeight()); + AllocateTexture(surface->texture, params.type, GetFormatTuple(surface->pixel_format), + surface->GetScaledWidth(), surface->GetScaledHeight()); return surface; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer_cache.h b/src/video_core/renderer_vulkan/vk_rasterizer_cache.h index a109374e8..f37017dd2 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer_cache.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer_cache.h @@ -342,7 +342,7 @@ private: std::recursive_mutex mutex; public: - VKTexture AllocateSurfaceTexture(SurfaceParams::SurfaceType type, vk::Format format, + void AllocateTexture(VKTexture& target, SurfaceParams::SurfaceType type, vk::Format format, u32 width, u32 height); std::unique_ptr format_reinterpreter; }; diff --git a/src/video_core/renderer_vulkan/vk_shader_gen.cpp b/src/video_core/renderer_vulkan/vk_shader_gen.cpp index d762c220d..5d5c75f3a 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_gen.cpp @@ -1539,6 +1539,7 @@ vec4 secondary_fragment_color = vec4(0.0); // do our own transformation according to PICA specification. out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n" "float depth = z_over_w * depth_scale + depth_offset;\n"; + //out += "if (gl_FragCoord.z == 0) { color = vec4(1.0, 0.0, 0.0, 1.0); return; }\n"; if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) { out += "depth /= gl_FragCoord.w;\n"; } @@ -1655,11 +1656,14 @@ void main() { texcoord0_w = vert_texcoord0_w; normquat = vert_normquat; view = vert_view; - gl_Position = vert_position + vec4(0.0, 0.0, 1.0, 0.0); -#if !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance) - //gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 - //gl_ClipDistance[1] = dot(clip_coef, vert_position); -#endif // !defined(CITRA_GLES) || defined(GL_EXT_clip_cull_distance) + + // Convert OpenGL [-1, 1] to Vulkan [0, 1] + vec4 position = vert_position; + position.z = (position.z + 1) * 0.5; + + gl_Position = position; + gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 + gl_ClipDistance[1] = dot(clip_coef, vert_position); } )"; diff --git a/src/video_core/renderer_vulkan/vk_state.cpp b/src/video_core/renderer_vulkan/vk_state.cpp index 2cb9061cc..2acd8c1ad 100644 --- a/src/video_core/renderer_vulkan/vk_state.cpp +++ b/src/video_core/renderer_vulkan/vk_state.cpp @@ -7,6 +7,7 @@ #include "video_core/renderer_vulkan/vk_state.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_task_scheduler.h" +#include "video_core/renderer_vulkan/vk_rasterizer_cache.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_shader_gen.h" @@ -82,7 +83,7 @@ VulkanState::VulkanState(const std::shared_ptr& swapchain) : swapch VKTexture::Info info{ .width = 1, .height = 1, - .format = vk::Format::eR8G8B8A8Srgb, + .format = vk::Format::eR8G8B8A8Unorm, .type = vk::ImageType::e2D, .view_type = vk::ImageViewType::e2D, .usage = vk::ImageUsageFlagBits::eSampled | @@ -124,9 +125,12 @@ VulkanState::VulkanState(const std::shared_ptr& swapchain) : swapch VulkanState::~VulkanState() { auto device = g_vk_instace->GetDevice(); + device.waitIdle(); // Destroy vertex shader device.destroyShaderModule(render_vertex_shader); + device.destroyShaderModule(present_vertex_shader); + device.destroyShaderModule(present_fragment_shader); // Destroy pipeline layouts device.destroyPipelineLayout(render_pipeline_layout); @@ -227,7 +231,7 @@ void VulkanState::UnbindTexture(u32 unit) { descriptors_dirty = true; } -void VulkanState::BeginRendering(OptRef color, OptRef depth, bool update_pipeline_formats, +void VulkanState::BeginRendering(VKTexture* color, VKTexture* depth, bool update_pipeline_formats, vk::ClearColorValue color_clear, vk::AttachmentLoadOp color_load_op, vk::AttachmentStoreOp color_store_op, vk::ClearDepthStencilValue depth_clear, vk::AttachmentLoadOp depth_load_op, vk::AttachmentStoreOp depth_store_op, @@ -236,38 +240,37 @@ void VulkanState::BeginRendering(OptRef color, OptRef dept EndRendering(); // Make sure attachments are in optimal layout - vk::RenderingInfo render_info{{}, color->get().GetArea(), 1, {}}; + vk::RenderingInfo render_info{{}, {}, 1, {}}; std::array infos{}; auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer(); - if (color.has_value()) { - auto& image = color->get(); - image.Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal); + if (color != nullptr) { + color->Transition(cmdbuffer, vk::ImageLayout::eColorAttachmentOptimal); infos[0] = vk::RenderingAttachmentInfo{ - image.GetView(), image.GetLayout(), {}, {}, {}, + color->GetView(), color->GetLayout(), {}, {}, {}, color_load_op, color_store_op, color_clear }; render_info.colorAttachmentCount = 1; render_info.pColorAttachments = &infos[0]; + render_info.renderArea = color->GetArea(); } - if (depth.has_value()) { - auto& image = depth->get(); - image.Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal); + if (depth != nullptr) { + depth->Transition(cmdbuffer, vk::ImageLayout::eDepthStencilAttachmentOptimal); infos[1] = vk::RenderingAttachmentInfo{ - image.GetView(), image.GetLayout(), {}, {}, {}, + depth->GetView(), depth->GetLayout(), {}, {}, {}, depth_load_op, depth_store_op, depth_clear }; render_info.pDepthAttachment = &infos[1]; - if (IsStencil(image.GetFormat())) { + if (IsStencil(depth->GetFormat())) { infos[2] = vk::RenderingAttachmentInfo{ - image.GetView(), image.GetLayout(), {}, {}, {}, + depth->GetView(), depth->GetLayout(), {}, {}, {}, stencil_load_op, stencil_store_op, depth_clear }; @@ -276,8 +279,12 @@ void VulkanState::BeginRendering(OptRef color, OptRef dept } if (update_pipeline_formats) { - render_pipeline_key.color = color.has_value() ? color->get().GetFormat() : vk::Format::eUndefined; - render_pipeline_key.depth_stencil = depth.has_value() ? depth->get().GetFormat() : vk::Format::eUndefined; + render_pipeline_key.color = color != nullptr ? + color->GetFormat() : + vk::Format::eUndefined; + render_pipeline_key.depth_stencil = depth != nullptr ? + depth->GetFormat() : + vk::Format::eUndefined; } // Begin rendering diff --git a/src/video_core/renderer_vulkan/vk_state.h b/src/video_core/renderer_vulkan/vk_state.h index 50dbbc460..89eeb3a2a 100644 --- a/src/video_core/renderer_vulkan/vk_state.h +++ b/src/video_core/renderer_vulkan/vk_state.h @@ -17,9 +17,6 @@ namespace Vulkan { constexpr u32 DESCRIPTOR_SET_COUNT = 4; -template -using OptRef = std::optional>; - struct DrawInfo { glm::mat4 modelview; glm::vec4 i_resolution; @@ -93,7 +90,7 @@ public: vk::BlendFactor src_alpha, vk::BlendFactor dst_alpha); /// Rendering - void BeginRendering(OptRef color, OptRef depth, bool update_pipeline_formats = false, + void BeginRendering(VKTexture* color, VKTexture* depth, bool update_pipeline_formats = false, vk::ClearColorValue color_clear = {}, vk::AttachmentLoadOp color_load_op = vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp color_store_op = vk::AttachmentStoreOp::eStore, diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 83f968a9c..dbf44a587 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -19,9 +19,13 @@ VKSwapChain::VKSwapChain(vk::SurfaceKHR surface_) : surface(surface_) { VKSwapChain::~VKSwapChain() { auto device = g_vk_instace->GetDevice(); + auto instance = g_vk_instace->GetInstance(); device.waitIdle(); + device.destroySemaphore(render_finished); + device.destroySemaphore(image_available); device.destroySwapchainKHR(swapchain); + instance.destroySurfaceKHR(surface); } bool VKSwapChain::Create(u32 width, u32 height, bool vsync_enabled) { @@ -59,11 +63,11 @@ bool VKSwapChain::Create(u32 width, u32 height, bool vsync_enabled) { // Create sync objects if not already created if (!image_available) { - image_available = device.createSemaphoreUnique({}); + image_available = device.createSemaphore({}); } if (!render_finished) { - render_finished = device.createSemaphoreUnique({}); + render_finished = device.createSemaphore({}); } // Create framebuffer and image views @@ -78,7 +82,7 @@ constexpr u64 ACQUIRE_TIMEOUT = 1000000000; void VKSwapChain::AcquireNextImage() { auto result = g_vk_instace->GetDevice().acquireNextImageKHR(swapchain, ACQUIRE_TIMEOUT, - image_available.get(), VK_NULL_HANDLE, + image_available, VK_NULL_HANDLE, &image_index); switch (result) { case vk::Result::eSuccess: @@ -98,7 +102,7 @@ void VKSwapChain::AcquireNextImage() { void VKSwapChain::Present() { const auto present_queue = g_vk_instace->GetPresentQueue(); - vk::PresentInfoKHR present_info(render_finished.get(), swapchain, image_index); + vk::PresentInfoKHR present_info(render_finished, swapchain, image_index); vk::Result result = present_queue.presentKHR(present_info); switch (result) { diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index a1d76ebe3..7f165efce 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -42,8 +42,8 @@ public: vk::SurfaceKHR GetSurface() const { return surface; } vk::SurfaceFormatKHR GetSurfaceFormat() const { return details.format; } vk::SwapchainKHR GetSwapChain() const { return swapchain; } - const vk::Semaphore& GetAvailableSemaphore() const { return image_available.get(); } - const vk::Semaphore& GetRenderSemaphore() const { return render_finished.get(); } + const vk::Semaphore& GetAvailableSemaphore() const { return image_available; } + const vk::Semaphore& GetRenderSemaphore() const { return render_finished; } VKTexture& GetCurrentImage() { return swapchain_images[image_index]; } private: @@ -53,7 +53,7 @@ private: private: SwapChainDetails details{}; vk::SurfaceKHR surface; - vk::UniqueSemaphore image_available, render_finished; + vk::Semaphore image_available, render_finished; bool vsync_enabled{false}, is_outdated{true}, is_suboptimal{true}; vk::SwapchainKHR swapchain{VK_NULL_HANDLE}; diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp index 268771ff3..56204f124 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp @@ -11,10 +11,11 @@ namespace Vulkan { VKTaskScheduler::~VKTaskScheduler() { - SyncToGPU(); - // Destroy Vulkan resources auto device = g_vk_instace->GetDevice(); + device.waitIdle(); + + SyncToGPU(); device.destroyCommandPool(command_pool); device.destroySemaphore(timeline); @@ -63,7 +64,8 @@ bool VKTaskScheduler::Create() { .size = STAGING_BUFFER_SIZE, .properties = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, - .usage = vk::BufferUsageFlagBits::eTransferSrc + .usage = vk::BufferUsageFlagBits::eTransferSrc | + vk::BufferUsageFlagBits::eTransferDst }; // Should be enough for a single frame @@ -131,6 +133,7 @@ void VKTaskScheduler::SyncToGPU(u64 task_index) { for (auto& func : task.cleanups) { func(); } + task.cleanups.clear(); } } } diff --git a/src/video_core/renderer_vulkan/vk_texture.cpp b/src/video_core/renderer_vulkan/vk_texture.cpp index 7a067b46b..fd18e30cf 100644 --- a/src/video_core/renderer_vulkan/vk_texture.cpp +++ b/src/video_core/renderer_vulkan/vk_texture.cpp @@ -19,10 +19,10 @@ static int BytesPerPixel(vk::Format format) { case vk::Format::eD32Sfloat: case vk::Format::eB8G8R8A8Unorm: case vk::Format::eR8G8B8A8Uint: - case vk::Format::eR8G8B8A8Srgb: + case vk::Format::eR8G8B8A8Unorm: case vk::Format::eD24UnormS8Uint: return 4; - case vk::Format::eR8G8B8Uint: + case vk::Format::eR8G8B8Unorm: case vk::Format::eR8G8B8Srgb: return 3; case vk::Format::eR5G6B5UnormPack16: @@ -58,15 +58,41 @@ VKTexture::~VKTexture() { Destroy(); } +VKTexture::VKTexture(VKTexture&& other) noexcept { + info = std::exchange(other.info, Info{}); + texture = std::exchange(other.texture, VK_NULL_HANDLE); + aspect = std::exchange(other.aspect, vk::ImageAspectFlagBits::eNone); + view = std::exchange(other.view, VK_NULL_HANDLE); + memory = std::exchange(other.memory, VK_NULL_HANDLE); + image_size = std::exchange(other.image_size, 0); + adopted = std::exchange(other.adopted, false); + is_rgb = std::exchange(other.is_rgb, false); + is_d24s8 = std::exchange(other.is_d24s8, false); +} + +VKTexture& VKTexture::operator=(VKTexture&& other) noexcept { + Destroy(); + info = std::exchange(other.info, Info{}); + texture = std::exchange(other.texture, VK_NULL_HANDLE); + aspect = std::exchange(other.aspect, vk::ImageAspectFlagBits::eNone); + view = std::exchange(other.view, VK_NULL_HANDLE); + memory = std::exchange(other.memory, VK_NULL_HANDLE); + image_size = std::exchange(other.image_size, 0); + adopted = std::exchange(other.adopted, false); + is_rgb = std::exchange(other.is_rgb, false); + is_d24s8 = std::exchange(other.is_d24s8, false); + return *this; +} + void VKTexture::Create(const Info& create_info) { auto device = g_vk_instace->GetDevice(); info = create_info; // Emulate RGB8 format with RGBA8 is_rgb = false; - if (info.format == vk::Format::eR8G8B8Srgb) { + if (info.format == vk::Format::eR8G8B8Unorm) { is_rgb = true; - info.format = vk::Format::eR8G8B8A8Srgb; + info.format = vk::Format::eR8G8B8A8Unorm; } is_d24s8 = false; @@ -136,7 +162,9 @@ void VKTexture::Destroy() { auto& state = VulkanState::Get(); state.UnbindTexture(*this); - auto deleter = [this]() { + auto deleter = [texture = texture, + view = view, + memory = memory]() { auto device = g_vk_instace->GetDevice(); if (texture) { std::cout << "Surface destroyed!\n"; @@ -154,7 +182,7 @@ void VKTexture::Destroy() { // If the image was adopted (probably from the swapchain) then only // destroy the view if (adopted) { - g_vk_task_scheduler->Schedule([this](){ + g_vk_task_scheduler->Schedule([view = view](){ auto device = g_vk_instace->GetDevice(); device.destroyImageView(view); }); @@ -281,19 +309,32 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::memcpy(buffer, pixels.data(), pixels.size()); } - vk::BufferImageCopy copy_region{ + std::array copy_regions; + u32 region_count = 1; + + copy_regions[0] = vk::BufferImageCopy{ offset, row_length, region.extent.height, {aspect, level, layer, 1}, {region.offset.x, region.offset.y, 0}, {region.extent.width, region.extent.height, 1} }; + if (aspect & vk::ImageAspectFlagBits::eDepth && + aspect & vk::ImageAspectFlagBits::eStencil) { + // Copying both depth and stencil requires two seperate regions + copy_regions[1] = copy_regions[0]; + copy_regions[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth; + copy_regions[1].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil; + + region_count++; + } + // Transition image to transfer format Transition(cmdbuffer, vk::ImageLayout::eTransferDstOptimal); cmdbuffer.copyBufferToImage(g_vk_task_scheduler->GetStaging().GetBuffer(), - texture, vk::ImageLayout::eTransferDstOptimal, - copy_region); + texture, vk::ImageLayout::eTransferDstOptimal, region_count, + copy_regions.data()); // Prepare image for shader reads Transition(cmdbuffer, vk::ImageLayout::eShaderReadOnlyOptimal); @@ -301,7 +342,7 @@ void VKTexture::Upload(u32 level, u32 layer, u32 row_length, vk::Rect2D region, void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region, std::span memory) { u32 request_size = is_rgb ? (memory.size() / 3) * 4 : - (is_d24s8 ? (memory.size() / 4) * 5 : memory.size()); + (is_d24s8 ? (memory.size() / 4) * 8 : memory.size()); auto [buffer, offset] = g_vk_task_scheduler->RequestStaging(request_size); if (!buffer) { LOG_ERROR(Render_Vulkan, "Cannot download texture without staging buffer!"); @@ -312,25 +353,24 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region auto cmdbuffer = g_vk_task_scheduler->GetRenderCommandBuffer(); - // Copy pixels to staging buffer - vk::BufferImageCopy download_region{ + std::array copy_regions; + u32 region_count = 1; + + copy_regions[0] = vk::BufferImageCopy{ offset, row_length, region.extent.height, {aspect, level, layer, 1}, {region.offset.x, region.offset.y, 0}, {region.extent.width, region.extent.height, 1} }; - // Automatically convert RGB to RGBA - if (is_rgb) { - auto data = RGBAToRGB(memory); - std::memcpy(buffer, data.data(), data.size()); - } - else if (is_d24s8) { - auto data = D32S8ToD24S8(memory); - std::memcpy(buffer, data.data(), data.size() * sizeof(data[0])); - } - else { - std::memcpy(buffer, memory.data(), memory.size()); + if (aspect & vk::ImageAspectFlagBits::eDepth && + aspect & vk::ImageAspectFlagBits::eStencil) { + // Copying both depth and stencil requires two seperate regions + copy_regions[1] = copy_regions[0]; + copy_regions[0].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth; + copy_regions[1].imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil; + + region_count++; } // Transition image to transfer format @@ -338,16 +378,28 @@ void VKTexture::Download(u32 level, u32 layer, u32 row_length, vk::Rect2D region Transition(cmdbuffer, vk::ImageLayout::eTransferSrcOptimal); cmdbuffer.copyImageToBuffer(texture, vk::ImageLayout::eTransferSrcOptimal, - g_vk_task_scheduler->GetStaging().GetBuffer(), - download_region); + g_vk_task_scheduler->GetStaging().GetBuffer(), + region_count, copy_regions.data()); + + // Restore layout + Transition(cmdbuffer, old_layout); // Wait for the data to be available // NOTE: This is really slow and should be reworked g_vk_task_scheduler->Submit(true); - std::memcpy(memory.data(), buffer, memory.size_bytes()); - // Restore layout - Transition(cmdbuffer, old_layout); + // Automatically convert RGB to RGBA + if (is_rgb) { + auto data = RGBAToRGB(std::span(buffer, request_size)); + std::memcpy(memory.data(), data.data(), memory.size()); + } + else if (is_d24s8) { + auto data = D32S8ToD24S8(std::span(buffer, request_size)); + std::memcpy(memory.data(), data.data(), memory.size()); + } + else { + std::memcpy(memory.data(), buffer, memory.size()); + } } template diff --git a/src/video_core/renderer_vulkan/vk_texture.h b/src/video_core/renderer_vulkan/vk_texture.h index 9a7ceb2b7..1470fc2ce 100644 --- a/src/video_core/renderer_vulkan/vk_texture.h +++ b/src/video_core/renderer_vulkan/vk_texture.h @@ -32,8 +32,8 @@ public: ~VKTexture(); /// Enable move operations - VKTexture(VKTexture&&) = default; - VKTexture& operator=(VKTexture&&) = default; + VKTexture(VKTexture&& other) noexcept; + VKTexture& operator=(VKTexture&& other) noexcept; /// Create a new Vulkan texture object void Create(const Info& info);