GPUDevice: Improve texture pooling

This commit is contained in:
Stenzek 2023-12-20 20:28:37 +10:00
parent efaee4ab50
commit dc5e4120cd
No known key found for this signature in database
17 changed files with 229 additions and 174 deletions

View File

@ -405,10 +405,16 @@ void ImGuiManager::DrawPerformanceOverlay()
#endif #endif
} }
if (g_settings.display_show_gpu && g_gpu_device->IsGPUTimingEnabled()) if (g_settings.display_show_gpu)
{ {
text.assign("GPU: "); if (g_gpu_device->IsGPUTimingEnabled())
FormatProcessorStat(text, System::GetGPUUsage(), System::GetGPUAverageTime()); {
text.assign("GPU: ");
FormatProcessorStat(text, System::GetGPUUsage(), System::GetGPUAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
text.format("VRAM: {} MB", (g_gpu_device->GetVRAMUsage() + (1048576 - 1)) / 1048576);
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255)); DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
} }

View File

@ -182,6 +182,7 @@ void D3D11Device::SetFeatures(FeatureMask disabled_features)
m_features.gpu_timing = true; m_features.gpu_timing = true;
m_features.shader_cache = true; m_features.shader_cache = true;
m_features.pipeline_cache = false; m_features.pipeline_cache = false;
m_features.prefer_unused_textures = false;
} }
bool D3D11Device::CreateSwapChain() bool D3D11Device::CreateSwapChain()

View File

