GS: Implement FidelityFX CAS

This commit is contained in:
Connor McLaughlin 2022-11-21 00:20:40 +10:00 committed by refractionpcsx2
parent 5363a90c6b
commit f7c79fe3cc
30 changed files with 976 additions and 113 deletions

View File

@ -0,0 +1,75 @@
// Based on CAS_Shader.hlsl
//
// Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
cbuffer cb : register(b0)
{
uint4 const0;
uint4 const1;
int2 srcOffset;
};
Texture2D InputTexture : register(t0);
RWTexture2D<float4> OutputTexture : register(u0);
#define A_GPU 1
#define A_HLSL 1
#include "ffx_a.h"
AF3 CasLoad(ASU2 p)
{
return InputTexture.Load(int3(srcOffset, 0) + int3(p, 0)).rgb;
}
// Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h
// In this case, our input is already linear and between 0 and 1
void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {}
#include "ffx_cas.h"
[numthreads(64, 1, 1)]
void main(uint3 LocalThreadId : SV_GroupThreadID, uint3 WorkGroupId : SV_GroupID)
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(LocalThreadId.x) + AU2(WorkGroupId.x << 4u, WorkGroupId.y << 4u);
#if CAS_SHARPEN_ONLY
const bool sharpenOnly = true;
#else
const bool sharpenOnly = false;
#endif
// Filter.
AF3 c;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly);
OutputTexture[ASU2(gxy)] = AF4(c, 1);
gxy.x += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly);
OutputTexture[ASU2(gxy)] = AF4(c, 1);
gxy.y += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly);
OutputTexture[ASU2(gxy)] = AF4(c, 1);
gxy.x -= 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly);
OutputTexture[ASU2(gxy)] = AF4(c, 1);
}

View File

@ -0,0 +1,64 @@
// Based on CAS_Shader.glsl
// Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
uniform uvec4 const0;
uniform uvec4 const1;
uniform ivec2 srcOffset;
layout(binding=0) uniform sampler2D imgSrc;
layout(binding=0, rgba8) uniform writeonly image2D imgDst;
#define A_GPU 1
#define A_GLSL 1
#include "ffx_a.h"
AF3 CasLoad(ASU2 p)
{
return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb;
}
// Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h
// In this case, our input is already linear and between 0 and 1
void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {}
#include "ffx_cas.h"
layout(local_size_x=64) in;
void main()
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
// Filter.
AF4 c;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.x += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.y += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.x -= 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
}

View File

@ -0,0 +1,71 @@
#version 460 core
#extension GL_EXT_samplerless_texture_functions : require
// Based on CAS_Shader.glsl
// Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
layout(push_constant) uniform const_buffer
{
uvec4 const0;
uvec4 const1;
ivec2 srcOffset;
};
layout(set=0, binding=0) uniform texture2D imgSrc;
layout(set=0, binding=1, rgba8) uniform writeonly image2D imgDst;
layout(constant_id=0) const int sharpenOnly = 0;
#define A_GPU 1
#define A_GLSL 1
#include "ffx_a.h"
AF3 CasLoad(ASU2 p)
{
return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb;
}
// Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h
// In this case, our input is already linear and between 0 and 1
void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {}
#include "ffx_cas.h"
layout(local_size_x=64) in;
void main()
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
// Filter.
AF4 c;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.x += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.y += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.x -= 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
}

View File

@ -47,22 +47,22 @@ Texture::Texture(Texture&& texture)
: m_resource(std::move(texture.m_resource)) : m_resource(std::move(texture.m_resource))
, m_allocation(std::move(texture.m_allocation)) , m_allocation(std::move(texture.m_allocation))
, m_srv_descriptor(texture.m_srv_descriptor) , m_srv_descriptor(texture.m_srv_descriptor)
, m_rtv_or_dsv_descriptor(texture.m_rtv_or_dsv_descriptor) , m_write_descriptor(texture.m_write_descriptor)
, m_width(texture.m_width) , m_width(texture.m_width)
, m_height(texture.m_height) , m_height(texture.m_height)
, m_levels(texture.m_levels) , m_levels(texture.m_levels)
, m_format(texture.m_format) , m_format(texture.m_format)
, m_state(texture.m_state) , m_state(texture.m_state)
, m_is_depth_view(texture.m_is_depth_view) , m_write_descriptor_type(texture.m_write_descriptor_type)
{ {
texture.m_srv_descriptor = {}; texture.m_srv_descriptor = {};
texture.m_rtv_or_dsv_descriptor = {}; texture.m_write_descriptor = {};
texture.m_width = 0; texture.m_width = 0;
texture.m_height = 0; texture.m_height = 0;
texture.m_levels = 0; texture.m_levels = 0;
texture.m_format = DXGI_FORMAT_UNKNOWN; texture.m_format = DXGI_FORMAT_UNKNOWN;
texture.m_state = D3D12_RESOURCE_STATE_COMMON; texture.m_state = D3D12_RESOURCE_STATE_COMMON;
texture.m_is_depth_view = false; texture.m_write_descriptor_type = WriteDescriptorType::None;
} }
Texture::~Texture() Texture::~Texture()
@ -76,21 +76,21 @@ Texture& Texture::operator=(Texture&& texture)
m_resource = std::move(texture.m_resource); m_resource = std::move(texture.m_resource);
m_allocation = std::move(texture.m_allocation); m_allocation = std::move(texture.m_allocation);
m_srv_descriptor = texture.m_srv_descriptor; m_srv_descriptor = texture.m_srv_descriptor;
m_rtv_or_dsv_descriptor = texture.m_rtv_or_dsv_descriptor; m_write_descriptor = texture.m_write_descriptor;
m_width = texture.m_width; m_width = texture.m_width;
m_height = texture.m_height; m_height = texture.m_height;
m_levels = texture.m_levels; m_levels = texture.m_levels;
m_format = texture.m_format; m_format = texture.m_format;
m_state = texture.m_state; m_state = texture.m_state;
m_is_depth_view = texture.m_is_depth_view; m_write_descriptor_type = texture.m_write_descriptor_type;
texture.m_srv_descriptor = {}; texture.m_srv_descriptor = {};
texture.m_rtv_or_dsv_descriptor = {}; texture.m_write_descriptor = {};
texture.m_width = 0; texture.m_width = 0;
texture.m_height = 0; texture.m_height = 0;
texture.m_levels = 0; texture.m_levels = 0;
texture.m_format = DXGI_FORMAT_UNKNOWN; texture.m_format = DXGI_FORMAT_UNKNOWN;
texture.m_state = D3D12_RESOURCE_STATE_COMMON; texture.m_state = D3D12_RESOURCE_STATE_COMMON;
texture.m_is_depth_view = false; texture.m_write_descriptor_type = WriteDescriptorType::None;
return *this; return *this;
} }
@ -149,8 +149,8 @@ bool Texture::Create(u32 width, u32 height, u32 levels, DXGI_FORMAT format, DXGI
return false; return false;
} }
DescriptorHandle srv_descriptor, rtv_descriptor; DescriptorHandle srv_descriptor, write_descriptor;
bool is_depth_view = false; WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
if (srv_format != DXGI_FORMAT_UNKNOWN) if (srv_format != DXGI_FORMAT_UNKNOWN)
{ {
if (!CreateSRVDescriptor(resource.get(), levels, srv_format, &srv_descriptor)) if (!CreateSRVDescriptor(resource.get(), levels, srv_format, &srv_descriptor))
@ -159,22 +159,32 @@ bool Texture::Create(u32 width, u32 height, u32 levels, DXGI_FORMAT format, DXGI
if (rtv_format != DXGI_FORMAT_UNKNOWN) if (rtv_format != DXGI_FORMAT_UNKNOWN)
{ {
pxAssert(dsv_format == DXGI_FORMAT_UNKNOWN); pxAssert(dsv_format == DXGI_FORMAT_UNKNOWN && !(flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS));
if (!CreateRTVDescriptor(resource.get(), rtv_format, &rtv_descriptor)) write_descriptor_type = Texture::WriteDescriptorType::RTV;
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
{ {
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor); g_d3d12_context->GetRTVHeapManager().Free(&srv_descriptor);
return false;
}
}
else if (dsv_format != DXGI_FORMAT_UNKNOWN)
{
if (!CreateDSVDescriptor(resource.get(), dsv_format, &rtv_descriptor))
{
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
return false; return false;
} }
is_depth_view = true; }
else if (dsv_format != DXGI_FORMAT_UNKNOWN && !(flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS))
{
write_descriptor_type = Texture::WriteDescriptorType::DSV;
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor))
{
g_d3d12_context->GetDSVHeapManager().Free(&srv_descriptor);
return false;
}
}
else if (flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
{
write_descriptor_type = Texture::WriteDescriptorType::UAV;
if (!CreateUAVDescriptor(resource.get(), dsv_format, &write_descriptor))
{
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
return false;
}
} }
Destroy(true); Destroy(true);
@ -182,13 +192,13 @@ bool Texture::Create(u32 width, u32 height, u32 levels, DXGI_FORMAT format, DXGI
m_resource = std::move(resource); m_resource = std::move(resource);
m_allocation = std::move(allocation); m_allocation = std::move(allocation);
m_srv_descriptor = std::move(srv_descriptor); m_srv_descriptor = std::move(srv_descriptor);
m_rtv_or_dsv_descriptor = std::move(rtv_descriptor); m_write_descriptor = std::move(write_descriptor);
m_width = width; m_width = width;
m_height = height; m_height = height;
m_levels = levels; m_levels = levels;
m_format = format; m_format = format;
m_state = state; m_state = state;
m_is_depth_view = is_depth_view; m_write_descriptor_type = write_descriptor_type;
return true; return true;
} }
@ -197,7 +207,8 @@ bool Texture::Adopt(ComPtr<ID3D12Resource> texture, DXGI_FORMAT srv_format, DXGI
{ {
const D3D12_RESOURCE_DESC desc(texture->GetDesc()); const D3D12_RESOURCE_DESC desc(texture->GetDesc());
DescriptorHandle srv_descriptor, rtv_descriptor; DescriptorHandle srv_descriptor, write_descriptor;
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
if (srv_format != DXGI_FORMAT_UNKNOWN) if (srv_format != DXGI_FORMAT_UNKNOWN)
{ {
if (!CreateSRVDescriptor(texture.get(), desc.MipLevels, srv_format, &srv_descriptor)) if (!CreateSRVDescriptor(texture.get(), desc.MipLevels, srv_format, &srv_descriptor))
@ -207,15 +218,26 @@ bool Texture::Adopt(ComPtr<ID3D12Resource> texture, DXGI_FORMAT srv_format, DXGI
if (rtv_format != DXGI_FORMAT_UNKNOWN) if (rtv_format != DXGI_FORMAT_UNKNOWN)
{ {
pxAssert(dsv_format == DXGI_FORMAT_UNKNOWN); pxAssert(dsv_format == DXGI_FORMAT_UNKNOWN);
if (!CreateRTVDescriptor(texture.get(), rtv_format, &rtv_descriptor)) write_descriptor_type = Texture::WriteDescriptorType::RTV;
if (!CreateRTVDescriptor(texture.get(), rtv_format, &write_descriptor))
{ {
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor); g_d3d12_context->GetRTVHeapManager().Free(&srv_descriptor);
return false; return false;
} }
} }
else if (dsv_format != DXGI_FORMAT_UNKNOWN) else if (dsv_format != DXGI_FORMAT_UNKNOWN)
{ {
if (!CreateDSVDescriptor(texture.get(), dsv_format, &rtv_descriptor)) write_descriptor_type = Texture::WriteDescriptorType::DSV;
if (!CreateDSVDescriptor(texture.get(), dsv_format, &write_descriptor))
{
g_d3d12_context->GetDSVHeapManager().Free(&srv_descriptor);
return false;
}
}
else if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
{
write_descriptor_type = Texture::WriteDescriptorType::UAV;
if (!CreateUAVDescriptor(texture.get(), srv_format, &write_descriptor))
{ {
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor); g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
return false; return false;
@ -225,7 +247,8 @@ bool Texture::Adopt(ComPtr<ID3D12Resource> texture, DXGI_FORMAT srv_format, DXGI
m_resource = std::move(texture); m_resource = std::move(texture);
m_allocation.reset(); m_allocation.reset();
m_srv_descriptor = std::move(srv_descriptor); m_srv_descriptor = std::move(srv_descriptor);
m_rtv_or_dsv_descriptor = std::move(rtv_descriptor); m_write_descriptor = std::move(write_descriptor);
m_write_descriptor_type = write_descriptor_type;
m_width = static_cast<u32>(desc.Width); m_width = static_cast<u32>(desc.Width);
m_height = desc.Height; m_height = desc.Height;
m_levels = desc.MipLevels; m_levels = desc.MipLevels;
@ -239,10 +262,23 @@ void Texture::Destroy(bool defer /* = true */)
if (defer) if (defer)
{ {
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_srv_descriptor); g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_srv_descriptor);
if (m_is_depth_view)
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDSVHeapManager(), &m_rtv_or_dsv_descriptor); switch (m_write_descriptor_type)
else {
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetRTVHeapManager(), &m_rtv_or_dsv_descriptor); case Texture::WriteDescriptorType::RTV:
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetRTVHeapManager(), &m_write_descriptor);
break;
case Texture::WriteDescriptorType::DSV:
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDSVHeapManager(), &m_write_descriptor);
break;
case Texture::WriteDescriptorType::UAV:
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_write_descriptor);
break;
case Texture::WriteDescriptorType::None:
default:
break;
}
g_d3d12_context->DeferResourceDestruction(m_allocation.get(), m_resource.get()); g_d3d12_context->DeferResourceDestruction(m_allocation.get(), m_resource.get());
m_resource.reset(); m_resource.reset();
m_allocation.reset(); m_allocation.reset();
@ -250,10 +286,22 @@ void Texture::Destroy(bool defer /* = true */)
else else
{ {
g_d3d12_context->GetDescriptorHeapManager().Free(&m_srv_descriptor); g_d3d12_context->GetDescriptorHeapManager().Free(&m_srv_descriptor);
if (m_is_depth_view)
g_d3d12_context->GetDSVHeapManager().Free(&m_rtv_or_dsv_descriptor); switch (m_write_descriptor_type)
else {
g_d3d12_context->GetRTVHeapManager().Free(&m_rtv_or_dsv_descriptor); case Texture::WriteDescriptorType::RTV:
g_d3d12_context->GetRTVHeapManager().Free(&m_write_descriptor);
break;
case Texture::WriteDescriptorType::DSV:
g_d3d12_context->GetDSVHeapManager().Free(&m_write_descriptor);
break;
case Texture::WriteDescriptorType::UAV:
g_d3d12_context->GetDescriptorHeapManager().Free(&m_write_descriptor);
break;
case Texture::WriteDescriptorType::None:
default:
break;
}
m_resource.reset(); m_resource.reset();
m_allocation.reset(); m_allocation.reset();
@ -263,7 +311,7 @@ void Texture::Destroy(bool defer /* = true */)
m_height = 0; m_height = 0;
m_levels = 0; m_levels = 0;
m_format = DXGI_FORMAT_UNKNOWN; m_format = DXGI_FORMAT_UNKNOWN;
m_is_depth_view = false; m_write_descriptor_type = WriteDescriptorType::None;
} }
void Texture::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state) void Texture::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state)
@ -448,3 +496,16 @@ bool Texture::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format,
g_d3d12_context->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle); g_d3d12_context->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle);
return true; return true;
} }
bool Texture::CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh)
{
if (!g_d3d12_context->GetDescriptorHeapManager().Allocate(dh))
{
Console.Error("Failed to allocate UAV descriptor");
return false;
}
const D3D12_UNORDERED_ACCESS_VIEW_DESC desc = {format, D3D12_UAV_DIMENSION_TEXTURE2D};
g_d3d12_context->GetDevice()->CreateUnorderedAccessView(resource, nullptr, &desc, dh->cpu_handle);
return true;
}

