mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 07:35:41 +00:00
GPUDevice: Improve texture pooling
This commit is contained in:
parent
efaee4ab50
commit
dc5e4120cd
|
@ -405,10 +405,16 @@ void ImGuiManager::DrawPerformanceOverlay()
|
|||
#endif
|
||||
}
|
||||
|
||||
if (g_settings.display_show_gpu && g_gpu_device->IsGPUTimingEnabled())
|
||||
if (g_settings.display_show_gpu)
|
||||
{
|
||||
text.assign("GPU: ");
|
||||
FormatProcessorStat(text, System::GetGPUUsage(), System::GetGPUAverageTime());
|
||||
if (g_gpu_device->IsGPUTimingEnabled())
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
@ -182,6 +182,7 @@ void D3D11Device::SetFeatures(FeatureMask disabled_features)
|
|||
m_features.gpu_timing = true;
|
||||
m_features.shader_cache = true;
|
||||
m_features.pipeline_cache = false;
|
||||
m_features.prefer_unused_textures = false;
|
||||
}
|
||||
|
||||
bool D3D11Device::CreateSwapChain()
|
||||
|
|
|
@ -23,11 +23,7 @@ std::unique_ptr<GPUTexture> D3D11Device::CreateTexture(u32 width, u32 height, u3
|
|||
GPUTexture::Type type, GPUTexture::Format format,
|
||||
const void* data, u32 data_stride)
|
||||
{
|
||||
std::unique_ptr<D3D11Texture> tex = std::make_unique<D3D11Texture>();
|
||||
if (!tex->Create(m_device.Get(), width, height, layers, levels, samples, type, format, data, data_stride))
|
||||
tex.reset();
|
||||
|
||||
return tex;
|
||||
return D3D11Texture::Create(m_device.Get(), width, height, layers, levels, samples, type, format, data, data_stride);
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Destroy();
|
||||
D3D11Device::GetInstance().UnbindTexture(this);
|
||||
m_rtv_dsv.Reset();
|
||||
m_srv.Reset();
|
||||
m_texture.Reset();
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC D3D11Texture::GetDesc() const
|
||||
|
@ -191,11 +197,6 @@ void D3D11Texture::CommitClear(ID3D11DeviceContext1* context)
|
|||
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*/,
|
||||
u32 level /*= 0*/)
|
||||
{
|
||||
|
@ -268,11 +269,13 @@ DXGI_FORMAT D3D11Texture::GetDXGIFormat() const
|
|||
return D3DCommon::GetFormatMapping(m_format).resource_format;
|
||||
}
|
||||
|
||||
bool D3D11Texture::Create(ID3D11Device* 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 */)
|
||||
std::unique_ptr<D3D11Texture> D3D11Texture::Create(ID3D11Device* 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 */)
|
||||
{
|
||||
if (!ValidateConfig(width, height, layers, layers, samples, type, format))
|
||||
return false;
|
||||
return nullptr;
|
||||
|
||||
u32 bind_flags = 0;
|
||||
D3D11_USAGE usage = D3D11_USAGE_DEFAULT;
|
||||
|
@ -317,7 +320,7 @@ bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layer
|
|||
Log_ErrorPrintf(
|
||||
"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);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ComPtr<ID3D11ShaderResourceView> srv;
|
||||
|
@ -332,7 +335,7 @@ bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layer
|
|||
if (FAILED(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))
|
||||
{
|
||||
Log_ErrorPrintf("Create RTV for texture failed: 0x%08X", hr);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rtv_dsv = std::move(rtv);
|
||||
|
@ -362,32 +365,14 @@ bool D3D11Texture::Create(ID3D11Device* device, u32 width, u32 height, u32 layer
|
|||
if (FAILED(hr))
|
||||
{
|
||||
Log_ErrorPrintf("Create DSV for texture failed: 0x%08X", hr);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rtv_dsv = std::move(dsv);
|
||||
}
|
||||
|
||||
m_texture = std::move(texture);
|
||||
m_srv = std::move(srv);
|
||||
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();
|
||||
return std::unique_ptr<D3D11Texture>(new D3D11Texture(width, height, layers, levels, samples, type, format,
|
||||
std::move(texture), std::move(srv), std::move(rtv_dsv)));
|
||||
}
|
||||
|
||||
D3D11TextureBuffer::D3D11TextureBuffer(Format format, u32 size_in_elements) : GPUTextureBuffer(format, size_in_elements)
|
||||
|
|
|
@ -42,7 +42,6 @@ public:
|
|||
template<typename T>
|
||||
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||
|
||||
D3D11Texture();
|
||||
~D3D11Texture();
|
||||
|
||||
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); }
|
||||
|
||||
bool Create(ID3D11Device* 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);
|
||||
|
||||
void Destroy();
|
||||
static std::unique_ptr<D3D11Texture> Create(ID3D11Device* 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);
|
||||
|
||||
D3D11_TEXTURE2D_DESC GetDesc() const;
|
||||
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 Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
|
||||
void Unmap() override;
|
||||
|
@ -92,6 +88,9 @@ public:
|
|||
void SetDebugName(const std::string_view& name) override;
|
||||
|
||||
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<ID3D11ShaderResourceView> m_srv;
|
||||
ComPtr<ID3D11View> m_rtv_dsv;
|
||||
|
|
|
@ -1187,6 +1187,7 @@ void D3D12Device::SetFeatures(FeatureMask disabled_features)
|
|||
m_features.gpu_timing = true;
|
||||
m_features.shader_cache = true;
|
||||
m_features.pipeline_cache = true;
|
||||
m_features.prefer_unused_textures = true;
|
||||
|
||||
BOOL allow_tearing_supported = false;
|
||||
HRESULT hr = m_dxgi_factory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing_supported,
|
||||
|
|
|
@ -37,7 +37,6 @@ public:
|
|||
ALWAYS_INLINE DXGI_FORMAT GetDXGIFormat() const { return m_dxgi_format; }
|
||||
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 Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
|
||||
void Unmap() override;
|
||||
|
|
|
@ -38,6 +38,7 @@ Log_SetChannel(GPUDevice);
|
|||
std::unique_ptr<GPUDevice> g_gpu_device;
|
||||
|
||||
static std::string s_pipeline_cache_path;
|
||||
size_t GPUDevice::s_total_vram_usage = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
GPUTexture::Type type, GPUTexture::Format format,
|
||||
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,
|
||||
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...
|
||||
Log_ErrorFmt("Failed to upload {}x{} to pooled texture", width, height);
|
||||
break;
|
||||
// We're into textures recycled this frame, not going to find anything newer.
|
||||
// But prefer reuse over creating a new texture.
|
||||
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);
|
||||
m_texture_pool.erase(it);
|
||||
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);
|
||||
|
@ -814,23 +855,6 @@ void GPUDevice::RecycleTexture(std::unique_ptr<GPUTexture> texture)
|
|||
if (!texture)
|
||||
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()),
|
||||
static_cast<u16>(texture->GetHeight()),
|
||||
static_cast<u8>(texture->GetLayers()),
|
||||
|
@ -840,28 +864,74 @@ void GPUDevice::RecycleTexture(std::unique_ptr<GPUTexture> texture)
|
|||
texture->GetFormat(),
|
||||
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()
|
||||
{
|
||||
m_texture_pool_counter = 0;
|
||||
m_texture_pool.clear();
|
||||
m_target_pool.clear();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
const u32 counter = m_texture_pool_counter++;
|
||||
for (auto it = m_texture_pool.begin(); it != m_texture_pool.end();)
|
||||
const u32 prev_counter = m_texture_pool_counter++;
|
||||
for (u32 pool_idx = 0; pool_idx < 2; pool_idx++)
|
||||
{
|
||||
if (counter < it->remove_count)
|
||||
break;
|
||||
TexturePool& pool = pool_idx ? m_target_pool : m_texture_pool;
|
||||
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());
|
||||
it = m_texture_pool.erase(it);
|
||||
Log_ProfileFmt("Trim {}x{} texture from pool", it->texture->GetWidth(), it->texture->GetHeight());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -424,6 +424,8 @@ protected:
|
|||
class GPUDevice
|
||||
{
|
||||
public:
|
||||
friend GPUTexture;
|
||||
|
||||
// TODO: drop virtuals
|
||||
// TODO: gpu crash handling on present
|
||||
using DrawIndex = u16;
|
||||
|
@ -451,6 +453,7 @@ public:
|
|||
bool gpu_timing : 1;
|
||||
bool shader_cache : 1;
|
||||
bool pipeline_cache : 1;
|
||||
bool prefer_unused_textures : 1;
|
||||
};
|
||||
|
||||
struct AdapterAndModeList
|
||||
|
@ -627,6 +630,7 @@ public:
|
|||
virtual void SetVSync(bool enabled) = 0;
|
||||
|
||||
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
||||
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
||||
|
||||
bool UpdateImGuiFontTexture();
|
||||
bool UsesLowerLeftOrigin() const;
|
||||
|
@ -675,7 +679,9 @@ protected:
|
|||
std::unique_ptr<GPUSampler> m_linear_sampler;
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -700,19 +706,27 @@ private:
|
|||
struct TexturePoolEntry
|
||||
{
|
||||
std::unique_ptr<GPUTexture> texture;
|
||||
u32 remove_count;
|
||||
u32 use_counter;
|
||||
TexturePoolKey key;
|
||||
};
|
||||
|
||||
using TexturePool = std::deque<TexturePoolEntry>;
|
||||
|
||||
void OpenShaderCache(const std::string_view& base_path, u32 version);
|
||||
void CloseShaderCache();
|
||||
bool CreateResources();
|
||||
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<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;
|
||||
|
||||
// TODO: Move out.
|
||||
|
|
|
@ -10,15 +10,17 @@
|
|||
|
||||
Log_SetChannel(GPUTexture);
|
||||
|
||||
GPUTexture::GPUTexture() = default;
|
||||
|
||||
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_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)
|
||||
{
|
||||
|
@ -48,23 +50,30 @@ const char* GPUTexture::GetFormatName(Format 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
|
||||
{
|
||||
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)
|
||||
{
|
||||
static constexpr std::array<u8, static_cast<size_t>(Format::MaxCount)> sizes = {{
|
||||
|
|
|
@ -73,9 +73,18 @@ public:
|
|||
};
|
||||
|
||||
public:
|
||||
GPUTexture(const GPUTexture&) = delete;
|
||||
virtual ~GPUTexture();
|
||||
|
||||
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 GetHeight() const { return m_height; }
|
||||
|
@ -122,16 +131,9 @@ public:
|
|||
m_clear_value.depth = depth;
|
||||
}
|
||||
|
||||
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);
|
||||
size_t GetVRAMUsage() const;
|
||||
|
||||
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);
|
||||
|
||||
virtual bool IsValid() const = 0;
|
||||
GPUTexture& operator=(const GPUTexture&) = delete;
|
||||
|
||||
virtual bool Update(u32 x, u32 y, u32 width, u32 height, const void* data, u32 pitch, u32 layer = 0,
|
||||
u32 level = 0) = 0;
|
||||
|
@ -144,11 +146,8 @@ public:
|
|||
virtual void SetDebugName(const std::string_view& name) = 0;
|
||||
|
||||
protected:
|
||||
GPUTexture();
|
||||
GPUTexture(u16 width, u16 height, u8 layers, u8 levels, u8 samples, Type type, Format format);
|
||||
|
||||
void ClearBaseProperties();
|
||||
|
||||
u16 m_width = 0;
|
||||
u16 m_height = 0;
|
||||
u8 m_layers = 0;
|
||||
|
|
|
@ -107,9 +107,6 @@ public:
|
|||
|
||||
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);
|
||||
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 Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
|
||||
|
|
|
@ -220,6 +220,7 @@ void MetalDevice::SetFeatures(FeatureMask disabled_features)
|
|||
m_features.partial_msaa_resolve = false;
|
||||
m_features.shader_cache = true;
|
||||
m_features.pipeline_cache = false;
|
||||
m_features.prefer_unused_textures = true;
|
||||
}
|
||||
|
||||
bool MetalDevice::LoadShaders()
|
||||
|
@ -871,13 +872,11 @@ MetalTexture::MetalTexture(id<MTLTexture> texture, u16 width, u16 height, u8 lay
|
|||
|
||||
MetalTexture::~MetalTexture()
|
||||
{
|
||||
MetalDevice::GetInstance().UnbindTexture(this);
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool MetalTexture::IsValid() const
|
||||
{
|
||||
return (m_texture != nil);
|
||||
if (m_texture != nil)
|
||||
{
|
||||
MetalDevice::GetInstance().UnbindTexture(this);
|
||||
MetalDevice::DeferRelease(m_texture);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
GPUTexture::Type type, GPUTexture::Format format,
|
||||
const void* data, u32 data_stride)
|
||||
|
|
|
@ -52,11 +52,7 @@ std::unique_ptr<GPUTexture> OpenGLDevice::CreateTexture(u32 width, u32 height, u
|
|||
GPUTexture::Type type, GPUTexture::Format format,
|
||||
const void* data, u32 data_stride)
|
||||
{
|
||||
std::unique_ptr<OpenGLTexture> tex(std::make_unique<OpenGLTexture>());
|
||||
if (!tex->Create(width, height, layers, levels, samples, type, format, data, data_stride))
|
||||
tex.reset();
|
||||
|
||||
return tex;
|
||||
return OpenGLTexture::Create(width, height, layers, levels, samples, type, format, data, data_stride);
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)];
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Destroy();
|
||||
if (m_id != 0)
|
||||
{
|
||||
OpenGLDevice::GetInstance().UnbindTexture(this);
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenGLTexture::UseTextureStorage(bool multisampled)
|
||||
|
@ -99,16 +110,16 @@ bool OpenGLTexture::UseTextureStorage() const
|
|||
return UseTextureStorage(IsMultisampled());
|
||||
}
|
||||
|
||||
bool OpenGLTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
|
||||
const void* data, u32 data_pitch)
|
||||
std::unique_ptr<OpenGLTexture> OpenGLTexture::Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||
Type type, Format format, const void* data, u32 data_pitch)
|
||||
{
|
||||
if (!ValidateConfig(width, height, layers, levels, samples, type, format))
|
||||
return false;
|
||||
return nullptr;
|
||||
|
||||
if (layers > 1 && data)
|
||||
{
|
||||
Log_ErrorPrintf("Loading texture array data not currently supported");
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
glDeleteTextures(1, &id);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (IsValid())
|
||||
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();
|
||||
return std::unique_ptr<OpenGLTexture>(new OpenGLTexture(width, height, layers, levels, samples, type, format, id));
|
||||
}
|
||||
|
||||
void OpenGLTexture::CommitClear()
|
||||
|
|
|
@ -15,7 +15,6 @@ class OpenGLTexture final : public GPUTexture
|
|||
friend OpenGLDevice;
|
||||
|
||||
public:
|
||||
OpenGLTexture();
|
||||
OpenGLTexture(const OpenGLTexture&) = delete;
|
||||
~OpenGLTexture();
|
||||
|
||||
|
@ -23,16 +22,15 @@ public:
|
|||
static const std::tuple<GLenum, GLenum, GLenum>& GetPixelFormatMapping(Format format, bool gles);
|
||||
|
||||
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 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 SetDebugName(const std::string_view& name) override;
|
||||
|
||||
bool Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format,
|
||||
const void* data = nullptr, u32 data_pitch = 0);
|
||||
void Destroy();
|
||||
static std::unique_ptr<OpenGLTexture> Create(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type,
|
||||
Format format, const void* data = nullptr, u32 data_pitch = 0);
|
||||
|
||||
bool UseTextureStorage() const;
|
||||
|
||||
|
@ -46,6 +44,8 @@ public:
|
|||
OpenGLTexture& operator=(const OpenGLTexture&) = delete;
|
||||
|
||||
private:
|
||||
OpenGLTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, Type type, Format format, GLuint id);
|
||||
|
||||
GLuint m_id = 0;
|
||||
|
||||
u32 m_map_offset = 0;
|
||||
|
|
|
@ -2414,6 +2414,7 @@ bool VulkanDevice::CheckFeatures(FeatureMask disabled_features)
|
|||
m_features.partial_msaa_resolve = true;
|
||||
m_features.shader_cache = true;
|
||||
m_features.pipeline_cache = true;
|
||||
m_features.prefer_unused_textures = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,6 @@ public:
|
|||
|
||||
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 Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32 height, u32 layer = 0, u32 level = 0) override;
|
||||
void Unmap() override;
|
||||
|
|
Loading…
Reference in a new issue