|
|
|
@ -15,14 +15,14 @@
|
|
|
|
|
|
|
|
|
|
#include "PrecompiledHeader.h"
|
|
|
|
|
|
|
|
|
|
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
|
|
|
|
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
|
|
|
|
#include "GS/Renderers/Vulkan/VKShaderCache.h"
|
|
|
|
|
#include "GS/Renderers/Vulkan/VKSwapChain.h"
|
|
|
|
|
#include "GS/GS.h"
|
|
|
|
|
#include "GS/GSGL.h"
|
|
|
|
|
#include "GS/GSPerfMon.h"
|
|
|
|
|
#include "GS/GSUtil.h"
|
|
|
|
|
#include "GS/Renderers/Vulkan/GSDeviceVK.h"
|
|
|
|
|
#include "GS/Renderers/Vulkan/VKBuilders.h"
|
|
|
|
|
#include "GS/Renderers/Vulkan/VKShaderCache.h"
|
|
|
|
|
#include "GS/Renderers/Vulkan/VKSwapChain.h"
|
|
|
|
|
|
|
|
|
|
#include "Host.h"
|
|
|
|
|
|
|
|
|
@ -33,9 +33,9 @@
|
|
|
|
|
#include "imgui.h"
|
|
|
|
|
|
|
|
|
|
#include <bit>
|
|
|
|
|
#include <limits>
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
|
|
// Tweakables
|
|
|
|
|
enum : u32
|
|
|
|
@ -60,8 +60,14 @@ enum : u32
|
|
|
|
|
static u32 s_debug_scope_depth = 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static bool IsDATMConvertShader(ShaderConvert i) { return (i == ShaderConvert::DATM_0 || i == ShaderConvert::DATM_1); }
|
|
|
|
|
static bool IsDATEModePrimIDInit(u32 flag) { return flag == 1 || flag == 2; }
|
|
|
|
|
static bool IsDATMConvertShader(ShaderConvert i)
|
|
|
|
|
{
|
|
|
|
|
return (i == ShaderConvert::DATM_0 || i == ShaderConvert::DATM_1);
|
|
|
|
|
}
|
|
|
|
|
static bool IsDATEModePrimIDInit(u32 flag)
|
|
|
|
|
{
|
|
|
|
|
return flag == 1 || flag == 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static VkAttachmentLoadOp GetLoadOpForTexture(GSTextureVK* tex)
|
|
|
|
|
{
|
|
|
|
@ -804,8 +810,8 @@ bool GSDeviceVK::CreateCommandBuffers()
|
|
|
|
|
}
|
|
|
|
|
for (u32 i = 0; i < resources.command_buffers.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
Vulkan::SetObjectName(m_device, resources.command_buffers[i],
|
|
|
|
|
"Frame %u %sCommand Buffer", frame_index, (i == 0) ? "Init" : "");
|
|
|
|
|
Vulkan::SetObjectName(m_device, resources.command_buffers[i], "Frame %u %sCommand Buffer", frame_index,
|
|
|
|
|
(i == 0) ? "Init" : "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkFenceCreateInfo fence_info = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT};
|
|
|
|
@ -1540,9 +1546,10 @@ VkRenderPass GSDeviceVK::CreateCachedRenderPass(RenderPassCacheKey key)
|
|
|
|
|
}
|
|
|
|
|
if (key.depth_format != VK_FORMAT_UNDEFINED)
|
|
|
|
|
{
|
|
|
|
|
const VkImageLayout layout = key.depth_sampling ? (UseFeedbackLoopLayout() ? VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT :
|
|
|
|
|
const VkImageLayout layout =
|
|
|
|
|
key.depth_sampling ? (UseFeedbackLoopLayout() ? VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT :
|
|
|
|
|
VK_IMAGE_LAYOUT_GENERAL) :
|
|
|
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
|
attachments[num_attachments] = {0, static_cast<VkFormat>(key.depth_format), VK_SAMPLE_COUNT_1_BIT,
|
|
|
|
|
static_cast<VkAttachmentLoadOp>(key.depth_load_op), static_cast<VkAttachmentStoreOp>(key.depth_store_op),
|
|
|
|
|
static_cast<VkAttachmentLoadOp>(key.stencil_load_op),
|
|
|
|
@ -2018,8 +2025,7 @@ bool GSDeviceVK::IsSuitableDefaultRenderer()
|
|
|
|
|
Console.WriteLn(fmt::format("Using Vulkan GPU '{}' for automatic renderer check.", name));
|
|
|
|
|
|
|
|
|
|
// Any software rendering (LLVMpipe, SwiftShader).
|
|
|
|
|
if (StringUtil::StartsWithNoCase(name, "llvmpipe") ||
|
|
|
|
|
StringUtil::StartsWithNoCase(name, "SwiftShader"))
|
|
|
|
|
if (StringUtil::StartsWithNoCase(name, "llvmpipe") || StringUtil::StartsWithNoCase(name, "SwiftShader"))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLn(Color_StrongOrange, "Not using Vulkan for software renderer.");
|
|
|
|
|
return false;
|
|
|
|
@ -2093,9 +2099,8 @@ bool GSDeviceVK::Create()
|
|
|
|
|
if (!CreateBuffers())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!CompileConvertPipelines() || !CompilePresentPipelines() ||
|
|
|
|
|
!CompileInterlacePipelines() || !CompileMergePipelines() ||
|
|
|
|
|
!CompilePostProcessingPipelines() || !InitSpinResources())
|
|
|
|
|
if (!CompileConvertPipelines() || !CompilePresentPipelines() || !CompileInterlacePipelines() ||
|
|
|
|
|
!CompileMergePipelines() || !CompilePostProcessingPipelines() || !InitSpinResources())
|
|
|
|
|
{
|
|
|
|
|
Host::ReportErrorAsync("GS", "Failed to compile utility pipelines");
|
|
|
|
|
return false;
|
|
|
|
@ -2243,15 +2248,13 @@ std::string GSDeviceVK::GetDriverInfo() const
|
|
|
|
|
VK_VERSION_MAJOR(driver_version), VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version),
|
|
|
|
|
VK_API_VERSION_MAJOR(api_version), VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version),
|
|
|
|
|
props.conformanceVersion.major, props.conformanceVersion.minor, props.conformanceVersion.subminor,
|
|
|
|
|
props.conformanceVersion.patch, props.driverInfo, props.driverName,
|
|
|
|
|
m_device_properties.deviceName);
|
|
|
|
|
props.conformanceVersion.patch, props.driverInfo, props.driverName, m_device_properties.deviceName);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret = StringUtil::StdStringFromFormat("Driver %u.%u.%u\nVulkan %u.%u.%u\n%s", VK_VERSION_MAJOR(driver_version),
|
|
|
|
|
VK_VERSION_MINOR(driver_version), VK_VERSION_PATCH(driver_version), VK_API_VERSION_MAJOR(api_version),
|
|
|
|
|
VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version),
|
|
|
|
|
m_device_properties.deviceName);
|
|
|
|
|
VK_API_VERSION_MINOR(api_version), VK_API_VERSION_PATCH(api_version), m_device_properties.deviceName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
@ -2344,8 +2347,8 @@ GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip)
|
|
|
|
|
return GSDevice::PresentResult::FrameSkipped;
|
|
|
|
|
|
|
|
|
|
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr,
|
|
|
|
|
GetRenderPass(swap_chain_texture->GetVkFormat(), VK_FORMAT_UNDEFINED,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE),
|
|
|
|
|
GetRenderPass(swap_chain_texture->GetVkFormat(), VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
|
|
|
VK_ATTACHMENT_STORE_OP_STORE),
|
|
|
|
|
fb,
|
|
|
|
|
{{0, 0}, {static_cast<u32>(swap_chain_texture->GetWidth()), static_cast<u32>(swap_chain_texture->GetHeight())}},
|
|
|
|
|
1u, &s_present_clear_color};
|
|
|
|
@ -2599,7 +2602,8 @@ bool GSDeviceVK::CheckFeatures()
|
|
|
|
|
const bool isAMD = (vendorID == 0x1002 || vendorID == 0x1022);
|
|
|
|
|
// const bool isNVIDIA = (vendorID == 0x10DE);
|
|
|
|
|
|
|
|
|
|
m_features.framebuffer_fetch = m_optional_extensions.vk_ext_rasterization_order_attachment_access && !GSConfig.DisableFramebufferFetch;
|
|
|
|
|
m_features.framebuffer_fetch =
|
|
|
|
|
m_optional_extensions.vk_ext_rasterization_order_attachment_access && !GSConfig.DisableFramebufferFetch;
|
|
|
|
|
m_features.texture_barrier = GSConfig.OverrideTextureBarriers != 0;
|
|
|
|
|
m_features.broken_point_sampler = isAMD;
|
|
|
|
|
|
|
|
|
@ -2617,8 +2621,7 @@ bool GSDeviceVK::CheckFeatures()
|
|
|
|
|
m_features.provoking_vertex_last = m_optional_extensions.vk_ext_provoking_vertex;
|
|
|
|
|
m_features.dual_source_blend = m_device_features.dualSrcBlend && !GSConfig.DisableDualSourceBlend;
|
|
|
|
|
m_features.clip_control = true;
|
|
|
|
|
m_features.vs_expand =
|
|
|
|
|
!GSConfig.DisableVertexShaderExpand && m_optional_extensions.vk_khr_shader_draw_parameters;
|
|
|
|
|
m_features.vs_expand = !GSConfig.DisableVertexShaderExpand && m_optional_extensions.vk_khr_shader_draw_parameters;
|
|
|
|
|
|
|
|
|
|
if (!m_features.dual_source_blend)
|
|
|
|
|
Console.Warning("Vulkan driver is missing dual-source blending. This will have an impact on performance.");
|
|
|
|
@ -2627,13 +2630,15 @@ bool GSDeviceVK::CheckFeatures()
|
|
|
|
|
Console.Warning("Texture buffers are disabled. This may break some graphical effects.");
|
|
|
|
|
|
|
|
|
|
if (!m_optional_extensions.vk_ext_line_rasterization)
|
|
|
|
|
Console.WriteLn("VK_EXT_line_rasterization or the BRESENHAM mode is not supported, this may cause rendering inaccuracies.");
|
|
|
|
|
Console.WriteLn(
|
|
|
|
|
"VK_EXT_line_rasterization or the BRESENHAM mode is not supported, this may cause rendering inaccuracies.");
|
|
|
|
|
|
|
|
|
|
// Test for D32S8 support.
|
|
|
|
|
{
|
|
|
|
|
VkFormatProperties props = {};
|
|
|
|
|
vkGetPhysicalDeviceFormatProperties(m_physical_device, VK_FORMAT_D32_SFLOAT_S8_UINT, &props);
|
|
|
|
|
m_features.stencil_buffer = ((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0);
|
|
|
|
|
m_features.stencil_buffer =
|
|
|
|
|
((props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fbfetch is useless if we don't have barriers enabled.
|
|
|
|
@ -2647,8 +2652,8 @@ bool GSDeviceVK::CheckFeatures()
|
|
|
|
|
|
|
|
|
|
// whether we can do point/line expand depends on the range of the device
|
|
|
|
|
const float f_upscale = static_cast<float>(GSConfig.UpscaleMultiplier);
|
|
|
|
|
m_features.point_expand =
|
|
|
|
|
(m_device_features.largePoints && limits.pointSizeRange[0] <= f_upscale && limits.pointSizeRange[1] >= f_upscale);
|
|
|
|
|
m_features.point_expand = (m_device_features.largePoints && limits.pointSizeRange[0] <= f_upscale &&
|
|
|
|
|
limits.pointSizeRange[1] >= f_upscale);
|
|
|
|
|
m_features.line_expand =
|
|
|
|
|
(m_device_features.wideLines && limits.lineWidthRange[0] <= f_upscale && limits.lineWidthRange[1] >= f_upscale);
|
|
|
|
|
|
|
|
|
@ -2664,9 +2669,10 @@ bool GSDeviceVK::CheckFeatures()
|
|
|
|
|
for (u32 fmt = static_cast<u32>(GSTexture::Format::Color); fmt < static_cast<u32>(GSTexture::Format::PrimID); fmt++)
|
|
|
|
|
{
|
|
|
|
|
const VkFormat vkfmt = LookupNativeFormat(static_cast<GSTexture::Format>(fmt));
|
|
|
|
|
const VkFormatFeatureFlags bits = (static_cast<GSTexture::Format>(fmt) == GSTexture::Format::DepthStencil) ?
|
|
|
|
|
(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) :
|
|
|
|
|
(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
|
|
|
|
|
const VkFormatFeatureFlags bits =
|
|
|
|
|
(static_cast<GSTexture::Format>(fmt) == GSTexture::Format::DepthStencil) ?
|
|
|
|
|
(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) :
|
|
|
|
|
(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
|
|
|
|
|
|
|
|
|
|
VkFormatProperties props = {};
|
|
|
|
|
vkGetPhysicalDeviceFormatProperties(m_physical_device, vkfmt, &props);
|
|
|
|
@ -2685,7 +2691,8 @@ bool GSDeviceVK::CheckFeatures()
|
|
|
|
|
if (!m_features.texture_barrier && !m_features.stencil_buffer)
|
|
|
|
|
{
|
|
|
|
|
Host::AddKeyedOSDMessage("GSDeviceVK_NoTextureBarrierOrStencilBuffer",
|
|
|
|
|
TRANSLATE_STR("GS", "Stencil buffers and texture barriers are both unavailable, this will break some graphical effects."),
|
|
|
|
|
TRANSLATE_STR("GS",
|
|
|
|
|
"Stencil buffers and texture barriers are both unavailable, this will break some graphical effects."),
|
|
|
|
|
Host::OSD_WARNING_DURATION);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2729,14 +2736,16 @@ VkFormat GSDeviceVK::LookupNativeFormat(GSTexture::Format format) const
|
|
|
|
|
}};
|
|
|
|
|
|
|
|
|
|
return (format != GSTexture::Format::DepthStencil || m_features.stencil_buffer) ?
|
|
|
|
|
s_format_mapping[static_cast<int>(format)] :
|
|
|
|
|
VK_FORMAT_D32_SFLOAT;
|
|
|
|
|
s_format_mapping[static_cast<int>(format)] :
|
|
|
|
|
VK_FORMAT_D32_SFLOAT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GSTexture* GSDeviceVK::CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format)
|
|
|
|
|
{
|
|
|
|
|
const u32 clamped_width = static_cast<u32>(std::clamp<int>(width, 1, m_device_properties.limits.maxImageDimension2D));
|
|
|
|
|
const u32 clamped_height = static_cast<u32>(std::clamp<int>(height, 1, m_device_properties.limits.maxImageDimension2D));
|
|
|
|
|
const u32 clamped_width =
|
|
|
|
|
static_cast<u32>(std::clamp<int>(width, 1, m_device_properties.limits.maxImageDimension2D));
|
|
|
|
|
const u32 clamped_height =
|
|
|
|
|
static_cast<u32>(std::clamp<int>(height, 1, m_device_properties.limits.maxImageDimension2D));
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<GSTexture> tex(GSTextureVK::Create(type, format, clamped_width, clamped_height, levels));
|
|
|
|
|
if (!tex)
|
|
|
|
@ -2797,7 +2806,8 @@ void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
|
|
|
|
// otherwise we need to do an attachment clear
|
|
|
|
|
const bool depth = (dTexVK->GetType() == GSTexture::Type::DepthStencil);
|
|
|
|
|
OMSetRenderTargets(depth ? nullptr : dTexVK, depth ? dTexVK : nullptr, dtex_rc);
|
|
|
|
|
BeginRenderPassForStretchRect(dTexVK, dtex_rc, GSVector4i(destX, destY, destX + r.width(), destY + r.height()));
|
|
|
|
|
BeginRenderPassForStretchRect(
|
|
|
|
|
dTexVK, dtex_rc, GSVector4i(destX, destY, destX + r.width(), destY + r.height()));
|
|
|
|
|
|
|
|
|
|
// so use an attachment clear
|
|
|
|
|
VkClearAttachment ca;
|
|
|
|
@ -2807,7 +2817,7 @@ void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
|
|
|
|
ca.clearValue.depthStencil.stencil = 0;
|
|
|
|
|
ca.colorAttachment = 0;
|
|
|
|
|
|
|
|
|
|
const VkClearRect cr = { {{0, 0}, {static_cast<u32>(r.width()), static_cast<u32>(r.height())}}, 0u, 1u };
|
|
|
|
|
const VkClearRect cr = {{{0, 0}, {static_cast<u32>(r.width()), static_cast<u32>(r.height())}}, 0u, 1u};
|
|
|
|
|
vkCmdClearAttachments(GetCurrentCommandBuffer(), 1, &ca, 1, &cr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -2822,8 +2832,10 @@ void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
|
|
|
|
dTexVK->CommitClear();
|
|
|
|
|
|
|
|
|
|
// *now* we can do a normal image copy.
|
|
|
|
|
const VkImageAspectFlags src_aspect = (sTexVK->IsDepthStencil()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
const VkImageAspectFlags dst_aspect = (dTexVK->IsDepthStencil()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
const VkImageAspectFlags src_aspect =
|
|
|
|
|
(sTexVK->IsDepthStencil()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
const VkImageAspectFlags dst_aspect =
|
|
|
|
|
(dTexVK->IsDepthStencil()) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
|
const VkImageCopy ic = {{src_aspect, 0u, 0u, 1u}, {r.left, r.top, 0u}, {dst_aspect, 0u, 0u, 1u},
|
|
|
|
|
{static_cast<s32>(destX), static_cast<s32>(destY), 0u},
|
|
|
|
|
{static_cast<u32>(r.width()), static_cast<u32>(r.height()), 1u}};
|
|
|
|
@ -2837,8 +2849,8 @@ void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
|
|
|
|
dTexVK->TransitionToLayout(
|
|
|
|
|
(dTexVK == sTexVK) ? GSTextureVK::Layout::TransferSelf : GSTextureVK::Layout::TransferDst);
|
|
|
|
|
|
|
|
|
|
vkCmdCopyImage(GetCurrentCommandBuffer(), sTexVK->GetImage(),
|
|
|
|
|
sTexVK->GetVkLayout(), dTexVK->GetImage(), dTexVK->GetVkLayout(), 1, &ic);
|
|
|
|
|
vkCmdCopyImage(GetCurrentCommandBuffer(), sTexVK->GetImage(), sTexVK->GetVkLayout(), dTexVK->GetImage(),
|
|
|
|
|
dTexVK->GetVkLayout(), 1, &ic);
|
|
|
|
|
|
|
|
|
|
dTexVK->SetState(GSTexture::State::Dirty);
|
|
|
|
|
}
|
|
|
|
@ -2989,7 +3001,8 @@ void GSDeviceVK::DoMultiStretchRects(
|
|
|
|
|
SetUtilityTexture(rects[0].src, rects[0].linear ? m_linear_sampler : m_point_sampler);
|
|
|
|
|
|
|
|
|
|
pxAssert(shader == ShaderConvert::COPY || rects[0].wmask.wrgba == 0xf);
|
|
|
|
|
SetPipeline((rects[0].wmask.wrgba != 0xf) ? m_color_copy[rects[0].wmask.wrgba] : m_convert[static_cast<int>(shader)]);
|
|
|
|
|
SetPipeline(
|
|
|
|
|
(rects[0].wmask.wrgba != 0xf) ? m_color_copy[rects[0].wmask.wrgba] : m_convert[static_cast<int>(shader)]);
|
|
|
|
|
|
|
|
|
|
if (ApplyUtilityState())
|
|
|
|
|
DrawIndexedPrimitive();
|
|
|
|
@ -3010,7 +3023,7 @@ void GSDeviceVK::BeginRenderPassForStretchRect(
|
|
|
|
|
BeginClearRenderPass(m_utility_depth_render_pass_clear, dtex_rc, dTex->GetClearDepth(), 0);
|
|
|
|
|
else
|
|
|
|
|
BeginRenderPass((load_op == VK_ATTACHMENT_LOAD_OP_DONT_CARE) ? m_utility_depth_render_pass_discard :
|
|
|
|
|
m_utility_depth_render_pass_load,
|
|
|
|
|
m_utility_depth_render_pass_load,
|
|
|
|
|
dtex_rc);
|
|
|
|
|
}
|
|
|
|
|
else if (dTex->GetFormat() == GSTexture::Format::Color)
|
|
|
|
@ -3019,14 +3032,14 @@ void GSDeviceVK::BeginRenderPassForStretchRect(
|
|
|
|
|
BeginClearRenderPass(m_utility_color_render_pass_clear, dtex_rc, dTex->GetClearColor());
|
|
|
|
|
else
|
|
|
|
|
BeginRenderPass((load_op == VK_ATTACHMENT_LOAD_OP_DONT_CARE) ? m_utility_color_render_pass_discard :
|
|
|
|
|
m_utility_color_render_pass_load,
|
|
|
|
|
m_utility_color_render_pass_load,
|
|
|
|
|
dtex_rc);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// integer formats, etc
|
|
|
|
|
const VkRenderPass rp = GetRenderPass(dTex->GetVkFormat(), VK_FORMAT_UNDEFINED,
|
|
|
|
|
load_op, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
|
|
|
|
|
const VkRenderPass rp = GetRenderPass(dTex->GetVkFormat(), VK_FORMAT_UNDEFINED, load_op,
|
|
|
|
|
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE);
|
|
|
|
|
if (load_op == VK_ATTACHMENT_LOAD_OP_CLEAR)
|
|
|
|
|
{
|
|
|
|
|
BeginClearRenderPass(rp, dtex_rc, dTex->GetClearColor());
|
|
|
|
@ -3053,8 +3066,7 @@ void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSText
|
|
|
|
|
|
|
|
|
|
const bool is_present = (!dTex);
|
|
|
|
|
const bool depth = (dTex && dTex->GetType() == GSTexture::Type::DepthStencil);
|
|
|
|
|
const GSVector2i size(
|
|
|
|
|
is_present ? GSVector2i(GetWindowWidth(), GetWindowHeight()) : dTex->GetSize());
|
|
|
|
|
const GSVector2i size(is_present ? GSVector2i(GetWindowWidth(), GetWindowHeight()) : dTex->GetSize());
|
|
|
|
|
const GSVector4i dtex_rc(0, 0, size.x, size.y);
|
|
|
|
|
const GSVector4i dst_rc(GSVector4i(dRect).rintersect(dtex_rc));
|
|
|
|
|
|
|
|
|
@ -3119,12 +3131,13 @@ void GSDeviceVK::BlitRect(GSTexture* sTex, const GSVector4i& sRect, u32 sLevel,
|
|
|
|
|
const VkImageBlit ib{{aspect, sLevel, 0u, 1u}, {{sRect.left, sRect.top, 0}, {sRect.right, sRect.bottom, 1}},
|
|
|
|
|
{aspect, dLevel, 0u, 1u}, {{dRect.left, dRect.top, 0}, {dRect.right, dRect.bottom, 1}}};
|
|
|
|
|
|
|
|
|
|
vkCmdBlitImage(GetCurrentCommandBuffer(), sTexVK->GetImage(),
|
|
|
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dTexVK->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &ib,
|
|
|
|
|
vkCmdBlitImage(GetCurrentCommandBuffer(), sTexVK->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
|
|
|
dTexVK->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &ib,
|
|
|
|
|
linear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GSDeviceVK::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
|
|
|
|
|
void GSDeviceVK::UpdateCLUTTexture(
|
|
|
|
|
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
|
|
|
|
|
{
|
|
|
|
|
// Super annoying, but apparently NVIDIA doesn't like floats/ints packed together in the same vec4?
|
|
|
|
|
struct Uniforms
|
|
|
|
@ -3143,7 +3156,8 @@ void GSDeviceVK::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u
|
|
|
|
|
m_convert[static_cast<int>(shader)], false, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GSDeviceVK::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
|
|
|
|
|
void GSDeviceVK::ConvertToIndexedTexture(
|
|
|
|
|
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
|
|
|
|
|
{
|
|
|
|
|
struct Uniforms
|
|
|
|
|
{
|
|
|
|
@ -3174,7 +3188,7 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
|
|
|
|
const bool feedback_write_2 = PMODE.EN2 && sTex[2] != nullptr && EXTBUF.FBIN == 1;
|
|
|
|
|
const bool feedback_write_1 = PMODE.EN1 && sTex[2] != nullptr && EXTBUF.FBIN == 0;
|
|
|
|
|
const bool feedback_write_2_but_blend_bg = feedback_write_2 && PMODE.SLBG == 1;
|
|
|
|
|
const VkSampler& sampler = linear? m_linear_sampler : m_point_sampler;
|
|
|
|
|
const VkSampler& sampler = linear ? m_linear_sampler : m_point_sampler;
|
|
|
|
|
// Merge the 2 source textures (sTex[0],sTex[1]). Final results go to dTex. Feedback write will go to sTex[2].
|
|
|
|
|
// If either 2nd output is disabled or SLBG is 1, a background color will be used.
|
|
|
|
|
// Note: background color is also used when outside of the unit rectangle area
|
|
|
|
@ -3286,7 +3300,8 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
|
|
|
|
|
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GSDeviceVK::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb)
|
|
|
|
|
void GSDeviceVK::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
|
|
|
|
|
ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb)
|
|
|
|
|
{
|
|
|
|
|
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(GSTextureVK::Layout::ColorAttachment);
|
|
|
|
|
|
|
|
|
@ -3374,7 +3389,8 @@ void GSDeviceVK::IASetIndexBuffer(const void* index, size_t count)
|
|
|
|
|
SetIndexBuffer(m_index_stream_buffer.GetBuffer(), 0, VK_INDEX_TYPE_UINT16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GSDeviceVK::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop)
|
|
|
|
|
void GSDeviceVK::OMSetRenderTargets(
|
|
|
|
|
GSTexture* rt, GSTexture* ds, const GSVector4i& scissor, FeedbackLoopFlag feedback_loop)
|
|
|
|
|
{
|
|
|
|
|
GSTextureVK* vkRt = static_cast<GSTextureVK*>(rt);
|
|
|
|
|
GSTextureVK* vkDs = static_cast<GSTextureVK*>(ds);
|
|
|
|
@ -3388,7 +3404,8 @@ void GSDeviceVK::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
|
|
|
|
|
|
|
|
|
|
if (vkRt)
|
|
|
|
|
{
|
|
|
|
|
m_current_framebuffer = vkRt->GetLinkedFramebuffer(vkDs, (feedback_loop & FeedbackLoopFlag_ReadAndWriteRT) != 0);
|
|
|
|
|
m_current_framebuffer =
|
|
|
|
|
vkRt->GetLinkedFramebuffer(vkDs, (feedback_loop & FeedbackLoopFlag_ReadAndWriteRT) != 0);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -3452,7 +3469,7 @@ void GSDeviceVK::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vkRt->TransitionToLayout((feedback_loop & FeedbackLoopFlag_ReadAndWriteRT) ?
|
|
|
|
|
GSTextureVK::Layout::FeedbackLoop :
|
|
|
|
|
GSTextureVK::Layout::FeedbackLoop :
|
|
|
|
|
GSTextureVK::Layout::ColorAttachment);
|
|
|
|
|
}
|
|
|
|
|
if (vkDs)
|
|
|
|
@ -3658,9 +3675,8 @@ bool GSDeviceVK::CreateBuffers()
|
|
|
|
|
|
|
|
|
|
SetVertexBuffer(m_vertex_stream_buffer.GetBuffer(), 0);
|
|
|
|
|
|
|
|
|
|
if (!AllocatePreinitializedGPUBuffer(EXPAND_BUFFER_SIZE, &m_expand_index_buffer,
|
|
|
|
|
&m_expand_index_buffer_allocation, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
|
|
|
|
&GSDevice::GenerateExpansionIndexBuffer))
|
|
|
|
|
if (!AllocatePreinitializedGPUBuffer(EXPAND_BUFFER_SIZE, &m_expand_index_buffer, &m_expand_index_buffer_allocation,
|
|
|
|
|
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, &GSDevice::GenerateExpansionIndexBuffer))
|
|
|
|
|
{
|
|
|
|
|
Host::ReportErrorAsync("GS", "Failed to allocate expansion index buffer");
|
|
|
|
|
return false;
|
|
|
|
@ -3731,10 +3747,10 @@ bool GSDeviceVK::CreateRenderPasses()
|
|
|
|
|
dest = GetRenderPass( \
|
|
|
|
|
(rt), (depth), ((rt) != VK_FORMAT_UNDEFINED) ? (opa) : VK_ATTACHMENT_LOAD_OP_DONT_CARE, /* color load */ \
|
|
|
|
|
((rt) != VK_FORMAT_UNDEFINED) ? VK_ATTACHMENT_STORE_OP_STORE : \
|
|
|
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE, /* color store */ \
|
|
|
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE, /* color store */ \
|
|
|
|
|
((depth) != VK_FORMAT_UNDEFINED) ? (opb) : VK_ATTACHMENT_LOAD_OP_DONT_CARE, /* depth load */ \
|
|
|
|
|
((depth) != VK_FORMAT_UNDEFINED) ? VK_ATTACHMENT_STORE_OP_STORE : \
|
|
|
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE, /* depth store */ \
|
|
|
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE, /* depth store */ \
|
|
|
|
|
((depth) != VK_FORMAT_UNDEFINED) ? (opc) : VK_ATTACHMENT_LOAD_OP_DONT_CARE, /* stencil load */ \
|
|
|
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE, /* stencil store */ \
|
|
|
|
|
(fbl), /* feedback loop */ \
|
|
|
|
@ -3798,8 +3814,8 @@ bool GSDeviceVK::CreateRenderPasses()
|
|
|
|
|
GET(m_utility_depth_render_pass_discard, VK_FORMAT_UNDEFINED, depth_format, false, false,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
|
|
|
|
|
|
|
|
m_date_setup_render_pass = GetRenderPass(VK_FORMAT_UNDEFINED, depth_format,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
|
|
|
|
|
m_date_setup_render_pass = GetRenderPass(VK_FORMAT_UNDEFINED, depth_format, VK_ATTACHMENT_LOAD_OP_LOAD,
|
|
|
|
|
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
|
|
|
|
|
m_features.stencil_buffer ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
|
|
|
m_features.stencil_buffer ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE);
|
|
|
|
|
if (m_date_setup_render_pass == VK_NULL_HANDLE)
|
|
|
|
@ -3847,14 +3863,14 @@ bool GSDeviceVK::CompileConvertPipelines()
|
|
|
|
|
case ShaderConvert::RGBA8_TO_16_BITS:
|
|
|
|
|
case ShaderConvert::FLOAT32_TO_16_BITS:
|
|
|
|
|
{
|
|
|
|
|
rp = GetRenderPass(LookupNativeFormat(GSTexture::Format::UInt16),
|
|
|
|
|
VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
|
|
|
rp = GetRenderPass(LookupNativeFormat(GSTexture::Format::UInt16), VK_FORMAT_UNDEFINED,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case ShaderConvert::FLOAT32_TO_32_BITS:
|
|
|
|
|
{
|
|
|
|
|
rp = GetRenderPass(LookupNativeFormat(GSTexture::Format::UInt32),
|
|
|
|
|
VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
|
|
|
rp = GetRenderPass(LookupNativeFormat(GSTexture::Format::UInt32), VK_FORMAT_UNDEFINED,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case ShaderConvert::DATM_0:
|
|
|
|
@ -3865,10 +3881,8 @@ bool GSDeviceVK::CompileConvertPipelines()
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
rp = GetRenderPass(
|
|
|
|
|
LookupNativeFormat(depth ? GSTexture::Format::Invalid : GSTexture::Format::Color),
|
|
|
|
|
LookupNativeFormat(
|
|
|
|
|
depth ? GSTexture::Format::DepthStencil : GSTexture::Format::Invalid),
|
|
|
|
|
rp = GetRenderPass(LookupNativeFormat(depth ? GSTexture::Format::Invalid : GSTexture::Format::Color),
|
|
|
|
|
LookupNativeFormat(depth ? GSTexture::Format::DepthStencil : GSTexture::Format::Invalid),
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
@ -3900,8 +3914,7 @@ bool GSDeviceVK::CompileConvertPipelines()
|
|
|
|
|
ScopedGuard ps_guard([this, &ps]() { vkDestroyShaderModule(m_device, ps, nullptr); });
|
|
|
|
|
gpb.SetFragmentShader(ps);
|
|
|
|
|
|
|
|
|
|
m_convert[index] =
|
|
|
|
|
gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
m_convert[index] = gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
if (!m_convert[index])
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
@ -3917,36 +3930,33 @@ bool GSDeviceVK::CompileConvertPipelines()
|
|
|
|
|
gpb.ClearBlendAttachments();
|
|
|
|
|
gpb.SetBlendAttachment(0, false, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD,
|
|
|
|
|
VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, static_cast<VkColorComponentFlags>(i));
|
|
|
|
|
m_color_copy[i] =
|
|
|
|
|
gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
m_color_copy[i] = gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
if (!m_color_copy[i])
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Vulkan::SetObjectName(m_device, m_color_copy[i],
|
|
|
|
|
"Color copy pipeline (r=%u, g=%u, b=%u, a=%u)", i & 1u, (i >> 1) & 1u, (i >> 2) & 1u,
|
|
|
|
|
(i >> 3) & 1u);
|
|
|
|
|
Vulkan::SetObjectName(m_device, m_color_copy[i], "Color copy pipeline (r=%u, g=%u, b=%u, a=%u)", i & 1u,
|
|
|
|
|
(i >> 1) & 1u, (i >> 2) & 1u, (i >> 3) & 1u);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (i == ShaderConvert::HDR_INIT || i == ShaderConvert::HDR_RESOLVE)
|
|
|
|
|
{
|
|
|
|
|
const bool is_setup = i == ShaderConvert::HDR_INIT;
|
|
|
|
|
VkPipeline (&arr)[2][2] = *(is_setup ? &m_hdr_setup_pipelines : &m_hdr_finish_pipelines);
|
|
|
|
|
VkPipeline(&arr)[2][2] = *(is_setup ? &m_hdr_setup_pipelines : &m_hdr_finish_pipelines);
|
|
|
|
|
for (u32 ds = 0; ds < 2; ds++)
|
|
|
|
|
{
|
|
|
|
|
for (u32 fbl = 0; fbl < 2; fbl++)
|
|
|
|
|
{
|
|
|
|
|
pxAssert(!arr[ds][fbl]);
|
|
|
|
|
|
|
|
|
|
gpb.SetRenderPass(
|
|
|
|
|
GetTFXRenderPass(true, ds != 0, is_setup, DATE_RENDER_PASS_NONE, fbl != 0, false,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE),
|
|
|
|
|
gpb.SetRenderPass(GetTFXRenderPass(true, ds != 0, is_setup, DATE_RENDER_PASS_NONE, fbl != 0, false,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_LOAD_OP_DONT_CARE),
|
|
|
|
|
0);
|
|
|
|
|
arr[ds][fbl] = gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
if (!arr[ds][fbl])
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
Vulkan::SetObjectName(m_device, arr[ds][fbl],
|
|
|
|
|
"HDR %s/copy pipeline (ds=%u, fbl=%u)", is_setup ? "setup" : "finish", i, ds, fbl);
|
|
|
|
|
Vulkan::SetObjectName(m_device, arr[ds][fbl], "HDR %s/copy pipeline (ds=%u, fbl=%u)",
|
|
|
|
|
is_setup ? "setup" : "finish", i, ds, fbl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -3957,12 +3967,12 @@ bool GSDeviceVK::CompileConvertPipelines()
|
|
|
|
|
{
|
|
|
|
|
for (u32 clear = 0; clear < 2; clear++)
|
|
|
|
|
{
|
|
|
|
|
m_date_image_setup_render_passes[ds][clear] =
|
|
|
|
|
GetRenderPass(LookupNativeFormat(GSTexture::Format::PrimID),
|
|
|
|
|
ds ? LookupNativeFormat(GSTexture::Format::DepthStencil) : VK_FORMAT_UNDEFINED,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE,
|
|
|
|
|
ds ? (clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD) : VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
|
|
|
ds ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE);
|
|
|
|
|
m_date_image_setup_render_passes[ds][clear] = GetRenderPass(LookupNativeFormat(GSTexture::Format::PrimID),
|
|
|
|
|
ds ? LookupNativeFormat(GSTexture::Format::DepthStencil) : VK_FORMAT_UNDEFINED,
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE,
|
|
|
|
|
ds ? (clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD) :
|
|
|
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
|
|
|
ds ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -4001,8 +4011,8 @@ bool GSDeviceVK::CompileConvertPipelines()
|
|
|
|
|
bool GSDeviceVK::CompilePresentPipelines()
|
|
|
|
|
{
|
|
|
|
|
// we may not have a swap chain if running in headless mode.
|
|
|
|
|
m_swap_chain_render_pass = GetRenderPass(
|
|
|
|
|
m_swap_chain ? m_swap_chain->GetTextureFormat() : VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
|
|
|
|
|
m_swap_chain_render_pass =
|
|
|
|
|
GetRenderPass(m_swap_chain ? m_swap_chain->GetTextureFormat() : VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
|
|
|
|
|
if (m_swap_chain_render_pass == VK_NULL_HANDLE)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
@ -4033,7 +4043,7 @@ bool GSDeviceVK::CompilePresentPipelines()
|
|
|
|
|
gpb.SetRenderPass(m_swap_chain_render_pass, 0);
|
|
|
|
|
|
|
|
|
|
for (PresentShader i = PresentShader::COPY; static_cast<int>(i) < static_cast<int>(PresentShader::Count);
|
|
|
|
|
i = static_cast<PresentShader>(static_cast<int>(i) + 1))
|
|
|
|
|
i = static_cast<PresentShader>(static_cast<int>(i) + 1))
|
|
|
|
|
{
|
|
|
|
|
const int index = static_cast<int>(i);
|
|
|
|
|
|
|
|
|
@ -4044,8 +4054,7 @@ bool GSDeviceVK::CompilePresentPipelines()
|
|
|
|
|
ScopedGuard ps_guard([this, &ps]() { vkDestroyShaderModule(m_device, ps, nullptr); });
|
|
|
|
|
gpb.SetFragmentShader(ps);
|
|
|
|
|
|
|
|
|
|
m_present[index] =
|
|
|
|
|
gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
m_present[index] = gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
if (!m_present[index])
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
@ -4065,8 +4074,8 @@ bool GSDeviceVK::CompileInterlacePipelines()
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkRenderPass rp = GetRenderPass(
|
|
|
|
|
LookupNativeFormat(GSTexture::Format::Color), VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
|
|
|
|
|
VkRenderPass rp =
|
|
|
|
|
GetRenderPass(LookupNativeFormat(GSTexture::Format::Color), VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
|
|
|
|
|
if (!rp)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
@ -4096,8 +4105,7 @@ bool GSDeviceVK::CompileInterlacePipelines()
|
|
|
|
|
|
|
|
|
|
gpb.SetFragmentShader(ps);
|
|
|
|
|
|
|
|
|
|
m_interlace[i] =
|
|
|
|
|
gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
m_interlace[i] = gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
vkDestroyShaderModule(m_device, ps, nullptr);
|
|
|
|
|
if (!m_interlace[i])
|
|
|
|
|
return false;
|
|
|
|
@ -4117,8 +4125,8 @@ bool GSDeviceVK::CompileMergePipelines()
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VkRenderPass rp = GetRenderPass(
|
|
|
|
|
LookupNativeFormat(GSTexture::Format::Color), VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
|
|
|
|
|
VkRenderPass rp =
|
|
|
|
|
GetRenderPass(LookupNativeFormat(GSTexture::Format::Color), VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
|
|
|
|
|
if (!rp)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
@ -4146,8 +4154,8 @@ bool GSDeviceVK::CompileMergePipelines()
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
gpb.SetFragmentShader(ps);
|
|
|
|
|
gpb.SetBlendAttachment(0, true, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
|
|
|
|
|
VK_BLEND_OP_ADD, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD);
|
|
|
|
|
gpb.SetBlendAttachment(0, true, VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_OP_ADD,
|
|
|
|
|
VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD);
|
|
|
|
|
|
|
|
|
|
m_merge[i] = gpb.Create(m_device, g_vulkan_shader_cache->GetPipelineCache(true), false);
|
|
|
|
|
vkDestroyShaderModule(m_device, ps, nullptr);
|
|
|
|
@ -4162,8 +4170,8 @@ bool GSDeviceVK::CompileMergePipelines()
|
|
|
|
|
|
|
|
|
|
bool GSDeviceVK::CompilePostProcessingPipelines()
|
|
|
|
|
{
|
|
|
|
|
VkRenderPass rp = GetRenderPass(
|
|
|
|
|
LookupNativeFormat(GSTexture::Format::Color), VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
|
|
|
|
|
VkRenderPass rp =
|
|
|
|
|
GetRenderPass(LookupNativeFormat(GSTexture::Format::Color), VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD);
|
|
|
|
|
if (!rp)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
@ -4297,7 +4305,7 @@ bool GSDeviceVK::CompileImGuiPipeline()
|
|
|
|
|
Console.Error("Failed to read imgui.glsl");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VkShaderModule vs = GetUtilityVertexShader(glsl.value(), "vs_main");
|
|
|
|
|
if (vs == VK_NULL_HANDLE)
|
|
|
|
|
{
|
|
|
|
@ -4411,8 +4419,8 @@ void GSDeviceVK::RenderImGui()
|
|
|
|
|
|
|
|
|
|
if (ApplyUtilityState())
|
|
|
|
|
{
|
|
|
|
|
vkCmdDrawIndexed(GetCurrentCommandBuffer(), pcmd->ElemCount, 1,
|
|
|
|
|
m_index.start + pcmd->IdxOffset, vertex_offset + pcmd->VtxOffset, 0);
|
|
|
|
|
vkCmdDrawIndexed(GetCurrentCommandBuffer(), pcmd->ElemCount, 1, m_index.start + pcmd->IdxOffset,
|
|
|
|
|
vertex_offset + pcmd->VtxOffset, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -4434,7 +4442,8 @@ void GSDeviceVK::RenderBlankFrame()
|
|
|
|
|
sctex->TransitionToLayout(cmdbuffer, GSTextureVK::Layout::TransferDst);
|
|
|
|
|
|
|
|
|
|
constexpr VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
|
|
|
|
vkCmdClearColorImage(cmdbuffer, sctex->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &s_present_clear_color.color, 1, &srr);
|
|
|
|
|
vkCmdClearColorImage(
|
|
|
|
|
cmdbuffer, sctex->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &s_present_clear_color.color, 1, &srr);
|
|
|
|
|
|
|
|
|
|
m_swap_chain->GetCurrentTexture()->TransitionToLayout(cmdbuffer, GSTextureVK::Layout::PresentSrc);
|
|
|
|
|
SubmitCommandBuffer(m_swap_chain.get(), !m_swap_chain->IsPresentModeSynchronizing());
|
|
|
|
@ -4443,7 +4452,8 @@ void GSDeviceVK::RenderBlankFrame()
|
|
|
|
|
InvalidateCachedState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
|
|
|
|
|
bool GSDeviceVK::DoCAS(
|
|
|
|
|
GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
|
|
|
|
|
{
|
|
|
|
|
EndRenderPass();
|
|
|
|
|
|
|
|
|
@ -4465,7 +4475,8 @@ bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, cons
|
|
|
|
|
const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
|
|
|
const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
|
|
|
|
|
|
|
|
|
vkCmdPushConstants(cmdbuf, m_cas_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, NUM_CAS_CONSTANTS * sizeof(u32), constants.data());
|
|
|
|
|
vkCmdPushConstants(cmdbuf, m_cas_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, NUM_CAS_CONSTANTS * sizeof(u32),
|
|
|
|
|
constants.data());
|
|
|
|
|
vkCmdBindPipeline(cmdbuf, VK_PIPELINE_BIND_POINT_COMPUTE, m_cas_pipelines[static_cast<u8>(sharpen_only)]);
|
|
|
|
|
vkCmdDispatch(cmdbuf, dispatchX, dispatchY, 1);
|
|
|
|
|
|
|
|
|
@ -4747,7 +4758,8 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p)
|
|
|
|
|
}
|
|
|
|
|
gpb.SetPrimitiveTopology(topology_lookup[p.topology]);
|
|
|
|
|
gpb.SetRasterizationState(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
|
|
|
|
|
if (p.topology == static_cast<u8>(GSHWDrawConfig::Topology::Line) && m_optional_extensions.vk_ext_line_rasterization)
|
|
|
|
|
if (p.topology == static_cast<u8>(GSHWDrawConfig::Topology::Line) &&
|
|
|
|
|
m_optional_extensions.vk_ext_line_rasterization)
|
|
|
|
|
gpb.SetLineRasterizationMode(VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT);
|
|
|
|
|
gpb.SetDynamicViewportAndScissorState();
|
|
|
|
|
gpb.AddDynamicState(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
|
|
|
|
@ -5125,7 +5137,10 @@ void GSDeviceVK::UnbindTexture(GSTextureVK* tex)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool GSDeviceVK::InRenderPass() { return m_current_render_pass != VK_NULL_HANDLE; }
|
|
|
|
|
bool GSDeviceVK::InRenderPass()
|
|
|
|
|
{
|
|
|
|
|
return m_current_render_pass != VK_NULL_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GSDeviceVK::BeginRenderPass(VkRenderPass rp, const GSVector4i& rect)
|
|
|
|
|
{
|
|
|
|
@ -5613,8 +5628,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
|
|
|
|
{
|
|
|
|
|
EndRenderPass();
|
|
|
|
|
|
|
|
|
|
GL_PUSH("Copy RT to temp texture for fbmask {%d,%d %dx%d}",
|
|
|
|
|
config.drawarea.left, config.drawarea.top,
|
|
|
|
|
GL_PUSH("Copy RT to temp texture for fbmask {%d,%d %dx%d}", config.drawarea.left, config.drawarea.top,
|
|
|
|
|
config.drawarea.width(), config.drawarea.height());
|
|
|
|
|
|
|
|
|
|
CopyRect(draw_rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
|
|
|
|
@ -5659,7 +5673,8 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
|
|
|
|
// We don't need the very first barrier if this is the first draw after switching to feedback loop,
|
|
|
|
|
// because the layout change in itself enforces the execution dependency. HDR needs a barrier between
|
|
|
|
|
// setup and the first draw to read it. TODO: Make HDR use subpasses instead.
|
|
|
|
|
const bool skip_first_barrier = (draw_rt && draw_rt->GetLayout() != GSTextureVK::Layout::FeedbackLoop && !pipe.ps.hdr);
|
|
|
|
|
const bool skip_first_barrier =
|
|
|
|
|
(draw_rt && draw_rt->GetLayout() != GSTextureVK::Layout::FeedbackLoop && !pipe.ps.hdr);
|
|
|
|
|
|
|
|
|
|
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
|
|
|
|
|
if (pipe.IsRTFeedbackLoop())
|
|
|
|
@ -5860,7 +5875,7 @@ VkImageMemoryBarrier GSDeviceVK::GetColorBufferBarrier(GSTextureVK* rt) const
|
|
|
|
|
VkDependencyFlags GSDeviceVK::GetColorBufferBarrierFlags() const
|
|
|
|
|
{
|
|
|
|
|
return UseFeedbackLoopLayout() ? (VK_DEPENDENCY_BY_REGION_BIT | VK_DEPENDENCY_FEEDBACK_LOOP_BIT_EXT) :
|
|
|
|
|
VK_DEPENDENCY_BY_REGION_BIT;
|
|
|
|
|
VK_DEPENDENCY_BY_REGION_BIT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt, bool skip_first_barrier)
|
|
|
|
@ -5888,9 +5903,8 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
|
|
|
|
|
|
|
|
|
|
for (; n < draw_list_size; n++)
|
|
|
|
|
{
|
|
|
|
|
vkCmdPipelineBarrier(GetCurrentCommandBuffer(),
|
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier_flags, 0,
|
|
|
|
|
nullptr, 0, nullptr, 1, &barrier);
|
|
|
|
|
vkCmdPipelineBarrier(GetCurrentCommandBuffer(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier_flags, 0, nullptr, 0, nullptr, 1, &barrier);
|
|
|
|
|
|
|
|
|
|
const u32 count = (*config.drawlist)[n] * indices_per_prim;
|
|
|
|
|
DrawIndexedPrimitive(p, count);
|
|
|
|
@ -5922,9 +5936,8 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
|
|
|
|
|
|
|
|
|
|
for (; p < config.nindices; p += indices_per_prim)
|
|
|
|
|
{
|
|
|
|
|
vkCmdPipelineBarrier(GetCurrentCommandBuffer(),
|
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier_flags,
|
|
|
|
|
0, nullptr, 0, nullptr, 1, &barrier);
|
|
|
|
|
vkCmdPipelineBarrier(GetCurrentCommandBuffer(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier_flags, 0, nullptr, 0, nullptr, 1, &barrier);
|
|
|
|
|
|
|
|
|
|
DrawIndexedPrimitive(p, indices_per_prim);
|
|
|
|
|
}
|
|
|
|
@ -5935,9 +5948,8 @@ void GSDeviceVK::SendHWDraw(const GSHWDrawConfig& config, GSTextureVK* draw_rt,
|
|
|
|
|
if (config.require_one_barrier && !skip_first_barrier)
|
|
|
|
|
{
|
|
|
|
|
g_perfmon.Put(GSPerfMon::Barriers, 1);
|
|
|
|
|
vkCmdPipelineBarrier(GetCurrentCommandBuffer(),
|
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier_flags, 0,
|
|
|
|
|
nullptr, 0, nullptr, 1, &barrier);
|
|
|
|
|
vkCmdPipelineBarrier(GetCurrentCommandBuffer(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, barrier_flags, 0, nullptr, 0, nullptr, 1, &barrier);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|