View File

@ -46,7 +46,7 @@ namespace D3D12
__fi ID3D12Resource* GetResource() const { return m_resource.get(); } __fi ID3D12Resource* GetResource() const { return m_resource.get(); }
__fi D3D12MA::Allocation* GetAllocation() const { return m_allocation.get(); } __fi D3D12MA::Allocation* GetAllocation() const { return m_allocation.get(); }
__fi const DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; } __fi const DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; }
__fi const DescriptorHandle& GetRTVOrDSVDescriptor() const { return m_rtv_or_dsv_descriptor; } __fi const DescriptorHandle& GetWriteDescriptor() const { return m_write_descriptor; }
__fi D3D12_RESOURCE_STATES GetState() const { return m_state; } __fi D3D12_RESOURCE_STATES GetState() const { return m_state; }
__fi u32 GetWidth() const { return m_width; } __fi u32 GetWidth() const { return m_width; }
@ -83,11 +83,20 @@ namespace D3D12
static bool CreateSRVDescriptor(ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, DescriptorHandle* dh); static bool CreateSRVDescriptor(ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, DescriptorHandle* dh);
static bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh); static bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh);
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh); static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh);
static bool CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, DescriptorHandle* dh);
enum class WriteDescriptorType : u8
{
None,
RTV,
DSV,
UAV
};
ComPtr<ID3D12Resource> m_resource; ComPtr<ID3D12Resource> m_resource;
ComPtr<D3D12MA::Allocation> m_allocation; ComPtr<D3D12MA::Allocation> m_allocation;
DescriptorHandle m_srv_descriptor = {}; DescriptorHandle m_srv_descriptor = {};
DescriptorHandle m_rtv_or_dsv_descriptor = {}; DescriptorHandle m_write_descriptor = {};
u32 m_width = 0; u32 m_width = 0;
u32 m_height = 0; u32 m_height = 0;
u32 m_levels = 0; u32 m_levels = 0;
@ -95,6 +104,6 @@ namespace D3D12
D3D12_RESOURCE_STATES m_state = D3D12_RESOURCE_STATE_COMMON; D3D12_RESOURCE_STATES m_state = D3D12_RESOURCE_STATE_COMMON;
bool m_is_depth_view = false; WriteDescriptorType m_write_descriptor_type = WriteDescriptorType::None;
}; };
} // namespace D3D12 } // namespace D3D12

View File

@ -77,6 +77,7 @@ static const char* s_anisotropic_filtering_values[] = {"0", "2", "4", "8", "16",
static constexpr int DEFAULT_INTERLACE_MODE = 0; static constexpr int DEFAULT_INTERLACE_MODE = 0;
static constexpr int DEFAULT_TV_SHADER_MODE = 0; static constexpr int DEFAULT_TV_SHADER_MODE = 0;
static constexpr int DEFAULT_CAS_SHARPNESS = 50;
GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* parent) GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent) : QWidget(parent)
@ -148,6 +149,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.shadeBoostContrast, "EmuCore/GS", "ShadeBoost_Contrast", false); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.shadeBoostContrast, "EmuCore/GS", "ShadeBoost_Contrast", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.shadeBoostSaturation, "EmuCore/GS", "ShadeBoost_Saturation", false); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.shadeBoostSaturation, "EmuCore/GS", "ShadeBoost_Saturation", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.tvShader, "EmuCore/GS", "TVShader", DEFAULT_TV_SHADER_MODE); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.tvShader, "EmuCore/GS", "TVShader", DEFAULT_TV_SHADER_MODE);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.casMode, "EmuCore/GS", "CASMode", static_cast<int>(GSCASMode::Disabled));
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.casSharpness, "EmuCore/GS", "CASSharpness", DEFAULT_CAS_SHARPNESS);
connect(m_ui.shadeBoost, QOverload<int>::of(&QCheckBox::stateChanged), this, &GraphicsSettingsWidget::onShadeBoostChanged); connect(m_ui.shadeBoost, QOverload<int>::of(&QCheckBox::stateChanged), this, &GraphicsSettingsWidget::onShadeBoostChanged);
onShadeBoostChanged(); onShadeBoostChanged();

View File

