GS: Combine texture classes and add custom layout for Vulkan

This commit is contained in:
Stenzek 2023-05-06 20:10:15 +10:00 committed by refractionpcsx2
parent cfdae77331
commit 4a5cf0efb9
23 changed files with 1374 additions and 1914 deletions

View File

@ -647,7 +647,6 @@ if(USE_VULKAN)
GS/Renderers/Vulkan/VKShaderCache.cpp
GS/Renderers/Vulkan/VKStreamBuffer.cpp
GS/Renderers/Vulkan/VKSwapChain.cpp
GS/Renderers/Vulkan/VKTexture.cpp
GS/Renderers/Vulkan/VKUtil.cpp
)
list(APPEND pcsx2GSHeaders
@ -661,7 +660,6 @@ if(USE_VULKAN)
GS/Renderers/Vulkan/VKShaderCache.h
GS/Renderers/Vulkan/VKStreamBuffer.h
GS/Renderers/Vulkan/VKSwapChain.h
GS/Renderers/Vulkan/VKTexture.h
GS/Renderers/Vulkan/VKUtil.h
)
target_link_libraries(PCSX2_FLAGS INTERFACE Vulkan-Headers glslang)
@ -706,7 +704,6 @@ if(WIN32)
GS/Renderers/DX12/D3D12DescriptorHeapManager.cpp
GS/Renderers/DX12/D3D12ShaderCache.cpp
GS/Renderers/DX12/D3D12StreamBuffer.cpp
GS/Renderers/DX12/D3D12Texture.cpp
GS/Renderers/DX12/GSDevice12.cpp
GS/Renderers/DX12/GSTexture12.cpp
)
@ -720,7 +717,6 @@ if(WIN32)
GS/Renderers/DX12/D3D12DescriptorHeapManager.h
GS/Renderers/DX12/D3D12ShaderCache.h
GS/Renderers/DX12/D3D12StreamBuffer.h
GS/Renderers/DX12/D3D12Texture.h
GS/Renderers/DX12/GSDevice12.h
GS/Renderers/DX12/GSTexture12.h
)

View File

@ -76,7 +76,7 @@ u32 GSTexture::GetCompressedBytesPerBlock(Format format)
1, // Invalid
4, // Color/RGBA8
8, // HDRColor/RGBA16
32, // DepthStencil
4, // DepthStencil
1, // UNorm8/R8
2, // UInt16/R16UI
4, // UInt32/R32UI
@ -103,6 +103,19 @@ u32 GSTexture::GetCompressedBlockSize(Format format)
return 1;
}
u32 GSTexture::CalcUploadPitch(Format format, u32 width)
{
if (format >= Format::BC1 && format <= Format::BC7)
width = Common::AlignUpPow2(width, 4) / 4;
return width * GetCompressedBytesPerBlock(format);
}
u32 GSTexture::CalcUploadPitch(u32 width) const
{
return CalcUploadPitch(m_format, width);
}
u32 GSTexture::CalcUploadRowLengthFromPitch(u32 pitch) const
{
return CalcUploadRowLengthFromPitch(m_format, pitch);

View File

@ -106,11 +106,13 @@ public:
static u32 GetCompressedBytesPerBlock(Format format);
static u32 GetCompressedBlockSize(Format format);
static u32 CalcUploadPitch(Format format, u32 width);
static u32 CalcUploadRowLengthFromPitch(Format format, u32 pitch);
static u32 CalcUploadSize(Format format, u32 height, u32 pitch);
u32 GetCompressedBytesPerBlock() const;
u32 GetCompressedBlockSize() const;
u32 CalcUploadPitch(u32 width) const;
u32 CalcUploadRowLengthFromPitch(u32 pitch) const;
u32 CalcUploadSize(u32 height, u32 pitch) const;

View File

@ -668,45 +668,6 @@ bool D3D12Context::AllocatePreinitializedGPUBuffer(u32 size, ID3D12Resource** gp
return true;
}
u32 D3D12::GetTexelSize(DXGI_FORMAT format)
{
switch (format)
{
case DXGI_FORMAT_R32G32B32A32_FLOAT:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC7_UNORM:
return 16;
case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
return 4;
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_SNORM:
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
case DXGI_FORMAT_R32_UINT:
case DXGI_FORMAT_R32_SINT:
return 4;
case DXGI_FORMAT_B5G5R5A1_UNORM:
case DXGI_FORMAT_B5G6R5_UNORM:
case DXGI_FORMAT_R16_UINT:
case DXGI_FORMAT_R16_SINT:
return 2;
case DXGI_FORMAT_A8_UNORM:
case DXGI_FORMAT_R8_UNORM:
return 1;
default:
pxFailRel("Unknown format");
return 1;
}
}
#ifdef _DEBUG
void D3D12::SetObjectName(ID3D12Object* object, const char* name)

View File

@ -217,8 +217,6 @@ extern std::unique_ptr<D3D12Context> g_d3d12_context;
namespace D3D12
{
u32 GetTexelSize(DXGI_FORMAT format);
#ifdef _DEBUG
void SetObjectName(ID3D12Object* object, const char* name);

View File

@ -1,508 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "GS/Renderers/DX12/D3D12Texture.h"
#include "GS/Renderers/DX12/D3D12Context.h"
#include "common/Align.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/StringUtil.h"
#include "D3D12MemAlloc.h"
D3D12Texture::D3D12Texture() = default;
D3D12Texture::D3D12Texture(ID3D12Resource* resource, D3D12_RESOURCE_STATES state)
: m_resource(std::move(resource))
{
const D3D12_RESOURCE_DESC desc = GetDesc();
m_width = static_cast<u32>(desc.Width);
m_height = desc.Height;
m_levels = desc.MipLevels;
m_format = desc.Format;
}
D3D12Texture::D3D12Texture(D3D12Texture&& texture)
: m_resource(std::move(texture.m_resource))
, m_allocation(std::move(texture.m_allocation))
, m_srv_descriptor(texture.m_srv_descriptor)
, m_write_descriptor(texture.m_write_descriptor)
, m_width(texture.m_width)
, m_height(texture.m_height)
, m_levels(texture.m_levels)
, m_format(texture.m_format)
, m_state(texture.m_state)
, m_write_descriptor_type(texture.m_write_descriptor_type)
{
texture.m_srv_descriptor = {};
texture.m_write_descriptor = {};
texture.m_width = 0;
texture.m_height = 0;
texture.m_levels = 0;
texture.m_format = DXGI_FORMAT_UNKNOWN;
texture.m_state = D3D12_RESOURCE_STATE_COMMON;
texture.m_write_descriptor_type = WriteDescriptorType::None;
}
D3D12Texture::~D3D12Texture()
{
Destroy();
}
D3D12Texture& D3D12Texture::operator=(D3D12Texture&& texture)
{
Destroy();
m_resource = std::move(texture.m_resource);
m_allocation = std::move(texture.m_allocation);
m_srv_descriptor = texture.m_srv_descriptor;
m_write_descriptor = texture.m_write_descriptor;
m_width = texture.m_width;
m_height = texture.m_height;
m_levels = texture.m_levels;
m_format = texture.m_format;
m_state = texture.m_state;
m_write_descriptor_type = texture.m_write_descriptor_type;
texture.m_srv_descriptor = {};
texture.m_write_descriptor = {};
texture.m_width = 0;
texture.m_height = 0;
texture.m_levels = 0;
texture.m_format = DXGI_FORMAT_UNKNOWN;
texture.m_state = D3D12_RESOURCE_STATE_COMMON;
texture.m_write_descriptor_type = WriteDescriptorType::None;
return *this;
}
D3D12_RESOURCE_DESC D3D12Texture::GetDesc() const
{
return m_resource->GetDesc();
}
bool D3D12Texture::Create(u32 width, u32 height, u32 levels, DXGI_FORMAT format, DXGI_FORMAT srv_format,
DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format, D3D12_RESOURCE_FLAGS flags, u32 alloc_flags)
{
D3D12_RESOURCE_DESC desc = {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Width = width;
desc.Height = height;
desc.DepthOrArraySize = 1;
desc.MipLevels = levels;
desc.Format = format;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
desc.Flags = flags;
D3D12_CLEAR_VALUE optimized_clear_value = {};
D3D12_RESOURCE_STATES state;
if (rtv_format != DXGI_FORMAT_UNKNOWN)
{
optimized_clear_value.Format = rtv_format;
state = D3D12_RESOURCE_STATE_RENDER_TARGET;
}
else if (dsv_format != DXGI_FORMAT_UNKNOWN)
{
optimized_clear_value.Format = dsv_format;
state = D3D12_RESOURCE_STATE_DEPTH_WRITE;
}
else
{
state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
}
D3D12MA::ALLOCATION_DESC allocationDesc = {};
allocationDesc.Flags = static_cast<D3D12MA::ALLOCATION_FLAGS>(alloc_flags) | D3D12MA::ALLOCATION_FLAG_WITHIN_BUDGET;
allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
ComPtr<ID3D12Resource> resource;
ComPtr<D3D12MA::Allocation> allocation;
HRESULT hr = g_d3d12_context->GetAllocator()->CreateResource(&allocationDesc, &desc, state,
(rtv_format != DXGI_FORMAT_UNKNOWN || dsv_format != DXGI_FORMAT_UNKNOWN) ? &optimized_clear_value : nullptr,
allocation.put(), IID_PPV_ARGS(resource.put()));
if (FAILED(hr))
{
// OOM isn't fatal.
if (hr != E_OUTOFMEMORY)
Console.Error("Create texture failed: 0x%08X", hr);
return false;
}
D3D12DescriptorHandle srv_descriptor, write_descriptor;
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
if (srv_format != DXGI_FORMAT_UNKNOWN)
{
if (!CreateSRVDescriptor(resource.get(), levels, srv_format, &srv_descriptor))
return false;
}
if (rtv_format != DXGI_FORMAT_UNKNOWN)
{
pxAssert(dsv_format == DXGI_FORMAT_UNKNOWN && !(flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS));
write_descriptor_type = D3D12Texture::WriteDescriptorType::RTV;
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
{
g_d3d12_context->GetRTVHeapManager().Free(&srv_descriptor);
return false;
}
}
else if (dsv_format != DXGI_FORMAT_UNKNOWN && !(flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS))
{
write_descriptor_type = D3D12Texture::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 = D3D12Texture::WriteDescriptorType::UAV;
if (!CreateUAVDescriptor(resource.get(), dsv_format, &write_descriptor))
{
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
return false;
}
}
Destroy(true);
m_resource = std::move(resource);
m_allocation = std::move(allocation);
m_srv_descriptor = std::move(srv_descriptor);
m_write_descriptor = std::move(write_descriptor);
m_width = width;
m_height = height;
m_levels = levels;
m_format = format;
m_state = state;
m_write_descriptor_type = write_descriptor_type;
return true;
}
bool D3D12Texture::Adopt(ComPtr<ID3D12Resource> texture, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format,
DXGI_FORMAT dsv_format, D3D12_RESOURCE_STATES state)
{
const D3D12_RESOURCE_DESC desc(texture->GetDesc());
D3D12DescriptorHandle srv_descriptor, write_descriptor;
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
if (srv_format != DXGI_FORMAT_UNKNOWN)
{
if (!CreateSRVDescriptor(texture.get(), desc.MipLevels, srv_format, &srv_descriptor))
return false;
}
if (rtv_format != DXGI_FORMAT_UNKNOWN)
{
pxAssert(dsv_format == DXGI_FORMAT_UNKNOWN);
write_descriptor_type = D3D12Texture::WriteDescriptorType::RTV;
if (!CreateRTVDescriptor(texture.get(), rtv_format, &write_descriptor))
{
g_d3d12_context->GetRTVHeapManager().Free(&srv_descriptor);
return false;
}
}
else if (dsv_format != DXGI_FORMAT_UNKNOWN)
{
write_descriptor_type = D3D12Texture::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 = D3D12Texture::WriteDescriptorType::UAV;
if (!CreateUAVDescriptor(texture.get(), srv_format, &write_descriptor))
{
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
return false;
}
}
m_resource = std::move(texture);
m_allocation.reset();
m_srv_descriptor = std::move(srv_descriptor);
m_write_descriptor = std::move(write_descriptor);
m_write_descriptor_type = write_descriptor_type;
m_width = static_cast<u32>(desc.Width);
m_height = desc.Height;
m_levels = desc.MipLevels;
m_format = desc.Format;
m_state = state;
return true;
}
void D3D12Texture::Destroy(bool defer /* = true */)
{
if (defer)
{
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_srv_descriptor);
switch (m_write_descriptor_type)
{
case D3D12Texture::WriteDescriptorType::RTV:
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetRTVHeapManager(), &m_write_descriptor);
break;
case D3D12Texture::WriteDescriptorType::DSV:
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDSVHeapManager(), &m_write_descriptor);
break;
case D3D12Texture::WriteDescriptorType::UAV:
g_d3d12_context->DeferDescriptorDestruction(
g_d3d12_context->GetDescriptorHeapManager(), &m_write_descriptor);
break;
case D3D12Texture::WriteDescriptorType::None:
default:
break;
}
g_d3d12_context->DeferResourceDestruction(m_allocation.get(), m_resource.get());
m_resource.reset();
m_allocation.reset();
}
else
{
g_d3d12_context->GetDescriptorHeapManager().Free(&m_srv_descriptor);
switch (m_write_descriptor_type)
{
case D3D12Texture::WriteDescriptorType::RTV:
g_d3d12_context->GetRTVHeapManager().Free(&m_write_descriptor);
break;
case D3D12Texture::WriteDescriptorType::DSV:
g_d3d12_context->GetDSVHeapManager().Free(&m_write_descriptor);
break;
case D3D12Texture::WriteDescriptorType::UAV:
g_d3d12_context->GetDescriptorHeapManager().Free(&m_write_descriptor);
break;
case D3D12Texture::WriteDescriptorType::None:
default:
break;
}
m_resource.reset();
m_allocation.reset();
}
m_width = 0;
m_height = 0;
m_levels = 0;
m_format = DXGI_FORMAT_UNKNOWN;
m_write_descriptor_type = WriteDescriptorType::None;
}
void D3D12Texture::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state)
{
if (m_state == state)
return;
TransitionSubresourceToState(cmdlist, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, m_state, state);
m_state = state;
}
void D3D12Texture::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 level,
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const
{
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
{{m_resource.get(), level, before_state, after_state}}};
cmdlist->ResourceBarrier(1, &barrier);
}
ID3D12GraphicsCommandList* D3D12Texture::BeginStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y,
u32 width, u32 height, void** out_data, u32* out_data_pitch)
{
const u32 copy_pitch = Common::AlignUpPow2(width * D3D12::GetTexelSize(m_format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 upload_size = copy_pitch * height;
if (!g_d3d12_context->GetTextureStreamBuffer().ReserveMemory(upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
{
DevCon.WriteLn(
"Executing command buffer while waiting for %u bytes (%ux%u) in upload buffer", upload_size, width, height);
g_d3d12_context->ExecuteCommandList(D3D12Context::WaitType::None);
if (!g_d3d12_context->GetTextureStreamBuffer().ReserveMemory(
upload_size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT))
{
Console.Error("Failed to reserve %u bytes for %ux%u upload", upload_size, width, height);
return nullptr;
}
// cmdlist change
cmdlist = g_d3d12_context->GetInitCommandList();
}
*out_data = g_d3d12_context->GetTextureStreamBuffer().GetCurrentHostPointer();
*out_data_pitch = copy_pitch;
return cmdlist;
}
void D3D12Texture::EndStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height)
{
const u32 copy_pitch = Common::AlignUpPow2(width * D3D12::GetTexelSize(m_format), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 upload_size = copy_pitch * height;
D3D12StreamBuffer& sb = g_d3d12_context->GetTextureStreamBuffer();
const u32 sb_offset = sb.GetCurrentOffset();
sb.CommitMemory(upload_size);
CopyFromBuffer(cmdlist, level, x, y, width, height, copy_pitch, sb.GetBuffer(), sb_offset);
}
void D3D12Texture::CopyFromBuffer(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height,
u32 pitch, ID3D12Resource* buffer, u32 buffer_offset)
{
D3D12_TEXTURE_COPY_LOCATION src;
src.pResource = buffer;
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
src.PlacedFootprint.Offset = buffer_offset;
src.PlacedFootprint.Footprint.Width = width;
src.PlacedFootprint.Footprint.Height = height;
src.PlacedFootprint.Footprint.Depth = 1;
src.PlacedFootprint.Footprint.RowPitch = pitch;
src.PlacedFootprint.Footprint.Format = m_format;
D3D12_TEXTURE_COPY_LOCATION dst;
dst.pResource = m_resource.get();
dst.SubresourceIndex = level;
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
const D3D12_BOX src_box{0u, 0u, 0u, width, height, 1u};
const D3D12_RESOURCE_STATES old_state = m_state;
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
cmdlist->CopyTextureRegion(&dst, x, y, 0, &src, &src_box);
TransitionToState(cmdlist, old_state);
}
static ID3D12Resource* CreateStagingBuffer(u32 height, const void* data, u32 pitch, u32 upload_pitch, u32 upload_size)
{
wil::com_ptr_nothrow<ID3D12Resource> resource;
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
const D3D12MA::ALLOCATION_DESC allocation_desc = {D3D12MA::ALLOCATION_FLAG_NONE, D3D12_HEAP_TYPE_UPLOAD};
const D3D12_RESOURCE_DESC resource_desc = {D3D12_RESOURCE_DIMENSION_BUFFER, 0, upload_size, 1, 1, 1,
DXGI_FORMAT_UNKNOWN, {1, 0}, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_FLAG_NONE};
HRESULT hr = g_d3d12_context->GetAllocator()->CreateResource(&allocation_desc, &resource_desc,
D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, allocation.put(), IID_PPV_ARGS(resource.put()));
if (FAILED(hr))
{
Console.Error("CreateResource() for upload staging buffer failed: %08X", hr);
return nullptr;
}
void* map;
const D3D12_RANGE read_range = {};
hr = resource->Map(0, &read_range, &map);
if (FAILED(hr))
{
Console.Error("Map() for upload staging buffer failed: %08X", hr);
return nullptr;
}
StringUtil::StrideMemCpy(map, upload_pitch, data, pitch, std::min(pitch, upload_pitch), height);
const D3D12_RANGE write_range = {0u, upload_size};
resource->Unmap(0, &write_range);
// queue them for destruction, since the upload happens in this cmdlist
g_d3d12_context->DeferResourceDestruction(allocation.get(), resource.get());
// AddRef()'ed by the defer above.
return resource.get();
}
bool D3D12Texture::LoadData(
ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch)
{
const u32 texel_size = D3D12::GetTexelSize(m_format);
const u32 upload_pitch = Common::AlignUpPow2(width * texel_size, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 upload_size = upload_pitch * height;
if (upload_size >= g_d3d12_context->GetTextureStreamBuffer().GetSize())
{
ID3D12Resource* staging_buffer = CreateStagingBuffer(height, data, pitch, upload_pitch, upload_size);
if (!staging_buffer)
return false;
CopyFromBuffer(cmdlist, level, x, y, width, height, upload_pitch, staging_buffer, 0);
return true;
}
void* write_ptr;
u32 write_pitch;
if (!(cmdlist = BeginStreamUpdate(cmdlist, level, x, y, width, height, &write_ptr, &write_pitch)))
return false;
StringUtil::StrideMemCpy(write_ptr, write_pitch, data, pitch, std::min(pitch, upload_pitch), height);
EndStreamUpdate(cmdlist, level, x, y, width, height);
return true;
}
bool D3D12Texture::CreateSRVDescriptor(
ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, D3D12DescriptorHandle* dh)
{
if (!g_d3d12_context->GetDescriptorHeapManager().Allocate(dh))
{
Console.Error("Failed to allocate SRV descriptor");
return false;
}
D3D12_SHADER_RESOURCE_VIEW_DESC desc = {
format, D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
desc.Texture2D.MipLevels = levels;
g_d3d12_context->GetDevice()->CreateShaderResourceView(resource, &desc, dh->cpu_handle);
return true;
}
bool D3D12Texture::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh)
{
if (!g_d3d12_context->GetRTVHeapManager().Allocate(dh))
{
Console.Error("Failed to allocate SRV descriptor");
return false;
}
const D3D12_RENDER_TARGET_VIEW_DESC desc = {format, D3D12_RTV_DIMENSION_TEXTURE2D};
g_d3d12_context->GetDevice()->CreateRenderTargetView(resource, &desc, dh->cpu_handle);
return true;
}
bool D3D12Texture::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh)
{
if (!g_d3d12_context->GetDSVHeapManager().Allocate(dh))
{
Console.Error("Failed to allocate SRV descriptor");
return false;
}
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, D3D12_DSV_FLAG_NONE};
g_d3d12_context->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle);
return true;
}
bool D3D12Texture::CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* 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

@ -1,105 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "GS/Renderers/DX12/D3D12DescriptorHeapManager.h"
namespace D3D12MA
{
class Allocation;
}
class D3D12StreamBuffer;
class D3D12Texture final
{
public:
template <typename T>
using ComPtr = wil::com_ptr_nothrow<T>;
D3D12Texture();
D3D12Texture(ID3D12Resource* resource, D3D12_RESOURCE_STATES state);
D3D12Texture(D3D12Texture&& texture);
D3D12Texture(const D3D12Texture&) = delete;
~D3D12Texture();
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
__fi D3D12MA::Allocation* GetAllocation() const { return m_allocation.get(); }
__fi const D3D12DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; }
__fi const D3D12DescriptorHandle& GetWriteDescriptor() const { return m_write_descriptor; }
__fi D3D12_RESOURCE_STATES GetState() const { return m_state; }
__fi u32 GetWidth() const { return m_width; }
__fi u32 GetHeight() const { return m_height; }
__fi u32 GetLevels() const { return m_levels; }
__fi DXGI_FORMAT GetFormat() const { return m_format; }
__fi operator ID3D12Resource*() const { return m_resource.get(); }
__fi operator bool() const { return static_cast<bool>(m_resource); }
bool Create(u32 width, u32 height, u32 levels, DXGI_FORMAT format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format,
DXGI_FORMAT dsv_format, D3D12_RESOURCE_FLAGS flags, u32 alloc_flags = 0);
bool Adopt(ComPtr<ID3D12Resource> texture, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format,
D3D12_RESOURCE_STATES state);
D3D12_RESOURCE_DESC GetDesc() const;
void Destroy(bool defer = true);
void TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state);
void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, u32 level, D3D12_RESOURCE_STATES before_state,
D3D12_RESOURCE_STATES after_state) const;
D3D12Texture& operator=(const D3D12Texture&) = delete;
D3D12Texture& operator=(D3D12Texture&& texture);
// NOTE: Does not handle compressed formats.
ID3D12GraphicsCommandList* BeginStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width,
u32 height, void** out_data, u32* out_data_pitch);
void EndStreamUpdate(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height);
bool LoadData(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, const void* data,
u32 pitch);
void CopyFromBuffer(ID3D12GraphicsCommandList* cmdlist, u32 level, u32 x, u32 y, u32 width, u32 height, u32 pitch,
ID3D12Resource* buffer, u32 buffer_offset);
private:
static bool CreateSRVDescriptor(
ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
static bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
static bool CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
enum class WriteDescriptorType : u8
{
None,
RTV,
DSV,
UAV
};
ComPtr<ID3D12Resource> m_resource;
ComPtr<D3D12MA::Allocation> m_allocation;
D3D12DescriptorHandle m_srv_descriptor = {};
D3D12DescriptorHandle m_write_descriptor = {};
u32 m_width = 0;
u32 m_height = 0;
u32 m_levels = 0;
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
D3D12_RESOURCE_STATES m_state = D3D12_RESOURCE_STATE_COMMON;
WriteDescriptorType m_write_descriptor_type = WriteDescriptorType::None;
};