@ -23,11 +23,7 @@ std::unique_ptr<GPUTexture> D3D11Device::CreateTexture(u32 width, u32 height, u3
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data, u32 data_stride) const void* data, u32 data_stride)
{ {
std::unique_ptr<D3D11Texture> tex = std::make_unique<D3D11Texture>(); return D3D11Texture::Create(m_device.Get(), width, height, layers, levels, samples, type, format, data, data_stride);
if (!tex->Create(m_device.Get(), width, height, layers, levels, samples, type, format, data, data_stride))
tex.reset();
return tex;
} }
bool D3D11Device::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data, bool D3D11Device::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
@ -154,11 +150,21 @@ std::unique_ptr<GPUSampler> D3D11Device::CreateSampler(const GPUSampler::Config&
return std::unique_ptr<GPUSampler>(new D3D11Sampler(std::move(ss))); return std::unique_ptr<GPUSampler>(new D3D11Sampler(std::move(ss)));
} }
D3D11Texture::D3D11Texture() = default; D3D11Texture::D3D11Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> srv,
ComPtr<ID3D11View> rtv_dsv)
: GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
static_cast<u8>(samples), type, format),
m_texture(std::move(texture)), m_srv(std::move(srv)), m_rtv_dsv(std::move(rtv_dsv))
{
}
D3D11Texture::~D3D11Texture() D3D11Texture::~D3D11Texture()
{ {
Destroy(); D3D11Device::GetInstance().UnbindTexture(this);
m_rtv_dsv.Reset();
m_srv.Reset();
m_texture.Reset();
} }
D3D11_TEXTURE2D_DESC D3D11Texture::GetDesc() const D3D11_TEXTURE2D_DESC D3D11Texture::GetDesc() const
@ -191,11 +197,6 @@ void D3D11Texture::CommitClear(ID3D11DeviceContext1* context)
m_state = GPUTexture::State::Dirty; m_state = GPUTexture::State::Dirty;
} }
bool D3D11Texture::IsValid() const
{
return static_cast<bool>(m_texture);
}
bool D3D11Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/, bool D3D11Texture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/,
u32 level /*= 0*/) u32 level /*= 0*/)
{ {
@ -268,11 +269,13 @@ DXGI_FORMAT D3D11Texture::GetDXGIFormat() const
return D3DCommon::GetFormatMapping(m_format).resource_format; return D3DCommon::GetFormatMapping(m_format).resource_format;
} }
bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels,
Format format, const void* initial_data /* = nullptr */, u32 initial_data_stride /* = 0 */) u32 samples, Type type, Format format,
const void* initial_data /* = nullptr */,
u32 initial_data_stride /* = 0 */)
{ {
if (!ValidateConfig(width, height, layers, layers, samples, type, format)) if (!ValidateConfig(width, height, layers, layers, samples, type, format))
return false; return nullptr;
u32 bind_flags = 0; u32 bind_flags = 0;
D3D11_USAGE usage = D3D11_USAGE_DEFAULT; D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
@ -317,7 +320,7 @@ bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layer
Log_ErrorPrintf( Log_ErrorPrintf(
"Create texture failed: 0x%08X (%ux%u levels:%u samples:%u format:%u bind_flags:%X initial_data:%p)", tex_hr, "Create texture failed: 0x%08X (%ux%u levels:%u samples:%u format:%u bind_flags:%X initial_data:%p)", tex_hr,
width, height, levels, samples, static_cast<unsigned>(format), bind_flags, initial_data); width, height, levels, samples, static_cast<unsigned>(format), bind_flags, initial_data);
return false; return nullptr;
} }
ComPtr<ID3D11ShaderResourceView> srv; ComPtr<ID3D11ShaderResourceView> srv;
@ -332,7 +335,7 @@ bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layer
if (FAILED(hr)) if (FAILED(hr))
{ {
Log_ErrorPrintf("Create SRV for texture failed: 0x%08X", hr); Log_ErrorPrintf("Create SRV for texture failed: 0x%08X", hr);
return false; return nullptr;
} }
} }
@ -347,7 +350,7 @@ bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layer
if (FAILED(hr)) if (FAILED(hr))
{ {
Log_ErrorPrintf("Create RTV for texture failed: 0x%08X", hr); Log_ErrorPrintf("Create RTV for texture failed: 0x%08X", hr);
return false; return nullptr;
} }
rtv_dsv = std::move(rtv); rtv_dsv = std::move(rtv);
@ -362,32 +365,14 @@ bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layer
if (FAILED(hr)) if (FAILED(hr))
{ {
Log_ErrorPrintf("Create DSV for texture failed: 0x%08X", hr); Log_ErrorPrintf("Create DSV for texture failed: 0x%08X", hr);
return false; return nullptr;
} }
rtv_dsv = std::move(dsv); rtv_dsv = std::move(dsv);
} }
m_texture = std::move(texture); return std::unique_ptr<D3D11Texture>(new D3D11Texture(width, height, layers, levels, samples, type, format,
m_srv = std::move(srv); std::move(texture), std::move(srv), std::move(rtv_dsv)));
m_rtv_dsv = std::move(rtv_dsv);
m_width = static_cast<u16>(width);
m_height = static_cast<u16>(height);
m_layers = static_cast<u8>(layers);
m_levels = static_cast<u8>(levels);
m_samples = static_cast<u8>(samples);
m_type = type;
m_format = format;
return true;
}
void D3D11Texture::Destroy()
{
D3D11Device::GetInstance().UnbindTexture(this);
m_rtv_dsv.Reset();
m_srv.Reset();
m_texture.Reset();
ClearBaseProperties();
} }
D3D11TextureBuffer::D3D11TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements) D3D11TextureBuffer::D3D11TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements)

View File

@ -42,7 +42,6 @@ public:
template<typename T> template<typename T>
using ComPtr = Microsoft::WRL::ComPtr<T>; using ComPtr = Microsoft::WRL::ComPtr<T>;
D3D11Texture();
~D3D11Texture(); ~D3D11Texture();
ALWAYS_INLINE ID3D11Texture2D* GetD3DTexture() const { return m_texture.Get(); } ALWAYS_INLINE ID3D11Texture2D* GetD3DTexture() const { return m_texture.Get(); }
@ -75,16 +74,13 @@ public:
} }
ALWAYS_INLINE operator bool() const { return static_cast<bool>(m_texture); } ALWAYS_INLINE operator bool() const { return static_cast<bool>(m_texture); }
bool Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, static std::unique_ptr<D3D11Texture> Create(ID3D11Device* device, u32 width, u32 height, u32 layers, u32 levels,
Format format, const void* initial_data = nullptr, u32 initial_data_stride = 0); u32 samples, Type type, Format format, const void* initial_data = nullptr,
u32 initial_data_stride = 0);
void Destroy();
D3D11_TEXTURE2D_DESC GetDesc() const; D3D11_TEXTURE2D_DESC GetDesc() const;
void CommitClear(ID3D11DeviceContext1* context); void CommitClear(ID3D11DeviceContext1* context);
bool IsValid() const override;
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
void Unmap() override; void Unmap() override;
@ -92,6 +88,9 @@ public:
void SetDebugName(const std::string_view& name) override; void SetDebugName(const std::string_view& name) override;
private: private:
D3D11Texture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
ComPtr<ID3D11Texture2D> texture, ComPtr<ID3D11ShaderResourceView> srv, ComPtr<ID3D11View> rtv_dsv);
ComPtr<ID3D11Texture2D> m_texture; ComPtr<ID3D11Texture2D> m_texture;
ComPtr<ID3D11ShaderResourceView> m_srv; ComPtr<ID3D11ShaderResourceView> m_srv;
ComPtr<ID3D11View> m_rtv_dsv; ComPtr<ID3D11View> m_rtv_dsv;