@ -1239,15 +1239,69 @@
<property name="title"> <property name="title">
<string>Post-Processing</string> <string>Post-Processing</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_9"> <layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>CAS Mode:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QComboBox" name="casMode">
<item>
<property name="text">
<string>Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Sharpen Only (Internal Resolution)</string>
</property>
</item>
<item>
<property name="text">
<string>Sharpen and Upscale (Display Resolution)</string>
</property>
</item>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_43">
<property name="text">
<string>Sharpness:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="casSharpness">
<property name="suffix">
<string>%</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="shadeBoost"> <widget class="QCheckBox" name="shadeBoost">
<property name="text"> <property name="text">
<string>Shade Boost</string> <string>Shade Boost</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
@ -1315,21 +1369,21 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="0"> <item row="3" column="0">
<widget class="QCheckBox" name="fxaa"> <widget class="QCheckBox" name="fxaa">
<property name="text"> <property name="text">
<string>FXAA</string> <string>FXAA</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_35"> <widget class="QLabel" name="label_35">
<property name="text"> <property name="text">
<string>TV Shader</string> <string>TV Shader</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="1" column="1">
<widget class="QComboBox" name="tvShader"> <widget class="QComboBox" name="tvShader">
<item> <item>
<property name="text"> <property name="text">

View File

@ -211,6 +211,13 @@ enum class GSHardwareDownloadMode : u8
Disabled Disabled
}; };
enum class GSCASMode : u8
{
Disabled,
SharpenOnly,
SharpenAndResize,
};
// Template function for casting enumerations to their underlying type // Template function for casting enumerations to their underlying type
template <typename Enumeration> template <typename Enumeration>
typename std::underlying_type<Enumeration>::type enum_cast(Enumeration E) typename std::underlying_type<Enumeration>::type enum_cast(Enumeration E)
@ -518,7 +525,9 @@ struct Pcsx2Config
UserHacks_TextureInsideRt : 1, UserHacks_TextureInsideRt : 1,
FXAA : 1, FXAA : 1,
ShadeBoost : 1, ShadeBoost : 1,
#ifndef PCSX2_CORE
ShaderFX : 1, ShaderFX : 1,
#endif
DumpGSData : 1, DumpGSData : 1,
SaveRT : 1, SaveRT : 1,
SaveFrame : 1, SaveFrame : 1,
@ -573,6 +582,7 @@ struct Pcsx2Config
TexturePreloadingLevel TexturePreloading{TexturePreloadingLevel::Full}; TexturePreloadingLevel TexturePreloading{TexturePreloadingLevel::Full};
GSDumpCompressionMethod GSDumpCompression{GSDumpCompressionMethod::Zstandard}; GSDumpCompressionMethod GSDumpCompression{GSDumpCompressionMethod::Zstandard};
GSHardwareDownloadMode HWDownloadMode{GSHardwareDownloadMode::Enabled}; GSHardwareDownloadMode HWDownloadMode{GSHardwareDownloadMode::Enabled};
GSCASMode CASMode{GSCASMode::Disabled};
int Dithering{2}; int Dithering{2};
int MaxAnisotropy{0}; int MaxAnisotropy{0};
int SWExtraThreads{2}; int SWExtraThreads{2};
@ -592,6 +602,7 @@ struct Pcsx2Config
int OverrideTextureBarriers{-1}; int OverrideTextureBarriers{-1};
int OverrideGeometryShaders{-1}; int OverrideGeometryShaders{-1};
int CAS_Sharpness{50};
int ShadeBoost_Brightness{50}; int ShadeBoost_Brightness{50};
int ShadeBoost_Contrast{50}; int ShadeBoost_Contrast{50};
int ShadeBoost_Saturation{50}; int ShadeBoost_Saturation{50};

View File

@ -567,8 +567,8 @@ bool D3D12HostDisplay::BeginPresent(bool frame_skip)
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList(); ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET); swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf.GetRTVOrDSVDescriptor(), clear_color.data(), 0, nullptr); cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), clear_color.data(), 0, nullptr);
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetRTVOrDSVDescriptor().cpu_handle, FALSE, nullptr); cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetWriteDescriptor().cpu_handle, FALSE, nullptr);
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f}; const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(m_window_info.surface_width), static_cast<float>(m_window_info.surface_height), 0.0f, 1.0f};
const D3D12_RECT scissor{0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)}; const D3D12_RECT scissor{0, 0, static_cast<LONG>(m_window_info.surface_width), static_cast<LONG>(m_window_info.surface_height)};

View File

