diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp index ced6b4edb..15dd71717 100644 --- a/src/core/imgui_overlays.cpp +++ b/src/core/imgui_overlays.cpp @@ -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)); } diff --git a/src/util/d3d11_device.cpp b/src/util/d3d11_device.cpp index b567f888a..2f6ae29ae 100644 --- a/src/util/d3d11_device.cpp +++ b/src/util/d3d11_device.cpp @@ -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() diff --git a/src/util/d3d11_texture.cpp b/src/util/d3d11_texture.cpp index a67a49342..a929bf61a 100644 --- a/src/util/d3d11_texture.cpp +++ b/src/util/d3d11_texture.cpp @@ -23,11 +23,7 @@ std::unique_ptr D3D11Device::CreateTexture(u32 width, u32 height, u3 GPUTexture::Type type, GPUTexture::Format format, const void* data, u32 data_stride) { - std::unique_ptr tex = std::make_unique(); - 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 D3D11Device::CreateSampler(const GPUSampler::Config& return std::unique_ptr(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 texture, ComPtr srv, + ComPtr rtv_dsv) + : GPUTexture(static_cast(width), static_cast(height), static_cast(layers), static_cast(levels), + static_cast(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(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::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(format), bind_flags, initial_data); - return false; + return nullptr; } ComPtr 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(width); - m_height = static_cast(height); - m_layers = static_cast(layers); - m_levels = static_cast(levels); - m_samples = static_cast(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(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) diff --git a/src/util/d3d11_texture.h b/src/util/d3d11_texture.h index 4d6815d98..5175af611 100644 --- a/src/util/d3d11_texture.h +++ b/src/util/d3d11_texture.h @@ -42,7 +42,6 @@ public: template using ComPtr = Microsoft::WRL::ComPtr; - D3D11Texture(); ~D3D11Texture(); ALWAYS_INLINE ID3D11Texture2D* GetD3DTexture() const { return m_texture.Get(); } @@ -75,16 +74,13 @@ public: } ALWAYS_INLINE operator bool() const { return static_cast(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 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 texture, ComPtr srv, ComPtr rtv_dsv); + ComPtr m_texture; ComPtr m_srv; ComPtr m_rtv_dsv; diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index fbbfe2698..2b64175b2 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -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, diff --git a/src/util/d3d12_texture.h b/src/util/d3d12_texture.h index b170d126c..277e22c73 100644 --- a/src/util/d3d12_texture.h +++ b/src/util/d3d12_texture.h @@ -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(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; diff --git a/src/util/gpu_device.cpp b/src/util/gpu_device.cpp index 0b649e06c..285c88ce3 100644 --- a/src/util/gpu_device.cpp +++ b/src/util/gpu_device.cpp @@ -38,6 +38,7 @@ Log_SetChannel(GPUDevice); std::unique_ptr 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 GPUDevice::FlipToLowerLeft(const Common::Rectangle& return Common::Rectangle(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 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 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 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(texture->GetWidth()), static_cast(texture->GetHeight()), static_cast(texture->GetLayers()), @@ -840,28 +864,74 @@ void GPUDevice::RecycleTexture(std::unique_ptr 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::max() : m_texture_pool.front().use_counter; + const u32 target_min = + m_target_pool.empty() ? std::numeric_limits::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; + } + } } } diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index c47c88550..76292ecdb 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -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 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 texture; - u32 remove_count; + u32 use_counter; TexturePoolKey key; }; + using TexturePool = std::deque; + 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 m_imgui_pipeline; std::unique_ptr m_imgui_font_texture; - std::deque 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. diff --git a/src/util/gpu_texture.cpp b/src/util/gpu_texture.cpp index 9994524f5..165d4dca3 100644 --- a/src/util/gpu_texture.cpp +++ b/src/util/gpu_texture.cpp @@ -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(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 GPUTexture::GetUNormClearColor() const { return GPUDevice::RGBA8ToFloat(m_clear_value.color); } +size_t GPUTexture::GetVRAMUsage() const +{ + if (m_levels == 1) [[likely]] + return ((static_cast(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(width * height) * ps; + } + + return ts; +} + u32 GPUTexture::GetPixelSize(GPUTexture::Format format) { static constexpr std::array(Format::MaxCount)> sizes = {{ diff --git a/src/util/gpu_texture.h b/src/util/gpu_texture.h index 566dc55fd..9dec6e654 100644 --- a/src/util/gpu_texture.h +++ b/src/util/gpu_texture.h @@ -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& texture_data, u32& texture_data_stride, + GPUTexture::Format format); + static void FlipTextureDataRGBA8(u32 width, u32 height, std::vector& 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& texture_data, u32& texture_data_stride, - GPUTexture::Format format); - static void FlipTextureDataRGBA8(u32 width, u32 height, std::vector& 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; diff --git a/src/util/metal_device.h b/src/util/metal_device.h index b121cbcd5..5b5ac32f3 100644 --- a/src/util/metal_device.h +++ b/src/util/metal_device.h @@ -107,9 +107,6 @@ public: bool Create(id 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; diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index b9a144d9f..7be3ec508 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -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 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 MetalDevice::CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type, GPUTexture::Format format, const void* data, u32 data_stride) diff --git a/src/util/opengl_device.cpp b/src/util/opengl_device.cpp index da8f52cc1..0c550947f 100644 --- a/src/util/opengl_device.cpp +++ b/src/util/opengl_device.cpp @@ -52,11 +52,7 @@ std::unique_ptr OpenGLDevice::CreateTexture(u32 width, u32 height, u GPUTexture::Type type, GPUTexture::Format format, const void* data, u32 data_stride) { - std::unique_ptr tex(std::make_unique()); - 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; } diff --git a/src/util/opengl_texture.cpp b/src/util/opengl_texture.cpp index d487442fb..dafafcd2e 100644 --- a/src/util/opengl_texture.cpp +++ b/src/util/opengl_texture.cpp @@ -82,11 +82,22 @@ const std::tuple& OpenGLTexture::GetPixelFormatMapping(G return gles ? mapping_gles[static_cast(format)] : mapping[static_cast(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(width), static_cast(height), static_cast(layers), static_cast(levels), + static_cast(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::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(width); - m_height = static_cast(height); - m_layers = static_cast(layers); - m_levels = static_cast(levels); - m_samples = static_cast(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(new OpenGLTexture(width, height, layers, levels, samples, type, format, id)); } void OpenGLTexture::CommitClear() diff --git a/src/util/opengl_texture.h b/src/util/opengl_texture.h index 336c49487..b8fc3e382 100644 --- a/src/util/opengl_texture.h +++ b/src/util/opengl_texture.h @@ -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& 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 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; diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index 49d4cf233..bf91d0e4b 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -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; } diff --git a/src/util/vulkan_texture.h b/src/util/vulkan_texture.h index 0dc84670e..f33b8c472 100644 --- a/src/util/vulkan_texture.h +++ b/src/util/vulkan_texture.h @@ -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;