View File

@ -1187,6 +1187,7 @@ void D3D12Device::SetFeatures(FeatureMask disabled_features)
m_features.gpu_timing = true; m_features.gpu_timing = true;
m_features.shader_cache = true; m_features.shader_cache = true;
m_features.pipeline_cache = true; m_features.pipeline_cache = true;
m_features.prefer_unused_textures = true;
BOOL allow_tearing_supported = false; BOOL allow_tearing_supported = false;
HRESULT hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported, HRESULT hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,

View File

@ -37,7 +37,6 @@ public:
ALWAYS_INLINE DXGI_FORMAT GetDXGIFormat() const { return m_dxgi_format; } ALWAYS_INLINE DXGI_FORMAT GetDXGIFormat() const { return m_dxgi_format; }
ALWAYS_INLINE ID3D12Resource* GetResource() const { return m_resource.Get(); } ALWAYS_INLINE ID3D12Resource* GetResource() const { return m_resource.Get(); }
bool IsValid() const override { return static_cast<bool>(m_resource); }
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
void Unmap() override; void Unmap() override;

View File

@ -38,6 +38,7 @@ Log_SetChannel(GPUDevice);
std::unique_ptr<GPUDevice> g_gpu_device; std::unique_ptr<GPUDevice> g_gpu_device;
static std::string s_pipeline_cache_path; static std::string s_pipeline_cache_path;
size_t GPUDevice::s_total_vram_usage = 0;
GPUSampler::GPUSampler() = default; GPUSampler::GPUSampler() = default;
@ -763,6 +764,11 @@ Common::Rectangle<s32> GPUDevice::FlipToLowerLeft(const Common::Rectangle<s32>&
return Common::Rectangle<s32>(rc.left, flipped_y, rc.right, flipped_y + height); return Common::Rectangle<s32>(rc.left, flipped_y, rc.right, flipped_y + height);
} }
bool GPUDevice::IsTexturePoolType(GPUTexture::Type type)
{
return (type == GPUTexture::Type::Texture || type == GPUTexture::Type::DynamicTexture);
}
std::unique_ptr<GPUTexture> GPUDevice::FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> GPUDevice::FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data /*= nullptr*/, u32 data_stride /*= 0*/) const void* data /*= nullptr*/, u32 data_stride /*= 0*/)
@ -778,21 +784,56 @@ std::unique_ptr<GPUTexture> GPUDevice::FetchTexture(u32 width, u32 height, u32 l
format, format,
0u}; 0u};
for (auto it = m_texture_pool.begin(); it != m_texture_pool.end(); ++it) const bool is_texture = IsTexturePoolType(type);
TexturePool& pool = is_texture ? m_texture_pool : m_target_pool;
const u32 pool_size = (is_texture ? MAX_TEXTURE_POOL_SIZE : MAX_TARGET_POOL_SIZE);
TexturePool::iterator it;
if (is_texture && m_features.prefer_unused_textures)
{ {
if (it->key == key) // Try to find a texture that wasn't used this frame first.
// Start at the back of the pool.
it = m_texture_pool.end();
for (auto rit = m_texture_pool.rbegin(); rit != m_texture_pool.rend(); ++rit)
{ {
if (data && !it->texture->Update(0, 0, width, height, data, data_stride, 0, 0)) if (rit->use_counter == m_texture_pool_counter)
{ {
// This shouldn't happen... // We're into textures recycled this frame, not going to find anything newer.
Log_ErrorFmt("Failed to upload {}x{} to pooled texture", width, height); // But prefer reuse over creating a new texture.
break; if (m_texture_pool.size() < pool_size)
break;
} }
if (rit->key == key)
{
it = std::prev(rit.base());
break;
}
}
}
else
{
for (it = pool.begin(); it != pool.end(); ++it)
{
if (it->key == key)
break;
}
}
if (it != pool.end())
{
if (!data || it->texture->Update(0, 0, width, height, data, data_stride, 0, 0))
{
ret = std::move(it->texture); ret = std::move(it->texture);
m_texture_pool.erase(it); m_texture_pool.erase(it);
return ret; return ret;
} }
else
{
// This shouldn't happen...
Log_ErrorFmt("Failed to upload {}x{} to pooled texture", width, height);
}
} }
ret = CreateTexture(width, height, layers, levels, samples, type, format, data, data_stride); ret = CreateTexture(width, height, layers, levels, samples, type, format, data, data_stride);
@ -814,23 +855,6 @@ void GPUDevice::RecycleTexture(std::unique_ptr<GPUTexture> texture)
if (!texture) if (!texture)
return; return;
u32 remove_count = m_texture_pool_counter + POOL_PURGE_DELAY;
if (remove_count < m_texture_pool_counter)
{
// wrapped around, handle it
if (m_texture_pool.empty())
{
m_texture_pool_counter = 0;
remove_count = POOL_PURGE_DELAY;
}
else
{
const u32 reduce = m_texture_pool.front().remove_count;
m_texture_pool_counter -= reduce;
remove_count -= reduce;
}
}
const TexturePoolKey key = {static_cast<u16>(texture->GetWidth()), const TexturePoolKey key = {static_cast<u16>(texture->GetWidth()),
static_cast<u16>(texture->GetHeight()), static_cast<u16>(texture->GetHeight()),
static_cast<u8>(texture->GetLayers()), static_cast<u8>(texture->GetLayers()),
@ -840,28 +864,74 @@ void GPUDevice::RecycleTexture(std::unique_ptr<GPUTexture> texture)
texture->GetFormat(), texture->GetFormat(),
0u}; 0u};
m_texture_pool.push_back({std::move(texture), remove_count, key}); const bool is_texture = IsTexturePoolType(texture->GetType());
TexturePool& pool = is_texture ? m_texture_pool : m_target_pool;
pool.push_back({std::move(texture), m_texture_pool_counter, key});
const u32 max_size = is_texture ? MAX_TEXTURE_POOL_SIZE : MAX_TARGET_POOL_SIZE;
while (pool.size() >= max_size)
{
Log_ProfileFmt("Trim {}x{} texture from pool", pool.front().texture->GetWidth(), pool.front().texture->GetHeight());
pool.pop_front();
}
} }
void GPUDevice::PurgeTexturePool() void GPUDevice::PurgeTexturePool()
{ {
m_texture_pool_counter = 0; m_texture_pool_counter = 0;
m_texture_pool.clear(); m_texture_pool.clear();
m_target_pool.clear();
} }
void GPUDevice::TrimTexturePool() void GPUDevice::TrimTexturePool()
{ {
if (m_texture_pool.empty()) GL_INS_FMT("Texture Pool Size: {}", m_texture_pool.size());
GL_INS_FMT("Target Pool Size: {}", m_target_pool.size());
GL_INS_FMT("VRAM Usage: {:.2f} MB", s_total_vram_usage / 1048576.0);
Log_DebugFmt("Texture Pool Size: {} Target Pool Size: {} VRAM: {:.2f} MB", m_texture_pool.size(),
m_target_pool.size(), s_total_vram_usage / 1048756.0);
if (m_texture_pool.empty() && m_target_pool.empty())
return; return;
const u32 counter = m_texture_pool_counter++; const u32 prev_counter = m_texture_pool_counter++;
for (auto it = m_texture_pool.begin(); it != m_texture_pool.end();) for (u32 pool_idx = 0; pool_idx < 2; pool_idx++)
{ {
if (counter < it->remove_count) TexturePool& pool = pool_idx ? m_target_pool : m_texture_pool;
break; for (auto it = pool.begin(); it != pool.end();)
{
const u32 delta = (it->use_counter - prev_counter);
if (delta < POOL_PURGE_DELAY)
break;
Log_ProfileFmt("Trim {}x{} texture from pool", it->texture->GetWidth(), it->texture->GetHeight()); Log_ProfileFmt("Trim {}x{} texture from pool", it->texture->GetWidth(), it->texture->GetHeight());
it = m_texture_pool.erase(it); it = pool.erase(it);
}
}
if (m_texture_pool_counter < prev_counter) [[unlikely]]
{
// wrapped around, handle it
if (m_texture_pool.empty() && m_target_pool.empty())
{
m_texture_pool_counter = 0;
}
else
{
const u32 texture_min =
m_texture_pool.empty() ? std::numeric_limits<u32>::max() : m_texture_pool.front().use_counter;
const u32 target_min =
m_target_pool.empty() ? std::numeric_limits<u32>::max() : m_target_pool.front().use_counter;
const u32 reduce = std::min(texture_min, target_min);
m_texture_pool_counter -= reduce;
for (u32 pool_idx = 0; pool_idx < 2; pool_idx++)
{
TexturePool& pool = pool_idx ? m_target_pool : m_texture_pool;
for (TexturePoolEntry& entry : pool)
entry.use_counter -= reduce;
}
}
} }
} }

