2016-01-03 23:09:37 +01:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
# include <algorithm>
# include <cstring>
# include "ext/xxhash.h"
2020-10-04 00:25:21 +02:00
# include "Common/File/VFS/VFS.h"
2020-10-01 13:05:04 +02:00
# include "Common/Data/Text/I18n.h"
2020-10-04 00:25:21 +02:00
# include "Common/Math/math_util.h"
2020-10-04 10:04:01 +02:00
# include "Common/Profiler/Profiler.h"
2020-10-04 23:24:14 +02:00
# include "Common/GPU/thin3d.h"
# include "Common/GPU/Vulkan/VulkanRenderManager.h"
2017-10-30 15:50:02 +01:00
2021-05-01 07:15:04 -07:00
# include "Common/Data/Convert/ColorConv.h"
2020-09-29 12:19:22 +02:00
# include "Common/StringUtils.h"
2021-10-17 08:54:45 -07:00
# include "Common/TimeUtil.h"
2016-01-03 23:09:37 +01:00
# include "Core/Config.h"
# include "Core/Host.h"
# include "Core/MemMap.h"
# include "Core/Reporting.h"
# include "Core/System.h"
2016-03-17 11:56:43 +01:00
2020-10-04 23:24:14 +02:00
# include "Common/GPU/Vulkan/VulkanContext.h"
# include "Common/GPU/Vulkan/VulkanImage.h"
# include "Common/GPU/Vulkan/VulkanMemory.h"
2016-03-17 11:56:43 +01:00
2016-01-03 23:09:37 +01:00
# include "GPU/ge_constants.h"
# include "GPU/GPUState.h"
2020-08-01 22:00:04 -07:00
# include "GPU/Common/PostShader.h"
2020-03-08 11:58:00 -07:00
# include "GPU/Common/TextureCacheCommon.h"
# include "GPU/Common/TextureDecoder.h"
2021-07-11 11:26:25 +02:00
# include "GPU/Vulkan/VulkanContext.h"
2016-01-03 00:46:41 +01:00
# include "GPU/Vulkan/TextureCacheVulkan.h"
2020-08-03 23:22:11 +02:00
# include "GPU/Vulkan/FramebufferManagerVulkan.h"
2016-01-03 23:09:37 +01:00
# include "GPU/Vulkan/DepalettizeShaderVulkan.h"
# include "GPU/Vulkan/ShaderManagerVulkan.h"
# include "GPU/Vulkan/DrawEngineVulkan.h"
2021-08-21 12:39:15 +02:00
using namespace PPSSPP_VK ;
2021-08-20 11:22:57 +02:00
2018-04-06 21:40:18 -07:00
# define TEXCACHE_MIN_SLAB_SIZE (8 * 1024 * 1024)
2016-03-26 18:22:21 -07:00
# define TEXCACHE_MAX_SLAB_SIZE (32 * 1024 * 1024)
2018-04-06 21:25:35 -07:00
# define TEXCACHE_SLAB_PRESSURE 4
2016-01-03 23:09:37 +01:00
2017-12-11 12:22:24 +01:00
const char * uploadShader = R " (
# version 450
# extension GL_ARB_separate_shader_objects : enable
2021-11-07 15:47:05 +01:00
// 8x8 is the most common compute shader workgroup size, and works great on all major
// hardware vendors.
layout ( local_size_x = 8 , local_size_y = 8 , local_size_z = 1 ) in ;
2017-12-11 12:22:24 +01:00
2017-12-11 13:23:06 +01:00
uniform layout ( binding = 0 , rgba8 ) writeonly image2D img ;
2017-12-11 12:22:24 +01:00
2017-12-11 13:23:06 +01:00
layout ( std430 , binding = 1 ) buffer Buf {
uint data [ ] ;
} buf ;
2017-12-11 12:22:24 +01:00
layout ( push_constant ) uniform Params {
int width ;
int height ;
} params ;
2019-09-29 16:14:34 -07:00
uint readColoru ( uvec2 p ) {
2021-11-07 16:08:57 +01:00
return buf . data [ p . y * params . width + p . x ] ;
2019-09-29 16:14:34 -07:00
}
vec4 readColorf ( uvec2 p ) {
2017-12-11 12:22:24 +01:00
// Unpack the color (we could look it up in a CLUT here if we wanted...)
2021-11-07 12:55:39 +01:00
// The imageStore repack is free.
2019-09-29 16:14:34 -07:00
return unpackUnorm4x8 ( readColoru ( p ) ) ;
}
2021-11-07 15:47:05 +01:00
void writeColorf ( ivec2 p , vec4 c ) {
imageStore ( img , p , c ) ;
}
2019-09-29 16:14:34 -07:00
% s
2021-11-07 15:47:05 +01:00
// Note that main runs once per INPUT pixel, unlike the old model.
2019-09-29 16:14:34 -07:00
void main ( ) {
uvec2 xy = gl_GlobalInvocationID . xy ;
// Kill off any out-of-image threads to avoid stray writes.
// Should only happen on the tiniest mipmaps as PSP textures are power-of-2,
2021-11-07 15:47:05 +01:00
// and we use a 8x8 workgroup size. Probably not really necessary.
2019-09-29 16:14:34 -07:00
if ( xy . x > = params . width | | xy . y > = params . height )
return ;
2021-11-07 15:47:05 +01:00
// applyScaling will write the upscaled pixels, using writeColorf above.
// It's expected to write a square of scale*scale pixels, at the location xy*scale.
applyScaling ( xy ) ;
2017-12-11 12:22:24 +01:00
}
2021-11-07 15:47:05 +01:00
2017-12-11 12:22:24 +01:00
) " ;
2016-01-09 01:23:32 +01:00
SamplerCache : : ~ SamplerCache ( ) {
2017-08-20 19:03:16 +02:00
DeviceLost ( ) ;
2016-01-09 01:23:32 +01:00
}
2016-01-03 23:09:37 +01:00
2016-01-09 01:23:32 +01:00
VkSampler SamplerCache : : GetOrCreateSampler ( const SamplerCacheKey & key ) {
2017-08-20 19:03:16 +02:00
VkSampler sampler = cache_ . Get ( key ) ;
if ( sampler ! = VK_NULL_HANDLE )
return sampler ;
2016-01-09 01:23:32 +01:00
2016-03-15 00:37:14 +01:00
VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO } ;
2016-01-09 01:23:32 +01:00
samp . addressModeU = key . sClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT ;
samp . addressModeV = key . tClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT ;
2017-03-03 14:15:27 +01:00
samp . addressModeW = samp . addressModeU ; // irrelevant, but Mali recommends that all clamp modes are the same if possible.
2016-01-09 21:19:18 +01:00
samp . compareOp = VK_COMPARE_OP_ALWAYS ;
2016-01-09 01:23:32 +01:00
samp . flags = 0 ;
2016-01-10 13:08:54 +01:00
samp . magFilter = key . magFilt ? VK_FILTER_LINEAR : VK_FILTER_NEAREST ;
2016-03-15 00:37:14 +01:00
samp . minFilter = key . minFilt ? VK_FILTER_LINEAR : VK_FILTER_NEAREST ;
samp . mipmapMode = key . mipFilt ? VK_SAMPLER_MIPMAP_MODE_LINEAR : VK_SAMPLER_MIPMAP_MODE_NEAREST ;
2017-11-15 19:07:41 +01:00
if ( key . aniso ) {
2016-03-17 21:56:04 -07:00
// Docs say the min of this value and the supported max are used.
samp . maxAnisotropy = 1 < < g_Config . iAnisotropyLevel ;
2016-03-15 00:37:14 +01:00
samp . anisotropyEnable = true ;
2016-03-17 21:59:16 -07:00
} else {
samp . maxAnisotropy = 1.0f ;
samp . anisotropyEnable = false ;
2016-03-15 00:37:14 +01:00
}
2017-11-15 16:31:17 +01:00
samp . maxLod = ( float ) ( int32_t ) key . maxLevel * ( 1.0f / 256.0f ) ;
samp . minLod = ( float ) ( int32_t ) key . minLevel * ( 1.0f / 256.0f ) ;
samp . mipLodBias = ( float ) ( int32_t ) key . lodBias * ( 1.0f / 256.0f ) ;
2016-01-09 01:23:32 +01:00
VkResult res = vkCreateSampler ( vulkan_ - > GetDevice ( ) , & samp , nullptr , & sampler ) ;
2020-08-16 00:38:55 +02:00
_assert_ ( res = = VK_SUCCESS ) ;
2017-08-20 19:03:16 +02:00
cache_ . Insert ( key , sampler ) ;
2016-01-09 01:23:32 +01:00
return sampler ;
}
2017-12-06 16:01:56 +01:00
std : : string SamplerCache : : DebugGetSamplerString ( std : : string id , DebugShaderStringType stringType ) {
SamplerCacheKey key ;
key . FromString ( id ) ;
return StringFromFormat ( " %s/%s mag:%s min:%s mip:%s maxLod:%f minLod:%f bias:%f " ,
key . sClamp ? " Clamp " : " Wrap " ,
key . tClamp ? " Clamp " : " Wrap " ,
key . magFilt ? " Linear " : " Nearest " ,
key . minFilt ? " Linear " : " Nearest " ,
key . mipFilt ? " Linear " : " Nearest " ,
key . maxLevel / 256.0f ,
key . minLevel / 256.0f ,
key . lodBias / 256.0f ) ;
}
2016-10-09 11:26:44 -07:00
void SamplerCache : : DeviceLost ( ) {
2017-08-20 19:03:16 +02:00
cache_ . Iterate ( [ & ] ( const SamplerCacheKey & key , VkSampler sampler ) {
vulkan_ - > Delete ( ) . QueueDeleteSampler ( sampler ) ;
} ) ;
cache_ . Clear ( ) ;
2016-10-09 11:26:44 -07:00
}
void SamplerCache : : DeviceRestore ( VulkanContext * vulkan ) {
vulkan_ = vulkan ;
}
2017-12-06 16:01:56 +01:00
std : : vector < std : : string > SamplerCache : : DebugGetSamplerIDs ( ) const {
std : : vector < std : : string > ids ;
cache_ . Iterate ( [ & ] ( const SamplerCacheKey & id , VkSampler sampler ) {
std : : string idstr ;
id . ToString ( & idstr ) ;
ids . push_back ( idstr ) ;
} ) ;
return ids ;
}
2017-02-05 19:51:50 +01:00
TextureCacheVulkan : : TextureCacheVulkan ( Draw : : DrawContext * draw , VulkanContext * vulkan )
: TextureCacheCommon ( draw ) ,
2021-02-15 10:29:34 -08:00
computeShaderManager_ ( vulkan ) ,
samplerCache_ ( vulkan ) {
2021-11-14 15:25:28 -08:00
DeviceRestore ( draw ) ;
2016-01-03 23:09:37 +01:00
}
TextureCacheVulkan : : ~ TextureCacheVulkan ( ) {
2017-11-10 12:40:51 +01:00
DeviceLost ( ) ;
2016-01-03 23:09:37 +01:00
}
2017-02-08 15:58:46 +01:00
void TextureCacheVulkan : : SetFramebufferManager ( FramebufferManagerVulkan * fbManager ) {
framebufferManager_ = fbManager ;
}
2017-10-30 12:05:08 +01:00
void TextureCacheVulkan : : SetVulkan2D ( Vulkan2D * vk2d ) {
vulkan2D_ = vk2d ;
depalShaderCache_ - > SetVulkan2D ( vk2d ) ;
}
2016-10-09 11:26:44 -07:00
void TextureCacheVulkan : : DeviceLost ( ) {
2022-02-06 23:34:44 -08:00
VulkanContext * vulkan = draw_ ? ( VulkanContext * ) draw_ - > GetNativeObject ( Draw : : NativeObject : : CONTEXT ) : nullptr ;
2021-11-14 15:25:28 -08:00
2016-10-09 11:26:44 -07:00
Clear ( true ) ;
samplerCache_ . DeviceLost ( ) ;
2018-03-22 22:10:05 +01:00
2018-03-23 04:14:10 +01:00
if ( samplerNearest_ )
2021-11-14 15:25:28 -08:00
vulkan - > Delete ( ) . QueueDeleteSampler ( samplerNearest_ ) ;
2016-10-09 11:26:44 -07:00
2019-09-30 00:36:42 -07:00
if ( uploadCS_ ! = VK_NULL_HANDLE )
2021-11-14 15:25:28 -08:00
vulkan - > Delete ( ) . QueueDeleteShaderModule ( uploadCS_ ) ;
2017-12-11 13:23:06 +01:00
computeShaderManager_ . DeviceLost ( ) ;
2017-12-11 11:26:05 +01:00
2016-10-09 11:26:44 -07:00
nextTexture_ = nullptr ;
2022-02-06 23:34:44 -08:00
draw_ = nullptr ;
2016-10-09 11:26:44 -07:00
}
2021-11-14 15:25:28 -08:00
void TextureCacheVulkan : : DeviceRestore ( Draw : : DrawContext * draw ) {
VulkanContext * vulkan = ( VulkanContext * ) draw - > GetNativeObject ( Draw : : NativeObject : : CONTEXT ) ;
2017-11-09 16:28:22 +01:00
draw_ = draw ;
2016-10-09 11:26:44 -07:00
2020-08-16 00:38:55 +02:00
_assert_ ( ! allocator_ ) ;
2017-10-09 12:17:54 +02:00
2016-10-09 11:26:44 -07:00
samplerCache_ . DeviceRestore ( vulkan ) ;
2017-11-10 12:40:51 +01:00
VkSamplerCreateInfo samp { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO } ;
samp . addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
samp . addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
samp . addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT ;
samp . magFilter = VK_FILTER_NEAREST ;
samp . minFilter = VK_FILTER_NEAREST ;
samp . mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST ;
2021-11-14 15:25:28 -08:00
VkResult res = vkCreateSampler ( vulkan - > GetDevice ( ) , & samp , nullptr , & samplerNearest_ ) ;
2020-09-15 23:09:58 +02:00
_assert_ ( res = = VK_SUCCESS ) ;
2017-12-11 11:26:05 +01:00
2020-08-01 22:00:04 -07:00
CompileScalingShader ( ) ;
2017-12-11 12:22:24 +01:00
2017-12-11 13:23:06 +01:00
computeShaderManager_ . DeviceRestore ( vulkan ) ;
2016-10-09 11:26:44 -07:00
}
2020-08-01 22:00:04 -07:00
void TextureCacheVulkan : : NotifyConfigChanged ( ) {
TextureCacheCommon : : NotifyConfigChanged ( ) ;
CompileScalingShader ( ) ;
}
2021-05-09 15:25:12 +02:00
static std : : string ReadShaderSrc ( const Path & filename ) {
2020-08-01 22:00:04 -07:00
size_t sz = 0 ;
char * data = ( char * ) VFSReadFile ( filename . c_str ( ) , & sz ) ;
if ( ! data )
2022-04-13 23:44:03 +02:00
return std : : string ( ) ;
2020-08-01 22:00:04 -07:00
std : : string src ( data , sz ) ;
2021-01-01 15:40:12 -08:00
delete [ ] data ;
2020-08-01 22:00:04 -07:00
return src ;
}
void TextureCacheVulkan : : CompileScalingShader ( ) {
2021-11-14 15:25:28 -08:00
VulkanContext * vulkan = ( VulkanContext * ) draw_ - > GetNativeObject ( Draw : : NativeObject : : CONTEXT ) ;
2020-08-01 22:00:04 -07:00
if ( ! g_Config . bTexHardwareScaling | | g_Config . sTextureShaderName ! = textureShader_ ) {
if ( uploadCS_ ! = VK_NULL_HANDLE )
2021-11-14 15:25:28 -08:00
vulkan - > Delete ( ) . QueueDeleteShaderModule ( uploadCS_ ) ;
2020-08-01 22:00:04 -07:00
textureShader_ . clear ( ) ;
2021-11-07 13:12:28 +01:00
shaderScaleFactor_ = 0 ; // no texture scaling shader
2021-11-07 12:55:39 +01:00
} else if ( uploadCS_ ) {
2021-01-18 13:31:43 -08:00
// No need to recreate.
return ;
2020-08-01 22:00:04 -07:00
}
2021-11-07 12:55:39 +01:00
2020-08-01 22:00:04 -07:00
if ( ! g_Config . bTexHardwareScaling )
return ;
2021-09-28 23:35:57 +02:00
ReloadAllPostShaderInfo ( draw_ ) ;
2020-08-01 22:00:04 -07:00
const TextureShaderInfo * shaderInfo = GetTextureShaderInfo ( g_Config . sTextureShaderName ) ;
if ( ! shaderInfo | | shaderInfo - > computeShaderFile . empty ( ) )
return ;
std : : string shaderSource = ReadShaderSrc ( shaderInfo - > computeShaderFile ) ;
std : : string fullUploadShader = StringFromFormat ( uploadShader , shaderSource . c_str ( ) ) ;
std : : string error ;
2021-11-14 15:25:28 -08:00
uploadCS_ = CompileShaderModule ( vulkan , VK_SHADER_STAGE_COMPUTE_BIT , fullUploadShader . c_str ( ) , & error ) ;
2020-08-01 22:00:04 -07:00
_dbg_assert_msg_ ( uploadCS_ ! = VK_NULL_HANDLE , " failed to compile upload shader " ) ;
textureShader_ = g_Config . sTextureShaderName ;
2021-11-07 13:12:28 +01:00
shaderScaleFactor_ = shaderInfo - > scaleFactor ;
2020-08-01 22:00:04 -07:00
}
2017-02-23 17:31:24 +01:00
void TextureCacheVulkan : : ReleaseTexture ( TexCacheEntry * entry , bool delete_them ) {
2017-02-19 22:31:07 +01:00
delete entry - > vkTex ;
2017-02-23 09:25:33 +01:00
entry - > vkTex = nullptr ;
2016-01-03 23:09:37 +01:00
}
VkFormat getClutDestFormatVulkan ( GEPaletteFormat format ) {
switch ( format ) {
case GE_CMODE_16BIT_ABGR4444 :
2016-01-09 21:19:18 +01:00
return VULKAN_4444_FORMAT ;
2016-01-03 23:09:37 +01:00
case GE_CMODE_16BIT_ABGR5551 :
2016-01-09 21:19:18 +01:00
return VULKAN_1555_FORMAT ;
2016-01-03 23:09:37 +01:00
case GE_CMODE_16BIT_BGR5650 :
2016-01-09 21:19:18 +01:00
return VULKAN_565_FORMAT ;
2016-01-03 23:09:37 +01:00
case GE_CMODE_32BIT_ABGR8888 :
2016-01-09 21:19:18 +01:00
return VULKAN_8888_FORMAT ;
2016-01-03 23:09:37 +01:00
}
return VK_FORMAT_UNDEFINED ;
}
static const VkFilter MagFiltVK [ 2 ] = {
VK_FILTER_NEAREST ,
VK_FILTER_LINEAR
} ;
void TextureCacheVulkan : : StartFrame ( ) {
2017-03-25 11:34:21 -07:00
InvalidateLastTexture ( ) ;
2017-10-30 12:05:08 +01:00
depalShaderCache_ - > Decimate ( ) ;
2016-01-03 23:09:37 +01:00
timesInvalidatedAllThisFrame_ = 0 ;
texelsScaledThisFrame_ = 0 ;
2021-10-17 08:54:45 -07:00
replacementTimeThisFrame_ = 0.0 ;
2016-03-27 08:33:22 -07:00
2016-01-03 23:09:37 +01:00
if ( clearCacheNextFrame_ ) {
Clear ( true ) ;
clearCacheNextFrame_ = false ;
} else {
2018-04-07 10:40:03 +02:00
int slabPressureLimit = TEXCACHE_SLAB_PRESSURE ;
if ( g_Config . iTexScalingLevel > 1 ) {
// Since textures are 2D maybe we should square this, but might get too non-aggressive.
slabPressureLimit * = g_Config . iTexScalingLevel ;
}
2021-11-23 08:53:19 +01:00
// TODO: Use some indication from VMA.
// Maybe see https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/staying_within_budget.html#staying_within_budget_querying_for_budget .
Decimate ( false ) ;
2016-01-03 23:09:37 +01:00
}
2016-03-26 18:22:21 -07:00
2017-12-11 13:23:06 +01:00
computeShaderManager_ . BeginFrame ( ) ;
2016-01-03 23:09:37 +01:00
}
2016-03-27 08:33:22 -07:00
void TextureCacheVulkan : : EndFrame ( ) {
2017-12-11 13:23:06 +01:00
computeShaderManager_ . EndFrame ( ) ;
2016-03-27 08:33:22 -07:00
if ( texelsScaledThisFrame_ ) {
2020-09-13 15:57:26 +02:00
VERBOSE_LOG ( G3D , " Scaled %i texels " , texelsScaledThisFrame_ ) ;
2016-03-27 08:33:22 -07:00
}
}
2016-01-03 23:09:37 +01:00
void TextureCacheVulkan : : UpdateCurrentClut ( GEPaletteFormat clutFormat , u32 clutBase , bool clutIndexIsSimple ) {
const u32 clutBaseBytes = clutFormat = = GE_CMODE_32BIT_ABGR8888 ? ( clutBase * sizeof ( u32 ) ) : ( clutBase * sizeof ( u16 ) ) ;
// Technically, these extra bytes weren't loaded, but hopefully it was loaded earlier.
// If not, we're going to hash random data, which hopefully doesn't cause a performance issue.
//
// TODO: Actually, this seems like a hack. The game can upload part of a CLUT and reference other data.
// clutTotalBytes_ is the last amount uploaded. We should hash clutMaxBytes_, but this will often hash
// unrelated old entries for small palettes.
// Adding clutBaseBytes may just be mitigating this for some usage patterns.
const u32 clutExtendedBytes = std : : min ( clutTotalBytes_ + clutBaseBytes , clutMaxBytes_ ) ;
2020-08-27 20:46:39 -07:00
if ( replacer_ . Enabled ( ) )
2020-08-28 01:15:22 -07:00
clutHash_ = XXH32 ( ( const char * ) clutBufRaw_ , clutExtendedBytes , 0xC0108888 ) ;
2020-08-27 20:46:39 -07:00
else
clutHash_ = XXH3_64bits ( ( const char * ) clutBufRaw_ , clutExtendedBytes ) & 0xFFFFFFFF ;
2016-03-26 13:17:17 -07:00
clutBuf_ = clutBufRaw_ ;
2016-01-03 23:09:37 +01:00
// Special optimization: fonts typically draw clut4 with just alpha values in a single color.
clutAlphaLinear_ = false ;
clutAlphaLinearColor_ = 0 ;
if ( clutFormat = = GE_CMODE_16BIT_ABGR4444 & & clutIndexIsSimple ) {
const u16_le * clut = GetCurrentClut < u16_le > ( ) ;
clutAlphaLinear_ = true ;
2016-03-26 16:13:51 -07:00
clutAlphaLinearColor_ = clut [ 15 ] & 0x0FFF ;
2016-01-03 23:09:37 +01:00
for ( int i = 0 ; i < 16 ; + + i ) {
2016-03-26 16:13:51 -07:00
u16 step = clutAlphaLinearColor_ | ( i < < 12 ) ;
2016-01-03 23:09:37 +01:00
if ( clut [ i ] ! = step ) {
clutAlphaLinear_ = false ;
break ;
}
}
}
clutLastFormat_ = gstate . clutformat ;
}
2017-02-19 23:07:00 +01:00
void TextureCacheVulkan : : BindTexture ( TexCacheEntry * entry ) {
2020-09-20 20:35:42 +02:00
_assert_ ( entry ) ;
_assert_ ( entry - > vkTex ) ;
2017-02-19 23:07:00 +01:00
2018-04-06 21:17:31 -07:00
entry - > vkTex - > Touch ( ) ;
2018-04-05 23:23:14 +02:00
imageView_ = entry - > vkTex - > GetImageView ( ) ;
2020-09-13 15:57:26 +02:00
int maxLevel = ( entry - > status & TexCacheEntry : : STATUS_BAD_MIPS ) ? 0 : entry - > maxLevel ;
2021-02-27 17:17:21 -08:00
SamplerCacheKey samplerKey = GetSamplingParams ( maxLevel , entry ) ;
2020-09-13 23:46:57 +02:00
curSampler_ = samplerCache_ . GetOrCreateSampler ( samplerKey ) ;
2018-04-13 12:25:57 +02:00
drawEngine_ - > SetDepalTexture ( VK_NULL_HANDLE ) ;
2018-05-04 22:20:41 +02:00
gstate_c . SetUseShaderDepal ( false ) ;
2017-02-19 23:07:00 +01:00
}
2017-02-08 15:43:53 +01:00
void TextureCacheVulkan : : Unbind ( ) {
2017-02-19 23:19:55 +01:00
imageView_ = VK_NULL_HANDLE ;
2017-10-30 15:50:02 +01:00
curSampler_ = VK_NULL_HANDLE ;
2017-03-25 11:34:21 -07:00
InvalidateLastTexture ( ) ;
2016-01-03 23:09:37 +01:00
}
2020-09-12 14:25:50 +02:00
void TextureCacheVulkan : : ApplyTextureFramebuffer ( VirtualFramebuffer * framebuffer , GETextureFormat texFormat , FramebufferNotificationChannel channel ) {
2020-09-13 23:46:57 +02:00
SamplerCacheKey samplerKey = GetFramebufferSamplingParams ( framebuffer - > bufferWidth , framebuffer - > bufferHeight ) ;
2018-04-13 12:25:57 +02:00
2017-10-30 12:05:08 +01:00
DepalShaderVulkan * depalShader = nullptr ;
2017-11-05 10:37:01 +01:00
uint32_t clutMode = gstate . clutformat & 0xFFFFFF ;
2018-04-13 13:47:45 +02:00
2020-03-01 14:07:13 +01:00
bool expand32 = ! gstate_c . Supports ( GPU_SUPPORTS_16BIT_FORMATS ) ;
2020-09-12 14:25:50 +02:00
bool depth = channel = = NOTIFY_FB_DEPTH ;
2017-11-22 12:24:05 +01:00
bool useShaderDepal = framebufferManager_ - > GetCurrentRenderVFB ( ) ! = framebuffer & & ! depth ;
2018-04-13 13:47:45 +02:00
2020-09-12 14:25:50 +02:00
bool need_depalettize = IsClutFormat ( texFormat ) ;
if ( need_depalettize & & ! g_Config . bDisableSlowFramebufEffects ) {
2018-04-13 12:25:57 +02:00
if ( useShaderDepal ) {
depalShaderCache_ - > SetPushBuffer ( drawEngine_ - > GetPushBufferForTextureData ( ) ) ;
const GEPaletteFormat clutFormat = gstate . getClutPaletteFormat ( ) ;
2020-03-01 14:07:13 +01:00
VulkanTexture * clutTexture = depalShaderCache_ - > GetClutTexture ( clutFormat , clutHash_ , clutBuf_ , expand32 ) ;
2019-02-24 22:22:51 +01:00
drawEngine_ - > SetDepalTexture ( clutTexture ? clutTexture - > GetImageView ( ) : VK_NULL_HANDLE ) ;
2018-04-13 12:25:57 +02:00
// Only point filtering enabled.
samplerKey . magFilt = false ;
samplerKey . minFilt = false ;
samplerKey . mipFilt = false ;
2018-05-04 22:20:41 +02:00
// Make sure to update the uniforms, and also texture - needs a recheck.
2018-04-13 12:25:57 +02:00
gstate_c . Dirty ( DIRTY_DEPAL ) ;
2018-05-04 22:20:41 +02:00
gstate_c . SetUseShaderDepal ( true ) ;
2018-04-13 12:25:57 +02:00
gstate_c . depalFramebufferFormat = framebuffer - > drawnFormat ;
2018-04-13 13:47:45 +02:00
const u32 bytesPerColor = clutFormat = = GE_CMODE_32BIT_ABGR8888 ? sizeof ( u32 ) : sizeof ( u16 ) ;
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor ;
TexCacheEntry : : TexStatus alphaStatus = CheckAlpha ( clutBuf_ , getClutDestFormatVulkan ( clutFormat ) , clutTotalColors , clutTotalColors , 1 ) ;
gstate_c . SetTextureFullAlpha ( alphaStatus = = TexCacheEntry : : STATUS_ALPHA_FULL ) ;
curSampler_ = samplerCache_ . GetOrCreateSampler ( samplerKey ) ;
2021-11-14 15:25:28 -08:00
if ( framebufferManager_ - > BindFramebufferAsColorTexture ( 0 , framebuffer , BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET ) ) {
2020-11-07 11:03:53 +01:00
imageView_ = ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : BOUND_TEXTURE0_IMAGEVIEW ) ;
} else {
imageView_ = ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : NULL_IMAGEVIEW ) ;
}
2018-04-13 13:47:45 +02:00
return ;
2018-04-13 12:25:57 +02:00
} else {
2017-11-22 12:24:05 +01:00
depalShader = depalShaderCache_ - > GetDepalettizeShader ( clutMode , depth ? GE_FORMAT_DEPTH16 : framebuffer - > drawnFormat ) ;
2018-05-04 22:57:13 +02:00
drawEngine_ - > SetDepalTexture ( VK_NULL_HANDLE ) ;
2018-05-04 22:20:41 +02:00
gstate_c . SetUseShaderDepal ( false ) ;
2018-04-13 12:25:57 +02:00
}
2016-01-03 23:09:37 +01:00
}
2017-10-30 12:05:08 +01:00
if ( depalShader ) {
depalShaderCache_ - > SetPushBuffer ( drawEngine_ - > GetPushBufferForTextureData ( ) ) ;
2017-11-05 10:37:01 +01:00
const GEPaletteFormat clutFormat = gstate . getClutPaletteFormat ( ) ;
2020-03-01 14:07:13 +01:00
VulkanTexture * clutTexture = depalShaderCache_ - > GetClutTexture ( clutFormat , clutHash_ , clutBuf_ , expand32 ) ;
2017-10-30 12:05:08 +01:00
2020-11-05 14:51:46 +01:00
Draw : : Framebuffer * depalFBO = framebufferManager_ - > GetTempFBO ( TempFBO : : DEPAL , framebuffer - > renderWidth , framebuffer - > renderHeight ) ;
2020-05-21 11:24:05 +02:00
draw_ - > BindFramebufferAsRenderTarget ( depalFBO , { Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE , Draw : : RPAction : : DONT_CARE } , " Depal " ) ;
2017-10-30 12:05:08 +01:00
Vulkan2D : : Vertex verts [ 4 ] = {
2017-10-31 12:02:10 +01:00
{ - 1 , - 1 , 0.0f , 0 , 0 } ,
{ 1 , - 1 , 0.0f , 1 , 0 } ,
{ - 1 , 1 , 0.0f , 0 , 1 } ,
{ 1 , 1 , 0.0f , 1 , 1 } ,
2016-01-03 23:09:37 +01:00
} ;
// If min is not < max, then we don't have values (wasn't set during decode.)
if ( gstate_c . vertBounds . minV < gstate_c . vertBounds . maxV ) {
const float invWidth = 1.0f / ( float ) framebuffer - > bufferWidth ;
const float invHeight = 1.0f / ( float ) framebuffer - > bufferHeight ;
// Inverse of half = double.
const float invHalfWidth = invWidth * 2.0f ;
const float invHalfHeight = invHeight * 2.0f ;
const int u1 = gstate_c . vertBounds . minU + gstate_c . curTextureXOffset ;
const int v1 = gstate_c . vertBounds . minV + gstate_c . curTextureYOffset ;
const int u2 = gstate_c . vertBounds . maxU + gstate_c . curTextureXOffset ;
const int v2 = gstate_c . vertBounds . maxV + gstate_c . curTextureYOffset ;
const float left = u1 * invHalfWidth - 1.0f ;
const float right = u2 * invHalfWidth - 1.0f ;
const float top = v1 * invHalfHeight - 1.0f ;
const float bottom = v2 * invHalfHeight - 1.0f ;
// Points are: BL, BR, TR, TL.
2017-10-30 12:05:08 +01:00
verts [ 0 ] . x = left ;
verts [ 0 ] . y = bottom ;
verts [ 1 ] . x = right ;
verts [ 1 ] . y = bottom ;
2017-10-31 12:02:10 +01:00
verts [ 2 ] . x = left ;
2017-10-30 12:05:08 +01:00
verts [ 2 ] . y = top ;
2017-10-31 12:02:10 +01:00
verts [ 3 ] . x = right ;
2017-10-30 12:05:08 +01:00
verts [ 3 ] . y = top ;
2016-01-03 23:09:37 +01:00
// And also the UVs, same order.
const float uvleft = u1 * invWidth ;
const float uvright = u2 * invWidth ;
const float uvtop = v1 * invHeight ;
const float uvbottom = v2 * invHeight ;
2017-10-30 12:05:08 +01:00
verts [ 0 ] . u = uvleft ;
verts [ 0 ] . v = uvbottom ;
verts [ 1 ] . u = uvright ;
verts [ 1 ] . v = uvbottom ;
2017-10-31 12:02:10 +01:00
verts [ 2 ] . u = uvleft ;
2017-10-30 12:05:08 +01:00
verts [ 2 ] . v = uvtop ;
2017-10-31 12:02:10 +01:00
verts [ 3 ] . u = uvright ;
2017-10-30 12:05:08 +01:00
verts [ 3 ] . v = uvtop ;
2018-04-22 10:43:46 -07:00
// We need to reapply the texture next time since we cropped UV.
gstate_c . Dirty ( DIRTY_TEXTURE_PARAMS ) ;
2016-01-03 23:09:37 +01:00
}
2017-10-30 15:50:02 +01:00
VkBuffer pushed ;
uint32_t offset = push_ - > PushAligned ( verts , sizeof ( verts ) , 4 , & pushed ) ;
2017-10-31 10:54:01 +01:00
2017-11-22 12:24:05 +01:00
draw_ - > BindFramebufferAsTexture ( framebuffer - > fbo , 0 , depth ? Draw : : FB_DEPTH_BIT : Draw : : FB_COLOR_BIT , 0 ) ;
2017-10-31 12:02:10 +01:00
VkImageView fbo = ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : BOUND_TEXTURE0_IMAGEVIEW ) ;
2017-10-30 15:50:02 +01:00
VkDescriptorSet descSet = vulkan2D_ - > GetDescriptorSet ( fbo , samplerNearest_ , clutTexture - > GetImageView ( ) , samplerNearest_ ) ;
VulkanRenderManager * renderManager = ( VulkanRenderManager * ) draw_ - > GetNativeObject ( Draw : : NativeObject : : RENDER_MANAGER ) ;
2020-10-11 11:47:24 +02:00
renderManager - > BindPipeline ( depalShader - > pipeline , ( PipelineFlags ) 0 ) ;
2017-11-22 12:24:05 +01:00
if ( depth ) {
DepthScaleFactors scaleFactors = GetDepthScaleFactors ( ) ;
struct DepthPushConstants {
float z_scale ;
float z_offset ;
} ;
DepthPushConstants push ;
push . z_scale = scaleFactors . scale ;
push . z_offset = scaleFactors . offset ;
renderManager - > PushConstants ( vulkan2D_ - > GetPipelineLayout ( ) , VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT , 0 , sizeof ( DepthPushConstants ) , & push ) ;
}
2021-12-08 22:34:47 +01:00
renderManager - > SetScissor ( 0 , 0 , ( int ) framebuffer - > renderWidth , ( int ) framebuffer - > renderHeight ) ;
2017-10-31 12:02:10 +01:00
renderManager - > SetViewport ( VkViewport { 0.f , 0.f , ( float ) framebuffer - > renderWidth , ( float ) framebuffer - > renderHeight , 0.f , 1.f } ) ;
2017-10-30 15:50:02 +01:00
renderManager - > Draw ( vulkan2D_ - > GetPipelineLayout ( ) , descSet , 0 , nullptr , pushed , offset , 4 ) ;
2017-02-19 23:07:00 +01:00
shaderManagerVulkan_ - > DirtyLastShader ( ) ;
2016-01-03 23:09:37 +01:00
2016-05-21 15:40:58 -07:00
const u32 bytesPerColor = clutFormat = = GE_CMODE_32BIT_ABGR8888 ? sizeof ( u32 ) : sizeof ( u16 ) ;
const u32 clutTotalColors = clutMaxBytes_ / bytesPerColor ;
2017-12-13 23:11:40 +01:00
TexCacheEntry : : TexStatus alphaStatus = CheckAlpha ( clutBuf_ , getClutDestFormatVulkan ( clutFormat ) , clutTotalColors , clutTotalColors , 1 ) ;
2017-04-03 17:04:58 +02:00
gstate_c . SetTextureFullAlpha ( alphaStatus = = TexCacheEntry : : STATUS_ALPHA_FULL ) ;
2017-05-22 10:42:40 +02:00
2020-06-02 09:51:38 +02:00
framebufferManager_ - > RebindFramebuffer ( " RebindFramebuffer - ApplyTextureFramebuffer " ) ;
2017-10-30 12:05:08 +01:00
draw_ - > BindFramebufferAsTexture ( depalFBO , 0 , Draw : : FB_COLOR_BIT , 0 ) ;
2017-10-31 12:02:10 +01:00
imageView_ = ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : BOUND_TEXTURE0_IMAGEVIEW ) ;
2017-10-31 10:54:01 +01:00
// Need to rebind the pipeline since we switched it.
drawEngine_ - > DirtyPipeline ( ) ;
2018-04-12 11:59:18 +02:00
// Since we may have switched render targets, we need to re-set depth/stencil etc states.
gstate_c . Dirty ( DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE ) ;
2016-05-21 15:40:58 -07:00
} else {
2021-11-14 15:25:28 -08:00
if ( framebufferManager_ - > BindFramebufferAsColorTexture ( 0 , framebuffer , BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET ) ) {
2020-11-07 11:03:53 +01:00
imageView_ = ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : BOUND_TEXTURE0_IMAGEVIEW ) ;
} else {
imageView_ = ( VkImageView ) draw_ - > GetNativeObject ( Draw : : NativeObject : : NULL_IMAGEVIEW ) ;
}
2018-05-04 22:57:13 +02:00
drawEngine_ - > SetDepalTexture ( VK_NULL_HANDLE ) ;
2018-05-04 22:20:41 +02:00
gstate_c . SetUseShaderDepal ( false ) ;
2017-05-22 10:42:40 +02:00
2017-04-03 17:04:58 +02:00
gstate_c . SetTextureFullAlpha ( gstate . getTextureFormat ( ) = = GE_TFMT_5650 ) ;
2016-01-03 23:09:37 +01:00
}
2020-09-12 14:25:50 +02:00
2017-10-30 15:50:02 +01:00
curSampler_ = samplerCache_ . GetOrCreateSampler ( samplerKey ) ;
2016-01-03 23:09:37 +01:00
}
2016-04-30 13:44:31 -07:00
ReplacedTextureFormat FromVulkanFormat ( VkFormat fmt ) {
switch ( fmt ) {
2017-02-08 15:48:36 +01:00
case VULKAN_565_FORMAT : return ReplacedTextureFormat : : F_5650 ;
case VULKAN_1555_FORMAT : return ReplacedTextureFormat : : F_5551 ;
case VULKAN_4444_FORMAT : return ReplacedTextureFormat : : F_4444 ;
case VULKAN_8888_FORMAT : default : return ReplacedTextureFormat : : F_8888 ;
2016-04-30 13:44:31 -07:00
}
}
VkFormat ToVulkanFormat ( ReplacedTextureFormat fmt ) {
switch ( fmt ) {
2017-02-08 15:48:36 +01:00
case ReplacedTextureFormat : : F_5650 : return VULKAN_565_FORMAT ;
case ReplacedTextureFormat : : F_5551 : return VULKAN_1555_FORMAT ;
case ReplacedTextureFormat : : F_4444 : return VULKAN_4444_FORMAT ;
case ReplacedTextureFormat : : F_8888 : default : return VULKAN_8888_FORMAT ;
2016-04-30 13:44:31 -07:00
}
}
2018-03-25 10:49:28 +02:00
void TextureCacheVulkan : : BuildTexture ( TexCacheEntry * const entry ) {
2016-05-01 17:27:14 -07:00
entry - > status & = ~ TexCacheEntry : : STATUS_ALPHA_MASK ;
2016-01-03 23:09:37 +01:00
// For the estimate, we assume cluts always point to 8888 for simplicity.
cacheSizeEstimate_ + = EstimateTexMemoryUsage ( entry ) ;
2016-05-01 17:27:14 -07:00
if ( ( entry - > bufw = = 0 | | ( gstate . texbufwidth [ 0 ] & 0xf800 ) ! = 0 ) & & entry - > addr > = PSP_GetKernelMemoryEnd ( ) ) {
ERROR_LOG_REPORT ( G3D , " Texture with unexpected bufw (full=%d) " , gstate . texbufwidth [ 0 ] & 0xffff ) ;
// Proceeding here can cause a crash.
2016-01-03 23:09:37 +01:00
return ;
}
2021-12-16 20:54:14 +01:00
VulkanContext * vulkan = ( VulkanContext * ) draw_ - > GetNativeObject ( Draw : : NativeObject : : CONTEXT ) ;
2016-01-03 23:09:37 +01:00
// Adjust maxLevel to actually present levels..
bool badMipSizes = false ;
2020-10-05 21:34:22 +02:00
// maxLevel here is the max level to upload. Not the count.
2016-05-01 17:27:14 -07:00
int maxLevel = entry - > maxLevel ;
2020-10-05 21:34:22 +02:00
2016-05-01 17:27:14 -07:00
for ( int i = 0 ; i < = maxLevel ; i + + ) {
2016-01-03 23:09:37 +01:00
// If encountering levels pointing to nothing, adjust max level.
u32 levelTexaddr = gstate . getTextureAddress ( i ) ;
if ( ! Memory : : IsValidAddress ( levelTexaddr ) ) {
maxLevel = i - 1 ;
break ;
}
2017-04-01 21:17:58 +02:00
// If size reaches 1, stop, and override maxlevel.
int tw = gstate . getTextureWidth ( i ) ;
int th = gstate . getTextureHeight ( i ) ;
if ( tw = = 1 | | th = = 1 ) {
maxLevel = i ;
break ;
}
2016-01-03 23:09:37 +01:00
if ( i > 0 & & gstate_c . Supports ( GPU_SUPPORTS_TEXTURE_LOD_CONTROL ) ) {
if ( tw ! = 1 & & tw ! = ( gstate . getTextureWidth ( i - 1 ) > > 1 ) )
badMipSizes = true ;
else if ( th ! = 1 & & th ! = ( gstate . getTextureHeight ( i - 1 ) > > 1 ) )
badMipSizes = true ;
}
}
// In addition, simply don't load more than level 0 if g_Config.bMipMap is false.
2017-05-26 10:16:43 +02:00
if ( badMipSizes ) {
2016-01-03 23:09:37 +01:00
maxLevel = 0 ;
}
2020-10-05 21:34:22 +02:00
// We generate missing mipmaps from maxLevel+1 up to this level. maxLevel can get overwritten below
// such as when using replacement textures - but let's keep the same amount of levels.
int maxLevelToGenerate = maxLevel ;
2021-09-07 23:41:03 +02:00
VkFormat dstFmt = GetDestFormat ( GETextureFormat ( entry - > format ) , gstate . getClutPaletteFormat ( ) ) ;
2016-03-26 11:44:22 -07:00
int scaleFactor = standardScaleFactor_ ;
2021-11-07 13:12:28 +01:00
bool hardwareScaling = g_Config . bTexHardwareScaling & & uploadCS_ ! = VK_NULL_HANDLE ;
if ( hardwareScaling ) {
scaleFactor = shaderScaleFactor_ ;
2021-11-07 16:08:57 +01:00
dstFmt = VK_FORMAT_R8G8B8A8_UNORM ;
2021-11-07 13:12:28 +01:00
}
2016-01-03 23:09:37 +01:00
// Rachet down scale factor in low-memory mode.
2021-11-07 13:12:28 +01:00
// TODO: I think really we should just turn it off?
if ( lowMemoryMode_ & & ! hardwareScaling ) {
2016-01-03 23:09:37 +01:00
// Keep it even, though, just in case of npot troubles.
scaleFactor = scaleFactor > 4 ? 4 : ( scaleFactor > 2 ? 2 : 1 ) ;
}
2016-05-01 17:27:14 -07:00
int w = gstate . getTextureWidth ( 0 ) ;
int h = gstate . getTextureHeight ( 0 ) ;
2021-10-17 12:36:20 -07:00
ReplacedTexture & replaced = FindReplacement ( entry , w , h ) ;
if ( replaced . Valid ( ) ) {
// We're replacing, so we won't scale.
scaleFactor = 1 ;
maxLevel = replaced . MaxLevel ( ) ;
badMipSizes = false ;
2016-04-30 13:44:31 -07:00
}
2016-01-03 23:09:37 +01:00
// Don't scale the PPGe texture.
2021-11-07 13:12:28 +01:00
if ( entry - > addr > 0x05000000 & & entry - > addr < PSP_GetKernelMemoryEnd ( ) ) {
2016-01-03 23:09:37 +01:00
scaleFactor = 1 ;
2021-11-07 13:12:28 +01:00
}
2021-12-16 20:54:14 +01:00
bool slowScaler = ! hardwareScaling | | vulkan - > DevicePerfClass ( ) = = PerfClass : : SLOW ; // Or the GPU is slow - TODO add check!
if ( ( entry - > status & TexCacheEntry : : STATUS_CHANGE_FREQUENT ) ! = 0 & & scaleFactor ! = 1 & & slowScaler ) {
2016-01-03 23:09:37 +01:00
// Remember for later that we /wanted/ to scale this texture.
entry - > status | = TexCacheEntry : : STATUS_TO_SCALE ;
scaleFactor = 1 ;
}
if ( scaleFactor ! = 1 ) {
2021-12-16 20:54:14 +01:00
if ( texelsScaledThisFrame_ > = TEXCACHE_MAX_TEXELS_SCALED & & slowScaler ) {
2016-01-03 23:09:37 +01:00
entry - > status | = TexCacheEntry : : STATUS_TO_SCALE ;
scaleFactor = 1 ;
} else {
entry - > status & = ~ TexCacheEntry : : STATUS_TO_SCALE ;
2016-06-06 19:35:58 -07:00
entry - > status | = TexCacheEntry : : STATUS_IS_SCALED ;
2016-01-03 23:09:37 +01:00
texelsScaledThisFrame_ + = w * h ;
}
}
2021-12-13 09:13:48 +01:00
bool isVideo = IsVideo ( entry - > addr ) ;
2021-12-07 21:28:58 +01:00
// TODO: Support reading actual mip levels for upscaled images, instead of just generating them.
2021-09-05 23:54:41 +02:00
// Probably can just remove this check?
2016-03-21 21:26:48 +01:00
if ( scaleFactor > 1 ) {
maxLevel = 0 ;
2021-12-13 09:13:48 +01:00
bool enableVideoUpscaling = false ;
if ( ! enableVideoUpscaling & & isVideo ) {
scaleFactor = 1 ;
}
2016-03-21 21:26:48 +01:00
}
2021-12-12 18:20:42 +01:00
int maxPossibleMipLevel = log2i ( std : : min ( w * scaleFactor , h * scaleFactor ) ) ;
if ( maxPossibleMipLevel > 0 & & isVideo ) {
maxPossibleMipLevel = 0 ;
}
2021-12-07 21:28:58 +01:00
2021-12-13 09:13:48 +01:00
// We don't generate mipmaps for 512x512 textures because they're almost exclusively used for menu backgrounds
// and similar, which don't really need it.
if ( g_Config . iTexFiltering = = TEX_FILTER_AUTO_MAX_QUALITY & & w < = 256 & & h < = 256 ) {
2021-12-07 21:28:58 +01:00
// Boost the number of mipmaps.
2021-12-12 18:20:42 +01:00
if ( maxPossibleMipLevel > maxLevelToGenerate ) {
2021-12-13 09:13:48 +01:00
// We have to generate mips with a shader. This requires decoding to R8G8B8A8_UNORM format to avoid extra complications.
2021-12-07 21:28:58 +01:00
dstFmt = VK_FORMAT_R8G8B8A8_UNORM ;
}
2021-12-12 18:20:42 +01:00
maxLevelToGenerate = maxPossibleMipLevel ;
2021-12-07 21:28:58 +01:00
}
2021-10-05 22:49:33 +02:00
// Any texture scaling is gonna move away from the original 16-bit format, if any.
2016-03-26 14:28:03 -07:00
VkFormat actualFmt = scaleFactor > 1 ? VULKAN_8888_FORMAT : dstFmt ;
2016-04-30 13:44:31 -07:00
if ( replaced . Valid ( ) ) {
actualFmt = ToVulkanFormat ( replaced . Format ( 0 ) ) ;
}
2017-10-31 23:49:47 +01:00
2017-12-11 12:22:24 +01:00
bool computeUpload = false ;
2020-08-03 23:14:27 +02:00
VkCommandBuffer cmdInit = ( VkCommandBuffer ) draw_ - > GetNativeObject ( Draw : : NativeObject : : INIT_COMMANDBUFFER ) ;
2017-12-11 12:22:24 +01:00
2017-11-01 01:17:01 +01:00
{
2017-10-31 23:49:47 +01:00
delete entry - > vkTex ;
2021-11-14 15:25:28 -08:00
entry - > vkTex = new VulkanTexture ( vulkan ) ;
2018-04-05 23:23:14 +02:00
VulkanTexture * image = entry - > vkTex ;
2016-03-26 13:17:17 -07:00
const VkComponentMapping * mapping ;
2016-03-26 14:28:03 -07:00
switch ( actualFmt ) {
2016-03-26 13:17:17 -07:00
case VULKAN_4444_FORMAT :
mapping = & VULKAN_4444_SWIZZLE ;
break ;
case VULKAN_1555_FORMAT :
mapping = & VULKAN_1555_SWIZZLE ;
break ;
case VULKAN_565_FORMAT :
mapping = & VULKAN_565_SWIZZLE ;
break ;
default :
mapping = & VULKAN_8888_SWIZZLE ;
break ;
}
2017-12-11 12:22:24 +01:00
VkImageLayout imageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ;
2020-10-05 21:34:22 +02:00
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT ;
2017-12-11 12:22:24 +01:00
2021-12-13 09:13:48 +01:00
if ( actualFmt = = VULKAN_8888_FORMAT & & scaleFactor > 1 & & hardwareScaling ) {
2021-12-16 20:54:14 +01:00
if ( uploadCS_ ! = VK_NULL_HANDLE ) {
2019-09-30 00:36:42 -07:00
computeUpload = true ;
2021-12-16 20:54:14 +01:00
} else {
WARN_LOG ( G3D , " Falling back to software scaling, hardware shader didn't compile " ) ;
}
2017-12-11 12:22:24 +01:00
}
if ( computeUpload ) {
usage | = VK_IMAGE_USAGE_STORAGE_BIT ;
imageLayout = VK_IMAGE_LAYOUT_GENERAL ;
}
2018-04-06 21:17:31 -07:00
char texName [ 128 ] { } ;
2021-03-13 17:51:40 +01:00
snprintf ( texName , sizeof ( texName ) , " tex_%08x_%s " , entry - > addr , GeTextureFormatToString ( ( GETextureFormat ) entry - > format , gstate . getClutPaletteFormat ( ) ) ) ;
2018-04-06 21:17:31 -07:00
image - > SetTag ( texName ) ;
2021-11-21 23:38:14 +01:00
bool allocSuccess = image - > CreateDirect ( cmdInit , w * scaleFactor , h * scaleFactor , maxLevelToGenerate + 1 , actualFmt , imageLayout , usage , mapping ) ;
2016-03-26 18:41:53 -07:00
if ( ! allocSuccess & & ! lowMemoryMode_ ) {
WARN_LOG_REPORT ( G3D , " Texture cache ran out of GPU memory; switching to low memory mode " ) ;
lowMemoryMode_ = true ;
decimationCounter_ = 0 ;
Decimate ( ) ;
// TODO: We should stall the GPU here and wipe things out of memory.
// As is, it will almost definitely fail the second time, but next frame it may recover.
2020-01-26 10:43:18 -08:00
auto err = GetI18NCategory ( " Error " ) ;
2016-03-26 18:41:53 -07:00
if ( scaleFactor > 1 ) {
2016-05-27 22:00:14 -07:00
host - > NotifyUserMessage ( err - > T ( " Warning: Video memory FULL, reducing upscaling and switching to slow caching mode " ) , 2.0f ) ;
2016-03-26 18:41:53 -07:00
} else {
2016-05-27 22:00:14 -07:00
host - > NotifyUserMessage ( err - > T ( " Warning: Video memory FULL, switching to slow caching mode " ) , 2.0f ) ;
2016-03-26 18:41:53 -07:00
}
scaleFactor = 1 ;
actualFmt = dstFmt ;
2021-11-21 23:38:14 +01:00
allocSuccess = image - > CreateDirect ( cmdInit , w * scaleFactor , h * scaleFactor , maxLevelToGenerate + 1 , actualFmt , VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL , VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT , mapping ) ;
2016-03-26 18:41:53 -07:00
}
if ( ! allocSuccess ) {
2017-12-03 10:21:51 +01:00
ERROR_LOG ( G3D , " Failed to create texture (%dx%d) " , w , h ) ;
2016-03-26 18:41:53 -07:00
delete entry - > vkTex ;
entry - > vkTex = nullptr ;
}
2016-01-09 21:54:57 +01:00
}
2016-01-09 21:19:18 +01:00
2016-05-01 08:54:43 -07:00
ReplacedTextureDecodeInfo replacedInfo ;
2017-02-20 00:19:58 +01:00
if ( replacer_ . Enabled ( ) & & ! replaced . Valid ( ) ) {
2021-10-17 12:36:20 -07:00
replacedInfo . cachekey = entry - > CacheKey ( ) ;
2016-05-01 08:54:43 -07:00
replacedInfo . hash = entry - > fullhash ;
2016-05-01 17:27:14 -07:00
replacedInfo . addr = entry - > addr ;
2021-12-12 18:20:42 +01:00
replacedInfo . isVideo = isVideo ;
2016-05-01 08:54:43 -07:00
replacedInfo . isFinal = ( entry - > status & TexCacheEntry : : STATUS_TO_SCALE ) = = 0 ;
replacedInfo . scaleFactor = scaleFactor ;
replacedInfo . fmt = FromVulkanFormat ( actualFmt ) ;
}
2016-03-26 18:41:53 -07:00
if ( entry - > vkTex ) {
2021-12-12 12:06:30 +01:00
VK_PROFILE_BEGIN ( vulkan , cmdInit , VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT ,
2021-12-19 19:39:15 +01:00
" Texture Upload (%08x) video=%d " , entry - > addr , isVideo ) ;
2021-12-12 11:56:29 +01:00
2017-05-31 21:42:07 -07:00
// NOTE: Since the level is not part of the cache key, we assume it never changes.
u8 level = std : : max ( 0 , gstate . getTexLevelOffset16 ( ) / 16 ) ;
2017-02-15 00:56:53 +09:00
bool fakeMipmap = IsFakeMipmapChange ( ) & & level > 0 ;
2016-03-26 18:41:53 -07:00
// Upload the texture data.
for ( int i = 0 ; i < = maxLevel ; i + + ) {
2021-11-07 15:47:05 +01:00
int mipUnscaledWidth = gstate . getTextureWidth ( i ) ;
int mipUnscaledHeight = gstate . getTextureHeight ( i ) ;
int mipWidth = mipUnscaledWidth * scaleFactor ;
int mipHeight = mipUnscaledHeight * scaleFactor ;
2016-04-30 13:44:31 -07:00
if ( replaced . Valid ( ) ) {
replaced . GetSize ( i , mipWidth , mipHeight ) ;
}
2016-03-26 18:41:53 -07:00
int bpp = actualFmt = = VULKAN_8888_FORMAT ? 4 : 2 ;
int stride = ( mipWidth * bpp + 15 ) & ~ 15 ;
int size = stride * mipHeight ;
uint32_t bufferOffset ;
VkBuffer texBuf ;
2021-11-07 12:55:39 +01:00
// NVIDIA reports a min alignment of 1 but that can't be healthy... let's align by 16 as a minimum.
2021-11-14 15:25:28 -08:00
int pushAlignment = std : : max ( 16 , ( int ) vulkan - > GetPhysicalDeviceProperties ( ) . properties . limits . optimalBufferCopyOffsetAlignment ) ;
2019-09-29 16:34:37 -07:00
void * data ;
bool dataScaled = true ;
2016-04-30 13:44:31 -07:00
if ( replaced . Valid ( ) ) {
2021-01-30 21:36:01 +01:00
// Directly load the replaced image.
2019-09-29 16:34:37 -07:00
data = drawEngine_ - > GetPushBufferForTextureData ( ) - > PushAligned ( size , & bufferOffset , & texBuf , pushAlignment ) ;
2021-10-17 12:36:20 -07:00
double replaceStart = time_now_d ( ) ;
2021-10-08 15:37:03 +02:00
replaced . Load ( i , data , stride ) ; // if it fails, it'll just be garbage data... OK for now.
2021-10-17 08:54:45 -07:00
replacementTimeThisFrame_ + = time_now_d ( ) - replaceStart ;
2021-12-12 11:56:29 +01:00
VK_PROFILE_BEGIN ( vulkan , cmdInit , VK_PIPELINE_STAGE_TRANSFER_BIT ,
2021-12-19 19:39:15 +01:00
" Copy Upload (replaced): %dx%d " , mipWidth , mipHeight ) ;
2017-12-11 12:22:24 +01:00
entry - > vkTex - > UploadMip ( cmdInit , i , mipWidth , mipHeight , texBuf , bufferOffset , stride / bpp ) ;
2021-12-12 11:56:29 +01:00
VK_PROFILE_END ( vulkan , cmdInit , VK_PIPELINE_STAGE_TRANSFER_BIT ) ;
2016-04-30 13:44:31 -07:00
} else {
2017-02-12 03:27:38 +09:00
if ( fakeMipmap ) {
2019-09-29 16:34:37 -07:00
data = drawEngine_ - > GetPushBufferForTextureData ( ) - > PushAligned ( size , & bufferOffset , & texBuf , pushAlignment ) ;
2017-02-12 03:27:38 +09:00
LoadTextureLevel ( * entry , ( uint8_t * ) data , stride , level , scaleFactor , dstFmt ) ;
2018-04-05 23:23:14 +02:00
entry - > vkTex - > UploadMip ( cmdInit , 0 , mipWidth , mipHeight , texBuf , bufferOffset , stride / bpp ) ;
2017-02-12 03:27:38 +09:00
break ;
2017-05-17 02:21:03 +02:00
} else {
2017-12-11 12:22:24 +01:00
if ( computeUpload ) {
2021-11-07 15:47:05 +01:00
int srcBpp = dstFmt = = VULKAN_8888_FORMAT ? 4 : 2 ;
int srcStride = mipUnscaledWidth * srcBpp ;
int srcSize = srcStride * mipUnscaledHeight ;
2019-09-29 16:34:37 -07:00
data = drawEngine_ - > GetPushBufferForTextureData ( ) - > PushAligned ( srcSize , & bufferOffset , & texBuf , pushAlignment ) ;
dataScaled = false ;
LoadTextureLevel ( * entry , ( uint8_t * ) data , srcStride , i , 1 , dstFmt ) ;
2017-12-11 12:22:24 +01:00
// This format can be used with storage images.
VkImageView view = entry - > vkTex - > CreateViewForMip ( i ) ;
2019-09-29 16:34:37 -07:00
VkDescriptorSet descSet = computeShaderManager_ . GetDescriptorSet ( view , texBuf , bufferOffset , srcSize ) ;
2021-11-07 16:08:57 +01:00
struct Params { int x ; int y ; } params { mipUnscaledWidth , mipUnscaledHeight } ;
2021-12-12 11:56:29 +01:00
VK_PROFILE_BEGIN ( vulkan , cmdInit , VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ,
2021-12-19 19:39:15 +01:00
" Compute Upload: %dx%d->%dx%d " , mipUnscaledWidth , mipUnscaledHeight , mipWidth , mipHeight ) ;
2017-12-11 13:23:06 +01:00
vkCmdBindPipeline ( cmdInit , VK_PIPELINE_BIND_POINT_COMPUTE , computeShaderManager_ . GetPipeline ( uploadCS_ ) ) ;
2021-11-07 15:47:05 +01:00
vkCmdBindDescriptorSets ( cmdInit , VK_PIPELINE_BIND_POINT_COMPUTE , computeShaderManager_ . GetPipelineLayout ( ) , 0 , 1 , & descSet , 0 , nullptr ) ;
vkCmdPushConstants ( cmdInit , computeShaderManager_ . GetPipelineLayout ( ) , VK_SHADER_STAGE_COMPUTE_BIT , 0 , sizeof ( params ) , & params ) ;
vkCmdDispatch ( cmdInit , ( mipUnscaledWidth + 7 ) / 8 , ( mipUnscaledHeight + 7 ) / 8 , 1 ) ;
2021-12-12 11:56:29 +01:00
VK_PROFILE_END ( vulkan , cmdInit , VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT ) ;
2021-11-14 15:25:28 -08:00
vulkan - > Delete ( ) . QueueDeleteImageView ( view ) ;
2017-12-11 12:22:24 +01:00
} else {
2020-09-12 14:25:50 +02:00
data = drawEngine_ - > GetPushBufferForTextureData ( ) - > PushAligned ( size , & bufferOffset , & texBuf , pushAlignment ) ;
LoadTextureLevel ( * entry , ( uint8_t * ) data , stride , i , scaleFactor , dstFmt ) ;
2021-12-12 11:56:29 +01:00
VK_PROFILE_BEGIN ( vulkan , cmdInit , VK_PIPELINE_STAGE_TRANSFER_BIT ,
2021-12-19 19:39:15 +01:00
" Copy Upload: %dx%d " , mipWidth , mipHeight ) ;
2020-09-12 14:25:50 +02:00
entry - > vkTex - > UploadMip ( cmdInit , i , mipWidth , mipHeight , texBuf , bufferOffset , stride / bpp ) ;
2021-12-12 11:56:29 +01:00
VK_PROFILE_END ( vulkan , cmdInit , VK_PIPELINE_STAGE_TRANSFER_BIT ) ;
2017-12-11 12:22:24 +01:00
}
2017-05-17 02:21:03 +02:00
}
2017-02-20 00:19:58 +01:00
if ( replacer_ . Enabled ( ) ) {
2019-09-29 16:34:37 -07:00
// When hardware texture scaling is enabled, this saves the original.
2021-11-07 15:47:05 +01:00
int w = dataScaled ? mipWidth : mipUnscaledWidth ;
int h = dataScaled ? mipHeight : mipUnscaledHeight ;
2019-09-29 16:34:37 -07:00
replacer_ . NotifyTextureDecoded ( replacedInfo , data , stride , i , w , h ) ;
2016-04-30 13:44:31 -07:00
}
}
2016-03-26 18:41:53 -07:00
}
2016-04-30 13:44:31 -07:00
2021-10-05 22:49:33 +02:00
VkImageLayout layout = computeUpload ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ;
2021-10-08 21:58:03 +02:00
VkPipelineStageFlags prevStage = computeUpload ? VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT : VK_PIPELINE_STAGE_TRANSFER_BIT ;
2021-10-05 22:49:33 +02:00
2020-10-05 21:34:22 +02:00
// Generate any additional mipmap levels.
2021-10-05 22:49:33 +02:00
// This will transition the whole stack to GENERAL if it wasn't already.
if ( maxLevel ! = maxLevelToGenerate ) {
2021-12-19 19:39:15 +01:00
VK_PROFILE_BEGIN ( vulkan , cmdInit , VK_PIPELINE_STAGE_TRANSFER_BIT , " Mipgen up to level %d " , maxLevelToGenerate ) ;
2021-10-05 22:49:33 +02:00
entry - > vkTex - > GenerateMips ( cmdInit , maxLevel + 1 , computeUpload ) ;
layout = VK_IMAGE_LAYOUT_GENERAL ;
2021-10-08 21:58:03 +02:00
prevStage = VK_PIPELINE_STAGE_TRANSFER_BIT ;
2021-12-12 11:56:29 +01:00
VK_PROFILE_END ( vulkan , cmdInit , VK_PIPELINE_STAGE_TRANSFER_BIT ) ;
2020-10-05 21:34:22 +02:00
}
2017-05-31 21:23:20 -07:00
if ( maxLevel = = 0 ) {
entry - > status | = TexCacheEntry : : STATUS_BAD_MIPS ;
} else {
entry - > status & = ~ TexCacheEntry : : STATUS_BAD_MIPS ;
}
2016-04-30 13:44:31 -07:00
if ( replaced . Valid ( ) ) {
2017-12-13 23:11:40 +01:00
entry - > SetAlphaStatus ( TexCacheEntry : : TexStatus ( replaced . AlphaStatus ( ) ) ) ;
2016-04-30 13:44:31 -07:00
}
2021-10-08 21:58:03 +02:00
entry - > vkTex - > EndCreate ( cmdInit , false , prevStage , layout ) ;
2021-12-12 12:06:30 +01:00
VK_PROFILE_END ( vulkan , cmdInit , VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT ) ;
2016-01-03 23:09:37 +01:00
}
}
VkFormat TextureCacheVulkan : : GetDestFormat ( GETextureFormat format , GEPaletteFormat clutFormat ) const {
2020-03-01 14:07:13 +01:00
if ( ! gstate_c . Supports ( GPU_SUPPORTS_16BIT_FORMATS ) ) {
return VK_FORMAT_R8G8B8A8_UNORM ;
}
2016-01-03 23:09:37 +01:00
switch ( format ) {
case GE_TFMT_CLUT4 :
case GE_TFMT_CLUT8 :
case GE_TFMT_CLUT16 :
case GE_TFMT_CLUT32 :
return getClutDestFormatVulkan ( clutFormat ) ;
case GE_TFMT_4444 :
2016-01-09 21:19:18 +01:00
return VULKAN_4444_FORMAT ;
2016-01-03 23:09:37 +01:00
case GE_TFMT_5551 :
2016-01-09 21:19:18 +01:00
return VULKAN_1555_FORMAT ;
2016-01-03 23:09:37 +01:00
case GE_TFMT_5650 :
2016-01-09 21:19:18 +01:00
return VULKAN_565_FORMAT ;
2016-01-03 23:09:37 +01:00
case GE_TFMT_8888 :
case GE_TFMT_DXT1 :
case GE_TFMT_DXT3 :
case GE_TFMT_DXT5 :
default :
2016-01-09 23:27:53 +01:00
return VULKAN_8888_FORMAT ;
2016-01-03 23:09:37 +01:00
}
}
2017-12-13 23:11:40 +01:00
TexCacheEntry : : TexStatus TextureCacheVulkan : : CheckAlpha ( const u32 * pixelData , VkFormat dstFmt , int stride , int w , int h ) {
2016-01-03 23:09:37 +01:00
CheckAlphaResult res ;
switch ( dstFmt ) {
2016-01-09 23:27:53 +01:00
case VULKAN_4444_FORMAT :
2016-03-26 13:17:17 -07:00
res = CheckAlphaRGBA4444Basic ( pixelData , stride , w , h ) ;
2016-01-03 23:09:37 +01:00
break ;
2016-01-09 23:27:53 +01:00
case VULKAN_1555_FORMAT :
2016-03-26 13:17:17 -07:00
res = CheckAlphaRGBA5551Basic ( pixelData , stride , w , h ) ;
2016-01-03 23:09:37 +01:00
break ;
2016-01-09 23:27:53 +01:00
case VULKAN_565_FORMAT :
2016-01-03 23:09:37 +01:00
// Never has any alpha.
res = CHECKALPHA_FULL ;
break ;
default :
res = CheckAlphaRGBA8888Basic ( pixelData , stride , w , h ) ;
break ;
}
2017-12-13 23:11:40 +01:00
return ( TexCacheEntry : : TexStatus ) res ;
2016-01-03 23:09:37 +01:00
}
2016-04-09 10:30:23 +02:00
void TextureCacheVulkan : : LoadTextureLevel ( TexCacheEntry & entry , uint8_t * writePtr , int rowPitch , int level , int scaleFactor , VkFormat dstFmt ) {
2016-01-03 23:09:37 +01:00
int w = gstate . getTextureWidth ( level ) ;
int h = gstate . getTextureHeight ( level ) ;
2016-06-25 09:10:55 -07:00
2022-04-13 23:44:03 +02:00
gpuStats . numTexturesDecoded + + ;
2016-01-03 23:09:37 +01:00
{
PROFILE_THIS_SCOPE ( " decodetex " ) ;
2016-01-10 12:27:45 +01:00
GETextureFormat tfmt = ( GETextureFormat ) entry . format ;
2016-01-03 23:09:37 +01:00
GEPaletteFormat clutformat = gstate . getClutPaletteFormat ( ) ;
2016-03-22 19:00:57 +01:00
u32 texaddr = gstate . getTextureAddress ( level ) ;
int bufw = GetTextureBufw ( level , texaddr , tfmt ) ;
2016-03-26 13:33:18 -07:00
int bpp = dstFmt = = VULKAN_8888_FORMAT ? 4 : 2 ;
2016-06-25 09:10:55 -07:00
u32 * pixelData = ( u32 * ) writePtr ;
int decPitch = rowPitch ;
2016-03-26 13:33:18 -07:00
if ( scaleFactor > 1 ) {
2017-02-20 00:19:58 +01:00
tmpTexBufRearrange_ . resize ( std : : max ( bufw , w ) * h ) ;
pixelData = tmpTexBufRearrange_ . data ( ) ;
2016-03-26 14:28:03 -07:00
// We want to end up with a neatly packed texture for scaling.
decPitch = w * bpp ;
2016-03-26 13:33:18 -07:00
}
2021-09-13 00:03:42 -07:00
bool expand32 = ! gstate_c . Supports ( GPU_SUPPORTS_16BIT_FORMATS ) | | dstFmt = = VK_FORMAT_R8G8B8A8_UNORM ;
2022-04-11 20:10:22 +02:00
2022-04-13 23:44:03 +02:00
CheckAlphaResult alphaResult = DecodeTextureLevel ( ( u8 * ) pixelData , decPitch , tfmt , clutformat , texaddr , level , bufw , false , false , expand32 ) ;
2022-04-14 00:36:06 +02:00
WARN_LOG ( G3D , " Alpha: full=%d w=%d h=%d level=%d %s/%s " , ( int ) ( alphaResult = = CHECKALPHA_FULL ) , w , h , level , GeTextureFormatToString ( tfmt ) , GEPaletteFormatToString ( clutformat ) ) ;
2022-04-13 23:44:03 +02:00
entry . SetAlphaStatus ( alphaResult , level ) ;
2017-11-12 15:53:59 -08:00
2017-11-12 16:19:28 -08:00
if ( scaleFactor > 1 ) {
2016-01-03 23:09:37 +01:00
u32 fmt = dstFmt ;
2020-03-08 11:58:00 -07:00
// CPU scaling reads from the destination buffer so we want cached RAM.
uint8_t * rearrange = ( uint8_t * ) AllocateAlignedMemory ( w * scaleFactor * h * scaleFactor * 4 , 16 ) ;
scaler . ScaleAlways ( ( u32 * ) rearrange , pixelData , fmt , w , h , scaleFactor ) ;
2016-06-19 12:21:32 -07:00
pixelData = ( u32 * ) writePtr ;
2016-01-03 23:09:37 +01:00
dstFmt = ( VkFormat ) fmt ;
2016-03-26 14:28:03 -07:00
2016-03-26 16:36:20 -07:00
// We always end up at 8888. Other parts assume this.
2020-08-16 00:38:55 +02:00
_assert_ ( dstFmt = = VULKAN_8888_FORMAT ) ;
2016-04-30 23:59:17 -07:00
bpp = sizeof ( u32 ) ;
decPitch = w * bpp ;
2016-06-19 12:21:32 -07:00
if ( decPitch ! = rowPitch ) {
2020-03-08 11:58:00 -07:00
for ( int y = 0 ; y < h ; + + y ) {
memcpy ( writePtr + rowPitch * y , rearrange + decPitch * y , w * bpp ) ;
2016-06-19 12:21:32 -07:00
}
2016-06-25 09:10:55 -07:00
decPitch = rowPitch ;
2020-03-08 11:58:00 -07:00
} else {
memcpy ( writePtr , rearrange , w * h * 4 ) ;
2016-06-19 12:21:32 -07:00
}
2020-03-08 11:58:00 -07:00
FreeAlignedMemory ( rearrange ) ;
2016-01-03 23:09:37 +01:00
}
}
2016-01-03 00:46:41 +01:00
}
2017-10-18 13:03:49 +02:00
bool TextureCacheVulkan : : GetCurrentTextureDebug ( GPUDebugBuffer & buffer , int level ) {
2020-09-20 20:35:42 +02:00
SetTexture ( ) ;
2020-09-13 09:33:06 +02:00
if ( ! nextTexture_ ) {
if ( nextFramebufferTexture_ ) {
VirtualFramebuffer * vfb = nextFramebufferTexture_ ;
buffer . Allocate ( vfb - > bufferWidth , vfb - > bufferHeight , GPU_DBG_FORMAT_8888 , false ) ;
bool retval = draw_ - > CopyFramebufferToMemorySync ( vfb - > fbo , Draw : : FB_COLOR_BIT , 0 , 0 , vfb - > bufferWidth , vfb - > bufferHeight , Draw : : DataFormat : : R8G8B8A8_UNORM , buffer . GetData ( ) , vfb - > bufferWidth , " GetCurrentTextureDebug " ) ;
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
gstate_c . Dirty ( DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE ) ;
// We may have blitted to a temp FBO.
framebufferManager_ - > RebindFramebuffer ( " RebindFramebuffer - GetCurrentTextureDebug " ) ;
return retval ;
} else {
return false ;
}
}
2017-11-05 18:04:56 -08:00
2018-01-19 22:41:18 -08:00
// Apply texture may need to rebuild the texture if we're about to render, or bind a framebuffer.
TexCacheEntry * entry = nextTexture_ ;
ApplyTexture ( ) ;
2018-04-05 23:23:14 +02:00
if ( ! entry - > vkTex )
2017-11-05 18:04:56 -08:00
return false ;
2021-12-12 18:20:42 +01:00
2018-04-05 23:23:14 +02:00
VulkanTexture * texture = entry - > vkTex ;
2017-11-05 18:04:56 -08:00
VulkanRenderManager * renderManager = ( VulkanRenderManager * ) draw_ - > GetNativeObject ( Draw : : NativeObject : : RENDER_MANAGER ) ;
GPUDebugBufferFormat bufferFormat ;
Draw : : DataFormat drawFormat ;
switch ( texture - > GetFormat ( ) ) {
case VULKAN_565_FORMAT :
bufferFormat = GPU_DBG_FORMAT_565 ;
drawFormat = Draw : : DataFormat : : B5G6R5_UNORM_PACK16 ;
break ;
case VULKAN_1555_FORMAT :
bufferFormat = GPU_DBG_FORMAT_5551 ;
drawFormat = Draw : : DataFormat : : B5G5R5A1_UNORM_PACK16 ;
break ;
case VULKAN_4444_FORMAT :
bufferFormat = GPU_DBG_FORMAT_4444 ;
drawFormat = Draw : : DataFormat : : B4G4R4A4_UNORM_PACK16 ;
break ;
case VULKAN_8888_FORMAT :
default :
bufferFormat = GPU_DBG_FORMAT_8888 ;
drawFormat = Draw : : DataFormat : : R8G8B8A8_UNORM ;
break ;
}
int w = texture - > GetWidth ( ) ;
int h = texture - > GetHeight ( ) ;
buffer . Allocate ( w , h , bufferFormat ) ;
2020-05-21 11:24:05 +02:00
renderManager - > CopyImageToMemorySync ( texture - > GetImage ( ) , level , 0 , 0 , w , h , drawFormat , ( uint8_t * ) buffer . GetData ( ) , w , " GetCurrentTextureDebug " ) ;
2017-11-05 18:04:56 -08:00
// Vulkan requires us to re-apply all dynamic state for each command buffer, and the above will cause us to start a new cmdbuf.
// So let's dirty the things that are involved in Vulkan dynamic state. Readbacks are not frequent so this won't hurt other backends.
gstate_c . Dirty ( DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE ) ;
2020-06-02 09:51:38 +02:00
framebufferManager_ - > RebindFramebuffer ( " RebindFramebuffer - GetCurrentTextureDebug " ) ;
2017-11-05 18:04:56 -08:00
return true ;
2017-10-18 13:03:49 +02:00
}
2017-12-03 10:29:41 +01:00
void TextureCacheVulkan : : GetStats ( char * ptr , size_t size ) {
2021-11-22 09:44:05 +01:00
snprintf ( ptr , size , " N/A " ) ;
2017-12-03 10:29:41 +01:00
}
2017-12-06 16:01:56 +01:00
std : : vector < std : : string > TextureCacheVulkan : : DebugGetSamplerIDs ( ) const {
return samplerCache_ . DebugGetSamplerIDs ( ) ;
}
std : : string TextureCacheVulkan : : DebugGetSamplerString ( std : : string id , DebugShaderStringType stringType ) {
return samplerCache_ . DebugGetSamplerString ( id , stringType ) ;
2017-12-13 23:11:40 +01:00
}