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() {
//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,

View File

@ -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();
}

View File

@ -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*>(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<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 = {
.image_info = vk::DescriptorImageInfo{
.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) {
present_renderpass = CreateRenderPass(format, vk::Format::eUndefined,
vk::AttachmentLoadOp::eClear,
vk::ImageLayout::eUndefined,
vk::ImageLayout::ePresentSrcKHR);
vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::eColorAttachmentOptimal);
}
}

View File

@ -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<Texture*>(textures[current_image].Get());
}
void Swapchain::Configure(u32 width, u32 height) {
vk::PhysicalDevice physical = instance.GetPhysicalDevice();

View File

@ -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;

View File

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

View File

@ -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<Texture>(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<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);
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<const u8> 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<const u8> data, u32
}
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();
// 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<u8> 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<u8> 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<u8> 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<u32> source_rect, Common::Rectangle<u32> 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) {

View File

@ -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<const 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,
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<vk::ImageLayout, TEXTURE_MAX_LEVELS> layouts{vk::ImageLayout::eUndefined};
vk::ImageLayout layout = vk::ImageLayout::eUndefined;
};
/**