View File

@ -424,6 +424,8 @@ protected:
class GPUDevice class GPUDevice
{ {
public: public:
friend GPUTexture;
// TODO: drop virtuals // TODO: drop virtuals
// TODO: gpu crash handling on present // TODO: gpu crash handling on present
using DrawIndex = u16; using DrawIndex = u16;
@ -451,6 +453,7 @@ public:
bool gpu_timing : 1; bool gpu_timing : 1;
bool shader_cache : 1; bool shader_cache : 1;
bool pipeline_cache : 1; bool pipeline_cache : 1;
bool prefer_unused_textures : 1;
}; };
struct AdapterAndModeList struct AdapterAndModeList
@ -627,6 +630,7 @@ public:
virtual void SetVSync(bool enabled) = 0; virtual void SetVSync(bool enabled) = 0;
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; } ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
bool UpdateImGuiFontTexture(); bool UpdateImGuiFontTexture();
bool UsesLowerLeftOrigin() const; bool UsesLowerLeftOrigin() const;
@ -675,7 +679,9 @@ protected:
std::unique_ptr<GPUSampler> m_linear_sampler; std::unique_ptr<GPUSampler> m_linear_sampler;
private: private:
static constexpr u32 POOL_PURGE_DELAY = 60; static constexpr u32 MAX_TEXTURE_POOL_SIZE = 100;
static constexpr u32 MAX_TARGET_POOL_SIZE = 50;
static constexpr u32 POOL_PURGE_DELAY = 300;
struct TexturePoolKey struct TexturePoolKey
{ {
@ -700,19 +706,27 @@ private:
struct TexturePoolEntry struct TexturePoolEntry
{ {
std::unique_ptr<GPUTexture> texture; std::unique_ptr<GPUTexture> texture;
u32 remove_count; u32 use_counter;
TexturePoolKey key; TexturePoolKey key;
}; };
using TexturePool = std::deque<TexturePoolEntry>;
void OpenShaderCache(const std::string_view& base_path, u32 version); void OpenShaderCache(const std::string_view& base_path, u32 version);
void CloseShaderCache(); void CloseShaderCache();
bool CreateResources(); bool CreateResources();
void DestroyResources(); void DestroyResources();
static bool IsTexturePoolType(GPUTexture::Type type);
static size_t s_total_vram_usage;
std::unique_ptr<GPUPipeline> m_imgui_pipeline; std::unique_ptr<GPUPipeline> m_imgui_pipeline;
std::unique_ptr<GPUTexture> m_imgui_font_texture; std::unique_ptr<GPUTexture> m_imgui_font_texture;
std::deque<TexturePoolEntry> m_texture_pool; TexturePool m_texture_pool;
TexturePool m_target_pool;
size_t m_pool_vram_usage = 0;
u32 m_texture_pool_counter = 0; u32 m_texture_pool_counter = 0;
// TODO: Move out. // TODO: Move out.

View File

@ -10,15 +10,17 @@
Log_SetChannel(GPUTexture); Log_SetChannel(GPUTexture);
GPUTexture::GPUTexture() = default;
GPUTexture::GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format) GPUTexture::GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format)
: m_width(width), m_height(height), m_layers(layers), m_levels(levels), m_samples(samples), m_type(type), : m_width(width), m_height(height), m_layers(layers), m_levels(levels), m_samples(samples), m_type(type),
m_format(format) m_format(format)
{ {
GPUDevice::s_total_vram_usage += GetVRAMUsage();
} }
GPUTexture::~GPUTexture() = default; GPUTexture::~GPUTexture()
{
GPUDevice::s_total_vram_usage -= GetVRAMUsage();
}
const char* GPUTexture::GetFormatName(Format format) const char* GPUTexture::GetFormatName(Format format)
{ {
@ -48,23 +50,30 @@ const char* GPUTexture::GetFormatName(Format format)
return format_names[static_cast<u8>(format)]; return format_names[static_cast<u8>(format)];
} }
void GPUTexture::ClearBaseProperties()
{
m_width = 0;
m_height = 0;
m_layers = 0;
m_levels = 0;
m_samples = 0;
m_type = GPUTexture::Type::Unknown;
m_format = GPUTexture::Format::Unknown;
m_state = State::Dirty;
}
std::array<float, 4> GPUTexture::GetUNormClearColor() const std::array<float, 4> GPUTexture::GetUNormClearColor() const
{ {
return GPUDevice::RGBA8ToFloat(m_clear_value.color); return GPUDevice::RGBA8ToFloat(m_clear_value.color);
} }
size_t GPUTexture::GetVRAMUsage() const
{
if (m_levels == 1) [[likely]]
return ((static_cast<size_t>(m_width * m_height) * GetPixelSize(m_format)) * m_layers * m_samples);
const size_t ps = GetPixelSize(m_format) * m_layers * m_samples;
u32 width = m_width;
u32 height = m_height;
size_t ts = 0;
for (u32 i = 0; i < m_levels; i++)
{
width = (width > 1) ? (width / 2) : width;
height = (height > 1) ? (height / 2) : height;
ts += static_cast<size_t>(width * height) * ps;
}
return ts;
}
u32 GPUTexture::GetPixelSize(GPUTexture::Format format) u32 GPUTexture::GetPixelSize(GPUTexture::Format format)
{ {
static constexpr std::array<u8, static_cast<size_t>(Format::MaxCount)> sizes = {{ static constexpr std::array<u8, static_cast<size_t>(Format::MaxCount)> sizes = {{

View File

@ -73,9 +73,18 @@ public:
}; };
public: public:
GPUTexture(const GPUTexture&) = delete;
virtual ~GPUTexture(); virtual ~GPUTexture();
static const char* GetFormatName(Format format); static const char* GetFormatName(Format format);
static u32 GetPixelSize(GPUTexture::Format format);
static bool IsDepthFormat(GPUTexture::Format format);
static bool IsDepthStencilFormat(GPUTexture::Format format);
static bool ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format);
static bool ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32& texture_data_stride,
GPUTexture::Format format);
static void FlipTextureDataRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32 texture_data_stride);
ALWAYS_INLINE u32 GetWidth() const { return m_width; } ALWAYS_INLINE u32 GetWidth() const { return m_width; }
ALWAYS_INLINE u32 GetHeight() const { return m_height; } ALWAYS_INLINE u32 GetHeight() const { return m_height; }
@ -122,16 +131,9 @@ public:
m_clear_value.depth = depth; m_clear_value.depth = depth;
} }
static u32 GetPixelSize(GPUTexture::Format format); size_t GetVRAMUsage() const;
static bool IsDepthFormat(GPUTexture::Format format);
static bool IsDepthStencilFormat(GPUTexture::Format format);
static bool ValidateConfig(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format);
static bool ConvertTextureDataToRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32& texture_data_stride, GPUTexture& operator=(const GPUTexture&) = delete;
GPUTexture::Format format);
static void FlipTextureDataRGBA8(u32 width, u32 height, std::vector<u32>& texture_data, u32 texture_data_stride);
virtual bool IsValid() const = 0;
virtual bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, virtual bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0,
u32 level = 0) = 0; u32 level = 0) = 0;
@ -144,11 +146,8 @@ public:
virtual void SetDebugName(const std::string_view& name) = 0; virtual void SetDebugName(const std::string_view& name) = 0;
protected: protected:
GPUTexture();
GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format); GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format);
void ClearBaseProperties();
u16 m_width = 0; u16 m_width = 0;
u16 m_height = 0; u16 m_height = 0;
u8 m_layers = 0; u8 m_layers = 0;

