renderer_vulkan: Add pipeline barriers for attachments
This commit is contained in:
@@ -28,7 +28,6 @@
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
MICROPROFILE_DEFINE(Vulkan_RenderFrame, "Vulkan", "Render Frame", MP_RGB(128, 128, 64));
|
||||
MICROPROFILE_DEFINE(Vulkan_WaitPresent, "Vulkan", "Wait For Present", MP_RGB(128, 128, 128));
|
||||
MICROPROFILE_DEFINE(Vulkan_SwapchainCopy, "Vulkan", "Swapchain Copy", MP_RGB(64, 64, 0));
|
||||
|
||||
namespace Vulkan {
|
||||
@@ -195,72 +194,57 @@ void RendererVulkan::PrepareRendertarget() {
|
||||
void RendererVulkan::RenderToMailbox(const Layout::FramebufferLayout& layout,
|
||||
std::unique_ptr<Frontend::TextureMailbox>& mailbox,
|
||||
bool flipped) {
|
||||
const vk::Device device = instance.GetDevice();
|
||||
Frontend::Frame* frame;
|
||||
{
|
||||
MICROPROFILE_SCOPE(Vulkan_WaitPresent);
|
||||
frame = mailbox->GetRenderFrame();
|
||||
Frontend::Frame* frame = mailbox->GetRenderFrame();
|
||||
MICROPROFILE_SCOPE(Vulkan_RenderFrame);
|
||||
|
||||
std::scoped_lock lock{frame->fence_mutex};
|
||||
[[maybe_unused]] vk::Result result =
|
||||
device.waitForFences(frame->present_done, false, std::numeric_limits<u64>::max());
|
||||
device.resetFences(frame->present_done);
|
||||
const auto [width, height] = swapchain.GetExtent();
|
||||
if (width != frame->width || height != frame->height) {
|
||||
mailbox->ReloadRenderFrame(frame, width, height);
|
||||
}
|
||||
|
||||
{
|
||||
MICROPROFILE_SCOPE(Vulkan_RenderFrame);
|
||||
scheduler.Record([layout](vk::CommandBuffer cmdbuf) {
|
||||
const vk::Viewport viewport = {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(layout.width),
|
||||
.height = static_cast<float>(layout.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
|
||||
const auto [width, height] = swapchain.GetExtent();
|
||||
if (width != frame->width || height != frame->height) {
|
||||
mailbox->ReloadRenderFrame(frame, width, height);
|
||||
}
|
||||
const vk::Rect2D scissor = {
|
||||
.offset = {0, 0},
|
||||
.extent = {layout.width, layout.height},
|
||||
};
|
||||
|
||||
scheduler.Record([layout](vk::CommandBuffer cmdbuf) {
|
||||
const vk::Viewport viewport = {
|
||||
.x = 0.0f,
|
||||
.y = 0.0f,
|
||||
.width = static_cast<float>(layout.width),
|
||||
.height = static_cast<float>(layout.height),
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f,
|
||||
};
|
||||
cmdbuf.setViewport(0, viewport);
|
||||
cmdbuf.setScissor(0, scissor);
|
||||
});
|
||||
|
||||
const vk::Rect2D scissor = {
|
||||
.offset = {0, 0},
|
||||
.extent = {layout.width, layout.height},
|
||||
};
|
||||
renderpass_cache.ExitRenderpass();
|
||||
scheduler.Record([this, framebuffer = frame->framebuffer, width = frame->width,
|
||||
height = frame->height](vk::CommandBuffer cmdbuf) {
|
||||
const vk::ClearValue clear{.color = clear_color};
|
||||
const vk::RenderPassBeginInfo renderpass_begin_info = {
|
||||
.renderPass = renderpass_cache.GetPresentRenderpass(),
|
||||
.framebuffer = framebuffer,
|
||||
.renderArea =
|
||||
vk::Rect2D{
|
||||
.offset = {0, 0},
|
||||
.extent = {width, height},
|
||||
},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clear,
|
||||
};
|
||||
|
||||
cmdbuf.setViewport(0, viewport);
|
||||
cmdbuf.setScissor(0, scissor);
|
||||
});
|
||||
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
|
||||
});
|
||||
|
||||
renderpass_cache.ExitRenderpass();
|
||||
DrawScreens(layout, flipped);
|
||||
|
||||
scheduler.Record([this, framebuffer = frame->framebuffer, width = frame->width,
|
||||
height = frame->height](vk::CommandBuffer cmdbuf) {
|
||||
const vk::ClearValue clear{.color = clear_color};
|
||||
const vk::RenderPassBeginInfo renderpass_begin_info = {
|
||||
.renderPass = renderpass_cache.GetPresentRenderpass(),
|
||||
.framebuffer = framebuffer,
|
||||
.renderArea =
|
||||
vk::Rect2D{
|
||||
.offset = {0, 0},
|
||||
.extent = {width, height},
|
||||
},
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clear,
|
||||
};
|
||||
|
||||
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
|
||||
});
|
||||
|
||||
DrawScreens(layout, flipped);
|
||||
|
||||
scheduler.Flush(frame->render_ready);
|
||||
scheduler.Record(
|
||||
[&mailbox, frame](vk::CommandBuffer) { mailbox->ReleaseRenderFrame(frame); });
|
||||
scheduler.DispatchWork();
|
||||
}
|
||||
scheduler.Flush(frame->render_ready);
|
||||
scheduler.Record([&mailbox, frame](vk::CommandBuffer) { mailbox->ReleaseRenderFrame(frame); });
|
||||
scheduler.DispatchWork();
|
||||
}
|
||||
|
||||
void RendererVulkan::BeginRendering() {
|
||||
@@ -1100,7 +1084,7 @@ void RendererVulkan::TryPresent(int timeout_ms, bool is_secondary) {
|
||||
cmdbuf.end();
|
||||
|
||||
static constexpr std::array<vk::PipelineStageFlags, 2> wait_stage_masks = {
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::PipelineStageFlagBits::eAllCommands,
|
||||
};
|
||||
|
||||
|
@@ -318,12 +318,12 @@ bool BlitHelper::BlitDepthStencil(Surface& source, Surface& dest,
|
||||
const std::array textures = {
|
||||
vk::DescriptorImageInfo{
|
||||
.sampler = nearest_sampler,
|
||||
.imageView = source.GetDepthView(),
|
||||
.imageView = source.DepthView(),
|
||||
.imageLayout = vk::ImageLayout::eGeneral,
|
||||
},
|
||||
vk::DescriptorImageInfo{
|
||||
.sampler = nearest_sampler,
|
||||
.imageView = source.GetStencilView(),
|
||||
.imageView = source.StencilView(),
|
||||
.imageLayout = vk::ImageLayout::eGeneral,
|
||||
},
|
||||
};
|
||||
@@ -348,15 +348,15 @@ void BlitHelper::BlitD24S8ToR32(Surface& source, Surface& dest,
|
||||
const VideoCore::TextureBlit& blit) {
|
||||
const std::array textures = {
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = source.GetDepthView(),
|
||||
.imageView = source.DepthView(),
|
||||
.imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
|
||||
},
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = source.GetStencilView(),
|
||||
.imageView = source.StencilView(),
|
||||
.imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
|
||||
},
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = dest.GetImageView(),
|
||||
.imageView = dest.ImageView(),
|
||||
.imageLayout = vk::ImageLayout::eGeneral,
|
||||
},
|
||||
};
|
||||
@@ -365,8 +365,8 @@ void BlitHelper::BlitD24S8ToR32(Surface& source, Surface& dest,
|
||||
device.updateDescriptorSetWithTemplate(set, compute_update_template, textures[0]);
|
||||
|
||||
renderpass_cache.ExitRenderpass();
|
||||
scheduler.Record([this, set, blit, src_image = source.alloc.image,
|
||||
dst_image = dest.alloc.image](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([this, set, blit, src_image = source.Image(),
|
||||
dst_image = dest.Image()](vk::CommandBuffer cmdbuf) {
|
||||
const std::array pre_barriers = {
|
||||
vk::ImageMemoryBarrier{
|
||||
.srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
|
@@ -147,15 +147,15 @@ void D24S8toRGBA8::Reinterpret(Surface& source, VideoCore::Rect2D src_rect, Surf
|
||||
VideoCore::Rect2D dst_rect) {
|
||||
const std::array textures = {
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = source.GetDepthView(),
|
||||
.imageView = source.DepthView(),
|
||||
.imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
|
||||
},
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = source.GetStencilView(),
|
||||
.imageView = source.StencilView(),
|
||||
.imageLayout = vk::ImageLayout::eDepthStencilReadOnlyOptimal,
|
||||
},
|
||||
vk::DescriptorImageInfo{
|
||||
.imageView = dest.GetImageView(),
|
||||
.imageView = dest.ImageView(),
|
||||
.imageLayout = vk::ImageLayout::eGeneral,
|
||||
},
|
||||
};
|
||||
@@ -164,8 +164,8 @@ void D24S8toRGBA8::Reinterpret(Surface& source, VideoCore::Rect2D src_rect, Surf
|
||||
device.updateDescriptorSetWithTemplate(set, update_template, textures[0]);
|
||||
|
||||
runtime.GetRenderpassCache().ExitRenderpass();
|
||||
scheduler.Record([this, set, src_rect, src_image = source.alloc.image,
|
||||
dst_image = dest.alloc.image](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([this, set, src_rect, src_image = source.Image(),
|
||||
dst_image = dest.Image()](vk::CommandBuffer cmdbuf) {
|
||||
const std::array pre_barriers = {
|
||||
vk::ImageMemoryBarrier{
|
||||
.srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
|
@@ -607,7 +607,8 @@ void PipelineCache::UseFragmentShader(const Pica::Regs& regs) {
|
||||
const vk::Device device = instance.GetDevice();
|
||||
|
||||
// When using SPIR-V emit the fragment shader on the main thread
|
||||
// since it's quite fast. This also heavily reduces flicker
|
||||
// since it's quite fast. This also heavily reduces flicker when
|
||||
// using asychronous shader compilation
|
||||
if (emit_spirv) {
|
||||
const std::vector code = GenerateFragmentShaderSPV(config);
|
||||
shader.module = CompileSPV(code, device);
|
||||
|
@@ -123,12 +123,12 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instan
|
||||
pipeline_cache.BindTexelBuffer(4, texture_rgba_view);
|
||||
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
pipeline_cache.BindTexture(i, null_surface.GetImageView());
|
||||
pipeline_cache.BindTexture(i, null_surface.ImageView());
|
||||
pipeline_cache.BindSampler(i, default_sampler);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 7; i++) {
|
||||
pipeline_cache.BindStorageImage(i, null_storage_surface.GetImageView());
|
||||
pipeline_cache.BindStorageImage(i, null_storage_surface.ImageView());
|
||||
}
|
||||
|
||||
// Explicitly call the derived version to avoid warnings about calling virtual
|
||||
@@ -542,9 +542,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
const u32 binding = static_cast<u32>(face);
|
||||
if (surface) {
|
||||
pipeline_cache.BindStorageImage(binding, surface->GetImageView());
|
||||
pipeline_cache.BindStorageImage(binding, surface->ImageView());
|
||||
} else {
|
||||
pipeline_cache.BindStorageImage(binding, null_storage_surface.GetImageView());
|
||||
pipeline_cache.BindStorageImage(binding, null_storage_surface.ImageView());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -585,9 +585,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
case TextureType::Shadow2D: {
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface) {
|
||||
pipeline_cache.BindStorageImage(0, surface->GetStorageView());
|
||||
pipeline_cache.BindStorageImage(0, surface->StorageView());
|
||||
} else {
|
||||
pipeline_cache.BindStorageImage(0, null_storage_surface.GetImageView());
|
||||
pipeline_cache.BindStorageImage(0, null_storage_surface.ImageView());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -617,9 +617,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
auto surface = res_cache.GetTextureCube(config);
|
||||
if (surface) {
|
||||
pipeline_cache.BindTexture(3, surface->GetImageView());
|
||||
pipeline_cache.BindTexture(3, surface->ImageView());
|
||||
} else {
|
||||
pipeline_cache.BindTexture(3, null_surface.GetImageView());
|
||||
pipeline_cache.BindTexture(3, null_surface.ImageView());
|
||||
}
|
||||
|
||||
BindSampler(3, texture_cube_sampler, texture.config);
|
||||
@@ -635,7 +635,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
|
||||
auto surface = res_cache.GetTextureSurface(texture);
|
||||
if (surface) {
|
||||
if (color_surface && color_surface->GetImageView() == surface->GetImageView()) {
|
||||
if (color_surface && color_surface->ImageView() == surface->ImageView()) {
|
||||
Surface temp{*color_surface, runtime};
|
||||
const VideoCore::TextureCopy copy = {
|
||||
.src_level = 0,
|
||||
@@ -647,9 +647,9 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
.extent = VideoCore::Extent{temp.GetScaledWidth(), temp.GetScaledHeight()}};
|
||||
|
||||
runtime.CopyTextures(*color_surface, temp, copy);
|
||||
pipeline_cache.BindTexture(texture_index, temp.GetImageView());
|
||||
pipeline_cache.BindTexture(texture_index, temp.ImageView());
|
||||
} else {
|
||||
pipeline_cache.BindTexture(texture_index, surface->GetImageView());
|
||||
pipeline_cache.BindTexture(texture_index, surface->ImageView());
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -660,10 +660,10 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
|
||||
// the geometry in question.
|
||||
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
|
||||
// on the male character's face, which in the OpenGL default appear black.
|
||||
pipeline_cache.BindTexture(texture_index, null_surface.GetImageView());
|
||||
pipeline_cache.BindTexture(texture_index, null_surface.ImageView());
|
||||
}
|
||||
} else {
|
||||
pipeline_cache.BindTexture(texture_index, null_surface.GetImageView());
|
||||
pipeline_cache.BindTexture(texture_index, null_surface.ImageView());
|
||||
pipeline_cache.BindSampler(texture_index, default_sampler);
|
||||
}
|
||||
}
|
||||
@@ -1016,7 +1016,7 @@ bool RasterizerVulkan::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con
|
||||
(float)src_rect.bottom / (float)scaled_height, (float)src_rect.left / (float)scaled_width,
|
||||
(float)src_rect.top / (float)scaled_height, (float)src_rect.right / (float)scaled_width);
|
||||
|
||||
screen_info.image_view = src_surface->GetImageView();
|
||||
screen_info.image_view = src_surface->ImageView();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -48,6 +48,7 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
|
||||
u32 height = UINT32_MAX;
|
||||
u32 cursor = 0;
|
||||
std::array<VideoCore::PixelFormat, 2> formats{};
|
||||
std::array<vk::Image, 2> images{};
|
||||
std::array<vk::ImageView, 2> views{};
|
||||
|
||||
const auto Prepare = [&](Surface* const surface) {
|
||||
@@ -59,12 +60,37 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
|
||||
width = std::min(width, surface->GetScaledWidth());
|
||||
height = std::min(height, surface->GetScaledHeight());
|
||||
formats[cursor] = surface->pixel_format;
|
||||
views[cursor++] = surface->GetFramebufferView();
|
||||
images[cursor] = surface->Image();
|
||||
views[cursor++] = surface->FramebufferView();
|
||||
};
|
||||
|
||||
Prepare(color);
|
||||
Prepare(depth_stencil);
|
||||
|
||||
const RenderingInfo new_info = {
|
||||
.color =
|
||||
RenderTarget{
|
||||
.aspect = vk::ImageAspectFlagBits::eColor,
|
||||
.image = images[0],
|
||||
.image_view = views[0],
|
||||
},
|
||||
.depth =
|
||||
RenderTarget{
|
||||
.aspect = depth_stencil ? depth_stencil->Aspect() : vk::ImageAspectFlagBits::eDepth,
|
||||
.image = images[1],
|
||||
.image_view = views[1],
|
||||
},
|
||||
.render_area = render_area,
|
||||
.clear = clear,
|
||||
.do_clear = do_clear,
|
||||
};
|
||||
|
||||
const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass);
|
||||
if (info == new_info && rendering && !is_dirty) {
|
||||
cmd_count++;
|
||||
return;
|
||||
}
|
||||
|
||||
const vk::RenderPass renderpass = GetRenderpass(formats[0], formats[1], do_clear);
|
||||
|
||||
const FramebufferInfo framebuffer_info = {
|
||||
@@ -79,38 +105,24 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
|
||||
it->second = CreateFramebuffer(framebuffer_info, renderpass);
|
||||
}
|
||||
|
||||
const RenderpassState new_state = {
|
||||
.renderpass = renderpass,
|
||||
.framebuffer = it->second,
|
||||
.render_area = render_area,
|
||||
.clear = clear,
|
||||
};
|
||||
|
||||
const u64 new_state_hash = Common::ComputeStructHash64(new_state);
|
||||
const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass);
|
||||
if (state_hash == new_state_hash && rendering && !is_dirty) {
|
||||
cmd_count++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rendering) {
|
||||
ExitRenderpass();
|
||||
}
|
||||
scheduler.Record(
|
||||
[render_area, clear, renderpass, framebuffer = it->second](vk::CommandBuffer cmdbuf) {
|
||||
const vk::RenderPassBeginInfo renderpass_begin_info = {
|
||||
.renderPass = renderpass,
|
||||
.framebuffer = framebuffer,
|
||||
.renderArea = render_area,
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &clear,
|
||||
};
|
||||
|
||||
scheduler.Record([new_state](vk::CommandBuffer cmdbuf) {
|
||||
const vk::RenderPassBeginInfo renderpass_begin_info = {
|
||||
.renderPass = new_state.renderpass,
|
||||
.framebuffer = new_state.framebuffer,
|
||||
.renderArea = new_state.render_area,
|
||||
.clearValueCount = 1,
|
||||
.pClearValues = &new_state.clear,
|
||||
};
|
||||
|
||||
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
|
||||
});
|
||||
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
|
||||
});
|
||||
|
||||
scheduler.MarkStateNonDirty(StateFlags::Renderpass);
|
||||
state_hash = new_state_hash;
|
||||
info = new_info;
|
||||
rendering = true;
|
||||
}
|
||||
|
||||
@@ -120,13 +132,65 @@ void RenderpassCache::ExitRenderpass() {
|
||||
}
|
||||
|
||||
rendering = false;
|
||||
scheduler.Record([dynamic_rendering = dynamic_rendering](vk::CommandBuffer cmdbuf) {
|
||||
if (dynamic_rendering) {
|
||||
cmdbuf.endRenderingKHR();
|
||||
} else {
|
||||
cmdbuf.endRenderPass();
|
||||
}
|
||||
});
|
||||
scheduler.Record(
|
||||
[info = info, dynamic_rendering = dynamic_rendering](vk::CommandBuffer cmdbuf) {
|
||||
u32 num_barriers = 0;
|
||||
std::array<vk::ImageMemoryBarrier, 2> barriers;
|
||||
vk::PipelineStageFlags src_stage{};
|
||||
vk::PipelineStageFlags dst_stage{};
|
||||
|
||||
if (info.color) {
|
||||
barriers[num_barriers++] = vk::ImageMemoryBarrier{
|
||||
.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite,
|
||||
.dstAccessMask = vk::AccessFlagBits::eShaderRead,
|
||||
.oldLayout = vk::ImageLayout::eGeneral,
|
||||
.newLayout = vk::ImageLayout::eGeneral,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = info.color.image,
|
||||
.subresourceRange{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
|
||||
src_stage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||
dst_stage |= vk::PipelineStageFlagBits::eFragmentShader;
|
||||
}
|
||||
if (info.depth) {
|
||||
barriers[num_barriers++] = vk::ImageMemoryBarrier{
|
||||
.srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentRead |
|
||||
vk::AccessFlagBits::eDepthStencilAttachmentWrite,
|
||||
.oldLayout = vk::ImageLayout::eGeneral,
|
||||
.newLayout = vk::ImageLayout::eGeneral,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = info.depth.image,
|
||||
.subresourceRange{
|
||||
.aspectMask = info.depth.aspect,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
|
||||
src_stage |= vk::PipelineStageFlagBits::eEarlyFragmentTests |
|
||||
vk::PipelineStageFlagBits::eLateFragmentTests;
|
||||
dst_stage |= vk::PipelineStageFlagBits::eLateFragmentTests;
|
||||
}
|
||||
if (dynamic_rendering) {
|
||||
cmdbuf.endRenderingKHR();
|
||||
} else {
|
||||
cmdbuf.endRenderPass();
|
||||
}
|
||||
cmdbuf.pipelineBarrier(src_stage, dst_stage, vk::DependencyFlagBits::eByRegion, 0,
|
||||
nullptr, 0, nullptr, num_barriers, barriers.data());
|
||||
});
|
||||
|
||||
// The Mali guide recommends flushing at the end of each major renderpass
|
||||
// Testing has shown this has a significant effect on rendering performance
|
||||
@@ -138,33 +202,40 @@ void RenderpassCache::ExitRenderpass() {
|
||||
|
||||
void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_stencil,
|
||||
vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) {
|
||||
RenderingState new_state = {
|
||||
RenderingInfo new_info = {
|
||||
.render_area = render_area,
|
||||
.clear = clear,
|
||||
.do_clear = do_clear,
|
||||
};
|
||||
|
||||
if (color) {
|
||||
new_state.color_view = color->GetFramebufferView();
|
||||
new_info.color = RenderTarget{
|
||||
.aspect = vk::ImageAspectFlagBits::eColor,
|
||||
.image = color->Image(),
|
||||
.image_view = color->FramebufferView(),
|
||||
};
|
||||
}
|
||||
if (depth_stencil) {
|
||||
new_state.depth_view = depth_stencil->GetFramebufferView();
|
||||
new_info.depth = RenderTarget{
|
||||
.aspect = depth_stencil->Aspect(),
|
||||
.image = depth_stencil->Image(),
|
||||
.image_view = depth_stencil->FramebufferView(),
|
||||
};
|
||||
}
|
||||
|
||||
const u64 new_state_hash = Common::ComputeStructHash64(new_state);
|
||||
const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass);
|
||||
if (state_hash == new_state_hash && rendering && !is_dirty) {
|
||||
if (info == new_info && rendering && !is_dirty) {
|
||||
cmd_count++;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool has_stencil =
|
||||
depth_stencil && depth_stencil->type == VideoCore::SurfaceType::DepthStencil;
|
||||
|
||||
if (rendering) {
|
||||
ExitRenderpass();
|
||||
}
|
||||
|
||||
const bool has_stencil =
|
||||
depth_stencil && depth_stencil->type == VideoCore::SurfaceType::DepthStencil;
|
||||
scheduler.Record([new_state, has_stencil](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([new_info, has_stencil](vk::CommandBuffer cmdbuf) {
|
||||
u32 cursor = 0;
|
||||
std::array<vk::RenderingAttachmentInfoKHR, 2> infos{};
|
||||
|
||||
@@ -178,21 +249,20 @@ void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_
|
||||
.imageView = image_view,
|
||||
.imageLayout = vk::ImageLayout::eGeneral,
|
||||
.loadOp =
|
||||
new_state.do_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
|
||||
new_info.do_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
|
||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||
.clearValue = new_state.clear,
|
||||
.clearValue = new_info.clear,
|
||||
};
|
||||
};
|
||||
|
||||
Prepare(new_state.color_view);
|
||||
Prepare(new_state.depth_view);
|
||||
Prepare(new_info.color.image_view);
|
||||
Prepare(new_info.depth.image_view);
|
||||
|
||||
const u32 color_attachment_count = new_state.color_view ? 1u : 0u;
|
||||
const vk::RenderingAttachmentInfoKHR* depth_info =
|
||||
new_state.depth_view ? &infos[1] : nullptr;
|
||||
const u32 color_attachment_count = new_info.color ? 1u : 0u;
|
||||
const vk::RenderingAttachmentInfoKHR* depth_info = new_info.depth ? &infos[1] : nullptr;
|
||||
const vk::RenderingAttachmentInfoKHR* stencil_info = has_stencil ? &infos[1] : nullptr;
|
||||
const vk::RenderingInfoKHR rendering_info = {
|
||||
.renderArea = new_state.render_area,
|
||||
.renderArea = new_info.render_area,
|
||||
.layerCount = 1,
|
||||
.colorAttachmentCount = color_attachment_count,
|
||||
.pColorAttachments = &infos[0],
|
||||
@@ -204,7 +274,7 @@ void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_
|
||||
});
|
||||
|
||||
scheduler.MarkStateNonDirty(StateFlags::Renderpass);
|
||||
state_hash = new_state_hash;
|
||||
info = new_info;
|
||||
rendering = true;
|
||||
}
|
||||
|
||||
|
@@ -40,8 +40,8 @@ struct hash<Vulkan::FramebufferInfo> {
|
||||
namespace Vulkan {
|
||||
|
||||
class RenderpassCache {
|
||||
static constexpr u32 MAX_COLOR_FORMATS = 5;
|
||||
static constexpr u32 MAX_DEPTH_FORMATS = 4;
|
||||
static constexpr std::size_t MAX_COLOR_FORMATS = 5;
|
||||
static constexpr std::size_t MAX_DEPTH_FORMATS = 4;
|
||||
|
||||
public:
|
||||
RenderpassCache(const Instance& instance, Scheduler& scheduler);
|
||||
@@ -80,26 +80,31 @@ private:
|
||||
vk::Framebuffer CreateFramebuffer(const FramebufferInfo& info, vk::RenderPass renderpass);
|
||||
|
||||
private:
|
||||
struct RenderpassState {
|
||||
vk::RenderPass renderpass;
|
||||
vk::Framebuffer framebuffer;
|
||||
vk::Rect2D render_area;
|
||||
vk::ClearValue clear;
|
||||
struct RenderTarget {
|
||||
vk::ImageAspectFlags aspect;
|
||||
vk::Image image;
|
||||
vk::ImageView image_view;
|
||||
|
||||
[[nodiscard]] bool operator==(const RenderpassState& other) const {
|
||||
return std::memcmp(this, &other, sizeof(RenderpassState)) == 0;
|
||||
operator bool() const noexcept {
|
||||
return image;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const RenderTarget& other) const {
|
||||
return image_view == other.image_view;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderingState {
|
||||
vk::ImageView color_view;
|
||||
vk::ImageView depth_view;
|
||||
struct RenderingInfo {
|
||||
RenderTarget color;
|
||||
RenderTarget depth;
|
||||
vk::Rect2D render_area;
|
||||
vk::ClearValue clear;
|
||||
bool do_clear;
|
||||
|
||||
[[nodiscard]] bool operator==(const RenderpassState& other) const {
|
||||
return std::memcmp(this, &other, sizeof(RenderpassState)) == 0;
|
||||
[[nodiscard]] bool operator==(const RenderingInfo& other) const {
|
||||
return color == other.color && depth == other.depth &&
|
||||
render_area == other.render_area && do_clear == other.do_clear &&
|
||||
std::memcmp(&clear, &other.clear, sizeof(vk::ClearValue)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -108,10 +113,10 @@ private:
|
||||
vk::RenderPass present_renderpass{};
|
||||
vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS + 1][MAX_DEPTH_FORMATS + 1][2];
|
||||
std::unordered_map<FramebufferInfo, vk::Framebuffer> framebuffers;
|
||||
RenderingInfo info{};
|
||||
bool rendering = false;
|
||||
bool dynamic_rendering = false;
|
||||
u32 cmd_count{};
|
||||
u64 state_hash{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
|
||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
|
||||
@@ -9,6 +10,8 @@
|
||||
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
MICROPROFILE_DEFINE(Vulkan_WaitPresent, "Vulkan", "Wait For Present", MP_RGB(128, 128, 128));
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
TextureMailbox::TextureMailbox(const Instance& instance_, const Swapchain& swapchain_,
|
||||
@@ -129,14 +132,44 @@ void TextureMailbox::ReloadRenderFrame(Frontend::Frame* frame, u32 width, u32 he
|
||||
}
|
||||
|
||||
Frontend::Frame* TextureMailbox::GetRenderFrame() {
|
||||
std::unique_lock lock{free_mutex};
|
||||
MICROPROFILE_SCOPE(Vulkan_WaitPresent);
|
||||
|
||||
if (free_queue.empty()) {
|
||||
free_cv.wait(lock, [&] { return !free_queue.empty(); });
|
||||
Frontend::Frame* frame{};
|
||||
{
|
||||
std::unique_lock lock{free_mutex};
|
||||
if (free_queue.empty()) {
|
||||
free_cv.wait(lock, [&] { return !free_queue.empty(); });
|
||||
}
|
||||
|
||||
frame = free_queue.front();
|
||||
free_queue.pop();
|
||||
}
|
||||
|
||||
Frontend::Frame* frame = free_queue.front();
|
||||
free_queue.pop();
|
||||
std::scoped_lock lock{frame->fence_mutex};
|
||||
|
||||
vk::Device device = instance.GetDevice();
|
||||
vk::Result result{};
|
||||
|
||||
const auto Wait = [&]() {
|
||||
result = device.waitForFences(frame->present_done, false, std::numeric_limits<u64>::max());
|
||||
return result;
|
||||
};
|
||||
|
||||
while (Wait() != vk::Result::eSuccess) {
|
||||
// Retry if the waiting time out
|
||||
if (result == vk::Result::eTimeout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// eErrorInitializationFailed occurs on Mali GPU drivers due to them
|
||||
// using the ppoll() syscall which isn't correctly restarted after a signal,
|
||||
// we need to manually retry waiting in that case
|
||||
if (result == vk::Result::eErrorInitializationFailed) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
device.resetFences(frame->present_done);
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
@@ -935,7 +935,7 @@ void Surface::Download(const VideoCore::BufferTextureCopy& download, const Stagi
|
||||
// to avoid having the interleave the data later. These should(?) be
|
||||
// uncommon anyways and the perf hit is very small
|
||||
if (type == VideoCore::SurfaceType::DepthStencil) {
|
||||
return DepthStencilDownload(download, staging);
|
||||
return /*DepthStencilDownload(download, staging)*/;
|
||||
}
|
||||
|
||||
const bool is_scaled = res_scale != 1;
|
||||
|
@@ -162,7 +162,6 @@ private:
|
||||
|
||||
class Surface : public VideoCore::SurfaceBase<Surface> {
|
||||
friend class TextureRuntime;
|
||||
friend class RasterizerVulkan;
|
||||
|
||||
public:
|
||||
Surface(TextureRuntime& runtime);
|
||||
@@ -186,29 +185,39 @@ public:
|
||||
/// Returns the pipeline stage flags indicative of the surface
|
||||
vk::PipelineStageFlags PipelineStageFlags() const noexcept;
|
||||
|
||||
/// Returns the surface aspect
|
||||
vk::ImageAspectFlags Aspect() const noexcept {
|
||||
return alloc.aspect;
|
||||
}
|
||||
|
||||
/// Returns the surface image handle
|
||||
vk::Image Image() const noexcept {
|
||||
return alloc.image;
|
||||
}
|
||||
|
||||
/// Returns an image view used to sample the surface from a shader
|
||||
vk::ImageView GetImageView() const noexcept {
|
||||
vk::ImageView ImageView() const noexcept {
|
||||
return alloc.image_view;
|
||||
}
|
||||
|
||||
/// Returns an image view used to create a framebuffer
|
||||
vk::ImageView GetFramebufferView() noexcept {
|
||||
vk::ImageView FramebufferView() noexcept {
|
||||
is_framebuffer = true;
|
||||
return alloc.base_view;
|
||||
}
|
||||
|
||||
/// Returns the depth only image view of the surface, null otherwise
|
||||
vk::ImageView GetDepthView() const noexcept {
|
||||
vk::ImageView DepthView() const noexcept {
|
||||
return alloc.depth_view;
|
||||
}
|
||||
|
||||
/// Returns the stencil only image view of the surface, null otherwise
|
||||
vk::ImageView GetStencilView() const noexcept {
|
||||
vk::ImageView StencilView() const noexcept {
|
||||
return alloc.stencil_view;
|
||||
}
|
||||
|
||||
/// Returns the R32 image view used for atomic load/store
|
||||
vk::ImageView GetStorageView() noexcept {
|
||||
vk::ImageView StorageView() noexcept {
|
||||
if (!alloc.storage_view) {
|
||||
LOG_CRITICAL(Render_Vulkan,
|
||||
"Surface with pixel format {} and internal format {} "
|
||||
@@ -220,11 +229,6 @@ public:
|
||||
return alloc.storage_view;
|
||||
}
|
||||
|
||||
/// Returns the internal format of the allocated texture
|
||||
vk::Format GetInternalFormat() const noexcept {
|
||||
return alloc.format;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Uploads pixel data to scaled texture
|
||||
void ScaledUpload(const VideoCore::BufferTextureCopy& upload, const StagingData& staging);
|
||||
@@ -240,12 +244,10 @@ private:
|
||||
TextureRuntime& runtime;
|
||||
const Instance& instance;
|
||||
Scheduler& scheduler;
|
||||
|
||||
public:
|
||||
bool is_framebuffer{};
|
||||
bool is_storage{};
|
||||
ImageAlloc alloc;
|
||||
FormatTraits traits;
|
||||
bool is_framebuffer{};
|
||||
bool is_storage{};
|
||||
};
|
||||
|
||||
struct Traits {
|
||||
|
Reference in New Issue
Block a user