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
}
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));
}

View file

@ -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()

View file

@ -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)

View file

@ -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;

View file

@ -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,

View file

@ -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;

View file

@ -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;
}
}
}
}

View file

@ -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.

View file

@ -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 = {{

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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;
}

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)];
}
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()

View file

@ -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;

View file

@ -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;
}

View file

@ -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;