View File

@ -107,9 +107,6 @@ public:
bool Create(id<MTLDevice> device, u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, bool Create(id<MTLDevice> device, u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type,
Format format, const void* initial_data = nullptr, u32 initial_data_stride = 0); Format format, const void* initial_data = nullptr, u32 initial_data_stride = 0);
void Destroy();
bool IsValid() const override;
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;

View File

@ -220,6 +220,7 @@ void MetalDevice::SetFeatures(FeatureMask disabled_features)
m_features.partial_msaa_resolve = false; m_features.partial_msaa_resolve = false;
m_features.shader_cache = true; m_features.shader_cache = true;
m_features.pipeline_cache = false; m_features.pipeline_cache = false;
m_features.prefer_unused_textures = true;
} }
bool MetalDevice::LoadShaders() bool MetalDevice::LoadShaders()
@ -871,13 +872,11 @@ MetalTexture::MetalTexture(id<MTLTexture> texture, u16 width, u16 height, u8 lay
MetalTexture::~MetalTexture() MetalTexture::~MetalTexture()
{ {
MetalDevice::GetInstance().UnbindTexture(this); if (m_texture != nil)
Destroy(); {
} MetalDevice::GetInstance().UnbindTexture(this);
MetalDevice::DeferRelease(m_texture);
bool MetalTexture::IsValid() const }
{
return (m_texture != nil);
} }
bool MetalTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/, bool MetalTexture::Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer /*= 0*/,
@ -1029,16 +1028,6 @@ void MetalTexture::SetDebugName(const std::string_view& name)
} }
} }
void MetalTexture::Destroy()
{
if (m_texture != nil)
{
MetalDevice::DeferRelease(m_texture);
m_texture = nil;
}
ClearBaseProperties();
}
std::unique_ptr<GPUTexture> MetalDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, std::unique_ptr<GPUTexture> MetalDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data, u32 data_stride) const void* data, u32 data_stride)

