renderer_vulkan: Transition swapchain manually

This commit is contained in:
emufan4568
2022-08-12 17:53:50 +03:00
parent 734077642e
commit 371beafc10
9 changed files with 72 additions and 72 deletions

View File

@ -56,8 +56,7 @@ layout (std140, push_constant) uniform PresentUniformData {
}; };
void main() { void main() {
//color = texture(sampler2D(top_screen, screen_sampler), frag_tex_coord); color = texture(sampler2D(top_screen, screen_sampler), frag_tex_coord);
color = vec4(1.f, 0.f, 0.f, 1.f);
} }
)"; )";
@ -159,7 +158,7 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi
// Create vertex buffer for the screen rectangle // Create vertex buffer for the screen rectangle
const BufferInfo vertex_info = { const BufferInfo vertex_info = {
.capacity = sizeof(ScreenRectVertex) * 16, .capacity = sizeof(ScreenRectVertex) * 4 * 4,
.usage = BufferUsage::Vertex .usage = BufferUsage::Vertex
}; };
@ -179,6 +178,11 @@ DisplayRenderer::DisplayRenderer(Frontend::EmuWindow& window) : render_window(wi
// Set topology to strip // Set topology to strip
present_pipeline_info.rasterization.topology.Assign(Pica::TriangleTopology::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 // Create screen sampler
const SamplerInfo sampler_info = { const SamplerInfo sampler_info = {
.mag_filter = Pica::TextureFilter::Linear, .mag_filter = Pica::TextureFilter::Linear,

View File

@ -104,6 +104,11 @@ bool Backend::BeginPresent() {
} }
void Backend::EndPresent() { 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()); scheduler.Submit(false, true, swapchain.GetAvailableSemaphore(), swapchain.GetPresentSemaphore());
swapchain.Present(); swapchain.Present();
} }

View File

@ -198,12 +198,11 @@ Pipeline::Pipeline(Instance& instance, CommandScheduler& scheduler, PoolManager&
for (int i = 0; i < info.shaders.size(); i++) { for (int i = 0; i < info.shaders.size(); i++) {
auto& shader = info.shaders[i]; auto& shader = info.shaders[i];
if (!shader.IsValid()) { if (!shader.IsValid()) {
shader_count = i; continue;
break;
} }
Shader* vk_shader = static_cast<Shader*>(shader.Get()); Shader* vk_shader = static_cast<Shader*>(shader.Get());
shader_stages[i] = vk::PipelineShaderStageCreateInfo{ shader_stages[shader_count++] = vk::PipelineShaderStageCreateInfo{
.stage = ToVkShaderStage(shader->GetStage()), .stage = ToVkShaderStage(shader->GetStage()),
.module = vk_shader->GetHandle(), .module = vk_shader->GetHandle(),
.pName = "main" .pName = "main"
@ -389,10 +388,15 @@ void Pipeline::Free() {
void Pipeline::BindTexture(u32 group, u32 slot, TextureHandle handle) { void Pipeline::BindTexture(u32 group, u32 slot, TextureHandle handle) {
Texture* texture = static_cast<Texture*>(handle.Get()); Texture* texture = static_cast<Texture*>(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 = { const DescriptorData data = {
.image_info = vk::DescriptorImageInfo{ .image_info = vk::DescriptorImageInfo{
.imageView = texture->GetView(), .imageView = texture->GetView(),
.imageLayout = texture->GetLayout() .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal
} }
}; };

View File

@ -74,8 +74,8 @@ void RenderpassCache::CreatePresentRenderpass(vk::Format format) {
if (!present_renderpass) { if (!present_renderpass) {
present_renderpass = CreateRenderPass(format, vk::Format::eUndefined, present_renderpass = CreateRenderPass(format, vk::Format::eUndefined,
vk::AttachmentLoadOp::eClear, vk::AttachmentLoadOp::eClear,
vk::ImageLayout::eUndefined, vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR); vk::ImageLayout::eColorAttachmentOptimal);
} }
} }

View File

@ -7,9 +7,9 @@
#include "video_core/common/pool_manager.h" #include "video_core/common/pool_manager.h"
#include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/renderer_vulkan/vk_instance.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_framebuffer.h"
#include "video_core/renderer_vulkan/vk_texture.h" #include "video_core/renderer_vulkan/vk_texture.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
namespace VideoCore::Vulkan { namespace VideoCore::Vulkan {
@ -167,6 +167,15 @@ void Swapchain::Present() {
current_frame = (current_frame + 1) % vk_images.size(); 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<Texture*>(textures[current_image].Get());
}
void Swapchain::Configure(u32 width, u32 height) { void Swapchain::Configure(u32 width, u32 height) {
vk::PhysicalDevice physical = instance.GetPhysicalDevice(); vk::PhysicalDevice physical = instance.GetPhysicalDevice();

View File

@ -16,10 +16,10 @@ class PoolManager;
namespace VideoCore::Vulkan { namespace VideoCore::Vulkan {
class Instance; class Instance;
class Backend;
class CommandScheduler; class CommandScheduler;
class Texture;
class RenderpassCache; class RenderpassCache;
class Texture;
class Framebuffer;
class Swapchain { class Swapchain {
public: public:
@ -36,9 +36,11 @@ public:
// Presents the current image and move to the next one // Presents the current image and move to the next one
void Present(); void Present();
FramebufferHandle GetCurrentFramebuffer() const { // Returns the framebuffer the current image is attached to
return framebuffers[current_image]; FramebufferHandle GetCurrentFramebuffer();
}
// Returns the current swapchain image
Texture* GetCurrentImage();
// Returns current swapchain state // Returns current swapchain state
vk::Extent2D GetExtent() const { vk::Extent2D GetExtent() const {
@ -70,11 +72,6 @@ public:
return render_finished; return render_finished;
} }
// Returns the current swapchain image
vk::Image GetCurrentImage() {
return vk_images[current_image];
}
// Returns true when the swapchain should be recreated // Returns true when the swapchain should be recreated
bool NeedsRecreation() const { bool NeedsRecreation() const {
return is_suboptimal || is_outdated; return is_suboptimal || is_outdated;

View File

@ -123,8 +123,7 @@ void CommandScheduler::Submit(bool wait_completion, bool begin_next,
command.upload_command_buffer.end(); command.upload_command_buffer.end();
} }
constexpr std::array<vk::PipelineStageFlags, 2> wait_stage_masks{ const std::array<vk::PipelineStageFlags, 1> wait_stage_masks = {
vk::PipelineStageFlagBits::eAllCommands,
vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eColorAttachmentOutput,
}; };

View File

@ -122,11 +122,6 @@ Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& p
// Create image view // Create image view
image_view = device.createImageView(view_info); 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, Texture::Texture(Instance& instance, CommandScheduler& scheduler, PoolManager& pool_manager,
@ -170,19 +165,10 @@ void Texture::Free() {
pool_manager.Free<Texture>(this); pool_manager.Free<Texture>(this);
} }
void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, u32 level, u32 level_count) { void Texture::TransitionSubresource(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout,
ASSERT(level + level_count < TEXTURE_MAX_LEVELS); u32 level, u32 level_count) {
// 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);
}
}
// Don't do anything if the image is already in the wanted layout // Don't do anything if the image is already in the wanted layout
if (new_layout == old_layout) { if (new_layout == layout) {
return; return;
} }
@ -245,13 +231,13 @@ void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_l
return info; return info;
}; };
LayoutInfo source = GetLayoutInfo(old_layout); LayoutInfo source = GetLayoutInfo(layout);
LayoutInfo dest = GetLayoutInfo(new_layout); LayoutInfo dest = GetLayoutInfo(new_layout);
const vk::ImageMemoryBarrier barrier = { const vk::ImageMemoryBarrier barrier = {
.srcAccessMask = source.access, .srcAccessMask = source.access,
.dstAccessMask = dest.access, .dstAccessMask = dest.access,
.oldLayout = old_layout, .oldLayout = layout,
.newLayout = new_layout, .newLayout = new_layout,
.image = image, .image = image,
.subresourceRange = {aspect, level, level_count, 0, 1} .subresourceRange = {aspect, level, level_count, 0, 1}
@ -263,11 +249,11 @@ void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_l
{}, {}, barrier); {}, {}, barrier);
// Update layouts // Update layouts
SetLayout(new_layout, level, level_count); layout = new_layout;
} }
void Texture::SetLayout(vk::ImageLayout new_layout, u32 level, u32 level_count) { void Texture::Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout) {
std::fill_n(layouts.begin() + level, level_count, new_layout); TransitionSubresource(command_buffer, new_layout, 0, info.levels);
} }
void Texture::Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32 level) { void Texture::Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32 level) {
@ -309,7 +295,7 @@ void Texture::Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32
std::memcpy(staging.GetMappedPtr(), data.data(), byte_count); std::memcpy(staging.GetMappedPtr(), data.data(), byte_count);
staging.Commit(byte_count); staging.Commit(byte_count);
Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal, level); Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal);
// Blit // Blit
command_buffer.blitImage(staging.GetHandle(), vk::ImageLayout::eGeneral, command_buffer.blitImage(staging.GetHandle(), vk::ImageLayout::eGeneral,
@ -361,7 +347,7 @@ void Texture::Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32
}; };
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); 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 // Copy staging buffer to the texture
command_buffer.copyBufferToImage(staging.GetHandle(), image, command_buffer.copyBufferToImage(staging.GetHandle(), image,
@ -373,11 +359,10 @@ void Texture::Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32
} }
void Texture::Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 level) { void Texture::Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 level) {
const u64 byte_count = data.size(); const std::size_t byte_count = data.size();
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
// If the adverised format supports blitting then use GPU accelerated // If the adverised format supports blitting use GPU accelerated format conversion.
// format conversion.
if (internal_format != advertised_format && if (internal_format != advertised_format &&
instance.IsFormatSupported(advertised_format, vk::FormatFeatureFlagBits::eBlitDst)) { instance.IsFormatSupported(advertised_format, vk::FormatFeatureFlagBits::eBlitDst)) {
// Creating a new staging texture for each upload/download is expensive // Creating a new staging texture for each upload/download is expensive
@ -407,7 +392,7 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 lev
.dstOffsets = offsets .dstOffsets = offsets
}; };
Transition(command_buffer, vk::ImageLayout::eTransferSrcOptimal, level); Transition(command_buffer, vk::ImageLayout::eTransferSrcOptimal);
// Blit // Blit
command_buffer.blitImage(image, vk::ImageLayout::eTransferSrcOptimal, command_buffer.blitImage(image, vk::ImageLayout::eTransferSrcOptimal,
@ -444,14 +429,12 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 lev
.imageExtent = {rectangle.width, rectangle.height, 1} .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 // Copy pixel data to the staging buffer
command_buffer.copyImageToBuffer(image, vk::ImageLayout::eTransferSrcOptimal, command_buffer.copyImageToBuffer(image, vk::ImageLayout::eTransferSrcOptimal,
staging.GetHandle(), copy_region); staging.GetHandle(), copy_region);
Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal);
// TODO: Async downloads // TODO: Async downloads
scheduler.Submit(true); scheduler.Submit(true);
@ -459,6 +442,8 @@ void Texture::Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 lev
auto memory = staging.Map(byte_count); auto memory = staging.Map(byte_count);
std::memcpy(data.data(), memory.data(), byte_count); std::memcpy(data.data(), memory.data(), byte_count);
} }
Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal);
} }
void Texture::BlitTo(TextureHandle dest, Common::Rectangle<u32> source_rect, Common::Rectangle<u32> dest_rect, void Texture::BlitTo(TextureHandle dest, Common::Rectangle<u32> source_rect, Common::Rectangle<u32> dest_rect,
@ -514,8 +499,8 @@ void Texture::GenerateMipmaps() {
vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer();
for (u32 i = 1; i < info.levels; i++) { for (u32 i = 1; i < info.levels; i++) {
Transition(command_buffer, vk::ImageLayout::eTransferSrcOptimal, i - 1); TransitionSubresource(command_buffer, vk::ImageLayout::eTransferSrcOptimal, i - 1);
Transition(command_buffer, vk::ImageLayout::eTransferDstOptimal, i); TransitionSubresource(command_buffer, vk::ImageLayout::eTransferDstOptimal, i);
const std::array source_offsets = { const std::array source_offsets = {
vk::Offset3D{0, 0, 0}, vk::Offset3D{0, 0, 0},
@ -551,7 +536,7 @@ void Texture::GenerateMipmaps() {
} }
// Prepare for shader reads // Prepare for shader reads
Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal, 0, info.levels); Transition(command_buffer, vk::ImageLayout::eShaderReadOnlyOptimal);
} }
void Texture::CopyFrom(TextureHandle source) { void Texture::CopyFrom(TextureHandle source) {

View File

@ -33,39 +33,36 @@ public:
vk::Image image, vk::Format format, const TextureInfo& info); vk::Image image, vk::Format format, const TextureInfo& info);
~Texture() override; ~Texture() override;
void Free() override; void Free() override;
void Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32 level = 0) override; void Upload(Rect2D rectangle, u32 stride, std::span<const u8> data, u32 level = 0) override;
void Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 level = 0) override; void Download(Rect2D rectangle, u32 stride, std::span<u8> data, u32 level = 0) override;
void BlitTo(TextureHandle dest, Common::Rectangle<u32> src_rectangle, Common::Rectangle<u32> dest_rect, void BlitTo(TextureHandle dest, Common::Rectangle<u32> src_rectangle, Common::Rectangle<u32> dest_rect,
u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override; u32 src_level = 0, u32 dest_level = 0, u32 src_layer = 0, u32 dest_layer = 0) override;
void CopyFrom(TextureHandle source) override; void CopyFrom(TextureHandle source) override;
void GenerateMipmaps() 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); void SetLayout(vk::ImageLayout new_layout, u32 level = 0, u32 level_count = 1);
/// Transitions part of the image to the provided layout // Transitions the image to the provided layout
void Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout, u32 level = 0, void Transition(vk::CommandBuffer command_buffer, vk::ImageLayout new_layout);
u32 level_count = 1); 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 { vk::Image GetHandle() const {
return image; return image;
} }
/// Returns the Vulka image view // Returns the Vulka image view
vk::ImageView GetView() const { vk::ImageView GetView() const {
return image_view; return image_view;
} }
/// Returns the internal format backing the texture. // Returns the internal format backing the texture.
/// It may not match the input pixel format. // It may not match the input pixel format.
vk::Format GetInternalFormat() const { vk::Format GetInternalFormat() const {
return internal_format; return internal_format;
} }
@ -74,12 +71,12 @@ public:
return aspect; return aspect;
} }
/// Returns the current image layout // Returns the current image layout
vk::ImageLayout GetLayout(u32 level = 0) const { 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 { vk::Rect2D GetArea() const {
return {{0, 0},{info.width, info.height}}; return {{0, 0},{info.width, info.height}};
} }
@ -99,7 +96,7 @@ private:
vk::Format advertised_format = vk::Format::eUndefined; vk::Format advertised_format = vk::Format::eUndefined;
vk::Format internal_format = vk::Format::eUndefined; vk::Format internal_format = vk::Format::eUndefined;
vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eNone; vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eNone;
std::array<vk::ImageLayout, TEXTURE_MAX_LEVELS> layouts{vk::ImageLayout::eUndefined}; vk::ImageLayout layout = vk::ImageLayout::eUndefined;
}; };
/** /**