@ -2795,7 +2795,7 @@ void FullscreenUI::DrawGraphicsSettingsPage()
static constexpr const char* s_vsync_values[] = {"Off", "On", "Adaptive"}; static constexpr const char* s_vsync_values[] = {"Off", "On", "Adaptive"};
static constexpr const char* s_deinterlacing_options[] = {"Automatic (Default)", "None", "Weave (Top Field First, Sawtooth)", static constexpr const char* s_deinterlacing_options[] = {"Automatic (Default)", "None", "Weave (Top Field First, Sawtooth)",
"Weave (Bottom Field First, Sawtooth)", "Bob (Top Field First)", "Bob (Bottom Field First)", "Blend (Top Field First, Half FPS)", "Weave (Bottom Field First, Sawtooth)", "Bob (Top Field First)", "Bob (Bottom Field First)", "Blend (Top Field First, Half FPS)",
"Blend (Bottom Field First, Half FPS)", "Adaptive (Top Field First", "Adaptive (Bottom Field First)"}; "Blend (Bottom Field First, Half FPS)", "Adaptive (Top Field First)", "Adaptive (Bottom Field First)"};
static const char* s_resolution_options[] = { static const char* s_resolution_options[] = {
"Native (PS2)", "Native (PS2)",
"1.25x Native", "1.25x Native",
@ -3032,8 +3032,21 @@ void FullscreenUI::DrawGraphicsSettingsPage()
MenuHeading("Post-Processing"); MenuHeading("Post-Processing");
{ {
const bool shadeboost_active = GetEffectiveBoolSetting(bsi, "EmuCore/GS", "ShadeBoost", false); static constexpr const char* s_cas_options[] = {
"Disabled", "Sharpen Only (Internal Resolution)", "Sharpen and Resize (Display Resolution)"};
const bool cas_active = (GetEffectiveIntSetting(bsi, "EmuCore/GS", "CASMode", 0) != static_cast<int>(GSCASMode::Disabled));
DrawToggleSetting(bsi, "FXAA", "Enables FXAA post-processing shader.", "EmuCore/GS", "fxaa", false); DrawToggleSetting(bsi, "FXAA", "Enables FXAA post-processing shader.", "EmuCore/GS", "fxaa", false);
DrawIntListSetting(bsi, "Contrast Adaptive Sharpening", "Enables FidelityFX Contrast Adaptive Sharpening.", "EmuCore/GS", "CASMode",
static_cast<int>(GSCASMode::Disabled), s_cas_options, std::size(s_cas_options));
DrawIntSpinBoxSetting(bsi, "CAS Sharpness", "Determines the intensity the sharpening effect in CAS post-processing.", "EmuCore/GS",
"CASSharpness", 50, 0, 100, 1, "%d%%", cas_active);
}
MenuHeading("Filters");
{
const bool shadeboost_active = GetEffectiveBoolSetting(bsi, "EmuCore/GS", "ShadeBoost", false);
DrawToggleSetting(bsi, "Shade Boost", "Enables brightness/contrast/saturation adjustment.", "EmuCore/GS", "ShadeBoost", false); DrawToggleSetting(bsi, "Shade Boost", "Enables brightness/contrast/saturation adjustment.", "EmuCore/GS", "ShadeBoost", false);
DrawIntRangeSetting(bsi, "Shade Boost Brightness", "Adjusts brightness. 50 is normal.", "EmuCore/GS", "ShadeBoost_Brightness", 50, DrawIntRangeSetting(bsi, "Shade Boost Brightness", "Adjusts brightness. 50 is normal.", "EmuCore/GS", "ShadeBoost_Brightness", 50,
1, 100, "%d", shadeboost_active); 1, 100, "%d", shadeboost_active);
@ -3847,8 +3860,7 @@ void FullscreenUI::DrawGameFixesSettingsPage()
DrawToggleSetting(bsi, "EE Timing Hack", DrawToggleSetting(bsi, "EE Timing Hack",
"Known to affect following games: Digital Devil Saga (Fixes FMV and crashes), SSX (Fixes bad graphics and crashes).", "Known to affect following games: Digital Devil Saga (Fixes FMV and crashes), SSX (Fixes bad graphics and crashes).",
"EmuCore/Gamefixes", "EETimingHack", false); "EmuCore/Gamefixes", "EETimingHack", false);
DrawToggleSetting(bsi, "Instant DMA Hack", DrawToggleSetting(bsi, "Instant DMA Hack", "Known to affect following games: Fire Pro Wrestling Z (Bad ring graphics).",
"Known to affect following games: Fire Pro Wrestling Z (Bad ring graphics).",
"EmuCore/Gamefixes", "InstantDMAHack", false); "EmuCore/Gamefixes", "InstantDMAHack", false);
DrawToggleSetting(bsi, "Handle DMAC writes when it is busy.", DrawToggleSetting(bsi, "Handle DMAC writes when it is busy.",
"Known to affect following games: Mana Khemia 1 (Going \"off campus\"), Metal Saga (Intro FMV), Pilot Down Behind Enemy Lines.", "Known to affect following games: Mana Khemia 1 (Going \"off campus\"), Metal Saga (Intro FMV), Pilot Down Behind Enemy Lines.",

View File

@ -17,6 +17,8 @@
#include "GSDevice.h" #include "GSDevice.h"
#include "GS/GSGL.h" #include "GS/GSGL.h"
#include "GS/GS.h" #include "GS/GS.h"
#include "Host.h"
#include "common/StringUtil.h"
const char* shaderName(ShaderConvert value) const char* shaderName(ShaderConvert value)
{ {
@ -81,13 +83,8 @@ GSDevice::GSDevice() = default;
GSDevice::~GSDevice() GSDevice::~GSDevice()
{ {
PurgePool(); // should've been cleaned up in Destroy()
pxAssert(m_pool.empty() && !m_merge && !m_weavebob && !m_blend && !m_mad && !m_target_tmp && !m_cas);
delete m_merge;
delete m_weavebob;
delete m_blend;
delete m_mad;
delete m_target_tmp;
} }
bool GSDevice::Create() bool GSDevice::Create()
@ -97,21 +94,8 @@ bool GSDevice::Create()
void GSDevice::Destroy() void GSDevice::Destroy()
{ {
ClearCurrent();
PurgePool(); PurgePool();
delete m_merge;
delete m_weavebob;
delete m_blend;
delete m_mad;
delete m_target_tmp;
m_merge = nullptr;
m_weavebob = nullptr;
m_blend = nullptr;
m_mad = nullptr;
m_target_tmp = nullptr;
m_current = nullptr; // current is special, points to other textures, no need to delete
} }
void GSDevice::ResetAPIState() void GSDevice::ResetAPIState()
@ -317,12 +301,14 @@ void GSDevice::ClearCurrent()
delete m_blend; delete m_blend;
delete m_mad; delete m_mad;
delete m_target_tmp; delete m_target_tmp;
delete m_cas;
m_merge = nullptr; m_merge = nullptr;
m_weavebob = nullptr; m_weavebob = nullptr;
m_blend = nullptr; m_blend = nullptr;
m_mad = nullptr; m_mad = nullptr;
m_target_tmp = nullptr; m_target_tmp = nullptr;
m_cas = nullptr;
} }
void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c) void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
@ -403,6 +389,8 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
} }
} }
#ifndef PCSX2_CORE
void GSDevice::ExternalFX() void GSDevice::ExternalFX()
{ {
const GSVector2i s = m_current->GetSize(); const GSVector2i s = m_current->GetSize();
@ -417,6 +405,8 @@ void GSDevice::ExternalFX()
} }
} }
#endif
void GSDevice::FXAA() void GSDevice::FXAA()
{ {
const GSVector2i s = m_current->GetSize(); const GSVector2i s = m_current->GetSize();
@ -519,6 +509,62 @@ void GSDevice::SetHWDrawConfigForAlphaPass(GSHWDrawConfig::PSSelector* ps,
} }
} }
// Kinda grotty, but better than copy/pasting the relevant bits in..
#define A_CPU 1
#include "bin/resources/shaders/common/ffx_a.h"
#include "bin/resources/shaders/common/ffx_cas.h"
bool GSDevice::GetCASShaderSource(std::string* source)
{
std::optional<std::string> ffx_a_source(Host::ReadResourceFileToString("shaders/common/ffx_a.h"));
std::optional<std::string> ffx_cas_source(Host::ReadResourceFileToString("shaders/common/ffx_cas.h"));
if (!ffx_a_source.has_value() || !ffx_cas_source.has_value())
return false;
// Since our shader compilers don't support includes, and OpenGL doesn't at all... we'll do a really cheeky string replace.
StringUtil::ReplaceAll(source, "#include \"ffx_a.h\"", ffx_a_source.value());
StringUtil::ReplaceAll(source, "#include \"ffx_cas.h\"", ffx_cas_source.value());
return true;
}
void GSDevice::CAS(GSTexture*& tex, GSVector4i& src_rect, GSVector4& src_uv, const GSVector4& draw_rect, bool sharpen_only)
{
const int dst_width = sharpen_only ? src_rect.width() : static_cast<int>(std::ceil(draw_rect.z - draw_rect.x));
const int dst_height = sharpen_only ? src_rect.height() : static_cast<int>(std::ceil(draw_rect.w - draw_rect.y));
const int src_offset_x = static_cast<int>(src_rect.x);
const int src_offset_y = static_cast<int>(src_rect.y);
GSTexture* src_tex = tex;
if (!m_cas || m_cas->GetWidth() != dst_width || m_cas->GetHeight() != dst_height)
{
delete m_cas;
m_cas = CreateSurface(GSTexture::Type::RWTexture, dst_width, dst_height, 1, GSTexture::Format::Color);
if (!m_cas)
{
Console.Error("Failed to allocate CAS RW texture.");
return;
}
}
std::array<u32, NUM_CAS_CONSTANTS> consts;
CasSetup(&consts[0], &consts[4], static_cast<float>(GSConfig.CAS_Sharpness) * 0.01f,
static_cast<AF1>(src_rect.width()), static_cast<AF1>(src_rect.height()),
static_cast<AF1>(dst_width), static_cast<AF1>(dst_height));
consts[8] = static_cast<u32>(src_offset_x);
consts[9] = static_cast<u32>(src_offset_y);
if (!DoCAS(src_tex, m_cas, sharpen_only, consts))
{
// leave textures intact if we failed
Console.Warning("Applying CAS failed.");
return;
}
tex = m_cas;
src_rect = GSVector4i(0, 0, dst_width, dst_height);
src_uv = GSVector4(0.0f, 0.0f, 1.0f, 1.0f);
}
GSAdapter::operator std::string() const GSAdapter::operator std::string() const
{ {
char buf[sizeof "12345678:12345678:12345678:12345678"]; char buf[sizeof "12345678:12345678:12345678:12345678"];

View File

@ -710,6 +710,7 @@ public:
bool framebuffer_fetch : 1; ///< Can sample from the framebuffer without texture barriers. bool framebuffer_fetch : 1; ///< Can sample from the framebuffer without texture barriers.
bool dual_source_blend : 1; ///< Can use alpha output as a blend factor. bool dual_source_blend : 1; ///< Can use alpha output as a blend factor.
bool stencil_buffer : 1; ///< Supports stencil buffer, and can use for DATE. bool stencil_buffer : 1; ///< Supports stencil buffer, and can use for DATE.
bool cas_sharpening : 1; ///< Supports sufficient functionality for contrast adaptive sharpening.
FeatureSupport() FeatureSupport()
{ {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
@ -740,6 +741,7 @@ protected:
static constexpr int NUM_INTERLACE_SHADERS = 5; static constexpr int NUM_INTERLACE_SHADERS = 5;
static constexpr float MAD_SENSITIVITY = 0.08f; static constexpr float MAD_SENSITIVITY = 0.08f;
static constexpr u32 MAX_POOLED_TEXTURES = 300; static constexpr u32 MAX_POOLED_TEXTURES = 300;
static constexpr u32 NUM_CAS_CONSTANTS = 12; // 8 plus src offset x/y, 16 byte alignment
GSTexture* m_merge = nullptr; GSTexture* m_merge = nullptr;
GSTexture* m_weavebob = nullptr; GSTexture* m_weavebob = nullptr;
@ -747,6 +749,7 @@ protected:
GSTexture* m_mad = nullptr; GSTexture* m_mad = nullptr;
GSTexture* m_target_tmp = nullptr; GSTexture* m_target_tmp = nullptr;
GSTexture* m_current = nullptr; GSTexture* m_current = nullptr;
GSTexture* m_cas = nullptr;
struct struct
{ {
size_t stride, start, count, limit; size_t stride, start, count, limit;
@ -766,7 +769,15 @@ protected:
virtual void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) = 0; virtual void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) = 0;
virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) {} virtual void DoFXAA(GSTexture* sTex, GSTexture* dTex) {}
virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) {} virtual void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) {}
#ifndef PCSX2_CORE
virtual void DoExternalFX(GSTexture* sTex, GSTexture* dTex) {} virtual void DoExternalFX(GSTexture* sTex, GSTexture* dTex) {}
#endif
/// Resolves CAS shader includes for the specified source.
static bool GetCASShaderSource(std::string* source);
/// Applies CAS and writes to the destination texture, which should be a RWTexture.
virtual bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) = 0;
public: public:
GSDevice(); GSDevice();
@ -847,7 +858,11 @@ public:
void Interlace(const GSVector2i& ds, int field, int mode, float yoffset); void Interlace(const GSVector2i& ds, int field, int mode, float yoffset);
void FXAA(); void FXAA();
void ShadeBoost(); void ShadeBoost();
#ifndef PCSX2_CORE
void ExternalFX(); void ExternalFX();
#endif
void CAS(GSTexture*& tex, GSVector4i& src_rect, GSVector4& src_uv, const GSVector4& draw_rect, bool sharpen_only);
bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear = true, bool prefer_reuse = false); bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear = true, bool prefer_reuse = false);
bool ResizeTexture(GSTexture** t, int w, int h, bool prefer_reuse = false); bool ResizeTexture(GSTexture** t, int w, int h, bool prefer_reuse = false);

View File