View File

@ -52,11 +52,7 @@ std::unique_ptr<GPUTexture> OpenGLDevice::CreateTexture(u32 width, u32 height, u
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Type type, GPUTexture::Format format,
const void* data, u32 data_stride) const void* data, u32 data_stride)
{ {
std::unique_ptr<OpenGLTexture> tex(std::make_unique<OpenGLTexture>()); return OpenGLTexture::Create(width, height, layers, levels, samples, type, format, data, data_stride);
if (!tex->Create(width, height, layers, levels, samples, type, format, data, data_stride))
tex.reset();
return tex;
} }
bool OpenGLDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data, bool OpenGLDevice::DownloadTexture(GPUTexture* texture, u32 x, u32 y, u32 width, u32 height, void* out_data,
@ -533,6 +529,9 @@ bool OpenGLDevice::CheckFeatures(bool* buggy_pbo, FeatureMask disabled_features)
"startup will be slow due to compiling shaders."); "startup will be slow due to compiling shaders.");
} }
// Mobile drivers prefer textures to not be updated mid-frame.
m_features.prefer_unused_textures = is_gles || vendor_id_arm || vendor_id_powervr || vendor_id_qualcomm;
return true; return true;
} }

View File

@ -82,11 +82,22 @@ const std::tuple<GLenum, GLenum, GLenum>& OpenGLTexture::GetPixelFormatMapping(G
return gles ? mapping_gles[static_cast<u32>(format)] : mapping[static_cast<u32>(format)]; return gles ? mapping_gles[static_cast<u32>(format)] : mapping[static_cast<u32>(format)];
} }
OpenGLTexture::OpenGLTexture() = default; OpenGLTexture::OpenGLTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
GLuint id)
: GPUTexture(static_cast<u16>(width), static_cast<u16>(height), static_cast<u8>(layers), static_cast<u8>(levels),
static_cast<u8>(samples), type, format),
m_id(id)
{
}
OpenGLTexture::~OpenGLTexture() OpenGLTexture::~OpenGLTexture()
{ {
Destroy(); if (m_id != 0)
{
OpenGLDevice::GetInstance().UnbindTexture(this);
glDeleteTextures(1, &m_id);
m_id = 0;
}
} }
bool OpenGLTexture::UseTextureStorage(bool multisampled) bool OpenGLTexture::UseTextureStorage(bool multisampled)
@ -99,16 +110,16 @@ bool OpenGLTexture::UseTextureStorage() const
return UseTextureStorage(IsMultisampled()); return UseTextureStorage(IsMultisampled());
} }
bool OpenGLTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, std::unique_ptr<OpenGLTexture> OpenGLTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
const void* data, u32 data_pitch) Type type, Format format, const void* data, u32 data_pitch)
{ {
if (!ValidateConfig(width, height, layers, levels, samples, type, format)) if (!ValidateConfig(width, height, layers, levels, samples, type, format))
return false; return nullptr;
if (layers > 1 && data) if (layers > 1 && data)
{ {
Log_ErrorPrintf("Loading texture array data not currently supported"); Log_ErrorPrintf("Loading texture array data not currently supported");
return false; return nullptr;
} }
const GLenum target = const GLenum target =
@ -211,34 +222,10 @@ bool OpenGLTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 sa
{ {
Log_ErrorPrintf("Failed to create texture: 0x%X", error); Log_ErrorPrintf("Failed to create texture: 0x%X", error);
glDeleteTextures(1, &id); glDeleteTextures(1, &id);
return false; return nullptr;
} }
if (IsValid()) return std::unique_ptr<OpenGLTexture>(new OpenGLTexture(width, height, layers, levels, samples, type, format, id));
Destroy();
m_id = id;
m_width = static_cast<u16>(width);
m_height = static_cast<u16>(height);
m_layers = static_cast<u8>(layers);
m_levels = static_cast<u8>(levels);
m_samples = static_cast<u8>(samples);
m_type = type;
m_format = format;
m_state = GPUTexture::State::Dirty;
return true;
}
void OpenGLTexture::Destroy()
{
if (m_id != 0)
{
OpenGLDevice::GetInstance().UnbindTexture(this);
glDeleteTextures(1, &m_id);
m_id = 0;
}
ClearBaseProperties();
} }
void OpenGLTexture::CommitClear() void OpenGLTexture::CommitClear()

