diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 3b8d39dfa..4fe029938 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -80,8 +80,10 @@ constexpr std::array MakeOrthographicMatrix(u32 width, u32 height) { // clang-format on } -constexpr static std::array PRESENT_BINDINGS = {{ - {0, vk::DescriptorType::eCombinedImageSampler, 3, vk::ShaderStageFlagBits::eFragment}, +constexpr static std::array PRESENT_BINDINGS = {{ + {0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, + {1, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, + {2, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment}, }}; namespace { @@ -360,17 +362,79 @@ void RendererVulkan::CreatePPTextureFramebuffers(){ } }; -void RendererVulkan::PrepareDraw(Frame* frame, const Layout::FramebufferLayout& layout) { +//Helper Functions +void RendererVulkan::PrepareTextureDraw(TextureInfo framebufferTexture, vk::Framebuffer framebuffer, vk::Pipeline shaderPipeline, std::vector texturesToSample, int filterMode){ + const auto sampler = present_samplers[filterMode]; + const auto present_set = present_heap.Commit(); + for (u32 i = 0; i < texturesToSample.size(); i++) { + update_queue.AddImageSampler(present_set, i, 0, texturesToSample[i].image_view, sampler); + } + + renderpass_cache.EndRendering(); + scheduler.Record([this, framebufferTexture, framebuffer, shaderPipeline, present_set](vk::CommandBuffer cmdbuf) { + const vk::Viewport viewport = { + .x = 0.0f, + .y = 0.0f, + .width = static_cast(framebufferTexture.width), + .height = static_cast(framebufferTexture.height), + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + + const vk::Rect2D scissor = { + .offset = {0, 0}, + .extent = {framebufferTexture.width, framebufferTexture.height}, + }; + + const vk::ClearColorValue clear_color = { + .float32 = + std::array{ + 0.0f, + 0.0f, + 0.0f, + 0.0f, + }, + }; + cmdbuf.setViewport(0, viewport); + cmdbuf.setScissor(0, scissor); + + const vk::ClearValue clear{.color = clear_color}; + const vk::PipelineLayout layout{*present_pipeline_layout}; + const vk::RenderPassBeginInfo renderpass_begin_info = { + .renderPass = textureRenderpass, + .framebuffer = framebuffer, + .renderArea = + vk::Rect2D{ + .offset = {0, 0}, + .extent = {framebufferTexture.width, framebufferTexture.height}, + }, + .clearValueCount = 1, + .pClearValues = &clear, + }; + cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, shaderPipeline); + cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, present_set, {}); + }); +} + + +void RendererVulkan::PrepareDraw(Frame* frame, const Layout::FramebufferLayout& layout, std::vector screenids) { const auto sampler = present_samplers[Settings::values.filter_mode.GetValue()]; const auto present_set = present_heap.Commit(); - for (u32 index = 0; index < screen_infos.size(); index++) { - update_queue.AddImageSampler(present_set, 0, index, screen_infos[index].image_view, + for (u32 i = 0; i < screenids.size(); i++) { + update_queue.AddImageSampler(present_set, i, 0, screen_infos[screenids[i]].image_view, sampler); } renderpass_cache.EndRendering(); + vk::RenderPass currentRenderPass; + if (clearingColorAttachment){ + currentRenderPass = main_present_window.Renderpass(); + } else { + currentRenderPass = main_present_window.LoadRenderpass(); + } scheduler.Record([this, layout, frame, present_set, - renderpass = main_present_window.Renderpass(), + currentRenderPass, index = current_pipeline](vk::CommandBuffer cmdbuf) { const vk::Viewport viewport = { .x = 0.0f, @@ -392,7 +456,7 @@ void RendererVulkan::PrepareDraw(Frame* frame, const Layout::FramebufferLayout& const vk::ClearValue clear{.color = clear_color}; const vk::PipelineLayout layout{*present_pipeline_layout}; const vk::RenderPassBeginInfo renderpass_begin_info = { - .renderPass = renderpass, + .renderPass = currentRenderPass, .framebuffer = frame->framebuffer, .renderArea = vk::Rect2D{ @@ -1034,6 +1098,26 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr Layout::DisplayOrientation orientation) { const ScreenInfo& screen_info = screen_infos[screen_id]; const auto& texcoords = screen_info.texcoords; + std::vector screenids = {screen_id}; + PrepareDraw(currentFrame, currentFramebufferLayout, screenids); + + // Apply the initial default opacity value; Needed to avoid flickering + if (applyingOpacity){ + if (drawingPrimaryScreen){ + ApplySecondLayerOpacity(1.0f); + } else { + if (usingTopOpacity){ + if (currentFramebufferLayout.top_opacity < 1) { + ApplySecondLayerOpacity(currentFramebufferLayout.top_opacity); + } + } else { + if (currentFramebufferLayout.bottom_opacity < 1) { + ApplySecondLayerOpacity(currentFramebufferLayout.bottom_opacity); + } + } + } + } + const u32 scale_factor = GetResolutionScaleFactor(); float textureWidth = static_cast(screen_info.texture.height * scale_factor); @@ -1074,6 +1158,52 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr ScreenRectVertex(-1.f, -1.f, 0.f, 0.f), //Left, Bottom ScreenRectVertex(1.f, -1.f, 1.f, 0.f), //Right, Bottom }}; + + // Legacy Vertices. Will be deleted when converted to multipass + std::array legacy_vertices; + float x = screenLeft; + float y = screenTop; + float w = screenWidth; + float h = screenHeight; + switch (orientation) { + case Layout::DisplayOrientation::Landscape: + legacy_vertices = {{ + ScreenRectVertex(x, y, texcoords.bottom, texcoords.left), + ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.right), + ScreenRectVertex(x, y + h, texcoords.top, texcoords.left), + ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.right), + }}; + break; + case Layout::DisplayOrientation::Portrait: + legacy_vertices = {{ + ScreenRectVertex(x, y, texcoords.bottom, texcoords.right), + ScreenRectVertex(x + w, y, texcoords.top, texcoords.right), + ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.left), + ScreenRectVertex(x + w, y + h, texcoords.top, texcoords.left), + }}; + std::swap(h, w); + break; + case Layout::DisplayOrientation::LandscapeFlipped: + legacy_vertices = {{ + ScreenRectVertex(x, y, texcoords.top, texcoords.right), + ScreenRectVertex(x + w, y, texcoords.top, texcoords.left), + ScreenRectVertex(x, y + h, texcoords.bottom, texcoords.right), + ScreenRectVertex(x + w, y + h, texcoords.bottom, texcoords.left), + }}; + break; + case Layout::DisplayOrientation::PortraitFlipped: + legacy_vertices = {{ + ScreenRectVertex(x, y, texcoords.top, texcoords.left), + ScreenRectVertex(x + w, y, texcoords.bottom, texcoords.left), + ScreenRectVertex(x, y + h, texcoords.top, texcoords.right), + ScreenRectVertex(x + w, y + h, texcoords.bottom, texcoords.right), + }}; + std::swap(h, w); + break; + default: + LOG_ERROR(Render_Vulkan, "Unknown DisplayOrientation: {}", orientation); + break; + } // Vertices for Azahar's Output Layout std::array output_vertices; @@ -1116,9 +1246,10 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr LOG_ERROR(Render_OpenGL, "Unknown DisplayOrientation: {}", orientation); break; } - const u64 size = sizeof(ScreenRectVertex) * output_vertices.size(); + + const u64 size = sizeof(ScreenRectVertex) * legacy_vertices.size(); auto [data, offset, invalidate] = vertex_buffer.Map(size, 16); - std::memcpy(data, output_vertices.data(), size); + std::memcpy(data, legacy_vertices.data(), size); vertex_buffer.Commit(size); draw_info.i_resolution = @@ -1137,6 +1268,7 @@ void RendererVulkan::DrawSingleScreen(u32 screen_id, float screenLeft, float scr cmdbuf.bindVertexBuffers(0, vertex_buffer.Handle(), {0}); cmdbuf.draw(4, 1, first_vertex, 0); + cmdbuf.endRenderPass(); }); } @@ -1145,7 +1277,25 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl Layout::DisplayOrientation orientation) { const ScreenInfo& screen_info_l = screen_infos[screen_id_l]; const auto& texcoords = screen_info_l.texcoords; + std::vector screenids = {screen_id_l, screen_id_r}; + PrepareDraw(currentFrame, currentFramebufferLayout, screenids); + // Apply the initial default opacity value; Needed to avoid flickering + if (applyingOpacity){ + if (drawingPrimaryScreen){ + ApplySecondLayerOpacity(1.0f); + } else { + if (usingTopOpacity){ + if (currentFramebufferLayout.top_opacity < 1) { + ApplySecondLayerOpacity(currentFramebufferLayout.top_opacity); + } + } else { + if (currentFramebufferLayout.bottom_opacity < 1) { + ApplySecondLayerOpacity(currentFramebufferLayout.bottom_opacity); + } + } + } + } std::array vertices; switch (orientation) { case Layout::DisplayOrientation::Landscape: @@ -1210,6 +1360,7 @@ void RendererVulkan::DrawSingleScreenStereo(u32 screen_id_l, u32 screen_id_r, fl cmdbuf.bindVertexBuffers(0, vertex_buffer.Handle(), {0}); cmdbuf.draw(4, 1, first_vertex, 0); + cmdbuf.endRenderPass(); }); } @@ -1246,6 +1397,7 @@ void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout, DrawSingleScreen(leftside, top_screen_left / 2, top_screen_top, top_screen_width / 2, top_screen_height, orientation); draw_info.layer = 1; + clearingColorAttachment = false; DrawSingleScreen(rightside, static_cast((top_screen_left / 2) + (layout.width / 2)), top_screen_top, top_screen_width / 2, top_screen_height, orientation); break; @@ -1254,6 +1406,7 @@ void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout, DrawSingleScreen(leftside, top_screen_left, top_screen_top, top_screen_width, top_screen_height, orientation); draw_info.layer = 1; + clearingColorAttachment = false; DrawSingleScreen(rightside, top_screen_left + layout.width / 2, top_screen_top, top_screen_width, top_screen_height, orientation); break; @@ -1262,6 +1415,7 @@ void RendererVulkan::DrawTopScreen(const Layout::FramebufferLayout& layout, DrawSingleScreen(leftside, top_screen_left, top_screen_top, top_screen_width, top_screen_height, orientation); draw_info.layer = 1; + clearingColorAttachment = false; DrawSingleScreen( rightside, static_cast(layout.cardboard.top_screen_right_eye + (layout.width / 2)), @@ -1347,33 +1501,34 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout& ReloadPipeline(layout.render_3d_mode); } - PrepareDraw(frame, layout); - + currentFrame = frame; + currentFramebufferLayout = layout; const auto& top_screen = layout.top_screen; const auto& bottom_screen = layout.bottom_screen; draw_info.modelview = MakeOrthographicMatrix(layout.width, layout.height); - draw_info.layer = 0; - // Apply the initial default opacity value; Needed to avoid flickering - ApplySecondLayerOpacity(1.0f); - + clearingColorAttachment = true; + applyingOpacity = true; if (!Settings::values.swap_screen.GetValue()) { + drawingPrimaryScreen = true; DrawTopScreen(layout, top_screen); draw_info.layer = 0; - if (layout.bottom_opacity < 1) { - ApplySecondLayerOpacity(layout.bottom_opacity); - } + drawingPrimaryScreen = false; + usingTopOpacity = false; + clearingColorAttachment = false; DrawBottomScreen(layout, bottom_screen); } else { + drawingPrimaryScreen = true; DrawBottomScreen(layout, bottom_screen); draw_info.layer = 0; - if (layout.top_opacity < 1) { - ApplySecondLayerOpacity(layout.top_opacity); - } + drawingPrimaryScreen = false; + usingTopOpacity = true; + clearingColorAttachment = false; DrawTopScreen(layout, top_screen); } + applyingOpacity = false; if (layout.additional_screen_enabled) { const auto& additional_screen = layout.additional_screen; if (!Settings::values.swap_screen.GetValue()) { @@ -1383,9 +1538,8 @@ void RendererVulkan::DrawScreens(Frame* frame, const Layout::FramebufferLayout& } } - DrawCursor(layout); - - scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.endRenderPass(); }); + // Needs to be fixed + // DrawCursor(layout); } void RendererVulkan::DrawCursor(const Layout::FramebufferLayout& layout) { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 6e0a07906..111bdfa04 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -98,8 +98,8 @@ private: void RenderScreenshot(); void RenderScreenshotWithStagingCopy(); bool TryRenderScreenshotWithHostMemory(); - void PrepareDraw(Frame* frame, const Layout::FramebufferLayout& layout); - void PrepareTextureDraw(TextureInfo& textureInfo, vk::Pipeline& pipeline, int filterMode, std::vector imageViews); + void PrepareDraw(Frame* frame, const Layout::FramebufferLayout& layout, std::vector screenids); + void PrepareTextureDraw(TextureInfo framebufferTexture, vk::Framebuffer framebuffer, vk::Pipeline shaderPipeline, std::vector texturesToSample, int filterMode); void RenderToWindow(PresentWindow& window, const Layout::FramebufferLayout& layout, bool flipped); @@ -132,7 +132,6 @@ private: // Create Framebuffers that are attached to the Post Processing Textures void CreatePPTextureFramebuffers(); void AllocateSMAATextures(); - private: Memory::MemorySystem& memory; Pica::PicaCore& pica; @@ -195,7 +194,12 @@ private: int currBottomTextureWidth; int currBottomTextureHeight; u32 current_pipeline = 0; - + Frame* currentFrame; + Layout::FramebufferLayout currentFramebufferLayout; + bool clearingColorAttachment = true; + bool applyingOpacity = true; + bool drawingPrimaryScreen = false; + bool usingTopOpacity = false; std::array screen_infos{}; PresentUniformData draw_info{}; vk::ClearColorValue clear_color{}; diff --git a/src/video_core/renderer_vulkan/vk_present_window.cpp b/src/video_core/renderer_vulkan/vk_present_window.cpp index 4d049d97d..712fc91a0 100644 --- a/src/video_core/renderer_vulkan/vk_present_window.cpp +++ b/src/video_core/renderer_vulkan/vk_present_window.cpp @@ -104,7 +104,7 @@ PresentWindow::PresentWindow(Frontend::EmuWindow& emu_window_, const Instance& i surface{CreateSurface(instance.GetInstance(), emu_window)}, next_surface{surface}, swapchain{instance, emu_window.GetFramebufferLayout().width, emu_window.GetFramebufferLayout().height, surface, low_refresh_rate_}, - graphics_queue{instance.GetGraphicsQueue()}, present_renderpass{CreateRenderpass()}, + graphics_queue{instance.GetGraphicsQueue()}, present_renderpass{CreateRenderpass()}, present_load_renderpass{CreateLoadRenderpass()}, vsync_enabled{Settings::values.use_vsync.GetValue()}, blit_supported{ CanBlitToSwapchain(instance.GetPhysicalDevice(), swapchain.GetSurfaceFormat().format)}, @@ -157,6 +157,7 @@ PresentWindow::~PresentWindow() { const vk::Device device = instance.GetDevice(); device.destroyCommandPool(command_pool); device.destroyRenderPass(present_renderpass); + device.destroyRenderPass(present_load_renderpass); for (auto& frame : swap_chain) { device.destroyImageView(frame.image_view); device.destroyFramebuffer(frame.framebuffer); @@ -524,4 +525,41 @@ vk::RenderPass PresentWindow::CreateRenderpass() { return instance.GetDevice().createRenderPass(renderpass_info); } +vk::RenderPass PresentWindow::CreateLoadRenderpass() { + const vk::AttachmentReference color_ref = { + .attachment = 0, + .layout = vk::ImageLayout::eGeneral, + }; + + const vk::SubpassDescription subpass = { + .pipelineBindPoint = vk::PipelineBindPoint::eGraphics, + .inputAttachmentCount = 0, + .pInputAttachments = nullptr, + .colorAttachmentCount = 1u, + .pColorAttachments = &color_ref, + .pResolveAttachments = 0, + .pDepthStencilAttachment = nullptr, + }; + + const vk::AttachmentDescription color_attachment = { + .format = swapchain.GetSurfaceFormat().format, + .loadOp = vk::AttachmentLoadOp::eLoad, + .storeOp = vk::AttachmentStoreOp::eStore, + .stencilLoadOp = vk::AttachmentLoadOp::eDontCare, + .stencilStoreOp = vk::AttachmentStoreOp::eDontCare, + .initialLayout = vk::ImageLayout::eUndefined, + .finalLayout = vk::ImageLayout::eTransferSrcOptimal, + }; + + const vk::RenderPassCreateInfo renderpass_info = { + .attachmentCount = 1, + .pAttachments = &color_attachment, + .subpassCount = 1, + .pSubpasses = &subpass, + .dependencyCount = 0, + .pDependencies = nullptr, + }; + + return instance.GetDevice().createRenderPass(renderpass_info); +} } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_present_window.h b/src/video_core/renderer_vulkan/vk_present_window.h index 012dbf81b..facc77783 100644 --- a/src/video_core/renderer_vulkan/vk_present_window.h +++ b/src/video_core/renderer_vulkan/vk_present_window.h @@ -58,6 +58,10 @@ public: [[nodiscard]] vk::RenderPass Renderpass() const noexcept { return present_renderpass; } + // Returns Renderpass that doesn't clear the image + [[nodiscard]] vk::RenderPass LoadRenderpass() const noexcept { + return present_load_renderpass; + } u32 ImageCount() const noexcept { return swapchain.GetImageCount(); @@ -69,7 +73,7 @@ private: void CopyToSwapchain(Frame* frame); vk::RenderPass CreateRenderpass(); - + vk::RenderPass CreateLoadRenderpass(); private: Frontend::EmuWindow& emu_window; const Instance& instance; @@ -81,6 +85,7 @@ private: vk::CommandPool command_pool; vk::Queue graphics_queue; vk::RenderPass present_renderpass; + vk::RenderPass present_load_renderpass; std::vector swap_chain; std::queue free_queue; std::queue present_queue;