@ -375,38 +375,34 @@ bool GSRenderer::Merge(int field)
m_real_size = GSVector2i(fs.x, is_interlaced_resolution ? ds.y : fs.y); m_real_size = GSVector2i(fs.x, is_interlaced_resolution ? ds.y : fs.y);
if (tex[0] || tex[1]) if (!tex[0] && !tex[1])
return false;
if ((tex[0] == tex[1]) && (src_out_rect[0] == src_out_rect[1]).alltrue() && (dst[0] == dst[1]).alltrue() && !feedback_merge && !slbg)
{ {
if ((tex[0] == tex[1]) && (src_out_rect[0] == src_out_rect[1]).alltrue() && (dst[0] == dst[1]).alltrue() && !feedback_merge && !slbg) // the two outputs are identical, skip drawing one of them (the one that is alpha blended)
{
// the two outputs are identical, skip drawing one of them (the one that is alpha blended)
tex[0] = NULL; tex[0] = NULL;
}
GSVector4 c = GSVector4((int)m_regs->BGCOLOR.R, (int)m_regs->BGCOLOR.G, (int)m_regs->BGCOLOR.B, (int)m_regs->PMODE.ALP) / 255;
g_gs_device->Merge(tex, src_gs_read, dst, fs, m_regs->PMODE, m_regs->EXTBUF, c);
if (isReallyInterlaced() && GSConfig.InterlaceMode != GSInterlaceMode::Off)
g_gs_device->Interlace(ds, field ^ field2, mode, offset);
if (GSConfig.ShadeBoost)
{
g_gs_device->ShadeBoost();
}
if (GSConfig.ShaderFX)
{
g_gs_device->ExternalFX();
}
if (GSConfig.FXAA)
{
g_gs_device->FXAA();
}
} }
GSVector4 c = GSVector4((int)m_regs->BGCOLOR.R, (int)m_regs->BGCOLOR.G, (int)m_regs->BGCOLOR.B, (int)m_regs->PMODE.ALP) / 255;
g_gs_device->Merge(tex, src_gs_read, dst, fs, m_regs->PMODE, m_regs->EXTBUF, c);
if (isReallyInterlaced() && GSConfig.InterlaceMode != GSInterlaceMode::Off)
g_gs_device->Interlace(ds, field ^ field2, mode, offset);
if (GSConfig.ShadeBoost)
g_gs_device->ShadeBoost();
#ifndef PCSX2_CORE
if (GSConfig.ShaderFX)
g_gs_device->ExternalFX();
#endif
if (GSConfig.FXAA)
g_gs_device->FXAA();
if (m_scanmask_used) if (m_scanmask_used)
m_scanmask_used--; m_scanmask_used--;
@ -616,18 +612,33 @@ void GSRenderer::VSync(u32 field, bool registers_written)
if ((g_perfmon.GetFrame() & 0x1f) == 0) if ((g_perfmon.GetFrame() & 0x1f) == 0)
g_perfmon.Update(); g_perfmon.Update();
// Little bit ugly, but we can't do CAS inside the render pass.
GSVector4i src_rect;
GSVector4 src_uv, draw_rect;
GSTexture* current = g_gs_device->GetCurrent();
if (current && !blank_frame)
{
src_rect = CalculateDrawSrcRect(current);
src_uv = GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy();
draw_rect = CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(),
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets));
if (GSConfig.CASMode != GSCASMode::Disabled && g_gs_device->Features().cas_sharpening)
{
// sharpen only if the IR is higher than the display resolution
const bool sharpen_only = (GSConfig.CASMode == GSCASMode::SharpenOnly ||
(current->GetWidth() > g_host_display->GetWindowWidth() &&
current->GetHeight() > g_host_display->GetWindowHeight()));
g_gs_device->CAS(current, src_rect, src_uv, draw_rect, sharpen_only);
}
}
g_gs_device->ResetAPIState(); g_gs_device->ResetAPIState();
if (Host::BeginPresentFrame(false)) if (Host::BeginPresentFrame(false))
{ {
GSTexture* current = g_gs_device->GetCurrent();
if (current && !blank_frame) if (current && !blank_frame)
{ {
const GSVector4i src_rect(CalculateDrawSrcRect(current));
const GSVector4 src_uv(GSVector4(src_rect) / GSVector4(current->GetSize()).xyxy());
const GSVector4 draw_rect(CalculateDrawDstRect(g_host_display->GetWindowWidth(), g_host_display->GetWindowHeight(),
src_rect, current->GetSize(), g_host_display->GetDisplayAlignment(), g_host_display->UsesLowerLeftOrigin(),
GetVideoMode() == GSVideoMode::SDTV_480P || (GSConfig.PCRTCOverscan && GSConfig.PCRTCOffsets)));
const u64 current_time = Common::Timer::GetCurrentValue(); const u64 current_time = Common::Timer::GetCurrentValue();
const float shader_time = static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - m_shader_time_start)); const float shader_time = static_cast<float>(Common::Timer::ConvertValueToSeconds(current_time - m_shader_time_start));

View File

@ -33,6 +33,7 @@ public:
DepthStencil, DepthStencil,
Texture, Texture,
Offscreen, Offscreen,
RWTexture,
}; };
enum class Format : u8 enum class Format : u8

View File

@ -23,6 +23,7 @@
#include "Host.h" #include "Host.h"
#include "HostDisplay.h" #include "HostDisplay.h"
#include "ShaderCacheVersion.h" #include "ShaderCacheVersion.h"
#include "common/Path.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
@ -350,6 +351,7 @@ bool GSDevice11::Create()
return false; return false;
} }
CreateCASShaders();
return true; return true;
} }
@ -507,6 +509,9 @@ GSTexture* GSDevice11::CreateSurface(GSTexture::Type type, int width, int height
desc.Usage = D3D11_USAGE_STAGING; desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags |= D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; desc.CPUAccessFlags |= D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
break; break;
case GSTexture::Type::RWTexture:
desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
break;
} }
GSTexture11* t = nullptr; GSTexture11* t = nullptr;
@ -839,6 +844,8 @@ void GSDevice11::DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool
StretchRect(sTex, sRect, dTex, dRect, m_interlace.ps[shader].get(), m_interlace.cb.get(), linear); StretchRect(sTex, sRect, dTex, dRect, m_interlace.ps[shader].get(), m_interlace.cb.get(), linear);
} }
#ifndef PCSX2_CORE
void GSDevice11::DoExternalFX(GSTexture* sTex, GSTexture* dTex) void GSDevice11::DoExternalFX(GSTexture* sTex, GSTexture* dTex)
{ {
const GSVector2i s = dTex->GetSize(); const GSVector2i s = dTex->GetSize();
@ -885,6 +892,8 @@ void GSDevice11::DoExternalFX(GSTexture* sTex, GSTexture* dTex)
StretchRect(sTex, sRect, dTex, dRect, m_shaderfx.ps.get(), m_shaderfx.cb.get(), true); StretchRect(sTex, sRect, dTex, dRect, m_shaderfx.ps.get(), m_shaderfx.cb.get(), true);
} }
#endif
void GSDevice11::DoFXAA(GSTexture* sTex, GSTexture* dTex) void GSDevice11::DoFXAA(GSTexture* sTex, GSTexture* dTex)
{ {
const GSVector2i s = dTex->GetSize(); const GSVector2i s = dTex->GetSize();
@ -925,6 +934,55 @@ void GSDevice11::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float para
StretchRect(sTex, sRect, dTex, dRect, m_shadeboost.ps.get(), m_shadeboost.cb.get(), true); StretchRect(sTex, sRect, dTex, dRect, m_shadeboost.ps.get(), m_shadeboost.cb.get(), true);
} }
bool GSDevice11::CreateCASShaders()
{
CD3D11_BUFFER_DESC desc(NUM_CAS_CONSTANTS * sizeof(u32), D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DEFAULT);
HRESULT hr = m_dev->CreateBuffer(&desc, nullptr, m_cas.cb.put());
if (FAILED(hr))
return false;
std::optional<std::string> cas_source(Host::ReadResourceFileToString("shaders/dx11/cas.hlsl"));
if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value()))
return false;
static constexpr D3D_SHADER_MACRO sharpen_only_macros[] = {
{"CAS_SHARPEN_ONLY", "1"},
{nullptr, nullptr}};
m_cas.cs_sharpen = m_shader_cache.GetComputeShader(m_dev.get(), cas_source.value(), sharpen_only_macros, "main");
m_cas.cs_upscale = m_shader_cache.GetComputeShader(m_dev.get(), cas_source.value(), nullptr, "main");
if (!m_cas.cs_sharpen || !m_cas.cs_upscale)
return false;
m_features.cas_sharpening = true;
return true;
}
bool GSDevice11::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
{
static const int threadGroupWorkRegionDim = 16;
const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
ID3D11ShaderResourceView* srvs[1] = {*static_cast<GSTexture11*>(sTex)};
ID3D11UnorderedAccessView* uavs[1] = {*static_cast<GSTexture11*>(dTex)};
m_ctx->OMSetRenderTargets(0, nullptr, nullptr);
m_ctx->UpdateSubresource(m_cas.cb.get(), 0, nullptr, constants.data(), 0, 0);
m_ctx->CSSetConstantBuffers(0, 1, m_cas.cb.addressof());
m_ctx->CSSetShader(sharpen_only ? m_cas.cs_sharpen.get() : m_cas.cs_upscale.get(), nullptr, 0);
m_ctx->CSSetShaderResources(0, std::size(srvs), srvs);
m_ctx->CSSetUnorderedAccessViews(0, std::size(uavs), uavs, nullptr);
m_ctx->Dispatch(dispatchX, dispatchY, 1);
// clear bindings out to prevent hazards
uavs[0] = nullptr;
srvs[0] = nullptr;
m_ctx->CSSetShaderResources(0, std::size(srvs), srvs);
m_ctx->CSSetUnorderedAccessViews(0, std::size(uavs), uavs, nullptr);
return true;
}
void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm) void GSDevice11::SetupDATE(GSTexture* rt, GSTexture* ds, const GSVertexPT1* vertices, bool datm)
{ {
// sfex3 (after the capcom logo), vf4 (first menu fading in), ffxii shadows, rumble roses shadows, persona4 shadows // sfex3 (after the capcom logo), vf4 (first menu fading in), ffxii shadows, rumble roses shadows, persona4 shadows

View File

@ -122,7 +122,12 @@ private:
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final; void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
#ifndef PCSX2_CORE
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final; void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final;
#endif
bool CreateCASShaders();
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
wil::com_ptr_nothrow<ID3D11Device> m_dev; wil::com_ptr_nothrow<ID3D11Device> m_dev;
wil::com_ptr_nothrow<ID3D11DeviceContext> m_ctx; wil::com_ptr_nothrow<ID3D11DeviceContext> m_ctx;
@ -210,6 +215,13 @@ private:
wil::com_ptr_nothrow<ID3D11PixelShader> primid_init_ps[2]; wil::com_ptr_nothrow<ID3D11PixelShader> primid_init_ps[2];
} m_date; } m_date;
struct
{
wil::com_ptr_nothrow<ID3D11Buffer> cb;
wil::com_ptr_nothrow<ID3D11ComputeShader> cs_upscale;
wil::com_ptr_nothrow<ID3D11ComputeShader> cs_sharpen;
} m_cas;
// Shaders... // Shaders...
std::unordered_map<u32, GSVertexShader11> m_vs; std::unordered_map<u32, GSVertexShader11> m_vs;

View File

@ -258,6 +258,14 @@ GSTexture11::operator ID3D11DepthStencilView*()
return m_dsv.get(); return m_dsv.get();
} }
GSTexture11::operator ID3D11UnorderedAccessView*()
{
if (!m_uav)
GSDevice11::GetInstance()->GetD3DDevice()->CreateUnorderedAccessView(m_texture.get(), nullptr, m_uav.put());
return m_uav.get();
}
bool GSTexture11::Equal(GSTexture11* tex) bool GSTexture11::Equal(GSTexture11* tex)
{ {
return tex && m_texture == tex->m_texture; return tex && m_texture == tex->m_texture;

View File

@ -26,6 +26,7 @@ class GSTexture11 final : public GSTexture
wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_srv; wil::com_ptr_nothrow<ID3D11ShaderResourceView> m_srv;
wil::com_ptr_nothrow<ID3D11RenderTargetView> m_rtv; wil::com_ptr_nothrow<ID3D11RenderTargetView> m_rtv;
wil::com_ptr_nothrow<ID3D11DepthStencilView> m_dsv; wil::com_ptr_nothrow<ID3D11DepthStencilView> m_dsv;
wil::com_ptr_nothrow<ID3D11UnorderedAccessView> m_uav;
D3D11_TEXTURE2D_DESC m_desc; D3D11_TEXTURE2D_DESC m_desc;
int m_mapped_subresource; int m_mapped_subresource;
@ -47,4 +48,5 @@ public:
operator ID3D11ShaderResourceView*(); operator ID3D11ShaderResourceView*();
operator ID3D11RenderTargetView*(); operator ID3D11RenderTargetView*();
operator ID3D11DepthStencilView*(); operator ID3D11DepthStencilView*();
operator ID3D11UnorderedAccessView*();
}; };

