Fix crash when closing handles to asset file while any asset streaming task is active for asset from that file
This commit is contained in:
@@ -277,6 +277,11 @@ bool AudioClip::ExtractDataRaw(Array<byte>& resultData, AudioDataInfo& resultDat
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioClip::CancelStreaming()
|
||||||
|
{
|
||||||
|
CancelStreamingTasks();
|
||||||
|
}
|
||||||
|
|
||||||
int32 AudioClip::GetMaxResidency() const
|
int32 AudioClip::GetMaxResidency() const
|
||||||
{
|
{
|
||||||
return _totalChunks;
|
return _totalChunks;
|
||||||
@@ -338,6 +343,15 @@ Task* AudioClip::CreateStreamingTask(int32 residency)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioClip::CancelStreamingTasks()
|
||||||
|
{
|
||||||
|
if (_streamingTask)
|
||||||
|
{
|
||||||
|
_streamingTask->Cancel();
|
||||||
|
ASSERT_LOW_LAYER(_streamingTask == nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool AudioClip::init(AssetInitData& initData)
|
bool AudioClip::init(AssetInitData& initData)
|
||||||
{
|
{
|
||||||
// Validate
|
// Validate
|
||||||
|
|||||||
@@ -200,6 +200,9 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// [BinaryAsset]
|
||||||
|
void CancelStreaming() override;
|
||||||
|
|
||||||
// [StreamableResource]
|
// [StreamableResource]
|
||||||
int32 GetMaxResidency() const override;
|
int32 GetMaxResidency() const override;
|
||||||
int32 GetCurrentResidency() const override;
|
int32 GetCurrentResidency() const override;
|
||||||
@@ -207,6 +210,7 @@ public:
|
|||||||
bool CanBeUpdated() const override;
|
bool CanBeUpdated() const override;
|
||||||
Task* UpdateAllocation(int32 residency) override;
|
Task* UpdateAllocation(int32 residency) override;
|
||||||
Task* CreateStreamingTask(int32 residency) override;
|
Task* CreateStreamingTask(int32 residency) override;
|
||||||
|
void CancelStreamingTasks() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|||||||
@@ -481,6 +481,10 @@ void Asset::InitAsVirtual()
|
|||||||
_isLoaded = true;
|
_isLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Asset::CancelStreaming()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
|
|
||||||
void Asset::GetReferences(Array<Guid>& output) const
|
void Asset::GetReferences(Array<Guid>& output) const
|
||||||
|
|||||||
@@ -154,6 +154,11 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
virtual void InitAsVirtual();
|
virtual void InitAsVirtual();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cancels any asynchronous content streaming by this asset (eg. mesh data streaming into GPU memory). Will release any locks for asset storage container.
|
||||||
|
/// </summary>
|
||||||
|
virtual void CancelStreaming();
|
||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -769,6 +769,11 @@ void Model::InitAsVirtual()
|
|||||||
BinaryAsset::InitAsVirtual();
|
BinaryAsset::InitAsVirtual();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::CancelStreaming()
|
||||||
|
{
|
||||||
|
CancelStreamingTasks();
|
||||||
|
}
|
||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
|
|
||||||
void Model::GetReferences(Array<Guid>& output) const
|
void Model::GetReferences(Array<Guid>& output) const
|
||||||
@@ -849,6 +854,15 @@ Task* Model::CreateStreamingTask(int32 residency)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::CancelStreamingTasks()
|
||||||
|
{
|
||||||
|
if (_streamingTask)
|
||||||
|
{
|
||||||
|
_streamingTask->Cancel();
|
||||||
|
ASSERT_LOW_LAYER(_streamingTask == nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Asset::LoadResult Model::load()
|
Asset::LoadResult Model::load()
|
||||||
{
|
{
|
||||||
// Get header chunk
|
// Get header chunk
|
||||||
|
|||||||
@@ -251,6 +251,7 @@ public:
|
|||||||
int32 GetLODsCount() const override;
|
int32 GetLODsCount() const override;
|
||||||
void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) override;
|
void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) override;
|
||||||
void InitAsVirtual() override;
|
void InitAsVirtual() override;
|
||||||
|
void CancelStreaming() override;
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
void GetReferences(Array<Guid>& output) const override;
|
void GetReferences(Array<Guid>& output) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -262,6 +263,7 @@ public:
|
|||||||
bool CanBeUpdated() const override;
|
bool CanBeUpdated() const override;
|
||||||
Task* UpdateAllocation(int32 residency) override;
|
Task* UpdateAllocation(int32 residency) override;
|
||||||
Task* CreateStreamingTask(int32 residency) override;
|
Task* CreateStreamingTask(int32 residency) override;
|
||||||
|
void CancelStreamingTasks() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// [ModelBase]
|
// [ModelBase]
|
||||||
|
|||||||
@@ -760,6 +760,11 @@ void SkinnedModel::InitAsVirtual()
|
|||||||
BinaryAsset::InitAsVirtual();
|
BinaryAsset::InitAsVirtual();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkinnedModel::CancelStreaming()
|
||||||
|
{
|
||||||
|
CancelStreamingTasks();
|
||||||
|
}
|
||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
|
|
||||||
void SkinnedModel::GetReferences(Array<Guid>& output) const
|
void SkinnedModel::GetReferences(Array<Guid>& output) const
|
||||||
@@ -840,6 +845,15 @@ Task* SkinnedModel::CreateStreamingTask(int32 residency)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SkinnedModel::CancelStreamingTasks()
|
||||||
|
{
|
||||||
|
if (_streamingTask)
|
||||||
|
{
|
||||||
|
_streamingTask->Cancel();
|
||||||
|
ASSERT_LOW_LAYER(_streamingTask == nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Asset::LoadResult SkinnedModel::load()
|
Asset::LoadResult SkinnedModel::load()
|
||||||
{
|
{
|
||||||
// Get header chunk
|
// Get header chunk
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ public:
|
|||||||
int32 GetLODsCount() const override;
|
int32 GetLODsCount() const override;
|
||||||
void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) override;
|
void GetMeshes(Array<MeshBase*>& meshes, int32 lodIndex = 0) override;
|
||||||
void InitAsVirtual() override;
|
void InitAsVirtual() override;
|
||||||
|
void CancelStreaming() override;
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
void GetReferences(Array<Guid>& output) const override;
|
void GetReferences(Array<Guid>& output) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -284,6 +285,7 @@ public:
|
|||||||
bool CanBeUpdated() const override;
|
bool CanBeUpdated() const override;
|
||||||
Task* UpdateAllocation(int32 residency) override;
|
Task* UpdateAllocation(int32 residency) override;
|
||||||
Task* CreateStreamingTask(int32 residency) override;
|
Task* CreateStreamingTask(int32 residency) override;
|
||||||
|
void CancelStreamingTasks() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// [ModelBase]
|
// [ModelBase]
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "Engine/Platform/File.h"
|
#include "Engine/Platform/File.h"
|
||||||
#include "Engine/Profiler/ProfilerCPU.h"
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
#include "Engine/Serialization/FileWriteStream.h"
|
#include "Engine/Serialization/FileWriteStream.h"
|
||||||
|
#include "Engine/Content/Asset.h"
|
||||||
|
#include "Engine/Content/Content.h"
|
||||||
#include "Engine/Threading/Threading.h"
|
#include "Engine/Threading/Threading.h"
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
#include "Engine/Serialization/JsonWriter.h"
|
#include "Engine/Serialization/JsonWriter.h"
|
||||||
@@ -1289,9 +1291,24 @@ void FlaxStorage::CloseFileHandles()
|
|||||||
// In those situations all the async tasks using this storage should be cancelled externally
|
// In those situations all the async tasks using this storage should be cancelled externally
|
||||||
|
|
||||||
// Ensure that no one is using this resource
|
// Ensure that no one is using this resource
|
||||||
int32 waitTime = 500;
|
int32 waitTime = 10;
|
||||||
while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0)
|
while (Platform::AtomicRead(&_chunksLock) != 0 && waitTime-- > 0)
|
||||||
Platform::Sleep(10);
|
Platform::Sleep(10);
|
||||||
|
if (Platform::AtomicRead(&_chunksLock) != 0)
|
||||||
|
{
|
||||||
|
// File can be locked by some streaming tasks (eg. AudioClip::StreamingTask or StreamModelLODTask)
|
||||||
|
for (int32 i = 0; i < GetEntriesCount(); i++)
|
||||||
|
{
|
||||||
|
Entry e;
|
||||||
|
GetEntry(i, e);
|
||||||
|
Asset* asset = Content::GetAsset(e.ID);
|
||||||
|
if (asset)
|
||||||
|
{
|
||||||
|
LOG(Info, "Canceling streaming for asset {0}", asset->ToString());
|
||||||
|
asset->CancelStreaming();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ASSERT(_chunksLock == 0);
|
ASSERT(_chunksLock == 0);
|
||||||
|
|
||||||
_file.DeleteAll();
|
_file.DeleteAll();
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ StreamingTexture::StreamingTexture(ITextureOwner* parent, const String& name)
|
|||||||
: StreamableResource(StreamingGroups::Instance()->Textures())
|
: StreamableResource(StreamingGroups::Instance()->Textures())
|
||||||
, _owner(parent)
|
, _owner(parent)
|
||||||
, _texture(nullptr)
|
, _texture(nullptr)
|
||||||
, _streamingTasksCount(0)
|
|
||||||
, _isBlockCompressed(false)
|
, _isBlockCompressed(false)
|
||||||
{
|
{
|
||||||
ASSERT(_owner != nullptr);
|
ASSERT(_owner != nullptr);
|
||||||
@@ -62,7 +61,7 @@ StreamingTexture::~StreamingTexture()
|
|||||||
{
|
{
|
||||||
UnloadTexture();
|
UnloadTexture();
|
||||||
SAFE_DELETE(_texture);
|
SAFE_DELETE(_texture);
|
||||||
ASSERT(_streamingTasksCount == 0);
|
ASSERT(_streamingTasks.Count() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Float2 StreamingTexture::Size() const
|
Float2 StreamingTexture::Size() const
|
||||||
@@ -138,7 +137,7 @@ void StreamingTexture::UnloadTexture()
|
|||||||
_texture->ReleaseGPU();
|
_texture->ReleaseGPU();
|
||||||
_header.MipLevels = 0;
|
_header.MipLevels = 0;
|
||||||
|
|
||||||
ASSERT(_streamingTasksCount == 0);
|
ASSERT(_streamingTasks.Count() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 StreamingTexture::GetTotalMemoryUsage() const
|
uint64 StreamingTexture::GetTotalMemoryUsage() const
|
||||||
@@ -173,7 +172,12 @@ bool StreamingTexture::CanBeUpdated() const
|
|||||||
// - is not initialized
|
// - is not initialized
|
||||||
// - mip data uploading job running
|
// - mip data uploading job running
|
||||||
// - resize texture job running
|
// - resize texture job running
|
||||||
return IsInitialized() && Platform::AtomicRead(&_streamingTasksCount) == 0;
|
if (IsInitialized())
|
||||||
|
{
|
||||||
|
ScopeLock lock(_owner->GetOwnerLocker());
|
||||||
|
return _streamingTasks.Count() == 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
class StreamTextureResizeTask : public GPUTask
|
class StreamTextureResizeTask : public GPUTask
|
||||||
@@ -189,7 +193,7 @@ public:
|
|||||||
, _streamingTexture(texture)
|
, _streamingTexture(texture)
|
||||||
, _newTexture(newTexture)
|
, _newTexture(newTexture)
|
||||||
{
|
{
|
||||||
Platform::InterlockedIncrement(&_streamingTexture->_streamingTasksCount);
|
_streamingTexture->_streamingTasks.Add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
~StreamTextureResizeTask()
|
~StreamTextureResizeTask()
|
||||||
@@ -222,7 +226,11 @@ protected:
|
|||||||
|
|
||||||
void OnEnd() override
|
void OnEnd() override
|
||||||
{
|
{
|
||||||
Platform::InterlockedDecrement(&_streamingTexture->_streamingTasksCount);
|
if (_streamingTexture)
|
||||||
|
{
|
||||||
|
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
|
||||||
|
_streamingTexture->_streamingTasks.Remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
GPUTask::OnEnd();
|
GPUTask::OnEnd();
|
||||||
@@ -318,7 +326,7 @@ public:
|
|||||||
, _streamingTexture(texture)
|
, _streamingTexture(texture)
|
||||||
, _dataLock(_streamingTexture->GetOwner()->LockData())
|
, _dataLock(_streamingTexture->GetOwner()->LockData())
|
||||||
{
|
{
|
||||||
Platform::InterlockedIncrement(&_streamingTexture->_streamingTasksCount);
|
_streamingTexture->_streamingTasks.Add(this);
|
||||||
_texture.OnUnload.Bind<StreamTextureMipTask, &StreamTextureMipTask::onResourceUnload2>(this);
|
_texture.OnUnload.Bind<StreamTextureMipTask, &StreamTextureMipTask::onResourceUnload2>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +336,8 @@ private:
|
|||||||
// Unlink texture
|
// Unlink texture
|
||||||
if (_streamingTexture)
|
if (_streamingTexture)
|
||||||
{
|
{
|
||||||
Platform::InterlockedDecrement(&_streamingTexture->_streamingTasksCount);
|
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
|
||||||
|
_streamingTexture->_streamingTasks.Remove(this);
|
||||||
_streamingTexture = nullptr;
|
_streamingTexture = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,7 +388,8 @@ protected:
|
|||||||
_dataLock.Release();
|
_dataLock.Release();
|
||||||
if (_streamingTexture)
|
if (_streamingTexture)
|
||||||
{
|
{
|
||||||
Platform::InterlockedDecrement(&_streamingTexture->_streamingTasksCount);
|
ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker());
|
||||||
|
_streamingTexture->_streamingTasks.Remove(this);
|
||||||
_streamingTexture = nullptr;
|
_streamingTexture = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,3 +466,11 @@ Task* StreamingTexture::CreateStreamingTask(int32 residency)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StreamingTexture::CancelStreamingTasks()
|
||||||
|
{
|
||||||
|
ScopeLock lock(_owner->GetOwnerLocker());
|
||||||
|
auto tasks = _streamingTasks;
|
||||||
|
for (auto task : tasks)
|
||||||
|
task->Cancel();
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ protected:
|
|||||||
ITextureOwner* _owner;
|
ITextureOwner* _owner;
|
||||||
GPUTexture* _texture;
|
GPUTexture* _texture;
|
||||||
TextureHeader _header;
|
TextureHeader _header;
|
||||||
volatile mutable int64 _streamingTasksCount;
|
|
||||||
int32 _minMipCountBlockCompressed;
|
int32 _minMipCountBlockCompressed;
|
||||||
bool _isBlockCompressed;
|
bool _isBlockCompressed;
|
||||||
|
Array<Task*, FixedAllocation<16>> _streamingTasks;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StreamingTexture(ITextureOwner* owner, const String& name);
|
StreamingTexture(ITextureOwner* owner, const String& name);
|
||||||
@@ -173,4 +173,5 @@ public:
|
|||||||
bool CanBeUpdated() const override;
|
bool CanBeUpdated() const override;
|
||||||
Task* UpdateAllocation(int32 residency) override;
|
Task* UpdateAllocation(int32 residency) override;
|
||||||
Task* CreateStreamingTask(int32 residency) override;
|
Task* CreateStreamingTask(int32 residency) override;
|
||||||
|
void CancelStreamingTasks() override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -636,6 +636,11 @@ bool TextureBase::Init(void* ptr)
|
|||||||
return Init(initData);
|
return Init(initData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextureBase::CancelStreaming()
|
||||||
|
{
|
||||||
|
_texture.CancelStreamingTasks();
|
||||||
|
}
|
||||||
|
|
||||||
int32 TextureBase::CalculateChunkIndex(int32 mipIndex) const
|
int32 TextureBase::CalculateChunkIndex(int32 mipIndex) const
|
||||||
{
|
{
|
||||||
// Mips are in 0-13 chunks
|
// Mips are in 0-13 chunks
|
||||||
|
|||||||
@@ -218,6 +218,9 @@ private:
|
|||||||
API_FUNCTION(NoProxy) bool Init(void* ptr);
|
API_FUNCTION(NoProxy) bool Init(void* ptr);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// [BinaryAsset]
|
||||||
|
void CancelStreaming() override;
|
||||||
|
|
||||||
// [ITextureOwner]
|
// [ITextureOwner]
|
||||||
CriticalSection& GetOwnerLocker() const override;
|
CriticalSection& GetOwnerLocker() const override;
|
||||||
Task* RequestMipDataAsync(int32 mipIndex) override;
|
Task* RequestMipDataAsync(int32 mipIndex) override;
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ public:
|
|||||||
/// <returns>Async task or tasks that update resource residency level. Must be preceded with UpdateAllocation call.</returns>
|
/// <returns>Async task or tasks that update resource residency level. Must be preceded with UpdateAllocation call.</returns>
|
||||||
virtual Task* CreateStreamingTask(int32 residency) = 0;
|
virtual Task* CreateStreamingTask(int32 residency) = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cancels any streaming task (or tasks sequence) started for this resource.
|
||||||
|
/// </summary>
|
||||||
|
virtual void CancelStreamingTasks() = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
struct StreamingCache
|
struct StreamingCache
|
||||||
|
|||||||
Reference in New Issue
Block a user