renderer_vulkan: Transition swapchain manually
This commit is contained in:
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user