diff --git a/src/video_core/common/renderer.cpp b/src/video_core/common/renderer.cpp index 42f9baae2..eb1b081f3 100644 --- a/src/video_core/common/renderer.cpp +++ b/src/video_core/common/renderer.cpp @@ -56,8 +56,7 @@ layout (std140, push_constant) uniform PresentUniformData { }; void main() { - //color = texture(sampler2D(top_screen, screen_sampler), frag_tex_coord); - color = vec4(1.f, 0.f, 0.f, 1.f); + color = texture(sampler2D(top_screen, screen_sampler), frag_tex_coord); } )"; @@ -159,7 +158,7 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi // Create vertex buffer for the screen rectangle const BufferInfo vertex_info = { - .capacity = sizeof(ScreenRectVertex) * 16, + .capacity = sizeof(ScreenRectVertex) * 4 * 4, .usage = BufferUsage::Vertex }; @@ -179,6 +178,11 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi // Set topology to strip present_pipeline_info.rasterization.topology.Assign(Pica::TriangleTopology::Strip); + // Disable blending, depth and stencil tests + present_pipeline_info.blending.blend_enable.Assign(0); + present_pipeline_info.depth_stencil.depth_test_enable.Assign(0); + present_pipeline_info.depth_stencil.stencil_test_enable.Assign(0); + // Create screen sampler const SamplerInfo sampler_info = { .mag_filter = Pica::TextureFilter::Linear, diff --git a/src/video_core/renderer_vulkan/vk_backend.cpp b/src/video_core/renderer_vulkan/vk_backend.cpp index 618fb70a7..b07e0653c 100644 --- a/src/video_core/renderer_vulkan/vk_backend.cpp +++ b/src/video_core/renderer_vulkan/vk_backend.cpp @@ -104,6 +104,11 @@ bool Backend::BeginPresent() { } void Backend::EndPresent() { + // Transition swapchain image to present layout + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + swapchain.GetCurrentImage()->Transition(command_buffer, vk::ImageLayout::ePresentSrcKHR); + + // Submit and present scheduler.Submit(false, true, swapchain.GetAvailableSemaphore(), swapchain.GetPresentSemaphore()); swapchain.Present(); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline.cpp b/src/video_core/renderer_vulkan/vk_pipeline.cpp index 1c51a483d..1857b2cab 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline.cpp @@ -198,12 +198,11 @@ Pipeline::Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager& for (int i = 0; i < info.shaders.size(); i++) { auto& shader = info.shaders[i]; if (!shader.IsValid()) { - shader_count = i; - break; + continue; } Shader* vk_shader = static_cast(shader.Get()); - shader_stages[i] = vk::PipelineShaderStageCreateInfo{ + shader_stages[shader_count++] = vk::PipelineShaderStageCreateInfo{ .stage = ToVkShaderStage(shader->GetStage()), .module = vk_shader->GetHandle(), .pName = "main" @@ -389,10 +388,15 @@ void Pipeline::Free() { void Pipeline::BindTexture(u32 group, u32 slot, TextureHandle handle) { Texture* texture = static_cast(handle.Get()); + // NOTE: To prevent validation errors when using the image without uploading + // transition it now to VK_IMAGE_LAYOUT_SHADER_READONLY_OPTIMAL + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + texture->Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal); + const DescriptorData data = { .image_info = vk::DescriptorImageInfo{ .imageView = texture->GetView(), - .imageLayout = texture->GetLayout() + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal } }; diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp index 256be16b9..37c78dd51 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp @@ -74,8 +74,8 @@ void RenderpassCache::CreatePresentRenderpass(vk::Format format) { if (!present_renderpass) { present_renderpass = CreateRenderPass(format, vk::Format::eUndefined, vk::AttachmentLoadOp::eClear, - vk::ImageLayout::eUndefined, - vk::ImageLayout::ePresentSrcKHR); + vk::ImageLayout::eColorAttachmentOptimal, + vk::ImageLayout::eColorAttachmentOptimal); } } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index dd7c31373..8b80dd583 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -7,9 +7,9 @@ #include "video_core/common/pool_manager.h" #include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/renderer_vulkan/vk_instance.h" -#include "video_core/renderer_vulkan/vk_backend.h" #include "video_core/renderer_vulkan/vk_framebuffer.h" #include "video_core/renderer_vulkan/vk_texture.h" +#include "video_core/renderer_vulkan/vk_renderpass_cache.h" namespace VideoCore::Vulkan { @@ -167,6 +167,15 @@ void Swapchain::Present() { current_frame = (current_frame + 1) % vk_images.size(); } +FramebufferHandle Swapchain::GetCurrentFramebuffer() { + return framebuffers[current_image]; +} + +// Returns the current swapchain image +Texture* Swapchain::GetCurrentImage() { + return static_cast(textures[current_image].Get()); +} + void Swapchain::Configure(u32 width, u32 height) { vk::PhysicalDevice physical = instance.GetPhysicalDevice(); diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 68ed86a1d..92c48e989 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -16,10 +16,10 @@ class PoolManager; namespace VideoCore::Vulkan { class Instance; -class Backend; class CommandScheduler; -class Texture; class RenderpassCache; +class Texture; +class Framebuffer; class Swapchain { public: @@ -36,9 +36,11 @@ public: // Presents the current image and move to the next one void Present(); - FramebufferHandle GetCurrentFramebuffer() const { - return framebuffers[current_image]; - } + // Returns the framebuffer the current image is attached to + FramebufferHandle GetCurrentFramebuffer(); + + // Returns the current swapchain image + Texture* GetCurrentImage(); // Returns current swapchain state vk::Extent2D GetExtent() const { @@ -70,11 +72,6 @@ public: return render_finished; } - // Returns the current swapchain image - vk::Image GetCurrentImage() { - return vk_images[current_image]; - } - // Returns true when the swapchain should be recreated bool NeedsRecreation() const { return is_suboptimal || is_outdated; diff --git a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp index b58f59f21..f083b8ad3 100644 --- a/src/video_core/renderer_vulkan/vk_task_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_task_scheduler.cpp @@ -123,8 +123,7 @@ void CommandScheduler::Submit(bool wait_completion, bool begin_next, command.upload_command_buffer.end(); } - constexpr std::array wait_stage_masks{ - vk::PipelineStageFlagBits::eAllCommands, + const std::array wait_stage_masks = { vk::PipelineStageFlagBits::eColorAttachmentOutput, }; diff --git a/src/video_core/renderer_vulkan/vk_texture.cpp b/src/video_core/renderer_vulkan/vk_texture.cpp index 60ed08e17..100089fb1 100644 --- a/src/video_core/renderer_vulkan/vk_texture.cpp +++ b/src/video_core/renderer_vulkan/vk_texture.cpp @@ -122,11 +122,6 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& p // Create image view image_view = device.createImageView(view_info); - - // NOTE: To prevent validation errors when using the image without uploading - // transition it now to VK_IMAGE_LAYOUT_SHADER_READONLY_OPTIMAL - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal, 0, info.levels); } Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager, @@ -170,19 +165,10 @@ void Texture::Free() { pool_manager.Free(this); } -void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, u32 level, u32 level_count) { - ASSERT(level + level_count < TEXTURE_MAX_LEVELS); - - // Ensure all miplevels in the range have the same layout - vk::ImageLayout old_layout = layouts[level]; - if (old_layout != vk::ImageLayout::eUndefined) { - for (u32 i = 0; i < level_count; i++) { - ASSERT(layouts[level + i] == old_layout); - } - } - +void Texture::TransitionSubresource(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, + u32 level, u32 level_count) { // Don't do anything if the image is already in the wanted layout - if (new_layout == old_layout) { + if (new_layout == layout) { return; } @@ -245,13 +231,13 @@ void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_l return info; }; - LayoutInfo source = GetLayoutInfo(old_layout); + LayoutInfo source = GetLayoutInfo(layout); LayoutInfo dest = GetLayoutInfo(new_layout); const vk::ImageMemoryBarrier barrier = { .srcAccessMask = source.access, .dstAccessMask = dest.access, - .oldLayout = old_layout, + .oldLayout = layout, .newLayout = new_layout, .image = image, .subresourceRange = {aspect, level, level_count, 0, 1} @@ -263,11 +249,11 @@ void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_l {}, {}, barrier); // Update layouts - SetLayout(new_layout, level, level_count); + layout = new_layout; } -void Texture::SetLayout(vk::ImageLayout new_layout, u32 level, u32 level_count) { - std::fill_n(layouts.begin() + level, level_count, new_layout); +void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout) { + TransitionSubresource(command_buffer, new_layout, 0, info.levels); } void Texture::Upload(Rect2D rectangle, u32 stride, std::span data, u32 level) { @@ -309,7 +295,7 @@ void Texture::Upload(Rect2D rectangle, u32 stride, std::span data, u32 std::memcpy(staging.GetMappedPtr(), data.data(), byte_count); staging.Commit(byte_count); - Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal, level); + Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal); // Blit command_buffer.blitImage(staging.GetHandle(), vk::ImageLayout::eGeneral, @@ -361,7 +347,7 @@ void Texture::Upload(Rect2D rectangle, u32 stride, std::span data, u32 }; vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal, level); + Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal); // Copy staging buffer to the texture command_buffer.copyBufferToImage(staging.GetHandle(), image, @@ -373,11 +359,10 @@ void Texture::Upload(Rect2D rectangle, u32 stride, std::span data, u32 } void Texture::Download(Rect2D rectangle, u32 stride, std::span data, u32 level) { - const u64 byte_count = data.size(); + const std::size_t byte_count = data.size(); vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - // If the adverised format supports blitting then use GPU accelerated - // format conversion. + // If the adverised format supports blitting use GPU accelerated format conversion. if (internal_format != advertised_format && instance.IsFormatSupported(advertised_format, vk::FormatFeatureFlagBits::eBlitDst)) { // Creating a new staging texture for each upload/download is expensive @@ -407,7 +392,7 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span data, u32 lev .dstOffsets = offsets }; - Transition(command_buffer, vk::ImageLayout::eTransferSrcOptimal, level); + Transition(command_buffer, vk::ImageLayout::eTransferSrcOptimal); // Blit command_buffer.blitImage(image, vk::ImageLayout::eTransferSrcOptimal, @@ -444,14 +429,12 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span data, u32 lev .imageExtent = {rectangle.width, rectangle.height, 1} }; - Transition(command_buffer, vk::ImageLayout::eTransferSrcOptimal, level); + Transition(command_buffer, vk::ImageLayout::eTransferSrcOptimal); // Copy pixel data to the staging buffer command_buffer.copyImageToBuffer(image, vk::ImageLayout::eTransferSrcOptimal, staging.GetHandle(), copy_region); - Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal); - // TODO: Async downloads scheduler.Submit(true); @@ -459,6 +442,8 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span data, u32 lev auto memory = staging.Map(byte_count); std::memcpy(data.data(), memory.data(), byte_count); } + + Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal); } void Texture::BlitTo(TextureHandle dest, Common::Rectangle source_rect, Common::Rectangle dest_rect, @@ -514,8 +499,8 @@ void Texture::GenerateMipmaps() { vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); for (u32 i = 1; i < info.levels; i++) { - Transition(command_buffer, vk::ImageLayout::eTransferSrcOptimal, i - 1); - Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal, i); + TransitionSubresource(command_buffer, vk::ImageLayout::eTransferSrcOptimal, i - 1); + TransitionSubresource(command_buffer, vk::ImageLayout::eTransferDstOptimal, i); const std::array source_offsets = { vk::Offset3D{0, 0, 0}, @@ -551,7 +536,7 @@ void Texture::GenerateMipmaps() { } // Prepare for shader reads - Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal, 0, info.levels); + Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal); } void Texture::CopyFrom(TextureHandle source) { diff --git a/src/video_core/renderer_vulkan/vk_texture.h b/src/video_core/renderer_vulkan/vk_texture.h index d05975ad6..c02f39cff 100644 --- a/src/video_core/renderer_vulkan/vk_texture.h +++ b/src/video_core/renderer_vulkan/vk_texture.h @@ -33,39 +33,36 @@ public: vk::Image image, vk::Format format, const TextureInfo& info); ~Texture() override; - void Free() override; void Upload(Rect2D rectangle, u32 stride, std::span data, u32 level = 0) override; - void Download(Rect2D rectangle, u32 stride, std::span data, u32 level = 0) override; - void BlitTo(TextureHandle dest, Common::Rectangle src_rectangle, Common::Rectangle dest_rect, u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override; void CopyFrom(TextureHandle source) override; - void GenerateMipmaps() override; - /// Overrides the layout of provided image subresource + // Overrides the layout of provided image subresource void SetLayout(vk::ImageLayout new_layout, u32 level = 0, u32 level_count = 1); - /// Transitions part of the image to the provided layout - void Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, u32 level = 0, - u32 level_count = 1); + // Transitions the image to the provided layout + void Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout); + void TransitionSubresource(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, + u32 level = 0, u32 level_count = 1); - /// Returns the underlying vulkan image handle + // Returns the underlying vulkan image handle vk::Image GetHandle() const { return image; } - /// Returns the Vulka image view + // Returns the Vulka image view vk::ImageView GetView() const { return image_view; } - /// Returns the internal format backing the texture. - /// It may not match the input pixel format. + // Returns the internal format backing the texture. + // It may not match the input pixel format. vk::Format GetInternalFormat() const { return internal_format; } @@ -74,12 +71,12 @@ public: return aspect; } - /// Returns the current image layout + // Returns the current image layout vk::ImageLayout GetLayout(u32 level = 0) const { - return layouts.at(level); + return layout; } - /// Returns a rectangle that represents the complete area of the texture + // Returns a rectangle that represents the complete area of the texture vk::Rect2D GetArea() const { return {{0, 0},{info.width, info.height}}; } @@ -99,7 +96,7 @@ private: vk::Format advertised_format = vk::Format::eUndefined; vk::Format internal_format = vk::Format::eUndefined; vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eNone; - std::array layouts{vk::ImageLayout::eUndefined}; + vk::ImageLayout layout = vk::ImageLayout::eUndefined; }; /**