View File

@ -156,6 +156,7 @@ bool GSDevice12::Create()
return false; return false;
} }
CompileCASPipelines();
InitializeState(); InitializeState();
InitializeSamplers(); InitializeSamplers();
return true; return true;
@ -787,6 +788,82 @@ void GSDevice12::DoFXAA(GSTexture* sTex, GSTexture* dTex)
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
} }
bool GSDevice12::CompileCASPipelines()
{
D3D12::RootSignatureBuilder rsb;
rsb.Add32BitConstants(0, NUM_CAS_CONSTANTS, D3D12_SHADER_VISIBILITY_ALL);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 1, D3D12_SHADER_VISIBILITY_ALL);
rsb.AddDescriptorTable(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 1, D3D12_SHADER_VISIBILITY_ALL);
m_cas_root_signature = rsb.Create(false);
if (!m_cas_root_signature)
return false;
std::optional<std::string> cas_source(Host::ReadResourceFileToString("shaders/dx11/cas.hlsl"));
if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value()))
return false;
static constexpr D3D_SHADER_MACRO sharpen_only_macros[] = {
{"CAS_SHARPEN_ONLY", "1"},
{nullptr, nullptr}};
const ComPtr<ID3DBlob> cs_upscale(m_shader_cache.GetComputeShader(cas_source.value(), nullptr, "main"));
const ComPtr<ID3DBlob> cs_sharpen(m_shader_cache.GetComputeShader(cas_source.value(), sharpen_only_macros, "main"));
if (!cs_upscale || !cs_sharpen)
return false;
D3D12::ComputePipelineBuilder cpb;
cpb.SetRootSignature(m_cas_root_signature.get());
cpb.SetShader(cs_upscale->GetBufferPointer(), cs_upscale->GetBufferSize());
m_cas_upscale_pipeline = cpb.Create(g_d3d12_context->GetDevice(), m_shader_cache, false);
cpb.SetShader(cs_sharpen->GetBufferPointer(), cs_sharpen->GetBufferSize());
m_cas_sharpen_pipeline = cpb.Create(g_d3d12_context->GetDevice(), m_shader_cache, false);
if (!m_cas_upscale_pipeline || !m_cas_sharpen_pipeline)
return false;
m_features.cas_sharpening = true;
return true;
}
bool GSDevice12::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
{
EndRenderPass();
GSTexture12* const sTex12 = static_cast<GSTexture12*>(sTex);
GSTexture12* const dTex12 = static_cast<GSTexture12*>(dTex);
D3D12::DescriptorHandle sTexDH, dTexDH;
if (!GetTextureGroupDescriptors(&sTexDH, &sTex12->GetTexture().GetSRVDescriptor(), 1) ||
!GetTextureGroupDescriptors(&dTexDH, &dTex12->GetTexture().GetWriteDescriptor(), 1))
{
ExecuteCommandList(false, "Ran out of descriptors for CAS");
if (!GetTextureGroupDescriptors(&sTexDH, &sTex12->GetTexture().GetSRVDescriptor(), 1) ||
!GetTextureGroupDescriptors(&dTexDH, &dTex12->GetTexture().GetWriteDescriptor(), 1))
{
Console.Error("Failed to allocate CAS descriptors.");
return false;
}
}
ID3D12GraphicsCommandList* const cmdlist = g_d3d12_context->GetCommandList();
const D3D12_RESOURCE_STATES old_state = sTex12->GetTexture().GetState();
sTex12->GetTexture().TransitionToState(cmdlist, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
dTex12->GetTexture().TransitionToState(cmdlist, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
cmdlist->SetComputeRootSignature(m_cas_root_signature.get());
cmdlist->SetComputeRoot32BitConstants(CAS_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS, NUM_CAS_CONSTANTS, constants.data(), 0);
cmdlist->SetComputeRootDescriptorTable(CAS_ROOT_SIGNATURE_PARAM_SRC_TEXTURE, sTexDH);
cmdlist->SetComputeRootDescriptorTable(CAS_ROOT_SIGNATURE_PARAM_DST_TEXTURE, dTexDH);
cmdlist->SetPipelineState(sharpen_only ? m_cas_sharpen_pipeline.get() : m_cas_upscale_pipeline.get());
m_dirty_flags |= DIRTY_FLAG_PIPELINE;
static const int threadGroupWorkRegionDim = 16;
const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
cmdlist->Dispatch(dispatchX, dispatchY, 1);
sTex12->GetTexture().TransitionToState(cmdlist, old_state);
return true;
}
void GSDevice12::IASetVertexBuffer(const void* vertex, size_t stride, size_t count) void GSDevice12::IASetVertexBuffer(const void* vertex, size_t stride, size_t count)
{ {
const u32 size = static_cast<u32>(stride) * static_cast<u32>(count); const u32 size = static_cast<u32>(stride) * static_cast<u32>(count);

View File

@ -131,6 +131,10 @@ public:
UTILITY_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS = 0, UTILITY_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS = 0,
UTILITY_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 1, UTILITY_ROOT_SIGNATURE_PARAM_PS_TEXTURES = 1,
UTILITY_ROOT_SIGNATURE_PARAM_PS_SAMPLERS = 2, UTILITY_ROOT_SIGNATURE_PARAM_PS_SAMPLERS = 2,
CAS_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS = 0,
CAS_ROOT_SIGNATURE_PARAM_SRC_TEXTURE = 1,
CAS_ROOT_SIGNATURE_PARAM_DST_TEXTURE = 2
}; };
private: private:
@ -168,6 +172,10 @@ private:
std::unordered_map<GSHWDrawConfig::PSSelector, ComPtr<ID3DBlob>, GSHWDrawConfig::PSSelectorHash> m_tfx_pixel_shaders; std::unordered_map<GSHWDrawConfig::PSSelector, ComPtr<ID3DBlob>, GSHWDrawConfig::PSSelectorHash> m_tfx_pixel_shaders;
std::unordered_map<PipelineSelector, ComPtr<ID3D12PipelineState>, PipelineSelectorHash> m_tfx_pipelines; std::unordered_map<PipelineSelector, ComPtr<ID3D12PipelineState>, PipelineSelectorHash> m_tfx_pipelines;
ComPtr<ID3D12RootSignature> m_cas_root_signature;
ComPtr<ID3D12PipelineState> m_cas_upscale_pipeline;
ComPtr<ID3D12PipelineState> m_cas_sharpen_pipeline;
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache; GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache; GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
@ -185,6 +193,8 @@ private:
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
bool GetSampler(D3D12::DescriptorHandle* cpu_handle, GSHWDrawConfig::SamplerSelector ss); bool GetSampler(D3D12::DescriptorHandle* cpu_handle, GSHWDrawConfig::SamplerSelector ss);
void ClearSamplerCache() final; void ClearSamplerCache() final;
bool GetTextureGroupDescriptors(D3D12::DescriptorHandle* gpu_handle, const D3D12::DescriptorHandle* cpu_handles, u32 count); bool GetTextureGroupDescriptors(D3D12::DescriptorHandle* gpu_handle, const D3D12::DescriptorHandle* cpu_handles, u32 count);
@ -208,6 +218,7 @@ private:
bool CompileInterlacePipelines(); bool CompileInterlacePipelines();
bool CompileMergePipelines(); bool CompileMergePipelines();
bool CompilePostProcessingPipelines(); bool CompilePostProcessingPipelines();
bool CompileCASPipelines();
bool CheckStagingBufferSize(u32 required_size); bool CheckStagingBufferSize(u32 required_size);
bool MapStagingBuffer(u32 size_to_read); bool MapStagingBuffer(u32 size_to_read);

View File

@ -99,6 +99,21 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, u32 width, u32 heigh
return std::make_unique<GSTexture12>(type, format, std::move(texture)); return std::make_unique<GSTexture12>(type, format, std::move(texture));
} }
case Type::RWTexture:
{
pxAssert(levels == 1);
D3D12::Texture texture;
if (!texture.Create(width, height, levels, d3d_format, srv_format, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, D3D12MA::ALLOCATION_FLAG_COMMITTED))
{
return {};
}
D3D12::SetObjectNameFormatted(texture.GetResource(), "%ux%u RW texture", width, height);
return std::make_unique<GSTexture12>(type, format, std::move(texture));
}
default: default:
return {}; return {};
} }
@ -381,12 +396,12 @@ void GSTexture12::CommitClear(ID3D12GraphicsCommandList* cmdlist)
if (IsDepthStencil()) if (IsDepthStencil())
{ {
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE); m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE);
cmdlist->ClearDepthStencilView(m_texture.GetRTVOrDSVDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr); cmdlist->ClearDepthStencilView(m_texture.GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr);
} }
else else
{ {
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET); m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(m_texture.GetRTVOrDSVDescriptor(), m_clear_value.color, 0, nullptr); cmdlist->ClearRenderTargetView(m_texture.GetWriteDescriptor(), m_clear_value.color, 0, nullptr);
} }
SetState(GSTexture::State::Dirty); SetState(GSTexture::State::Dirty);

View File