View File

@ -37,11 +37,17 @@
#include <sstream>
#include <limits>
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 constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY, 3> s_primitive_topology_mapping =
{{D3D_PRIMITIVE_TOPOLOGY_POINTLIST, D3D_PRIMITIVE_TOPOLOGY_LINELIST, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST}};
static constexpr std::array<D3D12_PRIMITIVE_TOPOLOGY, 3> s_primitive_topology_mapping = {
{D3D_PRIMITIVE_TOPOLOGY_POINTLIST, D3D_PRIMITIVE_TOPOLOGY_LINELIST, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST}};
static constexpr std::array<float, 4> s_present_clear_color = {};
@ -180,9 +186,8 @@ bool GSDevice12::Create()
if (!CreateBuffers())
return false;
if (!CompileConvertPipelines() || !CompilePresentPipelines() ||
!CompileInterlacePipelines() || !CompileMergePipelines() ||
!CompilePostProcessingPipelines())
if (!CompileConvertPipelines() || !CompilePresentPipelines() || !CompileInterlacePipelines() ||
!CompileMergePipelines() || !CompilePostProcessingPipelines())
{
Host::ReportErrorAsync("GS", "Failed to compile utility pipelines");
return false;
@ -321,12 +326,12 @@ bool GSDevice12::CreateSwapChain()
// Render a frame as soon as possible to clear out whatever was previously being displayed.
EndRenderPass();
D3D12Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
GSTexture12* swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer].get();
ID3D12GraphicsCommandList4* cmdlist = g_d3d12_context->GetCommandList();
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PRESENT);
swap_chain_buf->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf->GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
swap_chain_buf->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_PRESENT);
ExecuteCommandList(false);
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
return true;
@ -350,9 +355,11 @@ bool GSDevice12::CreateSwapChainRTV()
return false;
}
D3D12Texture tex;
if (!tex.Adopt(std::move(backbuffer), DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format,
DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_STATE_PRESENT))
std::unique_ptr<GSTexture12> tex = GSTexture12::Adopt(std::move(backbuffer), GSTexture::Type::RenderTarget,
GSTexture::Format::Color, swap_chain_desc.BufferDesc.Width, swap_chain_desc.BufferDesc.Height, 1,
swap_chain_desc.BufferDesc.Format, DXGI_FORMAT_UNKNOWN, swap_chain_desc.BufferDesc.Format,
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_STATE_COMMON);
if (!tex)
{
m_swap_chain_buffers.clear();
return false;
@ -387,8 +394,8 @@ bool GSDevice12::CreateSwapChainRTV()
void GSDevice12::DestroySwapChainRTVs()
{
for (D3D12Texture& buffer : m_swap_chain_buffers)
buffer.Destroy(false);
for (std::unique_ptr<GSTexture12>& buffer : m_swap_chain_buffers)
buffer->Destroy(false);
m_swap_chain_buffers.clear();
m_current_swap_chain_buffer = 0;
}
@ -522,12 +529,12 @@ GSDevice::PresentResult GSDevice12::BeginPresent(bool frame_skip)
return PresentResult::FrameSkipped;
}
D3D12Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
GSTexture12* swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer].get();
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
swap_chain_buf.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf.GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
cmdlist->OMSetRenderTargets(1, &swap_chain_buf.GetWriteDescriptor().cpu_handle, FALSE, nullptr);
swap_chain_buf->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(swap_chain_buf->GetWriteDescriptor(), s_present_clear_color.data(), 0, nullptr);
cmdlist->OMSetRenderTargets(1, &swap_chain_buf->GetWriteDescriptor().cpu_handle, FALSE, nullptr);
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
const D3D12_VIEWPORT vp{0.0f, 0.0f, static_cast<float>(m_window_info.surface_width),
@ -543,10 +550,10 @@ void GSDevice12::EndPresent()
{
RenderImGui();
D3D12Texture& swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer];
GSTexture12* swap_chain_buf = m_swap_chain_buffers[m_current_swap_chain_buffer].get();
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
swap_chain_buf.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
swap_chain_buf->TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PRESENT);
if (!g_d3d12_context->ExecuteCommandList(D3D12Context::WaitType::None))
{
m_device_lost = true;
@ -643,7 +650,10 @@ void GSDevice12::ClearRenderTarget(GSTexture* t, const GSVector4& c)
static_cast<GSTexture12*>(t)->SetClearColor(c);
}
void GSDevice12::ClearRenderTarget(GSTexture* t, u32 c) { ClearRenderTarget(t, GSVector4::rgba32(c) * (1.0f / 255)); }
void GSDevice12::ClearRenderTarget(GSTexture* t, u32 c)
{
ClearRenderTarget(t, GSVector4::rgba32(c) * (1.0f / 255));
}
void GSDevice12::InvalidateRenderTarget(GSTexture* t)
{
@ -676,25 +686,31 @@ void GSDevice12::ClearStencil(GSTexture* t, u8 c)
GSTexture12* dxt = static_cast<GSTexture12*>(t);
dxt->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
g_d3d12_context->GetCommandList()->ClearDepthStencilView(dxt->GetRTVOrDSVHandle(), D3D12_CLEAR_FLAG_STENCIL, 0.0f, c, 0, nullptr);
g_d3d12_context->GetCommandList()->ClearDepthStencilView(
dxt->GetWriteDescriptor(), D3D12_CLEAR_FLAG_STENCIL, 0.0f, c, 0, nullptr);
}
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const
void GSDevice12::LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const
{
static constexpr std::array<std::array<DXGI_FORMAT, 4>, static_cast<int>(GSTexture::Format::BC7) + 1> s_format_mapping = {{
{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // Invalid
{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN}, // Color
{DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_UNKNOWN}, // HDRColor
{DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_D32_FLOAT_S8X24_UINT}, // DepthStencil
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN}, // UNorm8
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN}, // UInt16
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN}, // UInt32
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN}, // Int32
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC1
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC2
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC3
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC7
}};
static constexpr std::array<std::array<DXGI_FORMAT, 4>, static_cast<int>(GSTexture::Format::BC7) + 1>
s_format_mapping = {{
{DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // Invalid
{DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN}, // Color
{DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_FORMAT_R16G16B16A16_UNORM,
DXGI_FORMAT_UNKNOWN}, // HDRColor
{DXGI_FORMAT_D32_FLOAT_S8X24_UINT, DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS, DXGI_FORMAT_UNKNOWN,
DXGI_FORMAT_D32_FLOAT_S8X24_UINT}, // DepthStencil
{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_UNKNOWN}, // UNorm8
{DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_UINT, DXGI_FORMAT_UNKNOWN}, // UInt16
{DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_UNKNOWN}, // UInt32
{DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_UNKNOWN}, // Int32
{DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC1
{DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC2
{DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC3
{DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_BC7_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN}, // BC7
}};
const auto& mapping = s_format_mapping[static_cast<int>(format)];
if (d3d_format)
@ -712,16 +728,20 @@ GSTexture* GSDevice12::CreateSurface(GSTexture::Type type, int width, int height
const u32 clamped_width = static_cast<u32>(std::clamp<int>(width, 1, D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION));
const u32 clamped_height = static_cast<u32>(std::clamp<int>(height, 1, D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION));
DXGI_FORMAT d3d_format, srv_format, rtv_format, dsv_format;
LookupNativeFormat(format, &d3d_format, &srv_format, &rtv_format, &dsv_format);
DXGI_FORMAT dxgi_format, srv_format, rtv_format, dsv_format;
LookupNativeFormat(format, &dxgi_format, &srv_format, &rtv_format, &dsv_format);
std::unique_ptr<GSTexture12> tex(GSTexture12::Create(type, clamped_width, clamped_height, levels, format, d3d_format, srv_format, rtv_format, dsv_format));
const DXGI_FORMAT uav_format = (type == GSTexture::Type::RWTexture) ? dxgi_format : DXGI_FORMAT_UNKNOWN;
std::unique_ptr<GSTexture12> tex(GSTexture12::Create(type, format, clamped_width, clamped_height, levels,
dxgi_format, srv_format, rtv_format, dsv_format, uav_format));
if (!tex)
{
// We're probably out of vram, try flushing the command buffer to release pending textures.
PurgePool();
ExecuteCommandListAndRestartRenderPass(true, "Couldn't allocate texture.");
tex = GSTexture12::Create(type, clamped_width, clamped_height, levels, format, d3d_format, srv_format, rtv_format, dsv_format);
tex = GSTexture12::Create(type, format, clamped_width, clamped_height, levels, dxgi_format, srv_format,
rtv_format, dsv_format, uav_format);
}
return tex.release();
@ -763,14 +783,14 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
if (dTexVK->GetType() != GSTexture::Type::DepthStencil)
{
dTexVK->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
g_d3d12_context->GetCommandList()->ClearRenderTargetView(dTexVK->GetRTVOrDSVHandle(),
sTexVK->GetClearColor().v, 0, nullptr);
g_d3d12_context->GetCommandList()->ClearRenderTargetView(
dTexVK->GetWriteDescriptor(), sTexVK->GetClearColor().v, 0, nullptr);
}
else
{
dTexVK->TransitionToState(D3D12_RESOURCE_STATE_DEPTH_WRITE);
g_d3d12_context->GetCommandList()->ClearDepthStencilView(dTexVK->GetRTVOrDSVHandle(),
D3D12_CLEAR_FLAG_DEPTH, sTexVK->GetClearDepth(), 0, 0, nullptr);
g_d3d12_context->GetCommandList()->ClearDepthStencilView(
dTexVK->GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, sTexVK->GetClearDepth(), 0, 0, nullptr);
}
return;
@ -806,11 +826,9 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstloc.SubresourceIndex = 0;
const D3D12_BOX srcbox{static_cast<UINT>(r.left), static_cast<UINT>(r.top), 0u,
static_cast<UINT>(r.right), static_cast<UINT>(r.bottom), 1u};
g_d3d12_context->GetCommandList()->CopyTextureRegion(
&dstloc, destX, destY, 0,
&srcloc, &srcbox);
const D3D12_BOX srcbox{static_cast<UINT>(r.left), static_cast<UINT>(r.top), 0u, static_cast<UINT>(r.right),
static_cast<UINT>(r.bottom), 1u};
g_d3d12_context->GetCommandList()->CopyTextureRegion(&dstloc, destX, destY, 0, &srcloc, &srcbox);
dTexVK->SetState(GSTexture::State::Dirty);
}
@ -853,7 +871,8 @@ void GSDevice12::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture*
m_present[static_cast<int>(shader)].get(), linear, true);
}
void GSDevice12::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
void GSDevice12::UpdateCLUTTexture(
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
{
// match merge cb
struct Uniforms
@ -873,7 +892,8 @@ void GSDevice12::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u
m_convert[static_cast<int>(shader)].get(), false, true);
}
void GSDevice12::ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
void GSDevice12::ConvertToIndexedTexture(
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM)
{
// match merge cb
struct Uniforms
@ -1135,9 +1155,10 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
static_cast<GSTexture12*>(sTex[1])->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
OMSetRenderTargets(dTex, nullptr, darea);
SetUtilityTexture(sTex[1], sampler);
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, c);
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, c);
SetUtilityRootSignature();
SetPipeline(m_convert[static_cast<int>(ShaderConvert::COPY)].get());
DrawStretchRect(sRect[1], PMODE.SLBG ? dRect[2] : dRect[1], dsize);
@ -1186,7 +1207,8 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
else if (!InRenderPass())
{
OMSetRenderTargets(dTex, nullptr, darea);
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE);
BeginRenderPass(
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE);
}
if (sTex[0] && sTex[0]->GetState() == GSTexture::State::Dirty)
@ -1205,7 +1227,8 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
SetPipeline(m_convert[static_cast<int>(ShaderConvert::YUV)].get());
SetUtilityTexture(dTex, sampler);
OMSetRenderTargets(sTex[2], nullptr, fbarea);
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE);
BeginRenderPass(
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE);
DrawStretchRect(full_r, dRect[2], dsize);
}
@ -1216,7 +1239,8 @@ void GSDevice12::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
void GSDevice12::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb)
void GSDevice12::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb)
{
static_cast<GSTexture12*>(dTex)->TransitionToState(D3D12_RESOURCE_STATE_RENDER_TARGET);
@ -1288,9 +1312,7 @@ bool GSDevice12::CompileCASPipelines()
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}};
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"));
@ -1430,8 +1452,8 @@ void GSDevice12::RenderImGui()
SetScissor(GSVector4i(clip));
// Since we don't have the GSTexture...
D3D12Texture* tex = static_cast<D3D12Texture*>(pcmd->GetTexID());
D3D12DescriptorHandle handle = tex ? tex->GetSRVDescriptor() : m_null_texture.GetSRVDescriptor();
GSTexture12* tex = static_cast<GSTexture12*>(pcmd->GetTexID());
D3D12DescriptorHandle handle = tex ? tex->GetSRVDescriptor() : m_null_texture->GetSRVDescriptor();
if (m_utility_texture_cpu != handle)
{
m_utility_texture_cpu = handle;
@ -1455,19 +1477,20 @@ void GSDevice12::RenderImGui()
}
}
bool GSDevice12::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants)
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);
D3D12DescriptorHandle sTexDH, dTexDH;
if (!GetTextureGroupDescriptors(&sTexDH, &sTex12->GetTexture().GetSRVDescriptor(), 1) ||
!GetTextureGroupDescriptors(&dTexDH, &dTex12->GetTexture().GetWriteDescriptor(), 1))
if (!GetTextureGroupDescriptors(&sTexDH, &sTex12->GetSRVDescriptor(), 1) ||
!GetTextureGroupDescriptors(&dTexDH, &dTex12->GetUAVDescriptor(), 1))
{
ExecuteCommandList(false, "Ran out of descriptors for CAS");
if (!GetTextureGroupDescriptors(&sTexDH, &sTex12->GetTexture().GetSRVDescriptor(), 1) ||
!GetTextureGroupDescriptors(&dTexDH, &dTex12->GetTexture().GetWriteDescriptor(), 1))
if (!GetTextureGroupDescriptors(&sTexDH, &sTex12->GetSRVDescriptor(), 1) ||
!GetTextureGroupDescriptors(&dTexDH, &dTex12->GetUAVDescriptor(), 1))
{
Console.Error("Failed to allocate CAS descriptors.");
return false;
@ -1475,12 +1498,13 @@ bool GSDevice12::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, cons
}
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);
const D3D12_RESOURCE_STATES old_state = sTex12->GetResourceState();
sTex12->TransitionToState(cmdlist, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE);
dTex12->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->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());
@ -1491,7 +1515,7 @@ bool GSDevice12::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, cons
const int dispatchY = (dTex->GetHeight() + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
cmdlist->Dispatch(dispatchX, dispatchY, 1);
sTex12->GetTexture().TransitionToState(cmdlist, old_state);
sTex12->TransitionToState(cmdlist, old_state);
return true;
}
@ -1591,8 +1615,7 @@ bool GSDevice12::GetSampler(D3D12DescriptorHandle* cpu_handle, GSHWDrawConfig::S
}};
const u8 index = (static_cast<u8>(ss.IsMipFilterLinear()) << 2) |
(static_cast<u8>(ss.IsMagFilterLinear()) << 1) |
static_cast<u8>(ss.IsMinFilterLinear());
(static_cast<u8>(ss.IsMagFilterLinear()) << 1) | static_cast<u8>(ss.IsMinFilterLinear());
sd.Filter = filters[index];
}
@ -1625,14 +1648,16 @@ void GSDevice12::ClearSamplerCache()
m_dirty_flags |= DIRTY_FLAG_TFX_SAMPLERS;
}
bool GSDevice12::GetTextureGroupDescriptors(D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle* cpu_handles, u32 count)
bool GSDevice12::GetTextureGroupDescriptors(
D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle* cpu_handles, u32 count)
{
if (!g_d3d12_context->GetDescriptorAllocator().Allocate(count, gpu_handle))
return false;
if (count == 1)
{
g_d3d12_context->GetDevice()->CopyDescriptorsSimple(1, *gpu_handle, cpu_handles[0], D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
g_d3d12_context->GetDevice()->CopyDescriptorsSimple(
1, *gpu_handle, cpu_handles[0], D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
return true;
}
@ -1645,7 +1670,8 @@ bool GSDevice12::GetTextureGroupDescriptors(D3D12DescriptorHandle* gpu_handle, c
src_handles[i] = cpu_handles[i];
src_sizes[i] = 1;
}
g_d3d12_context->GetDevice()->CopyDescriptors(1, &dst_handle, &count, count, src_handles, src_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
g_d3d12_context->GetDevice()->CopyDescriptors(
1, &dst_handle, &count, count, src_handles, src_sizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
return true;
}
@ -1671,14 +1697,14 @@ GSDevice12::ComPtr<ID3DBlob> GSDevice12::GetUtilityPixelShader(const std::string
bool GSDevice12::CreateNullTexture()
{
if (!m_null_texture.Create(1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE))
{
m_null_texture =
GSTexture12::Create(GSTexture::Type::Texture, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN);
if (!m_null_texture)
return false;
}
m_null_texture.TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
D3D12::SetObjectName(m_null_texture.GetResource(), "Null texture");
m_null_texture->TransitionToState(g_d3d12_context->GetCommandList(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
D3D12::SetObjectName(m_null_texture->GetResource(), "Null texture");
return true;
}
@ -1838,15 +1864,14 @@ bool GSDevice12::CompileConvertPipelines()
for (u32 i = 0; i < 16; i++)
{
pxAssert(!m_color_copy[i]);
gpb.SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, static_cast<u8>(i));
gpb.SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE,
D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, static_cast<u8>(i));
m_color_copy[i] = gpb.Create(g_d3d12_context->GetDevice(), m_shader_cache, false);
if (!m_color_copy[i])
return false;
D3D12::SetObjectNameFormatted(m_color_copy[i].get(),
"Color copy pipeline (r=%u, g=%u, b=%u, a=%u)", i & 1u, (i >> 1) & 1u, (i >> 2) & 1u,
(i >> 3) & 1u);
D3D12::SetObjectNameFormatted(m_color_copy[i].get(), "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)
@ -1863,14 +1888,16 @@ bool GSDevice12::CompileConvertPipelines()
if (!arr[ds])
return false;
D3D12::SetObjectNameFormatted(arr[ds].get(), "HDR %s/copy pipeline (ds=%u)", is_setup ? "setup" : "finish", ds);
D3D12::SetObjectNameFormatted(
arr[ds].get(), "HDR %s/copy pipeline (ds=%u)", is_setup ? "setup" : "finish", ds);
}
}
}
for (u32 datm = 0; datm < 2; datm++)
{
ComPtr<ID3DBlob> ps(GetUtilityPixelShader(*shader, datm ? "ps_stencil_image_init_1" : "ps_stencil_image_init_0"));
ComPtr<ID3DBlob> ps(
GetUtilityPixelShader(*shader, datm ? "ps_stencil_image_init_1" : "ps_stencil_image_init_0"));
if (!ps)
return false;
@ -1879,8 +1906,8 @@ bool GSDevice12::CompileConvertPipelines()
gpb.SetPixelShader(ps.get());
gpb.SetNoDepthTestState();
gpb.SetNoStencilState();
gpb.SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ONE, D3D12_BLEND_OP_ADD,
D3D12_BLEND_ZERO, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_COLOR_WRITE_ENABLE_RED);
gpb.SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ONE, D3D12_BLEND_OP_ADD, D3D12_BLEND_ZERO,
D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_COLOR_WRITE_ENABLE_RED);
for (u32 ds = 0; ds < 2; ds++)
{
@ -1889,7 +1916,8 @@ bool GSDevice12::CompileConvertPipelines()
if (!m_date_image_setup_pipelines[ds][datm])
return false;
D3D12::SetObjectNameFormatted(m_date_image_setup_pipelines[ds][datm].get(), "DATE image clear pipeline (ds=%u, datm=%u)", ds, datm);
D3D12::SetObjectNameFormatted(
m_date_image_setup_pipelines[ds][datm].get(), "DATE image clear pipeline (ds=%u, datm=%u)", ds, datm);
}
}
@ -2000,8 +2028,8 @@ bool GSDevice12::CompileMergePipelines()
return false;
gpb.SetPixelShader(ps.get());
gpb.SetBlendState(0, true, D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD);
gpb.SetBlendState(0, true, D3D12_BLEND_SRC_ALPHA, D3D12_BLEND_INV_SRC_ALPHA, D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD);
m_merge[i] = gpb.Create(g_d3d12_context->GetDevice(), m_shader_cache, false);
if (!m_merge[i])
@ -2115,7 +2143,11 @@ void GSDevice12::DestroyResources()
m_utility_root_signature.reset();
m_tfx_root_signature.reset();
m_null_texture.Destroy(false);
if (m_null_texture)
{
m_null_texture->Destroy(false);
m_null_texture.reset();
}
m_shader_cache.Close();
}
@ -2262,14 +2294,13 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
// DepthStencil
if (p.ds)
{
static const D3D12_COMPARISON_FUNC ztst[] = {
D3D12_COMPARISON_FUNC_NEVER, D3D12_COMPARISON_FUNC_ALWAYS, D3D12_COMPARISON_FUNC_GREATER_EQUAL, D3D12_COMPARISON_FUNC_GREATER};
static const D3D12_COMPARISON_FUNC ztst[] = {D3D12_COMPARISON_FUNC_NEVER, D3D12_COMPARISON_FUNC_ALWAYS,
D3D12_COMPARISON_FUNC_GREATER_EQUAL, D3D12_COMPARISON_FUNC_GREATER};
gpb.SetDepthState((p.dss.ztst != ZTST_ALWAYS || p.dss.zwe), p.dss.zwe, ztst[p.dss.ztst]);
if (p.dss.date)
{
const D3D12_DEPTH_STENCILOP_DESC sos{D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP,
p.dss.date_one ? D3D12_STENCIL_OP_ZERO : D3D12_STENCIL_OP_KEEP,
D3D12_COMPARISON_FUNC_EQUAL};
p.dss.date_one ? D3D12_STENCIL_OP_ZERO : D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_EQUAL};
gpb.SetStencilState(true, 1, 1, sos, sos);
}
}
@ -2304,8 +2335,8 @@ GSDevice12::ComPtr<ID3D12PipelineState> GSDevice12::CreateTFXPipeline(const Pipe
}
else
{
gpb.SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, p.cms.wrgba);
gpb.SetBlendState(0, false, D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, D3D12_BLEND_ONE,
D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, p.cms.wrgba);
}
ComPtr<ID3D12PipelineState> pipeline(gpb.Create(g_d3d12_context->GetDevice(), m_shader_cache));
@ -2343,7 +2374,7 @@ bool GSDevice12::BindDrawPipeline(const PipelineSelector& p)
void GSDevice12::InitializeState()
{
for (u32 i = 0; i < NUM_TOTAL_TFX_TEXTURES; i++)
m_tfx_textures[i] = m_null_texture.GetSRVDescriptor();
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
m_tfx_sampler_sel = GSHWDrawConfig::SamplerSelector::Point().key;
InvalidateCachedState();
@ -2403,11 +2434,14 @@ void GSDevice12::ExecuteCommandListAndRestartRenderPass(bool wait_for_completion
m_dirty_flags &= ~DIRTY_BASE_STATE;
// restart render pass
BeginRenderPass(
m_current_render_target ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
m_current_render_target ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
m_current_depth_target ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
m_current_depth_target ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS);
BeginRenderPass(m_current_render_target ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE :
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
m_current_render_target ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE :
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
m_current_depth_target ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE :
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
m_current_depth_target ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE :
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS);
}
}
@ -2431,7 +2465,8 @@ void GSDevice12::InvalidateCachedState()
void GSDevice12::SetVertexBuffer(D3D12_GPU_VIRTUAL_ADDRESS buffer, size_t size, size_t stride)
{
if (m_vertex_buffer.BufferLocation == buffer && m_vertex_buffer.SizeInBytes == size && m_vertex_buffer.StrideInBytes == stride)
if (m_vertex_buffer.BufferLocation == buffer && m_vertex_buffer.SizeInBytes == size &&
m_vertex_buffer.StrideInBytes == stride)
return;
m_vertex_buffer.BufferLocation = buffer;
@ -2486,9 +2521,9 @@ void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
GSTexture12* dtex = static_cast<GSTexture12*>(sr);
if (check_state)
{
if (dtex->GetTexture().GetState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE && InRenderPass())
if (dtex->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE && InRenderPass())
{
// Console.Warning("Ending render pass due to resource transition");
GL_INS("Ending render pass due to resource transition");
EndRenderPass();
}
@ -2500,7 +2535,7 @@ void GSDevice12::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
}
else
{
handle = m_null_texture.GetSRVDescriptor();
handle = m_null_texture->GetSRVDescriptor();
}
if (m_tfx_textures[i] == handle)
@ -2543,7 +2578,7 @@ void GSDevice12::SetUtilityTexture(GSTexture* dtex, const D3D12DescriptorHandle&
}
else
{
handle = m_null_texture.GetSRVDescriptor();
handle = m_null_texture->GetSRVDescriptor();
}
if (m_utility_texture_cpu != handle)
@ -2575,7 +2610,8 @@ void GSDevice12::SetUtilityTexture(GSTexture* dtex, const D3D12DescriptorHandle&
void GSDevice12::SetUtilityPushConstants(const void* data, u32 size)
{
g_d3d12_context->GetCommandList()->SetGraphicsRoot32BitConstants(UTILITY_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS, (size + 3) / sizeof(u32), data, 0);
g_d3d12_context->GetCommandList()->SetGraphicsRoot32BitConstants(
UTILITY_ROOT_SIGNATURE_PARAM_PUSH_CONSTANTS, (size + 3) / sizeof(u32), data, 0);
}
void GSDevice12::UnbindTexture(GSTexture12* tex)
@ -2584,7 +2620,7 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
{
if (m_tfx_textures[i] == tex->GetSRVDescriptor())
{
m_tfx_textures[i] = m_null_texture.GetSRVDescriptor();
m_tfx_textures[i] = m_null_texture->GetSRVDescriptor();
m_dirty_flags |= DIRTY_FLAG_TFX_TEXTURES;
}
}
@ -2600,8 +2636,8 @@ void GSDevice12::UnbindTexture(GSTexture12* tex)
}
}
void GSDevice12::RenderTextureMipmap(const D3D12Texture& texture,
u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level, u32 src_width, u32 src_height)
void GSDevice12::RenderTextureMipmap(
GSTexture12* texture, u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level, u32 src_width, u32 src_height)
{
EndRenderPass();
@ -2616,13 +2652,14 @@ void GSDevice12::RenderTextureMipmap(const D3D12Texture& texture,
ExecuteCommandList(false);
// Setup views. This will be a partial view for the SRV.
D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {texture.GetFormat(), D3D12_RTV_DIMENSION_TEXTURE2D};
D3D12_RENDER_TARGET_VIEW_DESC rtv_desc = {texture->GetDXGIFormat(), D3D12_RTV_DIMENSION_TEXTURE2D};
rtv_desc.Texture2D = {dst_level, 0u};
g_d3d12_context->GetDevice()->CreateRenderTargetView(texture.GetResource(), &rtv_desc, rtv_handle);
g_d3d12_context->GetDevice()->CreateRenderTargetView(texture->GetResource(), &rtv_desc, rtv_handle);
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {texture.GetFormat(), D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
D3D12_SHADER_RESOURCE_VIEW_DESC srv_desc = {
texture->GetDXGIFormat(), D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
srv_desc.Texture2D = {src_level, 1u, 0u, 0.0f};
g_d3d12_context->GetDevice()->CreateShaderResourceView(texture.GetResource(), &srv_desc, srv_handle);
g_d3d12_context->GetDevice()->CreateShaderResourceView(texture->GetResource(), &srv_desc, srv_handle);
// We need to set the descriptors up manually, because we're not going through GSTexture.
if (!GetTextureGroupDescriptors(&m_utility_texture_gpu, &srv_handle, 1))
@ -2636,10 +2673,12 @@ void GSDevice12::RenderTextureMipmap(const D3D12Texture& texture,
// *now* we don't have to worry about running out of anything.
ID3D12GraphicsCommandList* cmdlist = g_d3d12_context->GetCommandList();
if (texture.GetState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
texture.TransitionSubresourceToState(cmdlist, src_level, texture.GetState(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
if (texture.GetState() != D3D12_RESOURCE_STATE_RENDER_TARGET)
texture.TransitionSubresourceToState(cmdlist, dst_level, texture.GetState(), D3D12_RESOURCE_STATE_RENDER_TARGET);
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
texture->TransitionSubresourceToState(
cmdlist, src_level, texture->GetResourceState(), D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_RENDER_TARGET)
texture->TransitionSubresourceToState(
cmdlist, dst_level, texture->GetResourceState(), D3D12_RESOURCE_STATE_RENDER_TARGET);
// We set the state directly here.
constexpr u32 MODIFIED_STATE = DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET;
@ -2647,7 +2686,7 @@ void GSDevice12::RenderTextureMipmap(const D3D12Texture& texture,
// Using a render pass is probably a bit overkill.
const D3D12_DISCARD_REGION discard_region = {0u, nullptr, dst_level, 1u};
cmdlist->DiscardResource(texture.GetResource(), &discard_region);
cmdlist->DiscardResource(texture->GetResource(), &discard_region);
cmdlist->OMSetRenderTargets(1, &rtv_handle.cpu_handle, FALSE, nullptr);
const D3D12_VIEWPORT vp = {0.0f, 0.0f, static_cast<float>(dst_width), static_cast<float>(dst_height), 0.0f, 1.0f};
@ -2662,10 +2701,12 @@ void GSDevice12::RenderTextureMipmap(const D3D12Texture& texture,
GSVector4(0.0f, 0.0f, static_cast<float>(dst_width), static_cast<float>(dst_height)),
GSVector2i(dst_width, dst_height));
if (texture.GetState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
texture.TransitionSubresourceToState(cmdlist, src_level, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, texture.GetState());
if (texture.GetState() != D3D12_RESOURCE_STATE_RENDER_TARGET)
texture.TransitionSubresourceToState(cmdlist, dst_level, D3D12_RESOURCE_STATE_RENDER_TARGET, texture.GetState());
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
texture->TransitionSubresourceToState(
cmdlist, src_level, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, texture->GetResourceState());
if (texture->GetResourceState() != D3D12_RESOURCE_STATE_RENDER_TARGET)
texture->TransitionSubresourceToState(
cmdlist, dst_level, D3D12_RESOURCE_STATE_RENDER_TARGET, texture->GetResourceState());
// Must destroy after current cmdlist.
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &srv_handle);
@ -2680,12 +2721,11 @@ bool GSDevice12::InRenderPass()
return m_in_render_pass;
}
void GSDevice12::BeginRenderPass(
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_begin, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE color_end,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE depth_begin, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE depth_end,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE stencil_begin, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE stencil_end,
const GSVector4& clear_color /* = GSVector4::zero() */, float clear_depth /* = 0.0f */,
u8 clear_stencil /* = 0 */)
void GSDevice12::BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE color_begin,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE color_end, D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE depth_begin,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE depth_end, D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE stencil_begin,
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE stencil_end, const GSVector4& clear_color /* = GSVector4::zero() */,
float clear_depth /* = 0.0f */, u8 clear_stencil /* = 0 */)
{
if (m_in_render_pass)
EndRenderPass();
@ -2697,12 +2737,13 @@ void GSDevice12::BeginRenderPass(
D3D12_RENDER_PASS_RENDER_TARGET_DESC rt = {};
if (m_current_render_target)
{
rt.cpuDescriptor = m_current_render_target->GetRTVOrDSVHandle();
rt.cpuDescriptor = m_current_render_target->GetWriteDescriptor();
rt.EndingAccess.Type = color_end;
rt.BeginningAccess.Type = color_begin;
if (color_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_render_target->GetFormat(), nullptr, &rt.BeginningAccess.Clear.ClearValue.Format, nullptr, nullptr);
LookupNativeFormat(m_current_render_target->GetFormat(), nullptr,
&rt.BeginningAccess.Clear.ClearValue.Format, nullptr, nullptr);
GSVector4::store<false>(rt.BeginningAccess.Clear.ClearValue.Color, clear_color);
}
}
@ -2710,26 +2751,27 @@ void GSDevice12::BeginRenderPass(
D3D12_RENDER_PASS_DEPTH_STENCIL_DESC ds = {};
if (m_current_depth_target)
{
ds.cpuDescriptor = m_current_depth_target->GetRTVOrDSVHandle();
ds.cpuDescriptor = m_current_depth_target->GetWriteDescriptor();
ds.DepthEndingAccess.Type = depth_end;
ds.DepthBeginningAccess.Type = depth_begin;
if (depth_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr, &ds.DepthBeginningAccess.Clear.ClearValue.Format);
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr,
&ds.DepthBeginningAccess.Clear.ClearValue.Format);
ds.DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth = clear_depth;
}
ds.StencilEndingAccess.Type = stencil_end;
ds.StencilBeginningAccess.Type = stencil_begin;
if (stencil_begin == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR)
{
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr, &ds.StencilBeginningAccess.Clear.ClearValue.Format);
LookupNativeFormat(m_current_depth_target->GetFormat(), nullptr, nullptr, nullptr,
&ds.StencilBeginningAccess.Clear.ClearValue.Format);
ds.StencilBeginningAccess.Clear.ClearValue.DepthStencil.Stencil = clear_stencil;
}
}
g_d3d12_context->GetCommandList()->BeginRenderPass(
m_current_render_target ? 1 : 0, m_current_render_target ? &rt : nullptr,
m_current_depth_target ? &ds : nullptr, D3D12_RENDER_PASS_FLAG_NONE);
g_d3d12_context->GetCommandList()->BeginRenderPass(m_current_render_target ? 1 : 0,
m_current_render_target ? &rt : nullptr, m_current_depth_target ? &ds : nullptr, D3D12_RENDER_PASS_FLAG_NONE);
}
void GSDevice12::EndRenderPass()
@ -2745,7 +2787,6 @@ void GSDevice12::EndRenderPass()
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
g_d3d12_context->GetCommandList()->EndRenderPass();
}
void GSDevice12::SetViewport(const D3D12_VIEWPORT& viewport)
@ -2811,12 +2852,12 @@ __ri void GSDevice12::ApplyBaseState(u32 flags, ID3D12GraphicsCommandList* cmdli
{
if (m_current_render_target)
{
cmdlist->OMSetRenderTargets(1, &m_current_render_target->GetRTVOrDSVHandle().cpu_handle, FALSE,
m_current_depth_target ? &m_current_depth_target->GetRTVOrDSVHandle().cpu_handle : nullptr);
cmdlist->OMSetRenderTargets(1, &m_current_render_target->GetWriteDescriptor().cpu_handle, FALSE,
m_current_depth_target ? &m_current_depth_target->GetWriteDescriptor().cpu_handle : nullptr);
}
else if (m_current_depth_target)
{
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, &m_current_depth_target->GetRTVOrDSVHandle().cpu_handle);
cmdlist->OMSetRenderTargets(0, nullptr, FALSE, &m_current_depth_target->GetWriteDescriptor().cpu_handle);
}
}
}
@ -2911,8 +2952,8 @@ bool GSDevice12::ApplyTFXState(bool already_execed)
{
m_current_root_signature = RootSignature::TFX;
flags |= DIRTY_FLAG_VS_CONSTANT_BUFFER_BINDING | DIRTY_FLAG_PS_CONSTANT_BUFFER_BINDING |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE | DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE | DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 |
DIRTY_FLAG_PIPELINE;
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE | DIRTY_FLAG_SAMPLERS_DESCRIPTOR_TABLE |
DIRTY_FLAG_TEXTURES_DESCRIPTOR_TABLE_2 | DIRTY_FLAG_PIPELINE;
cmdlist->SetGraphicsRootSignature(m_tfx_root_signature.get());
}
@ -2998,8 +3039,8 @@ void GSDevice12::SetupDATE(GSTexture* rt, GSTexture* ds, bool datm, const GSVect
SetStencilRef(1);
BeginRenderPass(D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
GSVector4::zero(), 0.0f, 0);
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, GSVector4::zero(),
0.0f, 0);
if (ApplyUtilityState())
DrawPrimitive();
@ -3084,9 +3125,8 @@ GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, Pipe
void GSDevice12::RenderHW(GSHWDrawConfig& config)
{
// Destination Alpha Setup
const bool stencil_DATE =
(config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne);
const bool stencil_DATE = (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne);
if (stencil_DATE)
SetupDATE(config.rt, config.ds, config.datm, config.drawarea);
@ -3127,7 +3167,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
const GSVector4i render_area(
config.ps.hdr ? config.drawarea :
GSVector4i(Common::AlignDownPow2(config.scissor.left, render_area_alignment),
GSVector4i(Common::AlignDownPow2(config.scissor.left, render_area_alignment),
Common::AlignDownPow2(config.scissor.top, render_area_alignment),
std::min(Common::AlignUpPow2(config.scissor.right, render_area_alignment), rtsize.x),
std::min(Common::AlignUpPow2(config.scissor.bottom, render_area_alignment), rtsize.y)));
@ -3177,8 +3217,7 @@ void GSDevice12::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());
draw_rt_clone->SetState(GSTexture::State::Invalidated);
@ -3218,13 +3257,14 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// Begin render pass if new target or out of the area.
if (!m_in_render_pass)
{
BeginRenderPass(
GetLoadOpForTexture(draw_rt),
BeginRenderPass(GetLoadOpForTexture(draw_rt),
draw_rt ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
GetLoadOpForTexture(draw_ds),
draw_ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
stencil_DATE ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
stencil_DATE ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
stencil_DATE ? D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE :
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS,
stencil_DATE ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD :
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
draw_rt ? draw_rt->GetClearColor() : GSVector4::zero(), draw_ds ? draw_ds->GetClearDepth() : 0.0f, 1);
}
@ -3305,7 +3345,8 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
BeginRenderPass(GetLoadOpForTexture(draw_rt), D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
GetLoadOpForTexture(draw_ds), draw_ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
GetLoadOpForTexture(draw_ds),
draw_ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
draw_rt->GetClearColor(), 0.0f, 0);

View File

@ -141,7 +141,7 @@ public:
private:
ComPtr<IDXGIFactory5> m_dxgi_factory;
ComPtr<IDXGISwapChain1> m_swap_chain;
std::vector<D3D12Texture> m_swap_chain_buffers;
std::vector<std::unique_ptr<GSTexture12>> m_swap_chain_buffers;
u32 m_current_swap_chain_buffer = 0;
bool m_allow_tearing_supported = false;
@ -177,7 +177,8 @@ private:
ComPtr<ID3D12PipelineState> m_imgui_pipeline;
std::unordered_map<u32, ComPtr<ID3DBlob>> m_tfx_vertex_shaders;
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;
ComPtr<ID3D12RootSignature> m_cas_root_signature;
@ -191,26 +192,31 @@ private:
ComPtr<ID3DBlob> m_convert_vs;
std::string m_tfx_source;
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format, DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
void LookupNativeFormat(GSTexture::Format format, DXGI_FORMAT* d3d_format, DXGI_FORMAT* srv_format,
DXGI_FORMAT* rtv_format, DXGI_FORMAT* dsv_format) const;
bool CreateSwapChain();
bool CreateSwapChainRTV();
void DestroySwapChainRTVs();
void DestroySwapChain();
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
GSTexture* CreateSurface(
GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
const GSRegEXTBUF& EXTBUF, const GSVector4& c, const bool linear) final;
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
void DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
ShaderInterlace shader, bool linear, const InterlaceConstantBuffer& cb) final;
void DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4]) 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 DoCAS(
GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) final;
bool GetSampler(D3D12DescriptorHandle* cpu_handle, GSHWDrawConfig::SamplerSelector ss);
void ClearSamplerCache() final;
bool GetTextureGroupDescriptors(D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle* cpu_handles, u32 count);
bool GetTextureGroupDescriptors(
D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle* cpu_handles, u32 count);
const ID3DBlob* GetTFXVertexShader(GSHWDrawConfig::VSSelector sel);
const ID3DBlob* GetTFXPixelShader(const GSHWDrawConfig::PSSelector& sel);
@ -289,10 +295,13 @@ public:
bool green, bool blue, bool alpha) override;
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect,
PresentShader shader, float shaderTime, bool linear) override;
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
void UpdateCLUTTexture(
GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM,
GSTexture* dTex, u32 DBW, u32 DPSM) override;
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
void DrawMultiStretchRects(
const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
void DoMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture12* dTex, ShaderConvert shader);
void BeginRenderPassForStretchRect(
@ -347,8 +356,8 @@ public:
// Assumes that the previous level has been transitioned to PS resource,
// and the current level has been transitioned to RT.
void RenderTextureMipmap(const D3D12Texture& texture,
u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level, u32 src_width, u32 src_height);
void RenderTextureMipmap(GSTexture12* texture, u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level,
u32 src_width, u32 src_height);
// Ends a render pass if we're currently in one.
// When Bind() is next called, the pass will be restarted.
@ -401,7 +410,8 @@ private:
DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | DIRTY_FLAG_RENDER_TARGET | DIRTY_FLAG_PIPELINE |
DIRTY_FLAG_BLEND_CONSTANTS | DIRTY_FLAG_STENCIL_REF,
DIRTY_TFX_STATE = DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES | DIRTY_FLAG_TFX_SAMPLERS | DIRTY_FLAG_TFX_RT_TEXTURES,
DIRTY_TFX_STATE =
DIRTY_BASE_STATE | DIRTY_FLAG_TFX_TEXTURES | DIRTY_FLAG_TFX_SAMPLERS | DIRTY_FLAG_TFX_RT_TEXTURES,
DIRTY_UTILITY_STATE = DIRTY_BASE_STATE,
DIRTY_CONSTANT_BUFFER_STATE = DIRTY_FLAG_VS_CONSTANT_BUFFER | DIRTY_FLAG_PS_CONSTANT_BUFFER,
};
@ -451,7 +461,7 @@ private:
RootSignature m_current_root_signature = RootSignature::Undefined;
const ID3D12PipelineState* m_current_pipeline = nullptr;
D3D12Texture m_null_texture;
std::unique_ptr<GSTexture12> m_null_texture;
// current pipeline selector - we save this in the struct to avoid re-zeroing it every draw
PipelineSelector m_pipeline_selector = {};

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -28,99 +28,338 @@
#include "D3D12MemAlloc.h"
GSTexture12::GSTexture12(Type type, Format format, D3D12Texture texture)
: m_texture(std::move(texture))
GSTexture12::GSTexture12(Type type, Format format, int width, int height, int levels, DXGI_FORMAT dxgi_format,
wil::com_ptr_nothrow<ID3D12Resource> resource, wil::com_ptr_nothrow<D3D12MA::Allocation> allocation,
const D3D12DescriptorHandle& srv_descriptor, const D3D12DescriptorHandle& write_descriptor,
const D3D12DescriptorHandle& uav_descriptor, WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state)
: m_resource(std::move(resource))
, m_allocation(std::move(allocation))
, m_srv_descriptor(srv_descriptor)
, m_write_descriptor(write_descriptor)
, m_uav_descriptor(uav_descriptor)
, m_write_descriptor_type(wdtype)
, m_dxgi_format(dxgi_format)
, m_resource_state(resource_state)
{
m_type = type;
m_format = format;
m_size.x = m_texture.GetWidth();
m_size.y = m_texture.GetHeight();
m_mipmap_levels = m_texture.GetLevels();
m_size.x = width;
m_size.y = height;
m_mipmap_levels = levels;
}
GSTexture12::~GSTexture12()
{
GSDevice12::GetInstance()->UnbindTexture(this);
Destroy(true);
}
std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, u32 width, u32 height, u32 levels, Format format,
DXGI_FORMAT d3d_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format)
void GSTexture12::Destroy(bool defer)
{
GSDevice12::GetInstance()->UnbindTexture(this);
if (defer)
{
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_srv_descriptor);
switch (m_write_descriptor_type)
{
case WriteDescriptorType::RTV:
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetRTVHeapManager(), &m_write_descriptor);
break;
case WriteDescriptorType::DSV:
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDSVHeapManager(), &m_write_descriptor);
break;
case WriteDescriptorType::None:
default:
break;
}
if (m_uav_descriptor)
g_d3d12_context->DeferDescriptorDestruction(g_d3d12_context->GetDescriptorHeapManager(), &m_uav_descriptor);
g_d3d12_context->DeferResourceDestruction(m_allocation.get(), m_resource.get());
m_resource.reset();
m_allocation.reset();
}
else
{
g_d3d12_context->GetDescriptorHeapManager().Free(&m_srv_descriptor);
switch (m_write_descriptor_type)
{
case WriteDescriptorType::RTV:
g_d3d12_context->GetRTVHeapManager().Free(&m_write_descriptor);
break;
case WriteDescriptorType::DSV:
g_d3d12_context->GetDSVHeapManager().Free(&m_write_descriptor);
break;
case WriteDescriptorType::None:
default:
break;
}
if (m_uav_descriptor)
g_d3d12_context->GetDescriptorHeapManager().Free(&m_uav_descriptor);
m_resource.reset();
m_allocation.reset();
}
m_write_descriptor_type = WriteDescriptorType::None;
}
std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, Format format, int width, int height, int levels,
DXGI_FORMAT dxgi_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format,
DXGI_FORMAT uav_format)
{
D3D12_RESOURCE_DESC desc = {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Width = width;
desc.Height = height;
desc.DepthOrArraySize = 1;
desc.MipLevels = levels;
desc.Format = dxgi_format;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
D3D12MA::ALLOCATION_DESC allocationDesc = {};
allocationDesc.Flags = D3D12MA::ALLOCATION_FLAG_WITHIN_BUDGET;
allocationDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
D3D12_CLEAR_VALUE optimized_clear_value = {};
D3D12_RESOURCE_STATES state;
switch (type)
{
case Type::Texture:
{
// This is a little annoying. basically, to do mipmap generation, we need to be a render target.
// If it's a compressed texture, we won't be generating mips anyway, so this should be fine.
const D3D12_RESOURCE_FLAGS flags = (levels > 1 && !IsCompressedFormat(format)) ?
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET :
D3D12_RESOURCE_FLAG_NONE;
D3D12Texture texture;
if (!texture.Create(
width, height, levels, d3d_format, srv_format, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, flags))
{
return {};
}
D3D12::SetObjectNameFormatted(texture.GetResource(), "%ux%u texture", width, height);
return std::make_unique<GSTexture12>(type, format, std::move(texture));
desc.Flags = (levels > 1 && !IsCompressedFormat(format)) ? D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET :
D3D12_RESOURCE_FLAG_NONE;
state = D3D12_RESOURCE_STATE_COPY_DEST;
}
break;
case Type::RenderTarget:
{
pxAssert(levels == 1);
// RT's tend to be larger, so we'll keep them committed for speed.
D3D12Texture texture;
if (!texture.Create(width, height, levels, d3d_format, srv_format, rtv_format, DXGI_FORMAT_UNKNOWN,
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12MA::ALLOCATION_FLAG_COMMITTED))
{
return {};
}
D3D12::SetObjectNameFormatted(texture.GetResource(), "%ux%u render target", width, height);
return std::make_unique<GSTexture12>(type, format, std::move(texture));
pxAssert(levels == 1);
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
optimized_clear_value.Format = rtv_format;
state = D3D12_RESOURCE_STATE_RENDER_TARGET;
}
break;
case Type::DepthStencil:
{
pxAssert(levels == 1);
D3D12Texture texture;
if (!texture.Create(width, height, levels, d3d_format, srv_format, DXGI_FORMAT_UNKNOWN, dsv_format,
D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL, D3D12MA::ALLOCATION_FLAG_COMMITTED))
{
return {};
}
D3D12::SetObjectNameFormatted(texture.GetResource(), "%ux%u depth stencil", width, height);
return std::make_unique<GSTexture12>(type, format, std::move(texture));
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
optimized_clear_value.Format = dsv_format;
state = D3D12_RESOURCE_STATE_DEPTH_WRITE;
}
break;
case Type::RWTexture:
{
pxAssert(levels == 1);
D3D12Texture 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));
allocationDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED;
state = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
}
break;
default:
return {};
}
if (uav_format != DXGI_FORMAT_UNKNOWN)
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
wil::com_ptr_nothrow<ID3D12Resource> resource;
wil::com_ptr_nothrow<D3D12MA::Allocation> allocation;
HRESULT hr = g_d3d12_context->GetAllocator()->CreateResource(&allocationDesc, &desc, state,
(type == Type::RenderTarget || type == Type::DepthStencil) ? &optimized_clear_value : nullptr, allocation.put(),
IID_PPV_ARGS(resource.put()));
if (FAILED(hr))
{
// OOM isn't fatal.
if (hr != E_OUTOFMEMORY)
Console.Error("Create texture failed: 0x%08X", hr);
return {};
}
D3D12DescriptorHandle srv_descriptor, write_descriptor, uav_descriptor;
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
if (srv_format != DXGI_FORMAT_UNKNOWN)
{
if (!CreateSRVDescriptor(resource.get(), levels, srv_format, &srv_descriptor))
return {};
}
switch (type)
{
case Type::Texture:
{
D3D12::SetObjectNameFormatted(resource.get(), "%dx%d texture", width, height);
}
break;
case Type::RenderTarget:
{
D3D12::SetObjectNameFormatted(resource.get(), "%dx%d render target", width, height);
write_descriptor_type = WriteDescriptorType::RTV;
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
{
g_d3d12_context->GetRTVHeapManager().Free(&srv_descriptor);
return {};
}
}
break;
case Type::DepthStencil:
{
D3D12::SetObjectNameFormatted(resource.get(), "%dx%d depth stencil", width, height);
write_descriptor_type = WriteDescriptorType::DSV;
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor))
{
g_d3d12_context->GetDSVHeapManager().Free(&srv_descriptor);
return {};
}
}
break;
case Type::RWTexture:
{
D3D12::SetObjectNameFormatted(resource.get(), "%dx%d RW texture", width, height);
}
break;
default:
break;
}
if (uav_format != DXGI_FORMAT_UNKNOWN && !CreateUAVDescriptor(resource.get(), dsv_format, &uav_descriptor))
{
g_d3d12_context->GetDescriptorHeapManager().Free(&write_descriptor);
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
}
return std::unique_ptr<GSTexture12>(
new GSTexture12(type, format, width, height, levels, dxgi_format, std::move(resource), std::move(allocation),
srv_descriptor, write_descriptor, uav_descriptor, write_descriptor_type, state));
}
std::unique_ptr<GSTexture12> GSTexture12::Adopt(wil::com_ptr_nothrow<ID3D12Resource> resource, Type type, Format format,
int width, int height, int levels, DXGI_FORMAT dxgi_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format,
DXGI_FORMAT dsv_format, DXGI_FORMAT uav_format, D3D12_RESOURCE_STATES resource_state)
{
const D3D12_RESOURCE_DESC desc = resource->GetDesc();
D3D12DescriptorHandle srv_descriptor, write_descriptor, uav_descriptor;
WriteDescriptorType write_descriptor_type = WriteDescriptorType::None;
if (srv_format != DXGI_FORMAT_UNKNOWN)
{
if (!CreateSRVDescriptor(resource.get(), desc.MipLevels, srv_format, &srv_descriptor))
return {};
}
if (type == Type::RenderTarget)
{
write_descriptor_type = WriteDescriptorType::RTV;
if (!CreateRTVDescriptor(resource.get(), rtv_format, &write_descriptor))
{
g_d3d12_context->GetRTVHeapManager().Free(&srv_descriptor);
return {};
}
}
else if (type == Type::DepthStencil)
{
write_descriptor_type = WriteDescriptorType::DSV;
if (!CreateDSVDescriptor(resource.get(), dsv_format, &write_descriptor))
{
g_d3d12_context->GetDSVHeapManager().Free(&srv_descriptor);
return {};
}
}
if (uav_format != DXGI_FORMAT_UNKNOWN)
{
if (!CreateUAVDescriptor(resource.get(), srv_format, &uav_descriptor))
{
g_d3d12_context->GetDescriptorHeapManager().Free(&write_descriptor);
g_d3d12_context->GetDescriptorHeapManager().Free(&srv_descriptor);
return {};
}
}
return std::unique_ptr<GSTexture12>(new GSTexture12(type, format, static_cast<u32>(desc.Width), desc.Height,
desc.MipLevels, desc.Format, std::move(resource), {}, srv_descriptor, write_descriptor, uav_descriptor,
write_descriptor_type, resource_state));
}
bool GSTexture12::CreateSRVDescriptor(
ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, D3D12DescriptorHandle* dh)
{
if (!g_d3d12_context->GetDescriptorHeapManager().Allocate(dh))
{
Console.Error("Failed to allocate SRV descriptor");
return false;
}
D3D12_SHADER_RESOURCE_VIEW_DESC desc = {
format, D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING};
desc.Texture2D.MipLevels = levels;
g_d3d12_context->GetDevice()->CreateShaderResourceView(resource, &desc, dh->cpu_handle);
return true;
}
bool GSTexture12::CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh)
{
if (!g_d3d12_context->GetRTVHeapManager().Allocate(dh))
{
Console.Error("Failed to allocate SRV descriptor");
return false;
}
const D3D12_RENDER_TARGET_VIEW_DESC desc = {format, D3D12_RTV_DIMENSION_TEXTURE2D};
g_d3d12_context->GetDevice()->CreateRenderTargetView(resource, &desc, dh->cpu_handle);
return true;
}
bool GSTexture12::CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh)
{
if (!g_d3d12_context->GetDSVHeapManager().Allocate(dh))
{
Console.Error("Failed to allocate SRV descriptor");
return false;
}
const D3D12_DEPTH_STENCIL_VIEW_DESC desc = {format, D3D12_DSV_DIMENSION_TEXTURE2D, D3D12_DSV_FLAG_NONE};
g_d3d12_context->GetDevice()->CreateDepthStencilView(resource, &desc, dh->cpu_handle);
return true;
}
bool GSTexture12::CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* 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;
}
void* GSTexture12::GetNativeHandle() const
{
return const_cast<D3D12Texture*>(&m_texture);
return const_cast<GSTexture12*>(this);
}
ID3D12GraphicsCommandList* GSTexture12::GetCommandBufferForUpdate()
@ -198,7 +437,7 @@ bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int l
srcloc.PlacedFootprint.Footprint.Width = width;
srcloc.PlacedFootprint.Footprint.Height = height;
srcloc.PlacedFootprint.Footprint.Depth = 1;
srcloc.PlacedFootprint.Footprint.Format = m_texture.GetFormat();
srcloc.PlacedFootprint.Footprint.Format = m_dxgi_format;
srcloc.PlacedFootprint.Footprint.RowPitch = upload_pitch;
// If the texture is larger than half our streaming buffer size, use a separate buffer.
@ -235,10 +474,10 @@ bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int l
GL_PUSH("GSTexture12::Update({%d,%d} %dx%d Lvl:%u", r.x, r.y, r.width(), r.height(), layer);
// first time the texture is used? don't leave it undefined
if (m_texture.GetState() == D3D12_RESOURCE_STATE_COMMON)
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
else if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
m_texture.TransitionSubresourceToState(cmdlist, layer, m_texture.GetState(), D3D12_RESOURCE_STATE_COPY_DEST);
if (m_resource_state == D3D12_RESOURCE_STATE_COMMON)
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
else if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
TransitionSubresourceToState(cmdlist, layer, m_resource_state, D3D12_RESOURCE_STATE_COPY_DEST);
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
if (m_type == Type::RenderTarget)
@ -250,7 +489,7 @@ bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int l
}
D3D12_TEXTURE_COPY_LOCATION dstloc;
dstloc.pResource = m_texture.GetResource();
dstloc.pResource = m_resource.get();
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstloc.SubresourceIndex = layer;
@ -258,8 +497,8 @@ bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int l
cmdlist->CopyTextureRegion(&dstloc, Common::AlignDownPow2((u32)r.x, block_size),
Common::AlignDownPow2((u32)r.y, block_size), 0, &srcloc, &srcbox);
if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
m_texture.TransitionSubresourceToState(cmdlist, layer, D3D12_RESOURCE_STATE_COPY_DEST, m_texture.GetState());
if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
TransitionSubresourceToState(cmdlist, layer, D3D12_RESOURCE_STATE_COPY_DEST, m_resource_state);
if (m_type == Type::Texture)
m_needs_mipmaps_generated |= (layer == 0);
@ -273,11 +512,9 @@ bool GSTexture12::Map(GSMap& m, const GSVector4i* r, int layer)
return false;
// map for writing
m_map_area = r ? *r : GSVector4i(0, 0, m_texture.GetWidth(), m_texture.GetHeight());
m_map_area = r ? *r : GetRect();
m_map_level = layer;
m.pitch = Common::AlignUpPow2(
m_map_area.width() * D3D12::GetTexelSize(m_texture.GetFormat()), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
m.pitch = Common::AlignUpPow2(CalcUploadPitch(m_map_area.width()), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
// see note in Update() for the reason why.
const u32 required_size = CalcUploadSize(m_map_area.height(), m.pitch);
@ -300,14 +537,12 @@ bool GSTexture12::Map(GSMap& m, const GSVector4i* r, int layer)
void GSTexture12::Unmap()
{
// this can't handle blocks/compressed formats at the moment.
pxAssert(m_map_level < m_texture.GetLevels() && !IsCompressedFormat());
pxAssert(m_map_level < m_mipmap_levels && !IsCompressedFormat());
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
// TODO: non-tightly-packed formats
const u32 width = static_cast<u32>(m_map_area.width());
const u32 height = static_cast<u32>(m_map_area.height());
const u32 pitch = Common::AlignUpPow2(
m_map_area.width() * D3D12::GetTexelSize(m_texture.GetFormat()), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 width = m_map_area.width();
const u32 height = m_map_area.height();
const u32 pitch = Common::AlignUpPow2(CalcUploadPitch(width), D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
const u32 required_size = CalcUploadSize(height, pitch);
D3D12StreamBuffer& buffer = g_d3d12_context->GetTextureStreamBuffer();
const u32 buffer_offset = buffer.GetCurrentOffset();
@ -318,11 +553,10 @@ void GSTexture12::Unmap()
m_map_area.height(), m_map_level);
// first time the texture is used? don't leave it undefined
if (m_texture.GetState() == D3D12_RESOURCE_STATE_COMMON)
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
else if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
m_texture.TransitionSubresourceToState(
cmdlist, m_map_level, m_texture.GetState(), D3D12_RESOURCE_STATE_COPY_DEST);
if (m_resource_state == D3D12_RESOURCE_STATE_COMMON)
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_COPY_DEST);
else if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
TransitionSubresourceToState(cmdlist, m_map_level, m_resource_state, D3D12_RESOURCE_STATE_COPY_DEST);
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
if (m_type == Type::RenderTarget)
@ -340,20 +574,19 @@ void GSTexture12::Unmap()
srcloc.PlacedFootprint.Footprint.Width = width;
srcloc.PlacedFootprint.Footprint.Height = height;
srcloc.PlacedFootprint.Footprint.Depth = 1;
srcloc.PlacedFootprint.Footprint.Format = m_texture.GetFormat();
srcloc.PlacedFootprint.Footprint.Format = m_dxgi_format;
srcloc.PlacedFootprint.Footprint.RowPitch = pitch;
D3D12_TEXTURE_COPY_LOCATION dstloc;
dstloc.pResource = m_texture.GetResource();
dstloc.pResource = m_resource.get();
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstloc.SubresourceIndex = m_map_level;
const D3D12_BOX srcbox{0u, 0u, 0u, width, height, 1};
cmdlist->CopyTextureRegion(&dstloc, m_map_area.x, m_map_area.y, 0, &srcloc, &srcbox);
if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
m_texture.TransitionSubresourceToState(
cmdlist, m_map_level, D3D12_RESOURCE_STATE_COPY_DEST, m_texture.GetState());
if (m_resource_state != D3D12_RESOURCE_STATE_COPY_DEST)
TransitionSubresourceToState(cmdlist, m_map_level, D3D12_RESOURCE_STATE_COPY_DEST, m_resource_state);
if (m_type == Type::Texture)
m_needs_mipmaps_generated |= (m_map_level == 0);
@ -372,7 +605,7 @@ void GSTexture12::GenerateMipmap()
const int dst_height = std::max<int>(m_size.y >> dst_level, 1);
GSDevice12::GetInstance()->RenderTextureMipmap(
m_texture, dst_level, dst_width, dst_height, src_level, src_width, src_height);
this, dst_level, dst_width, dst_height, src_level, src_width, src_height);
}
SetUsedThisCommandBuffer();
@ -381,16 +614,39 @@ void GSTexture12::GenerateMipmap()
void GSTexture12::Swap(GSTexture* tex)
{
GSTexture::Swap(tex);
std::swap(m_texture, static_cast<GSTexture12*>(tex)->m_texture);
std::swap(m_resource, static_cast<GSTexture12*>(tex)->m_resource);
std::swap(m_allocation, static_cast<GSTexture12*>(tex)->m_allocation);
std::swap(m_srv_descriptor, static_cast<GSTexture12*>(tex)->m_srv_descriptor);
std::swap(m_write_descriptor, static_cast<GSTexture12*>(tex)->m_write_descriptor);
std::swap(m_write_descriptor_type, static_cast<GSTexture12*>(tex)->m_write_descriptor_type);
std::swap(m_dxgi_format, static_cast<GSTexture12*>(tex)->m_dxgi_format);
std::swap(m_resource_state, static_cast<GSTexture12*>(tex)->m_resource_state);
std::swap(m_use_fence_counter, static_cast<GSTexture12*>(tex)->m_use_fence_counter);
std::swap(m_clear_value, static_cast<GSTexture12*>(tex)->m_clear_value);
std::swap(m_map_area, static_cast<GSTexture12*>(tex)->m_map_area);
std::swap(m_map_level, static_cast<GSTexture12*>(tex)->m_map_level);
std::swap(m_map_area, static_cast<GSTexture12*>(tex)->m_map_area);
}
void GSTexture12::TransitionToState(D3D12_RESOURCE_STATES state)
{
m_texture.TransitionToState(g_d3d12_context->GetCommandList(), state);
TransitionToState(g_d3d12_context->GetCommandList(), state);
}
void GSTexture12::TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state)
{
if (m_resource_state == state)
return;
TransitionSubresourceToState(cmdlist, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, m_resource_state, state);
m_resource_state = state;
}
void GSTexture12::TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, int level,
D3D12_RESOURCE_STATES before_state, D3D12_RESOURCE_STATES after_state) const
{
const D3D12_RESOURCE_BARRIER barrier = {D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE,
{{m_resource.get(), static_cast<u32>(level), before_state, after_state}}};
cmdlist->ResourceBarrier(1, &barrier);
}
void GSTexture12::CommitClear()
@ -407,14 +663,14 @@ void GSTexture12::CommitClear(ID3D12GraphicsCommandList* cmdlist)
{
if (IsDepthStencil())
{
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE);
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_DEPTH_WRITE);
cmdlist->ClearDepthStencilView(
m_texture.GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr);
GetWriteDescriptor(), D3D12_CLEAR_FLAG_DEPTH, m_clear_value.depth, 0, 0, nullptr);
}
else
{
m_texture.TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(m_texture.GetWriteDescriptor(), m_clear_value.color, 0, nullptr);
TransitionToState(cmdlist, D3D12_RESOURCE_STATE_RENDER_TARGET);
cmdlist->ClearRenderTargetView(GetWriteDescriptor(), m_clear_value.color, 0, nullptr);
}
SetState(GSTexture::State::Dirty);
@ -498,7 +754,7 @@ void GSDownloadTexture12::CopyFromTexture(
dstloc.pResource = m_buffer.get();
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dstloc.PlacedFootprint.Offset = copy_offset;
dstloc.PlacedFootprint.Footprint.Format = tex12->GetNativeFormat();
dstloc.PlacedFootprint.Footprint.Format = tex12->GetDXGIFormat();
dstloc.PlacedFootprint.Footprint.Width = drc.width();
dstloc.PlacedFootprint.Footprint.Height = drc.height();
dstloc.PlacedFootprint.Footprint.Depth = 1;
@ -506,8 +762,7 @@ void GSDownloadTexture12::CopyFromTexture(
const D3D12_RESOURCE_STATES old_layout = tex12->GetResourceState();
if (old_layout != D3D12_RESOURCE_STATE_COPY_SOURCE)
tex12->GetTexture().TransitionSubresourceToState(
cmdlist, src_level, old_layout, D3D12_RESOURCE_STATE_COPY_SOURCE);
tex12->TransitionSubresourceToState(cmdlist, src_level, old_layout, D3D12_RESOURCE_STATE_COPY_SOURCE);
// TODO: Rules for depth buffers here?
const D3D12_BOX srcbox{static_cast<UINT>(src.left), static_cast<UINT>(src.top), 0u, static_cast<UINT>(src.right),
@ -515,8 +770,7 @@ void GSDownloadTexture12::CopyFromTexture(
cmdlist->CopyTextureRegion(&dstloc, 0, 0, 0, &srcloc, &srcbox);
if (old_layout != D3D12_RESOURCE_STATE_COPY_SOURCE)
tex12->GetTexture().TransitionSubresourceToState(
cmdlist, src_level, D3D12_RESOURCE_STATE_COPY_SOURCE, old_layout);
tex12->TransitionSubresourceToState(cmdlist, src_level, D3D12_RESOURCE_STATE_COPY_SOURCE, old_layout);
m_copy_fence_value = g_d3d12_context->GetCurrentFenceValue();
m_needs_flush = true;

View File

@ -1,5 +1,5 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2021 PCSX2 Dev Team
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
@ -18,23 +18,35 @@
#include "GS/GS.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "GS/Renderers/DX12/D3D12Context.h"
#include "GS/Renderers/DX12/D3D12Texture.h"
#include "GS/Renderers/DX12/D3D12DescriptorHeapManager.h"
#include "common/RedtapeWilCom.h"
#include <limits>
namespace D3D12MA
{
class Allocation;
}
class GSTexture12 final : public GSTexture
{
public:
GSTexture12(Type type, Format format, D3D12Texture texture);
~GSTexture12() override;
static std::unique_ptr<GSTexture12> Create(Type type, u32 width, u32 height, u32 levels, Format format,
DXGI_FORMAT d3d_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format);
static std::unique_ptr<GSTexture12> Create(Type type, Format format, int width, int height, int levels,
DXGI_FORMAT dxgi_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format, DXGI_FORMAT dsv_format,
DXGI_FORMAT uav_format);
static std::unique_ptr<GSTexture12> Adopt(wil::com_ptr_nothrow<ID3D12Resource> resource, Type type, Format format,
int width, int height, int levels, DXGI_FORMAT dxgi_format, DXGI_FORMAT srv_format, DXGI_FORMAT rtv_format,
DXGI_FORMAT dsv_format, DXGI_FORMAT uav_format, D3D12_RESOURCE_STATES resource_state);
__fi D3D12Texture& GetTexture() { return m_texture; }
__fi const D3D12DescriptorHandle& GetSRVDescriptor() const { return m_texture.GetSRVDescriptor(); }
__fi const D3D12DescriptorHandle& GetRTVOrDSVHandle() const { return m_texture.GetWriteDescriptor(); }
__fi D3D12_RESOURCE_STATES GetResourceState() const { return m_texture.GetState(); }
__fi DXGI_FORMAT GetNativeFormat() const { return m_texture.GetFormat(); }
__fi ID3D12Resource* GetResource() const { return m_texture.GetResource(); }
__fi const D3D12DescriptorHandle& GetSRVDescriptor() const { return m_srv_descriptor; }
__fi const D3D12DescriptorHandle& GetWriteDescriptor() const { return m_write_descriptor; }
__fi const D3D12DescriptorHandle& GetUAVDescriptor() const { return m_uav_descriptor; }
__fi D3D12_RESOURCE_STATES GetResourceState() const { return m_resource_state; }
__fi DXGI_FORMAT GetDXGIFormat() const { return m_dxgi_format; }
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
void* GetNativeHandle() const override;
@ -48,22 +60,55 @@ public:
void CommitClear();
void CommitClear(ID3D12GraphicsCommandList* cmdlist);
void Destroy(bool defer = true);
void TransitionToState(ID3D12GraphicsCommandList* cmdlist, D3D12_RESOURCE_STATES state);
void TransitionSubresourceToState(ID3D12GraphicsCommandList* cmdlist, int level, D3D12_RESOURCE_STATES before_state,
D3D12_RESOURCE_STATES after_state) const;
// Call when the texture is bound to the pipeline, or read from in a copy.
__fi void SetUsedThisCommandBuffer() { m_use_fence_counter = g_d3d12_context->GetCurrentFenceValue(); }
private:
enum class WriteDescriptorType : u8
{
None,
RTV,
DSV
};
GSTexture12(Type type, Format format, int width, int height, int levels, DXGI_FORMAT dxgi_format,
wil::com_ptr_nothrow<ID3D12Resource> resource, wil::com_ptr_nothrow<D3D12MA::Allocation> allocation,
const D3D12DescriptorHandle& srv_descriptor, const D3D12DescriptorHandle& write_descriptor,
const D3D12DescriptorHandle& uav_descriptor, WriteDescriptorType wdtype, D3D12_RESOURCE_STATES resource_state);
static bool CreateSRVDescriptor(
ID3D12Resource* resource, u32 levels, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
static bool CreateRTVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
static bool CreateDSVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
static bool CreateUAVDescriptor(ID3D12Resource* resource, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
ID3D12GraphicsCommandList* GetCommandBufferForUpdate();
ID3D12Resource* AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 height) const;
void CopyTextureDataForUpload(void* dst, const void* src, u32 pitch, u32 upload_pitch, u32 height) const;
D3D12Texture m_texture;
wil::com_ptr_nothrow<ID3D12Resource> m_resource;
wil::com_ptr_nothrow<D3D12MA::Allocation> m_allocation;
D3D12DescriptorHandle m_srv_descriptor = {};
D3D12DescriptorHandle m_write_descriptor = {};
D3D12DescriptorHandle m_uav_descriptor = {};
WriteDescriptorType m_write_descriptor_type = WriteDescriptorType::None;
DXGI_FORMAT m_dxgi_format = DXGI_FORMAT_UNKNOWN;
D3D12_RESOURCE_STATES m_resource_state = D3D12_RESOURCE_STATE_COMMON;
// Contains the fence counter when the texture was last used.
// When this matches the current fence counter, the texture was used this command buffer.
u64 m_use_fence_counter = 0;
int m_map_level = std::numeric_limits<int>::max();
GSVector4i m_map_area = GSVector4i::zero();
u32 m_map_level = UINT32_MAX;
};
class GSDownloadTexture12 final : public GSDownloadTexture

View File

@ -420,19 +420,26 @@ GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip)
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
// Swap chain images start in undefined
VKTexture& swap_chain_texture = m_swap_chain->GetCurrentTexture();
swap_chain_texture.OverrideImageLayout(VK_IMAGE_LAYOUT_UNDEFINED);
swap_chain_texture.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
GSTextureVK* swap_chain_texture = m_swap_chain->GetCurrentTexture();
swap_chain_texture->OverrideImageLayout(GSTextureVK::Layout::Undefined);
swap_chain_texture->TransitionToLayout(cmdbuffer, GSTextureVK::Layout::ColorAttachment);
const VkFramebuffer fb = swap_chain_texture->GetFramebuffer(false);
if (fb == VK_NULL_HANDLE)
return GSDevice::PresentResult::FrameSkipped;
const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr,
m_swap_chain->GetClearRenderPass(), m_swap_chain->GetCurrentFramebuffer(),
{{0, 0}, {swap_chain_texture.GetWidth(), swap_chain_texture.GetHeight()}}, 1u, &s_present_clear_color};
g_vulkan_context->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};
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
const VkViewport vp{0.0f, 0.0f, static_cast<float>(swap_chain_texture.GetWidth()),
static_cast<float>(swap_chain_texture.GetHeight()), 0.0f, 1.0f};
const VkViewport vp{0.0f, 0.0f, static_cast<float>(swap_chain_texture->GetWidth()),
static_cast<float>(swap_chain_texture->GetHeight()), 0.0f, 1.0f};
const VkRect2D scissor{
{0, 0}, {static_cast<u32>(swap_chain_texture.GetWidth()), static_cast<u32>(swap_chain_texture.GetHeight())}};
{0, 0}, {static_cast<u32>(swap_chain_texture->GetWidth()), static_cast<u32>(swap_chain_texture->GetHeight())}};
vkCmdSetViewport(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &vp);
vkCmdSetScissor(g_vulkan_context->GetCurrentCommandBuffer(), 0, 1, &scissor);
return PresentResult::OK;
@ -444,7 +451,7 @@ void GSDeviceVK::EndPresent()
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
vkCmdEndRenderPass(g_vulkan_context->GetCurrentCommandBuffer());
m_swap_chain->GetCurrentTexture().TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
m_swap_chain->GetCurrentTexture()->TransitionToLayout(cmdbuffer, GSTextureVK::Layout::PresentSrc);
g_perfmon.Put(GSPerfMon::RenderPasses, 1);
g_vulkan_context->SubmitCommandBuffer(m_swap_chain.get(), !m_swap_chain->IsPresentModeSynchronizing());
@ -811,7 +818,7 @@ void GSDeviceVK::ClearStencil(GSTexture* t, u8 c)
EndRenderPass();
static_cast<GSTextureVK*>(t)->TransitionToLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
static_cast<GSTextureVK*>(t)->TransitionToLayout(GSTextureVK::Layout::ClearDst);
const VkClearDepthStencilValue dsv{0.0f, static_cast<u32>(c)};
const VkImageSubresourceRange srr{VK_IMAGE_ASPECT_STENCIL_BIT, 0u, 1u, 0u, 1u};
@ -819,7 +826,7 @@ void GSDeviceVK::ClearStencil(GSTexture* t, u8 c)
vkCmdClearDepthStencilImage(g_vulkan_context->GetCurrentCommandBuffer(), static_cast<GSTextureVK*>(t)->GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &dsv, 1, &srr);
static_cast<GSTextureVK*>(t)->TransitionToLayout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
static_cast<GSTextureVK*>(t)->TransitionToLayout(GSTextureVK::Layout::DepthStencilAttachment);
}
VkFormat GSDeviceVK::LookupNativeFormat(GSTexture::Format format) const
@ -849,13 +856,13 @@ GSTexture* GSDeviceVK::CreateSurface(GSTexture::Type type, int width, int height
const u32 clamped_width = static_cast<u32>(std::clamp<int>(width, 1, g_vulkan_context->GetMaxImageDimension2D()));
const u32 clamped_height = static_cast<u32>(std::clamp<int>(height, 1, g_vulkan_context->GetMaxImageDimension2D()));
std::unique_ptr<GSTexture> tex(GSTextureVK::Create(type, clamped_width, clamped_height, levels, format, LookupNativeFormat(format)));
std::unique_ptr<GSTexture> tex(GSTextureVK::Create(type, format, clamped_width, clamped_height, levels));
if (!tex)
{
// We're probably out of vram, try flushing the command buffer to release pending textures.
PurgePool();
ExecuteCommandBufferAndRestartRenderPass(true, "Couldn't allocate texture.");
tex = GSTextureVK::Create(type, clamped_width, clamped_height, levels, format, LookupNativeFormat(format));
tex = GSTextureVK::Create(type, format, clamped_width, clamped_height, levels);
}
return tex.release();
@ -941,15 +948,15 @@ void GSDeviceVK::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
EndRenderPass();
dTexVK->TransitionToLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
dTexVK->TransitionToLayout(GSTextureVK::Layout::TransferDst);
dTexVK->SetUsedThisCommandBuffer();
sTexVK->TransitionToLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
sTexVK->TransitionToLayout(GSTextureVK::Layout::TransferSrc);
sTexVK->SetUsedThisCommandBuffer();
#ifdef PCSX2_DEVBUILD
// ensure we don't leave this bound later on, debug layer gets cranky
if (m_tfx_textures[0] == sTexVK->GetTexturePtr())
if (m_tfx_textures[0] == sTexVK)
PSSetShaderResource(0, nullptr, false);
#endif
@ -1013,10 +1020,10 @@ void GSDeviceVK::DrawMultiStretchRects(
{
GSTextureVK* const stex = static_cast<GSTextureVK*>(rects[i].src);
stex->CommitClear();
if (stex->GetLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
if (stex->GetLayout() != GSTextureVK::Layout::ShaderReadOnly)
{
EndRenderPass();
stex->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
stex->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
}
@ -1140,7 +1147,7 @@ void GSDeviceVK::BeginRenderPassForStretchRect(
else
{
// integer formats, etc
const VkRenderPass rp = g_vulkan_context->GetRenderPass(dTex->GetNativeFormat(), VK_FORMAT_UNDEFINED,
const VkRenderPass rp = g_vulkan_context->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)
{
@ -1156,11 +1163,11 @@ void GSDeviceVK::BeginRenderPassForStretchRect(
void GSDeviceVK::DoStretchRect(GSTextureVK* sTex, const GSVector4& sRect, GSTextureVK* dTex, const GSVector4& dRect,
VkPipeline pipeline, bool linear, bool allow_discard)
{
if (sTex->GetLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
if (sTex->GetLayout() != GSTextureVK::Layout::ShaderReadOnly)
{
// can't transition in a render pass
EndRenderPass();
sTex->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
sTex->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
SetUtilityTexture(sTex, linear ? m_linear_sampler : m_point_sampler);
@ -1220,11 +1227,11 @@ void GSDeviceVK::BlitRect(GSTexture* sTex, const GSVector4i& sRect, u32 sLevel,
EndRenderPass();
sTexVK->TransitionToLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
dTexVK->TransitionToLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
sTexVK->TransitionToLayout(GSTextureVK::Layout::TransferSrc);
dTexVK->TransitionToLayout(GSTextureVK::Layout::TransferDst);
// ensure we don't leave this bound later on
if (m_tfx_textures[0] == sTexVK->GetTexturePtr())
if (m_tfx_textures[0] == sTexVK)
PSSetShaderResource(0, nullptr, false);
pxAssert(
@ -1234,9 +1241,9 @@ 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(g_vulkan_context->GetCurrentCommandBuffer(), sTexVK->GetTexture().GetImage(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dTexVK->GetTexture().GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
&ib, linear ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
vkCmdBlitImage(g_vulkan_context->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)
@ -1295,9 +1302,9 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
EndRenderPass();
// transition everything before starting the new render pass
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(GSTextureVK::Layout::ColorAttachment);
if (sTex[0])
static_cast<GSTextureVK*>(sTex[0])->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
static_cast<GSTextureVK*>(sTex[0])->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
const GSVector2i dsize(dTex->GetSize());
const GSVector4i darea(0, 0, dsize.x, dsize.y);
@ -1308,7 +1315,7 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
// Note: value outside of dRect must contains the background color (c)
if (sTex[1]->GetState() == GSTexture::State::Dirty)
{
static_cast<GSTextureVK*>(sTex[1])->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
static_cast<GSTextureVK*>(sTex[1])->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
OMSetRenderTargets(dTex, nullptr, darea);
SetUtilityTexture(sTex[1], sampler);
BeginClearRenderPass(m_utility_color_render_pass_clear, darea, c);
@ -1341,7 +1348,7 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
if (sTex[0] == sTex[2])
{
// need a barrier here because of the render pass
static_cast<GSTextureVK*>(sTex[2])->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
static_cast<GSTextureVK*>(sTex[2])->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
}
@ -1383,12 +1390,12 @@ void GSDeviceVK::DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex,
// this texture is going to get used as an input, so make sure we don't read undefined data
static_cast<GSTextureVK*>(dTex)->CommitClear();
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
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)
{
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(GSTextureVK::Layout::ColorAttachment);
const GSVector4i rc = GSVector4i(dRect);
const GSVector4i dtex_rc = dTex->GetRect();
@ -1402,7 +1409,7 @@ void GSDeviceVK::DoInterlace(GSTexture* sTex, const GSVector4& sRect, GSTexture*
DrawStretchRect(sRect, dRect, dTex->GetSize());
EndRenderPass();
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
void GSDeviceVK::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float params[4])
@ -1419,7 +1426,7 @@ void GSDeviceVK::DoShadeBoost(GSTexture* sTex, GSTexture* dTex, const float para
DrawStretchRect(sRect, GSVector4(dRect), dTex->GetSize());
EndRenderPass();
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
void GSDeviceVK::DoFXAA(GSTexture* sTex, GSTexture* dTex)
@ -1435,7 +1442,7 @@ void GSDeviceVK::DoFXAA(GSTexture* sTex, GSTexture* dTex)
DrawStretchRect(sRect, GSVector4(dRect), dTex->GetSize());
EndRenderPass();
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
static_cast<GSTextureVK*>(dTex)->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
void GSDeviceVK::IASetVertexBuffer(const void* vertex, size_t stride, size_t count)
@ -1506,18 +1513,18 @@ void GSDeviceVK::OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector
if (vkRt)
{
vkRt->TransitionToLayout((feedback_loop & FeedbackLoopFlag_ReadAndWriteRT) ?
VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
GSTextureVK::Layout::FeedbackLoop :
GSTextureVK::Layout::ColorAttachment);
}
if (vkDs)
{
// need to update descriptors to reflect the new layout
if ((feedback_loop & FeedbackLoopFlag_ReadDS) && vkDs->GetLayout() != VK_IMAGE_LAYOUT_GENERAL)
if ((feedback_loop & FeedbackLoopFlag_ReadDS) && vkDs->GetLayout() != GSTextureVK::Layout::FeedbackLoop)
m_dirty_flags |= DIRTY_FLAG_TFX_SAMPLERS_DS;
vkDs->TransitionToLayout((feedback_loop & FeedbackLoopFlag_ReadDS) ?
VK_IMAGE_LAYOUT_GENERAL :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
GSTextureVK::Layout::FeedbackLoop :
GSTextureVK::Layout::DepthStencilAttachment);
}
}
@ -1653,21 +1660,18 @@ VkShaderModule GSDeviceVK::GetUtilityFragmentShader(const std::string& source, c
bool GSDeviceVK::CreateNullTexture()
{
if (!m_null_texture.Create(1, 1, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D,
VK_IMAGE_TILING_OPTIMAL,
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))
{
m_null_texture = GSTextureVK::Create(GSTexture::Type::RenderTarget, GSTexture::Format::Color, 1, 1, 1);
if (!m_null_texture)
return false;
}
const VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
const VkImageSubresourceRange srr{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
const VkClearColorValue ccv{};
m_null_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkCmdClearColorImage(cmdbuf, m_null_texture.GetImage(), m_null_texture.GetLayout(), &ccv, 1, &srr);
m_null_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_GENERAL);
Vulkan::SetObjectName(g_vulkan_context->GetDevice(), m_null_texture.GetImage(), "Null texture");
Vulkan::SetObjectName(g_vulkan_context->GetDevice(), m_null_texture.GetView(), "Null texture view");
m_null_texture->TransitionToLayout(cmdbuf, GSTextureVK::Layout::ClearDst);
vkCmdClearColorImage(cmdbuf, m_null_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &ccv, 1, &srr);
m_null_texture->TransitionToLayout(cmdbuf, GSTextureVK::Layout::General);
Vulkan::SetObjectName(g_vulkan_context->GetDevice(), m_null_texture->GetImage(), "Null texture");
Vulkan::SetObjectName(g_vulkan_context->GetDevice(), m_null_texture->GetView(), "Null texture view");
return true;
}
@ -2040,9 +2044,8 @@ bool GSDeviceVK::CompileConvertPipelines()
bool GSDeviceVK::CompilePresentPipelines()
{
// we may not have a swap chain if running in headless mode.
m_swap_chain_render_pass = m_swap_chain ?
m_swap_chain->GetClearRenderPass() :
g_vulkan_context->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_UNDEFINED);
m_swap_chain_render_pass = g_vulkan_context->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;
@ -2439,12 +2442,9 @@ void GSDeviceVK::RenderImGui()
SetScissor(GSVector4i(clip).max_i32(GSVector4i::zero()));
// Since we don't have the GSTexture...
VKTexture* tex = static_cast<VKTexture*>(pcmd->GetTexID());
if (m_utility_texture != tex)
{
m_utility_texture = tex;
m_dirty_flags |= DIRTY_FLAG_UTILITY_TEXTURE;
}
GSTextureVK* tex = static_cast<GSTextureVK*>(pcmd->GetTexID());
if (tex)
SetUtilityTexture(tex, m_linear_sampler);
if (ApplyUtilityState())
{
@ -2467,13 +2467,13 @@ void GSDeviceVK::RenderBlankFrame()
}
VkCommandBuffer cmdbuffer = g_vulkan_context->GetCurrentCommandBuffer();
VKTexture& sctex = m_swap_chain->GetCurrentTexture();
sctex.TransitionToLayout(cmdbuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
GSTextureVK* sctex = m_swap_chain->GetCurrentTexture();
sctex->TransitionToLayout(cmdbuffer, GSTextureVK::Layout::TransferDst);
constexpr VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
vkCmdClearColorImage(cmdbuffer, sctex.GetImage(), sctex.GetLayout(), &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, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
m_swap_chain->GetCurrentTexture()->TransitionToLayout(cmdbuffer, GSTextureVK::Layout::PresentSrc);
g_vulkan_context->SubmitCommandBuffer(m_swap_chain.get(), !m_swap_chain->IsPresentModeSynchronizing());
g_vulkan_context->MoveToNextCommandBuffer();
@ -2492,29 +2492,13 @@ bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, cons
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);
sTexVK->TransitionToLayout(cmdbuf, GSTextureVK::Layout::ShaderReadOnly);
dTexVK->TransitionToLayout(cmdbuf, GSTextureVK::Layout::ComputeReadWriteImage);
// 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.AddImageDescriptorWrite(ds, 0, sTexVK->GetView(), sTexVK->GetVkLayout());
dsub.AddStorageImageDescriptorWrite(ds, 1, dTexVK->GetView(), dTexVK->GetVkLayout());
dsub.Update(g_vulkan_context->GetDevice(), false);
// the actual meat and potatoes! only four commands.
@ -2527,22 +2511,7 @@ bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, cons
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);
dTexVK->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
// all done!
return true;
@ -2551,8 +2520,8 @@ bool GSDeviceVK::DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, cons
void GSDeviceVK::DestroyResources()
{
g_vulkan_context->ExecuteCommandBuffer(VKContext::WaitType::Sleep);
if (m_tfx_descriptor_sets[0] != VK_NULL_HANDLE)
g_vulkan_context->FreeGlobalDescriptorSet(m_tfx_descriptor_sets[0]);
if (m_tfx_ubo_descriptor_set != VK_NULL_HANDLE)
g_vulkan_context->FreeGlobalDescriptorSet(m_tfx_ubo_descriptor_set);
for (auto& it : m_tfx_pipelines)
Vulkan::SafeDestroyPipeline(it.second);
@ -2627,7 +2596,11 @@ void GSDeviceVK::DestroyResources()
Vulkan::SafeDestroyPipelineLayout(m_utility_pipeline_layout);
Vulkan::SafeDestroyDescriptorSetLayout(m_utility_ds_layout);
m_null_texture.Destroy(false);
if (m_null_texture)
{
m_null_texture->Destroy(false);
m_null_texture.reset();
}
}
VkShaderModule GSDeviceVK::GetTFXVertexShader(GSHWDrawConfig::VSSelector sel)
@ -2882,9 +2855,9 @@ void GSDeviceVK::InitializeState()
m_current_render_pass = VK_NULL_HANDLE;
for (u32 i = 0; i < NUM_TFX_TEXTURES; i++)
m_tfx_textures[i] = &m_null_texture;
m_tfx_textures[i] = m_null_texture.get();
m_utility_texture = &m_null_texture;
m_utility_texture = m_null_texture.get();
m_point_sampler = GetSampler(GSHWDrawConfig::SamplerSelector::Point());
if (m_point_sampler)
@ -2905,20 +2878,20 @@ bool GSDeviceVK::CreatePersistentDescriptorSets()
Vulkan::DescriptorSetUpdateBuilder dsub;
// Allocate UBO descriptor sets for TFX.
m_tfx_descriptor_sets[0] = g_vulkan_context->AllocatePersistentDescriptorSet(m_tfx_ubo_ds_layout);
if (m_tfx_descriptor_sets[0] == VK_NULL_HANDLE)
m_tfx_ubo_descriptor_set = g_vulkan_context->AllocatePersistentDescriptorSet(m_tfx_ubo_ds_layout);
if (m_tfx_ubo_descriptor_set == VK_NULL_HANDLE)
return false;
dsub.AddBufferDescriptorWrite(m_tfx_descriptor_sets[0], 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
dsub.AddBufferDescriptorWrite(m_tfx_ubo_descriptor_set, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
m_vertex_uniform_stream_buffer.GetBuffer(), 0, sizeof(GSHWDrawConfig::VSConstantBuffer));
dsub.AddBufferDescriptorWrite(m_tfx_descriptor_sets[0], 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
dsub.AddBufferDescriptorWrite(m_tfx_ubo_descriptor_set, 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
m_fragment_uniform_stream_buffer.GetBuffer(), 0, sizeof(GSHWDrawConfig::PSConstantBuffer));
if (m_features.vs_expand)
{
dsub.AddBufferDescriptorWrite(m_tfx_descriptor_sets[0], 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
dsub.AddBufferDescriptorWrite(m_tfx_ubo_descriptor_set, 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
m_vertex_stream_buffer.GetBuffer(), 0, VERTEX_BUFFER_SIZE);
}
dsub.Update(dev);
Vulkan::SetObjectName(dev, m_tfx_descriptor_sets[0], "Persistent TFX UBO set");
Vulkan::SetObjectName(dev, m_tfx_ubo_descriptor_set, "Persistent TFX UBO set");
return true;
}
@ -3003,16 +2976,16 @@ void GSDeviceVK::InvalidateCachedState()
m_dirty_flags |= DIRTY_FLAG_INDEX_BUFFER;
for (u32 i = 0; i < NUM_TFX_TEXTURES; i++)
m_tfx_textures[i] = &m_null_texture;
m_utility_texture = &m_null_texture;
m_tfx_textures[i] = m_null_texture.get();
m_utility_texture = m_null_texture.get();
m_current_framebuffer = VK_NULL_HANDLE;
m_current_render_target = nullptr;
m_current_depth_target = nullptr;
m_current_framebuffer_feedback_loop = FeedbackLoopFlag_None;
m_current_pipeline_layout = PipelineLayout::Undefined;
m_tfx_descriptor_sets[1] = VK_NULL_HANDLE;
m_tfx_descriptor_sets[2] = VK_NULL_HANDLE;
m_tfx_texture_descriptor_set = VK_NULL_HANDLE;
m_tfx_rt_descriptor_set = VK_NULL_HANDLE;
m_utility_descriptor_set = VK_NULL_HANDLE;
}
@ -3048,33 +3021,31 @@ void GSDeviceVK::SetBlendConstants(u8 color)
void GSDeviceVK::PSSetShaderResource(int i, GSTexture* sr, bool check_state)
{
const VKTexture* tex;
if (sr)
GSTextureVK* vkTex = static_cast<GSTextureVK*>(sr);
if (vkTex)
{
GSTextureVK* vkTex = static_cast<GSTextureVK*>(sr);
if (check_state)
{
if (vkTex->GetTexture().GetLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && InRenderPass())
if (vkTex->GetLayout() != GSTextureVK::Layout::ShaderReadOnly && InRenderPass())
{
GL_INS("Ending render pass due to resource transition");
EndRenderPass();
}
vkTex->CommitClear();
vkTex->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vkTex->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
vkTex->SetUsedThisCommandBuffer();
tex = vkTex->GetTexturePtr();
}
else
{
tex = &m_null_texture;
vkTex = m_null_texture.get();
}
if (m_tfx_textures[i] == tex)
if (m_tfx_textures[i] == vkTex)
return;
m_tfx_textures[i] = tex;
m_tfx_textures[i] = vkTex;
m_dirty_flags |= (i < 2) ? DIRTY_FLAG_TFX_SAMPLERS_DS : DIRTY_FLAG_TFX_RT_TEXTURE_DS;
}
@ -3091,24 +3062,22 @@ void GSDeviceVK::PSSetSampler(GSHWDrawConfig::SamplerSelector sel)
void GSDeviceVK::SetUtilityTexture(GSTexture* tex, VkSampler sampler)
{
const VKTexture* vtex;
if (tex)
GSTextureVK* vkTex = static_cast<GSTextureVK*>(tex);
if (vkTex)
{
GSTextureVK* vkTex = static_cast<GSTextureVK*>(tex);
vkTex->CommitClear();
vkTex->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
vkTex->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
vkTex->SetUsedThisCommandBuffer();
vtex = vkTex->GetTexturePtr();
}
else
{
vtex = &m_null_texture;
vkTex = m_null_texture.get();
}
if (m_utility_texture == vtex && m_utility_sampler == sampler)
if (m_utility_texture == vkTex && m_utility_sampler == sampler)
return;
m_utility_texture = vtex;
m_utility_texture = vkTex;
m_utility_sampler = sampler;
m_dirty_flags |= DIRTY_FLAG_UTILITY_TEXTURE;
}
@ -3121,18 +3090,17 @@ void GSDeviceVK::SetUtilityPushConstants(const void* data, u32 size)
void GSDeviceVK::UnbindTexture(GSTextureVK* tex)
{
const VKTexture* vtex = tex->GetTexturePtr();
for (u32 i = 0; i < NUM_TFX_TEXTURES; i++)
{
if (m_tfx_textures[i] == vtex)
if (m_tfx_textures[i] == tex)
{
m_tfx_textures[i] = &m_null_texture;
m_tfx_textures[i] = m_null_texture.get();
m_dirty_flags |= (i < 2) ? DIRTY_FLAG_TFX_SAMPLERS_DS : DIRTY_FLAG_TFX_RT_TEXTURE_DS;
}
}
if (m_utility_texture == vtex)
if (m_utility_texture == tex)
{
m_utility_texture = &m_null_texture;
m_utility_texture = m_null_texture.get();
m_dirty_flags |= DIRTY_FLAG_UTILITY_TEXTURE;
}
if (m_current_render_target == tex || m_current_depth_target == tex)
@ -3333,18 +3301,18 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
Vulkan::DescriptorSetUpdateBuilder dsub;
u32 dirty_descriptor_set_start = NUM_TFX_DESCRIPTOR_SETS;
u32 dirty_descriptor_set_end = 0;
std::array<VkDescriptorSet, NUM_TFX_DESCRIPTOR_SETS> dsets;
u32 num_dsets = 0;
u32 start_dset = 0;
const bool layout_changed = (m_current_pipeline_layout != PipelineLayout::TFX);
if (flags & DIRTY_FLAG_TFX_DYNAMIC_OFFSETS)
{
dirty_descriptor_set_start = 0;
}
if (!layout_changed && flags & DIRTY_FLAG_TFX_DYNAMIC_OFFSETS)
dsets[num_dsets++] = m_tfx_ubo_descriptor_set;
if ((flags & DIRTY_FLAG_TFX_SAMPLERS_DS) || m_tfx_descriptor_sets[1] == VK_NULL_HANDLE)
if ((flags & DIRTY_FLAG_TFX_SAMPLERS_DS) || m_tfx_texture_descriptor_set == VK_NULL_HANDLE)
{
VkDescriptorSet ds = g_vulkan_context->AllocateDescriptorSet(m_tfx_sampler_ds_layout);
if (ds == VK_NULL_HANDLE)
m_tfx_texture_descriptor_set = g_vulkan_context->AllocateDescriptorSet(m_tfx_sampler_ds_layout);
if (m_tfx_texture_descriptor_set == VK_NULL_HANDLE)
{
if (already_execed)
{
@ -3356,20 +3324,23 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
return ApplyTFXState(true);
}
dsub.AddCombinedImageSamplerDescriptorWrite(
ds, 0, m_tfx_textures[0]->GetView(), m_tfx_sampler, m_tfx_textures[0]->GetLayout());
dsub.AddImageDescriptorWrite(ds, 1, m_tfx_textures[1]->GetView(), m_tfx_textures[1]->GetLayout());
dsub.AddCombinedImageSamplerDescriptorWrite(m_tfx_texture_descriptor_set, 0, m_tfx_textures[0]->GetView(),
m_tfx_sampler, m_tfx_textures[0]->GetVkLayout());
dsub.AddImageDescriptorWrite(
m_tfx_texture_descriptor_set, 1, m_tfx_textures[1]->GetView(), m_tfx_textures[1]->GetVkLayout());
dsub.Update(dev);
m_tfx_descriptor_sets[1] = ds;
dirty_descriptor_set_start = std::min(dirty_descriptor_set_start, 1u);
dirty_descriptor_set_end = 1u;
if (!layout_changed)
{
start_dset = (num_dsets == 0) ? TFX_DESCRIPTOR_SET_TEXTURES : start_dset;
dsets[num_dsets++] = m_tfx_texture_descriptor_set;
}
}
if ((flags & DIRTY_FLAG_TFX_RT_TEXTURE_DS) || m_tfx_descriptor_sets[2] == VK_NULL_HANDLE)
if ((flags & DIRTY_FLAG_TFX_RT_TEXTURE_DS) || m_tfx_rt_descriptor_set == VK_NULL_HANDLE)
{
VkDescriptorSet ds = g_vulkan_context->AllocateDescriptorSet(m_tfx_rt_texture_ds_layout);
if (ds == VK_NULL_HANDLE)
m_tfx_rt_descriptor_set = g_vulkan_context->AllocateDescriptorSet(m_tfx_rt_texture_ds_layout);
if (m_tfx_rt_descriptor_set == VK_NULL_HANDLE)
{
if (already_execed)
{
@ -3384,35 +3355,45 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
if (m_features.texture_barrier)
{
dsub.AddInputAttachmentDescriptorWrite(
ds, 0, m_tfx_textures[NUM_TFX_DRAW_TEXTURES]->GetView(), VK_IMAGE_LAYOUT_GENERAL);
m_tfx_rt_descriptor_set, 0, m_tfx_textures[NUM_TFX_DRAW_TEXTURES]->GetView(), VK_IMAGE_LAYOUT_GENERAL);
}
else
{
dsub.AddImageDescriptorWrite(ds, 0, m_tfx_textures[NUM_TFX_DRAW_TEXTURES]->GetView(),
m_tfx_textures[NUM_TFX_DRAW_TEXTURES]->GetLayout());
dsub.AddImageDescriptorWrite(m_tfx_rt_descriptor_set, 0, m_tfx_textures[NUM_TFX_DRAW_TEXTURES]->GetView(),
m_tfx_textures[NUM_TFX_DRAW_TEXTURES]->GetVkLayout());
}
dsub.AddImageDescriptorWrite(ds, 1, m_tfx_textures[NUM_TFX_DRAW_TEXTURES + 1]->GetView(),
m_tfx_textures[NUM_TFX_DRAW_TEXTURES + 1]->GetLayout());
dsub.AddImageDescriptorWrite(m_tfx_rt_descriptor_set, 1, m_tfx_textures[NUM_TFX_DRAW_TEXTURES + 1]->GetView(),
m_tfx_textures[NUM_TFX_DRAW_TEXTURES + 1]->GetVkLayout());
dsub.Update(dev);
m_tfx_descriptor_sets[2] = ds;
dirty_descriptor_set_start = std::min(dirty_descriptor_set_start, 2u);
dirty_descriptor_set_end = 2u;
if (!layout_changed)
{
// need to add textures in, can't leave a gap
if (start_dset == TFX_DESCRIPTOR_SET_UBO && num_dsets == 1)
dsets[num_dsets++] = m_tfx_texture_descriptor_set;
else
start_dset = (num_dsets == 0) ? TFX_DESCRIPTOR_SET_RT : start_dset;
dsets[num_dsets++] = m_tfx_rt_descriptor_set;
}
}
if (m_current_pipeline_layout != PipelineLayout::TFX)
if (layout_changed)
{
m_current_pipeline_layout = PipelineLayout::TFX;
dsets[0] = m_tfx_ubo_descriptor_set;
dsets[1] = m_tfx_texture_descriptor_set;
dsets[2] = m_tfx_rt_descriptor_set;
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_tfx_pipeline_layout, 0,
NUM_TFX_DESCRIPTOR_SETS, m_tfx_descriptor_sets.data(), NUM_TFX_DYNAMIC_OFFSETS,
m_tfx_dynamic_offsets.data());
NUM_TFX_DESCRIPTOR_SETS, dsets.data(), NUM_TFX_DYNAMIC_OFFSETS, m_tfx_dynamic_offsets.data());
}
else if (dirty_descriptor_set_start <= dirty_descriptor_set_end)
else if (num_dsets > 0)
{
u32 dynamic_count;
const u32* dynamic_offsets;
if (dirty_descriptor_set_start == 0)
if (start_dset == TFX_DESCRIPTOR_SET_UBO)
{
dynamic_count = NUM_TFX_DYNAMIC_OFFSETS;
dynamic_offsets = m_tfx_dynamic_offsets.data();
@ -3423,14 +3404,10 @@ bool GSDeviceVK::ApplyTFXState(bool already_execed)
dynamic_offsets = nullptr;
}
const u32 count = dirty_descriptor_set_end - dirty_descriptor_set_start + 1;
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_tfx_pipeline_layout,
dirty_descriptor_set_start, count, &m_tfx_descriptor_sets[dirty_descriptor_set_start], dynamic_count,
dynamic_offsets);
vkCmdBindDescriptorSets(cmdbuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_tfx_pipeline_layout, start_dset, num_dsets,
dsets.data(), dynamic_count, dynamic_offsets);
}
ApplyBaseState(flags, cmdbuf);
return true;
}
@ -3464,7 +3441,7 @@ bool GSDeviceVK::ApplyUtilityState(bool already_execed)
Vulkan::DescriptorSetUpdateBuilder dsub;
dsub.AddCombinedImageSamplerDescriptorWrite(m_utility_descriptor_set, 0, m_utility_texture->GetView(),
m_utility_sampler, m_utility_texture->GetLayout());
m_utility_sampler, m_utility_texture->GetVkLayout());
dsub.Update(dev);
rebind = true;
}
@ -3498,7 +3475,7 @@ static void ColorBufferBarrier(GSTextureVK* rt)
const VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, nullptr,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED,
rt->GetTexture().GetImage(), {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u}};
rt->GetImage(), {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u}};
vkCmdPipelineBarrier(g_vulkan_context->GetCurrentCommandBuffer(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &barrier);
@ -3609,7 +3586,7 @@ GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
config.alpha_second_pass.ps.date = 3;
// and bind the image to the primitive sampler
image->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
image->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
PSSetShaderResource(3, image, false);
return image;
}
@ -3715,7 +3692,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
else
{
hdr_rt->SetState(GSTexture::State::Invalidated);
draw_rt->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
draw_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
// we're not drawing to the RT, so we can use it as a source
@ -3742,9 +3719,8 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
// clear texture binding when it's bound to RT or DS
if (!config.tex &&
((!pipe.IsRTFeedbackLoop() && static_cast<GSTextureVK*>(config.rt)->GetTexturePtr() == m_tfx_textures[0]) ||
(config.ds && static_cast<GSTextureVK*>(config.ds)->GetTexturePtr() == m_tfx_textures[0])))
if (!config.tex && ((!pipe.IsRTFeedbackLoop() && static_cast<GSTextureVK*>(config.rt) == m_tfx_textures[0]) ||
(config.ds && static_cast<GSTextureVK*>(config.ds) == m_tfx_textures[0])))
{
PSSetShaderResource(0, nullptr, false);
}
@ -3777,7 +3753,7 @@ 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() != VK_IMAGE_LAYOUT_GENERAL && !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())
@ -3883,7 +3859,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
GL_INS("Blit HDR back to RT");
EndRenderPass();
hdr_rt->TransitionToLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
hdr_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
draw_rt = static_cast<GSTextureVK*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));

View File

@ -87,7 +87,6 @@ public:
enum : u32
{
NUM_TFX_DESCRIPTOR_SETS = 3,
NUM_TFX_DYNAMIC_OFFSETS = 2,
NUM_TFX_DRAW_TEXTURES = 2,
NUM_TFX_RT_TEXTURES = 2,
@ -103,6 +102,14 @@ public:
NUM_CAS_PIPELINES = 2,
};
enum TFX_DESCRIPTOR_SET : u32
{
TFX_DESCRIPTOR_SET_UBO,
TFX_DESCRIPTOR_SET_TEXTURES,
TFX_DESCRIPTOR_SET_RT,
NUM_TFX_DESCRIPTOR_SETS,
};
enum DATE_RENDER_PASS : u32
{
DATE_RENDER_PASS_NONE = 0,
@ -170,8 +177,6 @@ private:
std::string m_tfx_source;
VkFormat LookupNativeFormat(GSTexture::Format format) const;
GSTexture* CreateSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format) override;
void DoMerge(GSTexture* sTex[3], GSVector4* sRect, GSTexture* dTex, GSVector4* dRect, const GSRegPMODE& PMODE,
@ -317,6 +322,8 @@ public:
//////////////////////////////////////////////////////////////////////////
public:
VkFormat LookupNativeFormat(GSTexture::Format format) const;
__fi VkFramebuffer GetCurrentFramebuffer() const { return m_current_framebuffer; }
/// Ends any render pass, executes the command buffer, and invalidates cached state.
@ -412,20 +419,22 @@ private:
GSVector4i m_scissor = GSVector4i::zero();
u8 m_blend_constant_color = 0;
std::array<const VKTexture*, NUM_TFX_TEXTURES> m_tfx_textures{};
std::array<const GSTextureVK*, NUM_TFX_TEXTURES> m_tfx_textures{};
VkSampler m_tfx_sampler = VK_NULL_HANDLE;
u32 m_tfx_sampler_sel = 0;
std::array<VkDescriptorSet, NUM_TFX_DESCRIPTOR_SETS> m_tfx_descriptor_sets{};
VkDescriptorSet m_tfx_ubo_descriptor_set = VK_NULL_HANDLE;
VkDescriptorSet m_tfx_texture_descriptor_set = VK_NULL_HANDLE;
VkDescriptorSet m_tfx_rt_descriptor_set = VK_NULL_HANDLE;
std::array<u32, NUM_TFX_DYNAMIC_OFFSETS> m_tfx_dynamic_offsets{};
const VKTexture* m_utility_texture = nullptr;
const GSTextureVK* m_utility_texture = nullptr;
VkSampler m_utility_sampler = VK_NULL_HANDLE;
VkDescriptorSet m_utility_descriptor_set = VK_NULL_HANDLE;
PipelineLayout m_current_pipeline_layout = PipelineLayout::Undefined;
VkPipeline m_current_pipeline = VK_NULL_HANDLE;
VKTexture m_null_texture;
std::unique_ptr<GSTextureVK> m_null_texture;
// current pipeline selector - we save this in the struct to avoid re-zeroing it every draw
PipelineSelector m_pipeline_selector = {};

View File

@ -26,17 +26,192 @@
#include "common/Align.h"
#include "common/Assertions.h"
GSTextureVK::GSTextureVK(Type type, Format format, VKTexture texture)
: m_texture(std::move(texture))
static constexpr const VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
static constexpr std::array<VkImageLayout, static_cast<u32>(GSTextureVK::Layout::Count)> s_vk_layout_mapping = {{
VK_IMAGE_LAYOUT_UNDEFINED, // Undefined
VK_IMAGE_LAYOUT_PREINITIALIZED, // Preinitialized
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // ColorAttachment
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // DepthStencilAttachment
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, // ShaderReadOnly
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // ClearDst
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // TransferSrc
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // TransferDst
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // PresentSrc
VK_IMAGE_LAYOUT_GENERAL, // FeedbackLoop
VK_IMAGE_LAYOUT_GENERAL, // ReadWriteImage
VK_IMAGE_LAYOUT_GENERAL, // ComputeReadWriteImage
VK_IMAGE_LAYOUT_GENERAL, // General
}};
GSTextureVK::GSTextureVK(Type type, Format format, int width, int height, int levels, VkImage image,
VmaAllocation allocation, VkImageView view, VkFormat vk_format)
: GSTexture()
, m_image(image)
, m_allocation(allocation)
, m_view(view)
, m_vk_format(vk_format)
{
m_type = type;
m_format = format;
m_size.x = m_texture.GetWidth();
m_size.y = m_texture.GetHeight();
m_mipmap_levels = m_texture.GetLevels();
m_size.x = width;
m_size.y = height;
m_mipmap_levels = levels;
}
GSTextureVK::~GSTextureVK()
{
Destroy(true);
}
std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, Format format, int width, int height, int levels)
{
const VkFormat vk_format = GSDeviceVK::GetInstance()->LookupNativeFormat(format);
VkImageCreateInfo ici = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr, 0, VK_IMAGE_TYPE_2D, vk_format,
{static_cast<u32>(width), static_cast<u32>(height), 1}, static_cast<u32>(levels), 1, VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_TILING_OPTIMAL};
VmaAllocationCreateInfo aci = {};
aci.usage = VMA_MEMORY_USAGE_GPU_ONLY;
aci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT;
aci.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
VkImageViewCreateInfo vci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr, 0, VK_NULL_HANDLE,
VK_IMAGE_VIEW_TYPE_2D, vk_format, s_identity_swizzle, VK_IMAGE_ASPECT_COLOR_BIT, 0, static_cast<u32>(levels), 0,
1};
switch (type)
{
case Type::Texture:
{
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
if (format == Format::UNorm8)
{
// for r8 textures, swizzle it across all 4 components. the shaders depend on it being in alpha.. why?
static constexpr const VkComponentMapping r8_swizzle = {
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R};
vci.components = r8_swizzle;
}
}
break;
case Type::RenderTarget:
{
pxAssert(levels == 1);
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
break;
case Type::DepthStencil:
{
pxAssert(levels == 1);
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
vci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
}
break;
case Type::RWTexture:
{
pxAssert(levels == 1);
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT;
}
break;
default:
return {};
}
// Use dedicated allocations for typical RT size
if ((type == Type::RenderTarget || type == Type::DepthStencil) && width >= 512 && height >= 448)
aci.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
VkImage image = VK_NULL_HANDLE;
VmaAllocation allocation = VK_NULL_HANDLE;
VkResult res = vmaCreateImage(g_vulkan_context->GetAllocator(), &ici, &aci, &image, &allocation, nullptr);
if (aci.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT && res != VK_SUCCESS)
{
// try without dedicated allocation
aci.flags &= ~VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
res = vmaCreateImage(g_vulkan_context->GetAllocator(), &ici, &aci, &image, &allocation, nullptr);
}
if (res == VK_ERROR_OUT_OF_DEVICE_MEMORY)
{
DevCon.WriteLn("Failed to allocate device memory for %ux%u texture", width, height);
return {};
}
else if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vmaCreateImage failed: ");
return {};
}
VkImageView view = VK_NULL_HANDLE;
vci.image = image;
res = vkCreateImageView(g_vulkan_context->GetDevice(), &vci, nullptr, &view);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
vmaDestroyImage(g_vulkan_context->GetAllocator(), image, allocation);
return {};
}
switch (type)
{
case Type::Texture:
Vulkan::SetObjectName(g_vulkan_context->GetDevice(), image, "%dx%d texture", width, height);
break;
case Type::RenderTarget:
Vulkan::SetObjectName(g_vulkan_context->GetDevice(), image, "%dx%d render target", width, height);
break;
case Type::DepthStencil:
Vulkan::SetObjectName(g_vulkan_context->GetDevice(), image, "%dx%d depth stencil", width, height);
break;
case Type::RWTexture:
Vulkan::SetObjectName(g_vulkan_context->GetDevice(), image, "%dx%d RW texture", width, height);
break;
default:
break;
}
return std::unique_ptr<GSTextureVK>(
new GSTextureVK(type, format, width, height, levels, image, allocation, view, vk_format));
}
std::unique_ptr<GSTextureVK> GSTextureVK::Adopt(
VkImage image, Type type, Format format, int width, int height, int levels, VkFormat vk_format)
{
// Only need to create the image view, this is mainly for swap chains.
const VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr, 0, image,
VK_IMAGE_VIEW_TYPE_2D, vk_format, s_identity_swizzle,
{(type == Type::DepthStencil) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT),
0u, static_cast<u32>(levels), 0u, 1u}};
// Memory is managed by the owner of the image.
VkImageView view = VK_NULL_HANDLE;
VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
return {};
}
return std::unique_ptr<GSTextureVK>(
new GSTextureVK(type, format, width, height, levels, image, VK_NULL_HANDLE, view, vk_format));
}
void GSTextureVK::Destroy(bool defer)
{
GSDeviceVK::GetInstance()->UnbindTexture(this);
@ -57,103 +232,43 @@ GSTextureVK::~GSTextureVK()
}
}
g_vulkan_context->DeferFramebufferDestruction(fb);
if (defer)
g_vulkan_context->DeferFramebufferDestruction(fb);
else
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), fb, nullptr);
}
m_framebuffers.clear();
}
if (m_view != VK_NULL_HANDLE)
{
if (defer)
g_vulkan_context->DeferImageViewDestruction(m_view);
else
vkDestroyImageView(g_vulkan_context->GetDevice(), m_view, nullptr);
m_view = VK_NULL_HANDLE;
}
// If we don't have device memory allocated, the image is not owned by us (e.g. swapchain)
if (m_allocation != VK_NULL_HANDLE)
{
if (defer)
g_vulkan_context->DeferImageDestruction(m_image, m_allocation);
else
vmaDestroyImage(g_vulkan_context->GetAllocator(), m_image, m_allocation);
m_image = VK_NULL_HANDLE;
m_allocation = VK_NULL_HANDLE;
}
}
std::unique_ptr<GSTextureVK> GSTextureVK::Create(Type type, u32 width, u32 height, u32 levels, Format format, VkFormat vk_format)
VkImageLayout GSTextureVK::GetVkLayout() const
{
switch (type)
{
case Type::Texture:
{
VkImageUsageFlags usage =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
const VkComponentMapping* swizzle = nullptr;
if (format == Format::UNorm8)
{
// for r8 textures, swizzle it across all 4 components. the shaders depend on it being in alpha.. why?
static constexpr VkComponentMapping r8_swizzle = {
VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R};
swizzle = &r8_swizzle;
}
VKTexture texture;
if (!texture.Create(width, height, levels, 1, vk_format, VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, usage, swizzle))
{
return {};
}
Vulkan::SetObjectName(
g_vulkan_context->GetDevice(), texture.GetImage(), "%ux%u texture", width, height);
return std::make_unique<GSTextureVK>(type, format, std::move(texture));
}
case Type::RenderTarget:
{
pxAssert(levels == 1);
VKTexture 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_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT))
{
return {};
}
Vulkan::SetObjectName(
g_vulkan_context->GetDevice(), texture.GetImage(), "%ux%u render target", width, height);
return std::make_unique<GSTextureVK>(type, format, std::move(texture));
}
case Type::DepthStencil:
{
pxAssert(levels == 1);
VKTexture 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_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT))
{
return {};
}
Vulkan::SetObjectName(
g_vulkan_context->GetDevice(), texture.GetImage(), "%ux%u depth stencil", width, height);
return std::make_unique<GSTextureVK>(type, format, std::move(texture));
}
case Type::RWTexture:
{
pxAssert(levels == 1);
VKTexture 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::SetObjectName(
g_vulkan_context->GetDevice(), texture.GetImage(), "%ux%u RW texture", width, height);
return std::make_unique<GSTextureVK>(type, format, std::move(texture));
}
default:
return {};
}
return s_vk_layout_mapping[static_cast<u32>(m_layout)];
}
void* GSTextureVK::GetNativeHandle() const
{
return const_cast<VKTexture*>(&m_texture);
return const_cast<GSTextureVK*>(this);
}
VkCommandBuffer GSTextureVK::GetCommandBufferForUpdate()
@ -178,8 +293,8 @@ void GSTextureVK::CopyTextureDataForUpload(void* dst, const void* src, u32 pitch
VkBuffer GSTextureVK::AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 height) const
{
const u32 size = upload_pitch * height;
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0,
static_cast<VkDeviceSize>(size), VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr};
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0, static_cast<VkDeviceSize>(size),
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr};
// Don't worry about setting the coherent bit for this upload, the main reason we had
// that set in StreamBuffer was for MoltenVK, which would upload the whole buffer on
@ -207,6 +322,23 @@ VkBuffer GSTextureVK::AllocateUploadStagingBuffer(const void* data, u32 pitch, u
return buffer;
}
void GSTextureVK::UpdateFromBuffer(VkCommandBuffer cmdbuf, int level, u32 x, u32 y, u32 width, u32 height,
u32 buffer_height, u32 row_length, VkBuffer buffer, u32 buffer_offset)
{
const Layout old_layout = m_layout;
if (old_layout != Layout::TransferDst)
TransitionSubresourcesToLayout(cmdbuf, level, 1, old_layout, Layout::TransferDst);
const VkBufferImageCopy bic = {static_cast<VkDeviceSize>(buffer_offset), row_length, buffer_height,
{VK_IMAGE_ASPECT_COLOR_BIT, static_cast<u32>(level), 0u, 1u}, {static_cast<s32>(x), static_cast<s32>(y), 0},
{width, height, 1u}};
vkCmdCopyBufferToImage(cmdbuf, buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bic);
if (old_layout != Layout::TransferDst)
TransitionSubresourcesToLayout(cmdbuf, level, 1, Layout::TransferDst, old_layout);
}
bool GSTextureVK::Update(const GSVector4i& r, const void* data, int pitch, int layer)
{
if (layer >= m_mipmap_levels)
@ -254,8 +386,8 @@ bool GSTextureVK::Update(const GSVector4i& r, const void* data, int pitch, int l
GL_PUSH("GSTextureVK::Update({%d,%d} %dx%d Lvl:%u", r.x, r.y, r.width(), r.height(), layer);
// first time the texture is used? don't leave it undefined
if (m_texture.GetLayout() == VK_IMAGE_LAYOUT_UNDEFINED)
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (m_layout == Layout::Undefined)
TransitionToLayout(cmdbuf, Layout::TransferDst);
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
if (m_type == Type::RenderTarget)
@ -266,10 +398,9 @@ bool GSTextureVK::Update(const GSVector4i& r, const void* data, int pitch, int l
m_state = State::Dirty;
}
m_texture.UpdateFromBuffer(cmdbuf, layer, 0, r.x, r.y, width, height,
Common::AlignUpPow2(height, GetCompressedBlockSize()),
UpdateFromBuffer(cmdbuf, layer, r.x, r.y, width, height, Common::AlignUpPow2(height, GetCompressedBlockSize()),
CalcUploadRowLengthFromPitch(upload_pitch), buffer, buffer_offset);
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
TransitionToLayout(cmdbuf, Layout::ShaderReadOnly);
if (m_type == Type::Texture)
m_needs_mipmaps_generated |= (layer == 0);
@ -283,14 +414,14 @@ bool GSTextureVK::Map(GSMap& m, const GSVector4i* r, int layer)
return false;
// map for writing
m_map_area = r ? *r : GSVector4i(0, 0, m_texture.GetWidth(), m_texture.GetHeight());
m_map_area = r ? *r : GetRect();
m_map_level = layer;
m.pitch = Common::AlignUpPow2(m_map_area.width() * Vulkan::GetTexelSize(m_texture.GetFormat()),
g_vulkan_context->GetBufferCopyRowPitchAlignment());
m.pitch =
Common::AlignUpPow2(CalcUploadPitch(m_map_area.width()), g_vulkan_context->GetBufferCopyRowPitchAlignment());
// see note in Update() for the reason why.
const u32 required_size = m.pitch * m_map_area.height();
const u32 required_size = CalcUploadSize(m_map_area.height(), m.pitch);
VKStreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
if (required_size >= (buffer.GetCurrentSize() / 2))
return false;
@ -310,15 +441,13 @@ bool GSTextureVK::Map(GSMap& m, const GSVector4i* r, int layer)
void GSTextureVK::Unmap()
{
// this can't handle blocks/compressed formats at the moment.
pxAssert(m_map_level < m_texture.GetLevels() && !IsCompressedFormat());
pxAssert(m_map_level < m_mipmap_levels && !IsCompressedFormat());
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
// TODO: non-tightly-packed formats
const u32 width = static_cast<u32>(m_map_area.width());
const u32 height = static_cast<u32>(m_map_area.height());
const u32 pitch = Common::AlignUpPow2(m_map_area.width() * Vulkan::GetTexelSize(m_texture.GetFormat()),
g_vulkan_context->GetBufferCopyRowPitchAlignment());
const u32 required_size = pitch * height;
const u32 width = m_map_area.width();
const u32 height = m_map_area.height();
const u32 pitch = Common::AlignUpPow2(CalcUploadPitch(width), g_vulkan_context->GetBufferCopyRowPitchAlignment());
const u32 required_size = CalcUploadSize(height, pitch);
VKStreamBuffer& buffer = g_vulkan_context->GetTextureUploadBuffer();
const u32 buffer_offset = buffer.GetCurrentOffset();
buffer.CommitMemory(required_size);
@ -328,8 +457,8 @@ void GSTextureVK::Unmap()
m_map_area.height(), m_map_level);
// first time the texture is used? don't leave it undefined
if (m_texture.GetLayout() == VK_IMAGE_LAYOUT_UNDEFINED)
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (m_layout == Layout::Undefined)
TransitionToLayout(cmdbuf, Layout::TransferDst);
// if we're an rt and have been cleared, and the full rect isn't being uploaded, do the clear
if (m_type == Type::RenderTarget)
@ -340,10 +469,10 @@ void GSTextureVK::Unmap()
m_state = State::Dirty;
}
m_texture.UpdateFromBuffer(cmdbuf, m_map_level, 0, m_map_area.x, m_map_area.y, width, height,
Common::AlignUpPow2(height, GetCompressedBlockSize()),
CalcUploadRowLengthFromPitch(pitch), buffer.GetBuffer(), buffer_offset);
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
UpdateFromBuffer(cmdbuf, m_map_level, m_map_area.x, m_map_area.y, width, height,
Common::AlignUpPow2(height, GetCompressedBlockSize()), CalcUploadRowLengthFromPitch(pitch), buffer.GetBuffer(),
buffer_offset);
TransitionToLayout(cmdbuf, Layout::ShaderReadOnly);
if (m_type == Type::Texture)
m_needs_mipmaps_generated |= (m_map_level == 0);
@ -361,10 +490,8 @@ void GSTextureVK::GenerateMipmap()
const int dst_width = std::max<int>(m_size.x >> dst_level, 1);
const int dst_height = std::max<int>(m_size.y >> dst_level, 1);
m_texture.TransitionSubresourcesToLayout(
cmdbuf, src_level, 1, 0, 1, m_texture.GetLayout(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_texture.TransitionSubresourcesToLayout(
cmdbuf, dst_level, 1, 0, 1, m_texture.GetLayout(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
TransitionSubresourcesToLayout(cmdbuf, src_level, 1, m_layout, Layout::TransferSrc);
TransitionSubresourcesToLayout(cmdbuf, dst_level, 1, m_layout, Layout::TransferDst);
const VkImageBlit blit = {
{VK_IMAGE_ASPECT_COLOR_BIT, static_cast<u32>(src_level), 0u, 1u}, // srcSubresource
@ -373,20 +500,22 @@ void GSTextureVK::GenerateMipmap()
{{0, 0, 0}, {dst_width, dst_height, 1}} // dstOffsets
};
vkCmdBlitImage(cmdbuf, m_texture.GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture.GetImage(),
vkCmdBlitImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
m_texture.TransitionSubresourcesToLayout(
cmdbuf, src_level, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_texture.GetLayout());
m_texture.TransitionSubresourcesToLayout(
cmdbuf, dst_level, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_texture.GetLayout());
TransitionSubresourcesToLayout(cmdbuf, src_level, 1, Layout::TransferSrc, m_layout);
TransitionSubresourcesToLayout(cmdbuf, dst_level, 1, Layout::TransferDst, m_layout);
}
}
void GSTextureVK::Swap(GSTexture* tex)
{
GSTexture::Swap(tex);
std::swap(m_texture, static_cast<GSTextureVK*>(tex)->m_texture);
std::swap(m_image, static_cast<GSTextureVK*>(tex)->m_image);
std::swap(m_allocation, static_cast<GSTextureVK*>(tex)->m_allocation);
std::swap(m_view, static_cast<GSTextureVK*>(tex)->m_view);
std::swap(m_vk_format, static_cast<GSTextureVK*>(tex)->m_vk_format);
std::swap(m_layout, static_cast<GSTextureVK*>(tex)->m_layout);
std::swap(m_use_fence_counter, static_cast<GSTextureVK*>(tex)->m_use_fence_counter);
std::swap(m_clear_value, static_cast<GSTextureVK*>(tex)->m_clear_value);
std::swap(m_map_area, static_cast<GSTextureVK*>(tex)->m_map_area);
@ -394,11 +523,6 @@ void GSTextureVK::Swap(GSTexture* tex)
std::swap(m_framebuffers, static_cast<GSTextureVK*>(tex)->m_framebuffers);
}
void GSTextureVK::TransitionToLayout(VkImageLayout layout)
{
m_texture.TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), layout);
}
void GSTextureVK::CommitClear()
{
if (m_state != GSTexture::State::Cleared)
@ -411,26 +535,223 @@ void GSTextureVK::CommitClear()
void GSTextureVK::CommitClear(VkCommandBuffer cmdbuf)
{
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
TransitionToLayout(cmdbuf, Layout::ClearDst);
if (IsDepthStencil())
{
const VkClearDepthStencilValue cv = { m_clear_value.depth };
const VkImageSubresourceRange srr = { VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u };
vkCmdClearDepthStencilImage(cmdbuf, m_texture.GetImage(), m_texture.GetLayout(), &cv, 1, &srr);
const VkClearDepthStencilValue cv = {m_clear_value.depth};
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u};
vkCmdClearDepthStencilImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
}
else
{
alignas(16) VkClearColorValue cv;
GSVector4::store<true>(cv.float32, GetClearColor());
const VkImageSubresourceRange srr = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};
vkCmdClearColorImage(cmdbuf, m_texture.GetImage(), m_texture.GetLayout(), &cv, 1, &srr);
vkCmdClearColorImage(cmdbuf, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &cv, 1, &srr);
}
SetState(GSTexture::State::Dirty);
}
VkFramebuffer GSTextureVK::GetFramebuffer(bool feedback_loop) { return GetLinkedFramebuffer(nullptr, feedback_loop); }
void GSTextureVK::OverrideImageLayout(Layout new_layout)
{
m_layout = new_layout;
}
void GSTextureVK::TransitionToLayout(Layout layout)
{
TransitionToLayout(g_vulkan_context->GetCurrentCommandBuffer(), layout);
}
void GSTextureVK::TransitionToLayout(VkCommandBuffer command_buffer, Layout new_layout)
{
if (m_layout == new_layout)
return;
TransitionSubresourcesToLayout(command_buffer, 0, m_mipmap_levels, m_layout, new_layout);
m_layout = new_layout;
}
void GSTextureVK::TransitionSubresourcesToLayout(
VkCommandBuffer command_buffer, int start_level, int num_levels, Layout old_layout, Layout new_layout)
{
VkImageAspectFlags aspect;
if (m_type == Type::DepthStencil)
{
aspect = g_gs_device->Features().stencil_buffer ? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) :
VK_IMAGE_ASPECT_DEPTH_BIT;
}
else
{
aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}
VkImageMemoryBarrier barrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, nullptr, 0, 0,
s_vk_layout_mapping[static_cast<u32>(old_layout)], s_vk_layout_mapping[static_cast<u32>(new_layout)],
VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, m_image,
{aspect, static_cast<u32>(start_level), static_cast<u32>(num_levels), 0u, 1u}};
// srcStageMask -> Stages that must complete before the barrier
// dstStageMask -> Stages that must wait for after the barrier before beginning
VkPipelineStageFlags srcStageMask, dstStageMask;
switch (old_layout)
{
case Layout::Undefined:
// Layout undefined therefore contents undefined, and we don't care what happens to it.
barrier.srcAccessMask = 0;
srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
case Layout::Preinitialized:
// Image has been pre-initialized by the host, so ensure all writes have completed.
barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_HOST_BIT;
break;
case Layout::ColorAttachment:
// Image was being used as a color attachment, so ensure all writes have completed.
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case Layout::DepthStencilAttachment:
// Image was being used as a depthstencil attachment, so ensure all writes have completed.
barrier.srcAccessMask =
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
case Layout::ShaderReadOnly:
// Image was being used as a shader resource, make sure all reads have finished.
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case Layout::ClearDst:
// Image was being used as a clear destination, ensure all writes have finished.
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case Layout::TransferSrc:
// Image was being used as a copy source, ensure all reads have finished.
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case Layout::TransferDst:
// Image was being used as a copy destination, ensure all writes have finished.
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case Layout::FeedbackLoop:
barrier.srcAccessMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) :
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT);
srcStageMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) :
(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
break;
case Layout::ReadWriteImage:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case Layout::ComputeReadWriteImage:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
break;
case Layout::General:
default:
srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
}
switch (new_layout)
{
case Layout::Undefined:
barrier.dstAccessMask = 0;
dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
case Layout::ColorAttachment:
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case Layout::DepthStencilAttachment:
barrier.dstAccessMask =
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
case Layout::ShaderReadOnly:
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case Layout::ClearDst:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case Layout::TransferSrc:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case Layout::TransferDst:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case Layout::PresentSrc:
srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
case Layout::FeedbackLoop:
barrier.dstAccessMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) :
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT);
dstStageMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) :
(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
break;
case Layout::ReadWriteImage:
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case Layout::ComputeReadWriteImage:
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
break;
case Layout::General:
default:
dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
break;
}
vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
VkFramebuffer GSTextureVK::GetFramebuffer(bool feedback_loop)
{
return GetLinkedFramebuffer(nullptr, feedback_loop);
}
VkFramebuffer GSTextureVK::GetLinkedFramebuffer(GSTextureVK* depth_texture, bool feedback_loop)
{
@ -442,21 +763,20 @@ VkFramebuffer GSTextureVK::GetLinkedFramebuffer(GSTextureVK* depth_texture, bool
return fb;
}
VkRenderPass rp = g_vulkan_context->GetRenderPass(
(m_type != GSTexture::Type::DepthStencil) ? GetNativeFormat() : VK_FORMAT_UNDEFINED,
(m_type != GSTexture::Type::DepthStencil) ?
(depth_texture ? depth_texture->GetNativeFormat() : VK_FORMAT_UNDEFINED) :
GetNativeFormat(),
const VkRenderPass rp = g_vulkan_context->GetRenderPass(
(m_type != GSTexture::Type::DepthStencil) ? m_vk_format : VK_FORMAT_UNDEFINED,
(m_type != GSTexture::Type::DepthStencil) ? (depth_texture ? depth_texture->m_vk_format : VK_FORMAT_UNDEFINED) :
m_vk_format,
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_LOAD,
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, feedback_loop);
if (!rp)
return VK_NULL_HANDLE;
Vulkan::FramebufferBuilder fbb;
fbb.AddAttachment(m_texture.GetView());
fbb.AddAttachment(m_view);
if (depth_texture)
fbb.AddAttachment(depth_texture->m_texture.GetView());
fbb.SetSize(m_texture.GetWidth(), m_texture.GetHeight(), m_texture.GetLayers());
fbb.AddAttachment(depth_texture->m_view);
fbb.SetSize(m_size.x, m_size.y, 1);
fbb.SetRenderPass(rp);
VkFramebuffer fb = fbb.Create(g_vulkan_context->GetDevice());
@ -485,8 +805,8 @@ std::unique_ptr<GSDownloadTextureVK> GSDownloadTextureVK::Create(u32 width, u32
{
const u32 buffer_size = GetBufferSize(width, height, format, g_vulkan_context->GetBufferCopyRowPitchAlignment());
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0u, buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_SHARING_MODE_EXCLUSIVE, 0u, nullptr};
const VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0u, buffer_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, 0u, nullptr};
VmaAllocationCreateInfo aci = {};
aci.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
@ -503,7 +823,8 @@ std::unique_ptr<GSDownloadTextureVK> GSDownloadTextureVK::Create(u32 width, u32
return {};
}
std::unique_ptr<GSDownloadTextureVK> tex = std::unique_ptr<GSDownloadTextureVK>(new GSDownloadTextureVK(width, height, format));
std::unique_ptr<GSDownloadTextureVK> tex =
std::unique_ptr<GSDownloadTextureVK>(new GSDownloadTextureVK(width, height, format));
tex->m_allocation = allocation;
tex->m_buffer = buffer;
tex->m_buffer_size = buffer_size;
@ -524,8 +845,8 @@ void GSDownloadTextureVK::CopyFromTexture(
pxAssert((drc.left == 0 && drc.top == 0) || !use_transfer_pitch);
u32 copy_offset, copy_size, copy_rows;
m_current_pitch =
GetTransferPitch(use_transfer_pitch ? static_cast<u32>(drc.width()) : m_width, g_vulkan_context->GetBufferCopyRowPitchAlignment());
m_current_pitch = GetTransferPitch(use_transfer_pitch ? static_cast<u32>(drc.width()) : m_width,
g_vulkan_context->GetBufferCopyRowPitchAlignment());
GetTransferSize(drc, &copy_offset, &copy_size, &copy_rows);
g_perfmon.Put(GSPerfMon::Readbacks, 1);
@ -535,16 +856,12 @@ void GSDownloadTextureVK::CopyFromTexture(
const VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
GL_INS("GSDownloadTextureVK::CopyFromTexture: {%d,%d} %ux%u", src.left, src.top, src.width(), src.height());
VkImageLayout old_layout = vkTex->GetTexture().GetLayout();
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
vkTex->GetTexture().TransitionSubresourcesToLayout(cmdbuf, src_level, 1, 0, 1, old_layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
}
GSTextureVK::Layout old_layout = vkTex->GetLayout();
if (old_layout != GSTextureVK::Layout::TransferSrc)
vkTex->TransitionSubresourcesToLayout(cmdbuf, src_level, 1, old_layout, GSTextureVK::Layout::TransferSrc);
VkBufferImageCopy image_copy = {};
const VkImageAspectFlags aspect = Vulkan::IsDepthFormat(static_cast<VkFormat>(vkTex->GetFormat())) ?
VK_IMAGE_ASPECT_DEPTH_BIT :
VK_IMAGE_ASPECT_COLOR_BIT;
const VkImageAspectFlags aspect = vkTex->IsDepthStencil() ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
image_copy.bufferOffset = copy_offset;
image_copy.bufferRowLength = GSTexture::CalcUploadRowLengthFromPitch(m_format, m_current_pitch);
image_copy.bufferImageHeight = 0;
@ -553,16 +870,14 @@ void GSDownloadTextureVK::CopyFromTexture(
image_copy.imageExtent = {static_cast<u32>(src.width()), static_cast<u32>(src.height()), 1u};
// do the copy
vkCmdCopyImageToBuffer(cmdbuf, vkTex->GetTexture().GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, &image_copy);
vkCmdCopyImageToBuffer(cmdbuf, vkTex->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1, &image_copy);
// flush gpu cache
Vulkan::BufferMemoryBarrier(cmdbuf, m_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, 0, copy_size,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_HOST_BIT);
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
vkTex->GetTexture().TransitionSubresourcesToLayout(cmdbuf, src_level, 1, 0, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, old_layout);
}
if (old_layout != GSTextureVK::Layout::TransferSrc)
vkTex->TransitionSubresourcesToLayout(cmdbuf, src_level, 1, GSTextureVK::Layout::TransferSrc, old_layout);
m_copy_fence_counter = g_vulkan_context->GetCurrentFenceCounter();
m_needs_cache_invalidate = true;

View File

@ -17,23 +17,46 @@
#include "GS/GS.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "GS/Renderers/Vulkan/VKTexture.h"
#include "GS/Renderers/Vulkan/VKContext.h"
#include "GS/Renderers/Vulkan/VKLoader.h"
#include <limits>
class GSTextureVK final : public GSTexture
{
public:
GSTextureVK(Type type, Format format, VKTexture texture);
enum class Layout : u32
{
Undefined,
Preinitialized,
ColorAttachment,
DepthStencilAttachment,
ShaderReadOnly,
ClearDst,
TransferSrc,
TransferDst,
PresentSrc,
FeedbackLoop,
ReadWriteImage,
ComputeReadWriteImage,
General,
Count
};
~GSTextureVK() override;
static std::unique_ptr<GSTextureVK> Create(Type type, u32 width, u32 height, u32 levels, Format format, VkFormat vk_format);
static std::unique_ptr<GSTextureVK> Create(Type type, Format format, int width, int height, int levels);
static std::unique_ptr<GSTextureVK> Adopt(
VkImage image, Type type, Format format, int width, int height, int levels, VkFormat vk_format);
__fi VKTexture& GetTexture() { return m_texture; }
__fi VKTexture* GetTexturePtr() { return &m_texture; }
__fi VkFormat GetNativeFormat() const { return m_texture.GetFormat(); }
__fi VkImage GetImage() const { return m_texture.GetImage(); }
__fi VkImageView GetView() const { return m_texture.GetView(); }
__fi VkImageLayout GetLayout() const { return m_texture.GetLayout(); }
void Destroy(bool defer);
__fi VkImage GetImage() const { return m_image; }
__fi VkImageView GetView() const { return m_view; }
__fi Layout GetLayout() const { return m_layout; }
__fi VkFormat GetVkFormat() const { return m_vk_format; }
VkImageLayout GetVkLayout() const;
void* GetNativeHandle() const override;
@ -43,34 +66,49 @@ public:
void GenerateMipmap() override;
void Swap(GSTexture* tex) override;
void TransitionToLayout(VkImageLayout layout);
void TransitionToLayout(Layout layout);
void CommitClear();
void CommitClear(VkCommandBuffer cmdbuf);
// Used when the render pass is changing the image layout, or to force it to
// VK_IMAGE_LAYOUT_UNDEFINED, if the existing contents of the image is
// irrelevant and will not be loaded.
void OverrideImageLayout(Layout new_layout);
void TransitionToLayout(VkCommandBuffer command_buffer, Layout new_layout);
void TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, int start_level, int num_levels,
Layout old_layout, Layout new_layout);
/// Framebuffers are lazily allocated.
VkFramebuffer GetFramebuffer(bool feedback_loop);
VkFramebuffer GetLinkedFramebuffer(GSTextureVK* depth_texture, bool feedback_loop);
// Call when the texture is bound to the pipeline, or read from in a copy.
__fi void SetUsedThisCommandBuffer()
{
m_use_fence_counter = g_vulkan_context->GetCurrentFenceCounter();
}
__fi void SetUsedThisCommandBuffer() { m_use_fence_counter = g_vulkan_context->GetCurrentFenceCounter(); }
private:
GSTextureVK(Type type, Format format, int width, int height, int levels, VkImage image, VmaAllocation allocation,
VkImageView view, VkFormat vk_format);
VkCommandBuffer GetCommandBufferForUpdate();
void CopyTextureDataForUpload(void* dst, const void* src, u32 pitch, u32 upload_pitch, u32 height) const;
VkBuffer AllocateUploadStagingBuffer(const void* data, u32 pitch, u32 upload_pitch, u32 height) const;
void UpdateFromBuffer(VkCommandBuffer cmdbuf, int level, u32 x, u32 y, u32 width, u32 height, u32 buffer_height,
u32 row_length, VkBuffer buffer, u32 buffer_offset);
VKTexture m_texture;
VkImage m_image = VK_NULL_HANDLE;
VmaAllocation m_allocation = VK_NULL_HANDLE;
VkImageView m_view = VK_NULL_HANDLE;
VkFormat m_vk_format = VK_FORMAT_UNDEFINED;
Layout m_layout = Layout::Undefined;
// Contains the fence counter when the texture was last used.
// When this matches the current fence counter, the texture was used this command buffer.
u64 m_use_fence_counter = 0;
int m_map_level = std::numeric_limits<int>::max();
GSVector4i m_map_area = GSVector4i::zero();
u32 m_map_level = UINT32_MAX;
// linked framebuffer is combined with depth texture
// list of color textures this depth texture is linked to or vice versa
@ -84,7 +122,8 @@ public:
static std::unique_ptr<GSDownloadTextureVK> Create(u32 width, u32 height, GSTexture::Format format);
void CopyFromTexture(const GSVector4i& drc, GSTexture* stex, const GSVector4i& src, u32 src_level, bool use_transfer_pitch) override;
void CopyFromTexture(
const GSVector4i& drc, GSTexture* stex, const GSVector4i& src, u32 src_level, bool use_transfer_pitch) override;
bool Map(const GSVector4i& read_rc) override;
void Unmap() override;

View File

@ -404,16 +404,9 @@ bool VKSwapChain::SetupSwapChainImages()
{
SwapChainImage image;
image.image = images[i];
// Create texture object, which creates a view of the backbuffer
if (!image.texture.Adopt(image.image, VK_IMAGE_VIEW_TYPE_2D, m_window_info.surface_width,
m_window_info.surface_height, 1, 1, m_surface_format.format, VK_SAMPLE_COUNT_1_BIT))
{
return false;
}
image.framebuffer = image.texture.CreateFramebuffer(m_load_render_pass);
if (image.framebuffer == VK_NULL_HANDLE)
image.texture = GSTextureVK::Adopt(images[i], GSTexture::Type::RenderTarget, GSTexture::Format::Color,
m_window_info.surface_width, m_window_info.surface_height, 1, m_surface_format.format);
if (!image.texture)
return false;
m_images.emplace_back(std::move(image));
@ -452,8 +445,8 @@ void VKSwapChain::DestroySwapChainImages()
{
for (auto& it : m_images)
{
// Images themselves are cleaned up by the swap chain object
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), it.framebuffer, nullptr);
// don't defer view destruction, images are no longer valid
it.texture->Destroy(false);
}
m_images.clear();
for (auto& it : m_semaphores)

View File

@ -15,8 +15,7 @@
#pragma once
#include "GS/Renderers/Vulkan/VKLoader.h"
#include "GS/Renderers/Vulkan/VKTexture.h"
#include "GS/Renderers/Vulkan/GSTextureVK.h"
#include "common/WindowInfo.h"
@ -53,11 +52,8 @@ public:
__fi const u32* GetCurrentImageIndexPtr() const { return &m_current_image; }
__fi u32 GetImageCount() const { return static_cast<u32>(m_images.size()); }
__fi VkImage GetCurrentImage() const { return m_images[m_current_image].image; }
__fi const VKTexture& GetCurrentTexture() const { return m_images[m_current_image].texture; }
__fi VKTexture& GetCurrentTexture() { return m_images[m_current_image].texture; }
__fi VkFramebuffer GetCurrentFramebuffer() const { return m_images[m_current_image].framebuffer; }
__fi VkRenderPass GetLoadRenderPass() const { return m_load_render_pass; }
__fi VkRenderPass GetClearRenderPass() const { return m_clear_render_pass; }
__fi const GSTextureVK* GetCurrentTexture() const { return m_images[m_current_image].texture.get(); }
__fi GSTextureVK* GetCurrentTexture() { return m_images[m_current_image].texture.get(); }
__fi VkSemaphore GetImageAvailableSemaphore() const
{
return m_semaphores[m_current_semaphore].available_semaphore;
@ -108,8 +104,7 @@ private:
struct SwapChainImage
{
VkImage image;
VKTexture texture;
VkFramebuffer framebuffer;
std::unique_ptr<GSTextureVK> texture;
};
struct ImageSemaphores

View File

@ -1,402 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "GS/Renderers/Vulkan/VKTexture.h"
#include "GS/Renderers/Vulkan/VKContext.h"
#include "GS/Renderers/Vulkan/VKUtil.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include <algorithm>
static constexpr VkComponentMapping s_identity_swizzle{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY};
VKTexture::VKTexture() = default;
VKTexture::VKTexture(VKTexture&& move)
: m_width(move.m_width)
, m_height(move.m_height)
, m_levels(move.m_levels)
, m_layers(move.m_layers)
, m_format(move.m_format)
, m_samples(move.m_samples)
, m_view_type(move.m_view_type)
, m_layout(move.m_layout)
, m_image(move.m_image)
, m_allocation(move.m_allocation)
, m_view(move.m_view)
{
move.m_width = 0;
move.m_height = 0;
move.m_levels = 0;
move.m_layers = 0;
move.m_format = VK_FORMAT_UNDEFINED;
move.m_samples = VK_SAMPLE_COUNT_1_BIT;
move.m_view_type = VK_IMAGE_VIEW_TYPE_2D;
move.m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
move.m_image = VK_NULL_HANDLE;
move.m_allocation = VK_NULL_HANDLE;
move.m_view = VK_NULL_HANDLE;
}
VKTexture::~VKTexture()
{
if (IsValid())
Destroy(true);
}
VKTexture& VKTexture::operator=(VKTexture&& move)
{
if (IsValid())
Destroy(true);
std::swap(m_width, move.m_width);
std::swap(m_height, move.m_height);
std::swap(m_levels, move.m_levels);
std::swap(m_layers, move.m_layers);
std::swap(m_format, move.m_format);
std::swap(m_samples, move.m_samples);
std::swap(m_view_type, move.m_view_type);
std::swap(m_layout, move.m_layout);
std::swap(m_image, move.m_image);
std::swap(m_allocation, move.m_allocation);
std::swap(m_view, move.m_view);
return *this;
}
bool VKTexture::Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, VkSampleCountFlagBits samples,
VkImageViewType view_type, VkImageTiling tiling, VkImageUsageFlags usage,
const VkComponentMapping* swizzle /* = nullptr*/)
{
const VkImageCreateInfo image_info = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr, 0, VK_IMAGE_TYPE_2D, format,
{width, height, 1}, levels, layers, samples, tiling, usage, VK_SHARING_MODE_EXCLUSIVE, 0, nullptr,
VK_IMAGE_LAYOUT_UNDEFINED};
VmaAllocationCreateInfo aci = {};
aci.usage = VMA_MEMORY_USAGE_GPU_ONLY;
aci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT;
aci.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
VkImage image = VK_NULL_HANDLE;
VmaAllocation allocation = VK_NULL_HANDLE;
VkResult res = vmaCreateImage(g_vulkan_context->GetAllocator(), &image_info, &aci, &image, &allocation, nullptr);
if (res == VK_ERROR_OUT_OF_DEVICE_MEMORY)
{
DevCon.WriteLn("Failed to allocate device memory for %ux%u texture", width, height);
return false;
}
else if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vmaCreateImage failed: ");
return false;
}
const VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr, 0, image, view_type,
format, swizzle ? *swizzle : s_identity_swizzle,
{Vulkan::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT),
0, levels, 0, layers}};
VkImageView view = VK_NULL_HANDLE;
res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
vmaDestroyImage(g_vulkan_context->GetAllocator(), image, allocation);
return false;
}
if (IsValid())
Destroy(true);
m_width = width;
m_height = height;
m_levels = levels;
m_layers = layers;
m_format = format;
m_samples = samples;
m_view_type = view_type;
m_image = image;
m_allocation = allocation;
m_view = view;
return true;
}
bool VKTexture::Adopt(VkImage existing_image, VkImageViewType view_type, u32 width, u32 height, u32 levels, u32 layers,
VkFormat format, VkSampleCountFlagBits samples, const VkComponentMapping* swizzle /* = nullptr*/)
{
// Only need to create the image view, this is mainly for swap chains.
const VkImageViewCreateInfo view_info = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, nullptr, 0, existing_image,
view_type, format, swizzle ? *swizzle : s_identity_swizzle,
{Vulkan::IsDepthFormat(format) ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) :
static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_COLOR_BIT),
0, levels, 0, layers}};
// Memory is managed by the owner of the image.
VkImageView view = VK_NULL_HANDLE;
VkResult res = vkCreateImageView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateImageView failed: ");
return false;
}
if (IsValid())
Destroy(true);
m_width = width;
m_height = height;
m_levels = levels;
m_layers = layers;
m_format = format;
m_samples = samples;
m_view_type = view_type;
m_image = existing_image;
m_view = view;
return true;
}
void VKTexture::Destroy(bool defer /* = true */)
{
if (m_view != VK_NULL_HANDLE)
{
if (defer)
g_vulkan_context->DeferImageViewDestruction(m_view);
else
vkDestroyImageView(g_vulkan_context->GetDevice(), m_view, nullptr);
m_view = VK_NULL_HANDLE;
}
// If we don't have device memory allocated, the image is not owned by us (e.g. swapchain)
if (m_allocation != VK_NULL_HANDLE)
{
pxAssert(m_image != VK_NULL_HANDLE);
if (defer)
g_vulkan_context->DeferImageDestruction(m_image, m_allocation);
else
vmaDestroyImage(g_vulkan_context->GetAllocator(), m_image, m_allocation);
m_image = VK_NULL_HANDLE;
m_allocation = VK_NULL_HANDLE;
}
m_width = 0;
m_height = 0;
m_levels = 0;
m_layers = 0;
m_format = VK_FORMAT_UNDEFINED;
m_samples = VK_SAMPLE_COUNT_1_BIT;
m_view_type = VK_IMAGE_VIEW_TYPE_2D;
m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
}
void VKTexture::OverrideImageLayout(VkImageLayout new_layout)
{
m_layout = new_layout;
}
void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout)
{
if (m_layout == new_layout)
return;
TransitionSubresourcesToLayout(command_buffer, 0, m_levels, 0, m_layers, m_layout, new_layout);
m_layout = new_layout;
}
void VKTexture::TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, u32 start_level, u32 num_levels,
u32 start_layer, u32 num_layers, VkImageLayout old_layout, VkImageLayout new_layout)
{
VkImageAspectFlags aspect;
if (Vulkan::IsDepthStencilFormat(m_format))
aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
else if (Vulkan::IsDepthFormat(m_format))
aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
else
aspect = VK_IMAGE_ASPECT_COLOR_BIT;
VkImageMemoryBarrier barrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType
nullptr, // const void* pNext
0, // VkAccessFlags srcAccessMask
0, // VkAccessFlags dstAccessMask
old_layout, // VkImageLayout oldLayout
new_layout, // VkImageLayout newLayout
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
m_image, // VkImage image
{aspect, start_level, num_levels, start_layer, num_layers} // VkImageSubresourceRange subresourceRange
};
// srcStageMask -> Stages that must complete before the barrier
// dstStageMask -> Stages that must wait for after the barrier before beginning
VkPipelineStageFlags srcStageMask, dstStageMask;
switch (old_layout)
{
case VK_IMAGE_LAYOUT_UNDEFINED:
// Layout undefined therefore contents undefined, and we don't care what happens to it.
barrier.srcAccessMask = 0;
srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
// Image has been pre-initialized by the host, so ensure all writes have completed.
barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_HOST_BIT;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
// Image was being used as a color attachment, so ensure all writes have completed.
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
// Image was being used as a depthstencil attachment, so ensure all writes have completed.
barrier.srcAccessMask =
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
// Image was being used as a shader resource, make sure all reads have finished.
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
// Image was being used as a copy source, ensure all reads have finished.
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
// Image was being used as a copy destination, ensure all writes have finished.
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_GENERAL:
// General is used for feedback loops.
barrier.srcAccessMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) :
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT);
srcStageMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) :
(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
break;
default:
srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
}
switch (new_layout)
{
case VK_IMAGE_LAYOUT_UNDEFINED:
barrier.dstAccessMask = 0;
dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask =
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
break;
case VK_IMAGE_LAYOUT_GENERAL:
// General is used for feedback loops.
barrier.dstAccessMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
(VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT) :
(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT);
dstStageMask = (aspect == VK_IMAGE_ASPECT_COLOR_BIT) ?
(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT) :
(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
break;
default:
dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
break;
}
vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
VkFramebuffer VKTexture::CreateFramebuffer(VkRenderPass render_pass)
{
const VkFramebufferCreateInfo ci = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0u, render_pass, 1, &m_view, m_width, m_height, m_layers};
VkFramebuffer fb = VK_NULL_HANDLE;
VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &ci, nullptr, &fb);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateFramebuffer() failed: ");
return VK_NULL_HANDLE;
}
return fb;
}
void VKTexture::UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width, u32 height,
u32 buffer_height, u32 row_length, VkBuffer buffer, u32 buffer_offset)
{
const VkImageLayout old_layout = m_layout;
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
TransitionSubresourcesToLayout(cmdbuf, level, 1, layer, 1, old_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
const VkBufferImageCopy bic = {static_cast<VkDeviceSize>(buffer_offset), row_length, buffer_height,
{VK_IMAGE_ASPECT_COLOR_BIT, level, layer, 1u}, {static_cast<int32_t>(x), static_cast<int32_t>(y), 0},
{width, height, 1u}};
vkCmdCopyBufferToImage(cmdbuf, buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bic);
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
TransitionSubresourcesToLayout(cmdbuf, level, 1, layer, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, old_layout);
}

View File

@ -1,89 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2023 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "GS/Renderers/Vulkan/VKLoader.h"
#include <algorithm>
#include <memory>
class VKTexture
{
public:
VKTexture();
VKTexture(VKTexture&& move);
VKTexture(const VKTexture&) = delete;
~VKTexture();
VKTexture& operator=(VKTexture&& move);
VKTexture& operator=(const VKTexture&) = delete;
__fi bool IsValid() const { return (m_image != VK_NULL_HANDLE); }
/// An image is considered owned/managed if we control the memory.
__fi bool IsOwned() const { return (m_allocation != VK_NULL_HANDLE); }
__fi u32 GetWidth() const { return m_width; }
__fi u32 GetHeight() const { return m_height; }
__fi u32 GetLevels() const { return m_levels; }
__fi u32 GetLayers() const { return m_layers; }
__fi u32 GetMipWidth(u32 level) const { return std::max<u32>(m_width >> level, 1u); }
__fi u32 GetMipHeight(u32 level) const { return std::max<u32>(m_height >> level, 1u); }
__fi VkFormat GetFormat() const { return m_format; }
__fi VkSampleCountFlagBits GetSamples() const { return m_samples; }
__fi VkImageLayout GetLayout() const { return m_layout; }
__fi VkImageViewType GetViewType() const { return m_view_type; }
__fi VkImage GetImage() const { return m_image; }
__fi VmaAllocation GetAllocation() const { return m_allocation; }
__fi VkImageView GetView() const { return m_view; }
bool Create(u32 width, u32 height, u32 levels, u32 layers, VkFormat format, VkSampleCountFlagBits samples,
VkImageViewType view_type, VkImageTiling tiling, VkImageUsageFlags usage,
const VkComponentMapping* swizzle = nullptr);
bool Adopt(VkImage existing_image, VkImageViewType view_type, u32 width, u32 height, u32 levels, u32 layers,
VkFormat format, VkSampleCountFlagBits samples, const VkComponentMapping* swizzle = nullptr);
void Destroy(bool defer = true);
// Used when the render pass is changing the image layout, or to force it to
// VK_IMAGE_LAYOUT_UNDEFINED, if the existing contents of the image is
// irrelevant and will not be loaded.
void OverrideImageLayout(VkImageLayout new_layout);
void TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout new_layout);
void TransitionSubresourcesToLayout(VkCommandBuffer command_buffer, u32 start_level, u32 num_levels,
u32 start_layer, u32 num_layers, VkImageLayout old_layout, VkImageLayout new_layout);
VkFramebuffer CreateFramebuffer(VkRenderPass render_pass);
void UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width, u32 height,
u32 buffer_height, u32 row_length, VkBuffer buffer, u32 buffer_offset);
private:
u32 m_width = 0;
u32 m_height = 0;
u32 m_levels = 0;
u32 m_layers = 0;
VkFormat m_format = VK_FORMAT_UNDEFINED;
VkSampleCountFlagBits m_samples = VK_SAMPLE_COUNT_1_BIT;
VkImageViewType m_view_type = VK_IMAGE_VIEW_TYPE_2D;
VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImage m_image = VK_NULL_HANDLE;
VmaAllocation m_allocation = VK_NULL_HANDLE;
VkImageView m_view = VK_NULL_HANDLE;
};

