mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	GPUDevice: Improve texture pooling
This commit is contained in:
		
							parent
							
								
									efaee4ab50
								
							
						
					
					
						commit
						dc5e4120cd
					
				|  | @ -405,13 +405,19 @@ void ImGuiManager::DrawPerformanceOverlay() | ||||||
| #endif | #endif | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (g_settings.display_show_gpu && g_gpu_device->IsGPUTimingEnabled()) |     if (g_settings.display_show_gpu) | ||||||
|  |     { | ||||||
|  |       if (g_gpu_device->IsGPUTimingEnabled()) | ||||||
|       { |       { | ||||||
|         text.assign("GPU: "); |         text.assign("GPU: "); | ||||||
|         FormatProcessorStat(text, System::GetGPUUsage(), System::GetGPUAverageTime()); |         FormatProcessorStat(text, System::GetGPUUsage(), System::GetGPUAverageTime()); | ||||||
|         DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255)); |         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)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (g_settings.display_show_status_indicators) |     if (g_settings.display_show_status_indicators) | ||||||
|     { |     { | ||||||
|       const bool rewinding = System::IsRewinding(); |       const bool rewinding = System::IsRewinding(); | ||||||
|  |  | ||||||
|  | @ -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() | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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.
 | ||||||
|  |         if (m_texture_pool.size() < pool_size) | ||||||
|           break; |           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; | ||||||
|  |     for (auto it = pool.begin(); it != pool.end();) | ||||||
|  |     { | ||||||
|  |       const u32 delta = (it->use_counter - prev_counter); | ||||||
|  |       if (delta < POOL_PURGE_DELAY) | ||||||
|         break; |         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; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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.
 | ||||||
|  |  | ||||||
|  | @ -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 = {{ | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool MetalTexture::IsValid() const |  | ||||||
|   { |   { | ||||||
|   return (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*/, | 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) | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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() | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Stenzek
						Stenzek