@ -38,7 +38,7 @@ public:
__fi D3D12::Texture& GetTexture() { return m_texture; } __fi D3D12::Texture& GetTexture() { return m_texture; }
__fi const D3D12::DescriptorHandle& GetSRVDescriptor() const { return m_texture.GetSRVDescriptor(); } __fi const D3D12::DescriptorHandle& GetSRVDescriptor() const { return m_texture.GetSRVDescriptor(); }
__fi const D3D12::DescriptorHandle& GetRTVOrDSVHandle() const { return m_texture.GetRTVOrDSVDescriptor(); } __fi const D3D12::DescriptorHandle& GetRTVOrDSVHandle() const { return m_texture.GetWriteDescriptor(); }
__fi D3D12_RESOURCE_STATES GetResourceState() const { return m_texture.GetState(); } __fi D3D12_RESOURCE_STATES GetResourceState() const { return m_texture.GetState(); }
__fi DXGI_FORMAT GetNativeFormat() const { return m_texture.GetFormat(); } __fi DXGI_FORMAT GetNativeFormat() const { return m_texture.GetFormat(); }
__fi ID3D12Resource* GetResource() const { return m_texture.GetResource(); } __fi ID3D12Resource* GetResource() const { return m_texture.GetResource(); }

View File

@ -350,7 +350,12 @@ public:
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) override; void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset, int bufIdx) override;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) override; void DoFXAA(GSTexture* sTex, GSTexture* dTex) override;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) override;
#ifndef PCSX2_CORE
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) override; void DoExternalFX(GSTexture* sTex, GSTexture* dTex) override;
#endif
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) override;
MRCOwned<id<MTLFunction>> LoadShader(NSString* name); MRCOwned<id<MTLFunction>> LoadShader(NSString* name);
MRCOwned<id<MTLRenderPipelineState>> MakePipeline(MTLRenderPipelineDescriptor* desc, id<MTLFunction> vertex, id<MTLFunction> fragment, NSString* name); MRCOwned<id<MTLRenderPipelineState>> MakePipeline(MTLRenderPipelineDescriptor* desc, id<MTLFunction> vertex, id<MTLFunction> fragment, NSString* name);

View File

@ -615,11 +615,20 @@ void GSDeviceMTL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float par
RenderCopy(sTex, m_shadeboost_pipeline, GSVector4i(0, 0, dTex->GetSize().x, dTex->GetSize().y)); RenderCopy(sTex, m_shadeboost_pipeline, GSVector4i(0, 0, dTex->GetSize().x, dTex->GetSize().y));
} }
#ifndef PCSX2_CORE
void GSDeviceMTL::DoExternalFX(GSTexture* sTex, GSTexture* dTex) void GSDeviceMTL::DoExternalFX(GSTexture* sTex, GSTexture* dTex)
{ {
// TODO: Implement // TODO: Implement
} }
#endif
bool GSDeviceMTL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
{
return false;
}
MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name) MRCOwned<id<MTLFunction>> GSDeviceMTL::LoadShader(NSString* name)
{ {
NSError* err = nil; NSError* err = nil;

View File

@ -23,6 +23,7 @@
#include "Host.h" #include "Host.h"
#include "HostDisplay.h" #include "HostDisplay.h"
#include "ShaderCacheVersion.h" #include "ShaderCacheVersion.h"
#include "IconsFontAwesome5.h"
#include <cinttypes> #include <cinttypes>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
@ -505,6 +506,12 @@ bool GSDeviceOGL::Create()
m_shadeboost.ps.SetName("Shadeboost pipe"); m_shadeboost.ps.SetName("Shadeboost pipe");
} }
if (!CreateCASPrograms() && GSConfig.CASMode != GSCASMode::Disabled)
{
Host::AddIconOSDMessage("CASUnsupported", ICON_FA_EXCLAMATION_TRIANGLE,
"CAS is not available, your graphics driver does not supported the required functionality.", 10.0f);
}
// **************************************************************** // ****************************************************************
// rasterization configuration // rasterization configuration
// **************************************************************** // ****************************************************************
@ -1425,9 +1432,10 @@ void GSDeviceOGL::DoFXAA(GSTexture* sTex, GSTexture* dTex)
StretchRect(sTex, sRect, dTex, dRect, m_fxaa.ps, true); StretchRect(sTex, sRect, dTex, dRect, m_fxaa.ps, true);
} }
#ifndef PCSX2_CORE
void GSDeviceOGL::DoExternalFX(GSTexture* sTex, GSTexture* dTex) void GSDeviceOGL::DoExternalFX(GSTexture* sTex, GSTexture* dTex)
{ {
#ifndef PCSX2_CORE
// Lazy compile // Lazy compile
if (!m_shaderfx.ps.IsValid()) if (!m_shaderfx.ps.IsValid())
{ {
@ -1480,9 +1488,10 @@ void GSDeviceOGL::DoExternalFX(GSTexture* sTex, GSTexture* dTex)
m_shaderfx.ps.Uniform4f(2, 0.0f, 0.0f, 0.0f, 0.0f); m_shaderfx.ps.Uniform4f(2, 0.0f, 0.0f, 0.0f, 0.0f);
StretchRect(sTex, sRect, dTex, dRect, m_shaderfx.ps, true); StretchRect(sTex, sRect, dTex, dRect, m_shaderfx.ps, true);
#endif
} }
#endif
void GSDeviceOGL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) void GSDeviceOGL::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4])
{ {
GL_PUSH("DoShadeBoost"); GL_PUSH("DoShadeBoost");
@ -1607,6 +1616,67 @@ void GSDeviceOGL::ClearSamplerCache()
} }
} }
bool GSDeviceOGL::CreateCASPrograms()
{
// Image load store and GLSL 420pack is core in GL4.2, no need to check.
m_features.cas_sharpening = GLAD_GL_VERSION_4_2 && GLAD_GL_ARB_compute_shader;
if (!m_features.cas_sharpening)
{
Console.Warning("Compute shaders not supported, CAS is unavailable.");
return false;
}
std::optional<std::string> cas_source(Host::ReadResourceFileToString("shaders/opengl/cas.glsl"));
if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value()))
{
m_features.cas_sharpening = false;
return false;
}
const char* header =
"#version 420\n"
"#extension GL_ARB_compute_shader : require\n";
const char* sharpen_params[2] = {
"#define CAS_SHARPEN_ONLY false\n",
"#define CAS_SHARPEN_ONLY true\n"};
if (!m_shader_cache.GetComputeProgram(&m_cas.upscale_ps, fmt::format("{}{}{}", header, sharpen_params[0], cas_source.value())) ||
!m_shader_cache.GetComputeProgram(&m_cas.sharpen_ps, fmt::format("{}{}{}", header, sharpen_params[1], cas_source.value())))
{
m_features.cas_sharpening = false;
return false;
}
const auto link_uniforms = [](GL::Program& prog) {
prog.RegisterUniform("const0");
prog.RegisterUniform("const1");
prog.RegisterUniform("srcOffset");
};
link_uniforms(m_cas.upscale_ps);
link_uniforms(m_cas.sharpen_ps);
return true;
}
bool GSDeviceOGL::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
{
const GL::Program& prog = sharpen_only ? m_cas.sharpen_ps : m_cas.upscale_ps;
prog.Bind();
prog.Uniform4uiv(0, &constants[0]);
prog.Uniform4uiv(1, &constants[4]);
prog.Uniform2iv(2, reinterpret_cast<const s32*>(&constants[8]));
PSSetShaderResource(0, sTex);
glBindImageTexture(0, static_cast<GSTextureOGL*>(dTex)->GetID(), 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);
static const int threadGroupWorkRegionDim = 16;
const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
glDispatchCompute(dispatchX, dispatchY, 1);
return true;
}
void GSDeviceOGL::OMAttachRt(GSTextureOGL* rt) void GSDeviceOGL::OMAttachRt(GSTextureOGL* rt)
{ {
GLuint id = 0; GLuint id = 0;

View File

@ -275,6 +275,12 @@ private:
GL::Program ps; GL::Program ps;
} m_shadeboost; } m_shadeboost;
struct
{
GL::Program upscale_ps;
GL::Program sharpen_ps;
} m_cas;
struct struct
{ {
u16 last_query = 0; u16 last_query = 0;
@ -301,7 +307,12 @@ private:
void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final; void DoInterlace(GSTexture* sTex, GSTexture* dTex, int shader, bool linear, float yoffset = 0, int bufIdx = 0) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
#ifndef PCSX2_CORE
void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final; void DoExternalFX(GSTexture* sTex, GSTexture* dTex) final;
#endif
bool CreateCASPrograms();
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
void OMAttachRt(GSTextureOGL* rt = NULL); void OMAttachRt(GSTextureOGL* rt = NULL);
void OMAttachDs(GSTextureOGL* ds = NULL); void OMAttachDs(GSTextureOGL* ds = NULL);

View File

@ -20,6 +20,7 @@
#include "common/Vulkan/SwapChain.h" #include "common/Vulkan/SwapChain.h"
#include "common/Vulkan/Util.h" #include "common/Vulkan/Util.h"
#include "common/Align.h" #include "common/Align.h"
#include "common/Path.h"
#include "common/ScopedGuard.h" #include "common/ScopedGuard.h"
#include "GS.h" #include "GS.h"
#include "GSDeviceVK.h" #include "GSDeviceVK.h"
@ -116,6 +117,8 @@ bool GSDeviceVK::Create()
return false; return false;
} }
CompileCASPipelines();
InitializeState(); InitializeState();
return true; return true;
} }
@ -1780,6 +1783,117 @@ bool GSDeviceVK::CompilePostProcessingPipelines()
return true; return true;
} }
bool GSDeviceVK::CompileCASPipelines()
{
VkDevice dev = g_vulkan_context->GetDevice();
Vulkan::DescriptorSetLayoutBuilder dslb;
Vulkan::PipelineLayoutBuilder plb;
dslb.AddBinding(0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT);
dslb.AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT);
if ((m_cas_ds_layout = dslb.Create(dev)) == VK_NULL_HANDLE)
return false;
Vulkan::Util::SetObjectName(dev, m_cas_pipeline_layout, "CAS descriptor layout");
plb.AddPushConstants(VK_SHADER_STAGE_COMPUTE_BIT, 0, NUM_CAS_CONSTANTS * sizeof(u32));
plb.AddDescriptorSet(m_cas_ds_layout);
if ((m_cas_pipeline_layout = plb.Create(dev)) == VK_NULL_HANDLE)
return false;
Vulkan::Util::SetObjectName(dev, m_cas_pipeline_layout, "CAS pipeline layout");
// we use specialization constants to avoid compiling it twice
std::optional<std::string> cas_source(Host::ReadResourceFileToString("shaders/vulkan/cas.glsl"));
if (!cas_source.has_value() || !GetCASShaderSource(&cas_source.value()))
return false;
VkShaderModule mod = g_vulkan_shader_cache->GetComputeShader(cas_source->c_str());
ScopedGuard mod_guard = [&mod]() { Vulkan::Util::SafeDestroyShaderModule(mod); };
if (mod == VK_NULL_HANDLE)
return false;
for (u8 sharpen_only = 0; sharpen_only < 2; sharpen_only++)
{
Vulkan::ComputePipelineBuilder cpb;
cpb.SetPipelineLayout(m_cas_pipeline_layout);
cpb.SetShader(mod, "main");
cpb.SetSpecializationBool(0, sharpen_only != 0);
m_cas_pipelines[sharpen_only] = cpb.Create(dev, g_vulkan_shader_cache->GetPipelineCache(true), false);
if (!m_cas_pipelines[sharpen_only])
return false;
}
m_features.cas_sharpening = true;
return true;
}
bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
{
EndRenderPass();
VkDescriptorSet ds = g_vulkan_context->AllocateDescriptorSet(m_cas_ds_layout);
if (ds == VK_NULL_HANDLE)
return false;
GSTextureVK* const sTexVK = static_cast<GSTextureVK*>(sTex);
GSTextureVK* const dTexVK = static_cast<GSTextureVK*>(dTex);
VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
sTexVK->GetTexture().TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// we have to make the barrier explicit here, because there's no free enums for us to use (general already hijacked)
const VkImageMemoryBarrier barrier_to_cs = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
nullptr,
VK_ACCESS_SHADER_READ_BIT,
VK_ACCESS_SHADER_WRITE_BIT,
dTexVK->GetLayout(),
VK_IMAGE_LAYOUT_GENERAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
dTexVK->GetImage(),
{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u},
};
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier_to_cs);
dTexVK->GetTexture().OverrideImageLayout(VK_IMAGE_LAYOUT_GENERAL);
// only happening once a frame, so the update isn't a huge deal.
Vulkan::DescriptorSetUpdateBuilder dsub;
dsub.AddImageDescriptorWrite(ds, 0, sTexVK->GetView(), sTexVK->GetLayout());
dsub.AddStorageImageDescriptorWrite(ds, 1, dTexVK->GetView(), dTexVK->GetLayout());
dsub.Update(g_vulkan_context->GetDevice(), false);
// the actual meat and potatoes! only four commands.
static const int threadGroupWorkRegionDim = 16;
const int dispatchX = (dTex->GetWidth() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_COMPUTE, m_cas_pipeline_layout, 0, 1, &ds, 0, nullptr);
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);
// and same deal here :(
const VkImageMemoryBarrier barrier_to_fs = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
nullptr,
VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_LAYOUT_GENERAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
dTexVK->GetImage(),
{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u},
};
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier_to_fs);
dTexVK->GetTexture().OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// all done!
return true;
}
bool GSDeviceVK::CheckStagingBufferSize(u32 required_size) bool GSDeviceVK::CheckStagingBufferSize(u32 required_size)
{ {
if (m_readback_staging_buffer_size >= required_size) if (m_readback_staging_buffer_size >= required_size)
@ -1866,6 +1980,11 @@ void GSDeviceVK::DestroyResources()
Vulkan::Util::SafeDestroyPipeline(m_fxaa_pipeline); Vulkan::Util::SafeDestroyPipeline(m_fxaa_pipeline);
Vulkan::Util::SafeDestroyPipeline(m_shadeboost_pipeline); Vulkan::Util::SafeDestroyPipeline(m_shadeboost_pipeline);
for (VkPipeline& it : m_cas_pipelines)
Vulkan::Util::SafeDestroyPipeline(it);
Vulkan::Util::SafeDestroyPipelineLayout(m_cas_pipeline_layout);
Vulkan::Util::SafeDestroyDescriptorSetLayout(m_cas_ds_layout);
for (auto& it : m_samplers) for (auto& it : m_samplers)
Vulkan::Util::SafeDestroySampler(it.second); Vulkan::Util::SafeDestroySampler(it.second);

View File

@ -83,6 +83,8 @@ public:
INDEX_BUFFER_SIZE = 16 * 1024 * 1024, INDEX_BUFFER_SIZE = 16 * 1024 * 1024,
VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024, VERTEX_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024,
FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024, FRAGMENT_UNIFORM_BUFFER_SIZE = 8 * 1024 * 1024,
NUM_CAS_PIPELINES = 2,
}; };
enum DATE_RENDER_PASS : u32 enum DATE_RENDER_PASS : u32
{ {
@ -144,6 +146,10 @@ private:
VkRenderPass m_tfx_render_pass[2][2][2][3][2][3][3] = {}; // [rt][ds][hdr][date][fbl][rt_op][ds_op] VkRenderPass m_tfx_render_pass[2][2][2][3][2][3][3] = {}; // [rt][ds][hdr][date][fbl][rt_op][ds_op]
VkDescriptorSetLayout m_cas_ds_layout = VK_NULL_HANDLE;
VkPipelineLayout m_cas_pipeline_layout = VK_NULL_HANDLE;
std::array<VkPipeline, NUM_CAS_PIPELINES> m_cas_pipelines = {};
GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache; GSHWDrawConfig::VSConstantBuffer m_vs_cb_cache;
GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache; GSHWDrawConfig::PSConstantBuffer m_ps_cb_cache;
@ -159,6 +165,8 @@ private:
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final; void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) final;
void DoFXAA(GSTexture* sTex, GSTexture* dTex) final; void DoFXAA(GSTexture* sTex, GSTexture* dTex) final;
bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
VkSampler GetSampler(GSHWDrawConfig::SamplerSelector ss); VkSampler GetSampler(GSHWDrawConfig::SamplerSelector ss);
void ClearSamplerCache() final; void ClearSamplerCache() final;
@ -182,6 +190,7 @@ private:
bool CompileInterlacePipelines(); bool CompileInterlacePipelines();
bool CompileMergePipelines(); bool CompileMergePipelines();
bool CompilePostProcessingPipelines(); bool CompilePostProcessingPipelines();
bool CompileCASPipelines();
bool CheckStagingBufferSize(u32 required_size); bool CheckStagingBufferSize(u32 required_size);
void DestroyStagingBuffer(); void DestroyStagingBuffer();

View File

@ -126,6 +126,24 @@ std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, u32 width, u32 heigh
return std::make_unique<GSTextureVK>(type, format, std::move(texture)); return std::make_unique<GSTextureVK>(type, format, std::move(texture));
} }
case Type::RWTexture:
{
pxAssert(levels == 1);
Vulkan::Texture texture;
if (!texture.Create(width, height, levels, 1, vk_format, VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT))
{
return {};
}
Vulkan::Util::SetObjectName(
g_vulkan_context->GetDevice(), texture.GetImage(), "%ux%u RW texture", width, height);
return std::make_unique<GSTextureVK>(type, format, std::move(texture));
}
default: default:
return {}; return {};
} }