View File

@ -15,7 +15,6 @@ class OpenGLTexture final : public GPUTexture
friend OpenGLDevice; friend OpenGLDevice;
public: public:
OpenGLTexture();
OpenGLTexture(const OpenGLTexture&) = delete; OpenGLTexture(const OpenGLTexture&) = delete;
~OpenGLTexture(); ~OpenGLTexture();
@ -23,16 +22,15 @@ public:
static const std::tuple<GLenum, GLenum, GLenum>& GetPixelFormatMapping(Format format, bool gles); static const std::tuple<GLenum, GLenum, GLenum>& GetPixelFormatMapping(Format format, bool gles);
ALWAYS_INLINE GLuint GetGLId() const { return m_id; } ALWAYS_INLINE GLuint GetGLId() const { return m_id; }
bool IsValid() const override { return m_id != 0; }
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
void Unmap() override; void Unmap() override;
void SetDebugName(const std::string_view& name) override; void SetDebugName(const std::string_view& name) override;
bool Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, static std::unique_ptr<OpenGLTexture> Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type,
const void* data = nullptr, u32 data_pitch = 0); Format format, const void* data = nullptr, u32 data_pitch = 0);
void Destroy();
bool UseTextureStorage() const; bool UseTextureStorage() const;
@ -46,6 +44,8 @@ public:
OpenGLTexture& operator=(const OpenGLTexture&) = delete; OpenGLTexture& operator=(const OpenGLTexture&) = delete;
private: private:
OpenGLTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, GLuint id);
GLuint m_id = 0; GLuint m_id = 0;
u32 m_map_offset = 0; u32 m_map_offset = 0;

View File

@ -2414,6 +2414,7 @@ bool VulkanDevice::CheckFeatures(FeatureMask disabled_features)
m_features.partial_msaa_resolve = true; m_features.partial_msaa_resolve = true;
m_features.shader_cache = true; m_features.shader_cache = true;
m_features.pipeline_cache = true; m_features.pipeline_cache = true;
m_features.prefer_unused_textures = true;
return true; return true;
} }

View File

@ -48,7 +48,6 @@ public:
VkImageLayout GetVkLayout() const; VkImageLayout GetVkLayout() const;
bool IsValid() const override { return (m_image != VK_NULL_HANDLE); }
bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override; bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0, u32 level = 0) override;
bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override; bool Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
void Unmap() override; void Unmap() override;