View File

@ -24,34 +24,6 @@
#include <cmath>
bool Vulkan::IsDepthFormat(VkFormat format)
{
switch (format)
{
case VK_FORMAT_D16_UNORM:
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return true;
default:
return false;
}
}
bool Vulkan::IsDepthStencilFormat(VkFormat format)
{
switch (format)
{
case VK_FORMAT_D16_UNORM_S8_UINT:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return true;
default:
return false;
}
}
VkFormat Vulkan::GetLinearFormat(VkFormat format)
{
switch (format)
@ -73,42 +45,6 @@ VkFormat Vulkan::GetLinearFormat(VkFormat format)
}
}
u32 Vulkan::GetTexelSize(VkFormat format)
{
// Only contains pixel formats we use.
switch (format)
{
case VK_FORMAT_R8_UNORM:
return 1;
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
case VK_FORMAT_R5G6B5_UNORM_PACK16:
case VK_FORMAT_B5G6R5_UNORM_PACK16:
case VK_FORMAT_R16_UINT:
return 2;
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_R32_UINT:
case VK_FORMAT_R32_SFLOAT:
case VK_FORMAT_D32_SFLOAT:
return 4;
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
return 8;
case VK_FORMAT_BC2_UNORM_BLOCK:
case VK_FORMAT_BC3_UNORM_BLOCK:
case VK_FORMAT_BC7_UNORM_BLOCK:
return 16;
default:
pxFailRel("Unhandled pixel format");
return 1;
}
}
void Vulkan::SafeDestroyFramebuffer(VkFramebuffer& fb)
{
if (fb != VK_NULL_HANDLE)

View File

@ -26,10 +26,7 @@
namespace Vulkan
{
bool IsDepthFormat(VkFormat format);
bool IsDepthStencilFormat(VkFormat format);
VkFormat GetLinearFormat(VkFormat format);
u32 GetTexelSize(VkFormat format);
// Safe destroy helpers
void SafeDestroyFramebuffer(VkFramebuffer& fb);

View File

@ -207,13 +207,11 @@
<ClCompile Include="GS\Renderers\DX12\D3D12DescriptorHeapManager.cpp" />
<ClCompile Include="GS\Renderers\DX12\D3D12ShaderCache.cpp" />
<ClCompile Include="GS\Renderers\DX12\D3D12StreamBuffer.cpp" />
<ClCompile Include="GS\Renderers\DX12\D3D12Texture.cpp" />
<ClCompile Include="GS\Renderers\OpenGL\GLContext.cpp" />
<ClCompile Include="GS\Renderers\OpenGL\GLContextWGL.cpp" />
<ClCompile Include="GS\Renderers\OpenGL\GLProgram.cpp" />
<ClCompile Include="GS\Renderers\OpenGL\GLShaderCache.cpp" />
<ClCompile Include="GS\Renderers\OpenGL\GLStreamBuffer.cpp" />
<ClCompile Include="GS\Renderers\Vulkan\VKTexture.cpp" />
<ClCompile Include="GS\Renderers\Vulkan\VKBuilders.cpp" />
<ClCompile Include="GS\Renderers\Vulkan\VKContext.cpp" />
<ClCompile Include="GS\Renderers\Vulkan\VKLoader.cpp" />
@ -567,14 +565,12 @@
<ClInclude Include="GS\Renderers\DX12\D3D12DescriptorHeapManager.h" />
<ClInclude Include="GS\Renderers\DX12\D3D12ShaderCache.h" />
<ClInclude Include="GS\Renderers\DX12\D3D12StreamBuffer.h" />
<ClInclude Include="GS\Renderers\DX12\D3D12Texture.h" />
<ClInclude Include="GS\Renderers\HW\GSHwHack.h" />
<ClInclude Include="GS\Renderers\OpenGL\GLContext.h" />
<ClInclude Include="GS\Renderers\OpenGL\GLContextWGL.h" />
<ClInclude Include="GS\Renderers\OpenGL\GLProgram.h" />
<ClInclude Include="GS\Renderers\OpenGL\GLShaderCache.h" />
<ClInclude Include="GS\Renderers\OpenGL\GLStreamBuffer.h" />
<ClInclude Include="GS\Renderers\Vulkan\VKTexture.h" />
<ClInclude Include="GS\Renderers\Vulkan\VKBuilders.h" />
<ClInclude Include="GS\Renderers\Vulkan\VKContext.h" />
<ClInclude Include="GS\Renderers\Vulkan\VKEntryPoints.h" />

View File

@ -1373,9 +1373,6 @@
<ClCompile Include="GS\Renderers\DX12\D3D12StreamBuffer.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\DX12\D3D12Texture.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\DX12\D3D12Builders.cpp">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClCompile>
@ -1418,9 +1415,6 @@
<ClCompile Include="GS\Renderers\Vulkan\VKUtil.cpp">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClCompile>
<ClCompile Include="GS\Renderers\Vulkan\VKTexture.cpp">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Patch.h">
@ -2336,9 +2330,6 @@
<ClInclude Include="GS\Renderers\DX12\D3D12StreamBuffer.h">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\DX12\D3D12Texture.h">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\DX12\D3D12Builders.h">
<Filter>System\Ps2\GS\Renderers\Direct3D12</Filter>
</ClInclude>
@ -2357,9 +2348,6 @@
<ClInclude Include="GS\Renderers\OpenGL\GLContext.h">
<Filter>System\Ps2\GS\Renderers\OpenGL</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\Vulkan\VKTexture.h">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClInclude>
<ClInclude Include="GS\Renderers\Vulkan\VKBuilders.h">
<Filter>System\Ps2\GS\Renderers\Vulkan</Filter>
</ClInclude>