View File

@ -406,6 +406,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(TexturePreloading) && OpEqu(TexturePreloading) &&
OpEqu(GSDumpCompression) && OpEqu(GSDumpCompression) &&
OpEqu(HWDownloadMode) && OpEqu(HWDownloadMode) &&
OpEqu(CASMode) &&
OpEqu(Dithering) && OpEqu(Dithering) &&
OpEqu(MaxAnisotropy) && OpEqu(MaxAnisotropy) &&
OpEqu(SWExtraThreads) && OpEqu(SWExtraThreads) &&
@ -425,6 +426,7 @@ bool Pcsx2Config::GSOptions::OptionsAreEqual(const GSOptions& right) const
OpEqu(OverrideTextureBarriers) && OpEqu(OverrideTextureBarriers) &&
OpEqu(OverrideGeometryShaders) && OpEqu(OverrideGeometryShaders) &&
OpEqu(CAS_Sharpness) &&
OpEqu(ShadeBoost_Brightness) && OpEqu(ShadeBoost_Brightness) &&
OpEqu(ShadeBoost_Contrast) && OpEqu(ShadeBoost_Contrast) &&
OpEqu(ShadeBoost_Saturation) && OpEqu(ShadeBoost_Saturation) &&
@ -568,7 +570,9 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingBoolEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt"); GSSettingBoolEx(UserHacks_TextureInsideRt, "UserHacks_TextureInsideRt");
GSSettingBoolEx(FXAA, "fxaa"); GSSettingBoolEx(FXAA, "fxaa");
GSSettingBool(ShadeBoost); GSSettingBool(ShadeBoost);
#ifndef PCSX2_CORE
GSSettingBoolEx(ShaderFX, "shaderfx"); GSSettingBoolEx(ShaderFX, "shaderfx");
#endif
GSSettingBoolEx(DumpGSData, "dump"); GSSettingBoolEx(DumpGSData, "dump");
GSSettingBoolEx(SaveRT, "save"); GSSettingBoolEx(SaveRT, "save");
GSSettingBoolEx(SaveFrame, "savef"); GSSettingBoolEx(SaveFrame, "savef");
@ -600,6 +604,8 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingIntEnumEx(TexturePreloading, "texture_preloading"); GSSettingIntEnumEx(TexturePreloading, "texture_preloading");
GSSettingIntEnumEx(GSDumpCompression, "GSDumpCompression"); GSSettingIntEnumEx(GSDumpCompression, "GSDumpCompression");
GSSettingIntEnumEx(HWDownloadMode, "HWDownloadMode"); GSSettingIntEnumEx(HWDownloadMode, "HWDownloadMode");
GSSettingIntEnumEx(CASMode, "CASMode");
GSSettingIntEx(CAS_Sharpness, "CASSharpness");
GSSettingIntEx(Dithering, "dithering_ps2"); GSSettingIntEx(Dithering, "dithering_ps2");
GSSettingIntEx(MaxAnisotropy, "MaxAnisotropy"); GSSettingIntEx(MaxAnisotropy, "MaxAnisotropy");
GSSettingIntEx(SWExtraThreads, "extrathreads"); GSSettingIntEx(SWExtraThreads, "extrathreads");