diff --git a/Source/Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h b/Source/Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h index f96b93d68..2573505ba 100644 --- a/Source/Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h +++ b/Source/Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h @@ -16,7 +16,7 @@ class GPUUploadTextureMipTask : public GPUTask protected: GPUTextureReference _texture; - int32 _mipIndex; + int32 _mipIndex, _rowPitch, _slicePitch; BytesContainer _data; public: @@ -27,11 +27,15 @@ public: /// The target texture. /// The target texture mip data. /// The data to upload. + /// The data row pitch. + /// The data slice pitch. /// True if copy data to the temporary buffer, otherwise the input data will be used directly. Then ensure it is valid during the copy operation period (for the next few frames). - GPUUploadTextureMipTask(GPUTexture* texture, int32 mipIndex, Span data, bool copyData) + GPUUploadTextureMipTask(GPUTexture* texture, int32 mipIndex, Span data, int32 rowPitch, int32 slicePitch, bool copyData) : GPUTask(Type::UploadTexture) , _texture(texture) , _mipIndex(mipIndex) + , _rowPitch(rowPitch) + , _slicePitch(slicePitch) { _texture.OnUnload.Bind(this); @@ -66,18 +70,14 @@ protected: return Result::MissingResources; ASSERT(texture->IsAllocated()); - // Cache data - const int32 arraySize = texture->ArraySize(); - uint32 rowPitch, slicePitch; - texture->ComputePitch(_mipIndex, rowPitch, slicePitch); - ASSERT((uint32)_data.Length() >= slicePitch * arraySize); - // Update all array slices const byte* dataSource = _data.Get(); + const int32 arraySize = texture->ArraySize(); + ASSERT(_data.Length() >= _slicePitch * arraySize); for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { - context->GPU->UpdateTexture(texture, arrayIndex, _mipIndex, dataSource, rowPitch, slicePitch); - dataSource += slicePitch; + context->GPU->UpdateTexture(texture, arrayIndex, _mipIndex, dataSource, _rowPitch, _slicePitch); + dataSource += _slicePitch; } return Result::Ok; diff --git a/Source/Engine/Graphics/Textures/GPUTexture.cpp b/Source/Engine/Graphics/Textures/GPUTexture.cpp index 4c37a7ed4..f9cf13cab 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.cpp +++ b/Source/Engine/Graphics/Textures/GPUTexture.cpp @@ -595,17 +595,21 @@ void GPUTexture::OnReleaseGPU() _residentMipLevels = 0; } -GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipIndex) +GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, bool copyData) { - ASSERT(IsRegularTexture() && IsAllocated()); - //ASSERT(Math::IsInRange(mipIndex, HighestResidentMipIndex() - 1, MipLevels() - 1) && mipIndex < MipLevels() && data.IsValid()); + uint32 rowPitch, slicePitch; + ComputePitch(mipIndex, rowPitch, slicePitch); + return UploadMipMapAsync(data, mipIndex, rowPitch, slicePitch, copyData); +} + +GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, int32 rowPitch, int32 slicePitch, bool copyData) +{ + ASSERT(IsAllocated()); ASSERT(mipIndex < MipLevels() && data.IsValid()); - ASSERT(data.Length() == SlicePitch(mipIndex) * ArraySize()); - - // Create task - auto task = ::New(this, mipIndex, data, false); - ASSERT(task && task->HasReference(this)); - + ASSERT(data.Length() >= slicePitch); + // TODO: support texture data upload to the GPU on a main thread during rendering without this async task (faster direct upload) + auto task = ::New(this, mipIndex, data, rowPitch, slicePitch, copyData); + ASSERT_LOW_LAYER(task && task->HasReference(this)); return task; } diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h index be817a49e..28007196d 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.h +++ b/Source/Engine/Graphics/Textures/GPUTexture.h @@ -524,8 +524,20 @@ public: /// /// Data to upload (it must be valid for the next a few frames due to GPU latency and async works executing) /// Mip level index. + /// If true, the data will be copied to the async execution task instead of using the input pointer provided. /// Created async task or null if cannot. - GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex); + GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, bool copyData = false); + + /// + /// Uploads mip map data to the GPU. Creates async GPU task. + /// + /// Data to upload (it must be valid for the next a few frames due to GPU latency and async works executing) + /// Mip level index. + /// The data row pitch. + /// The data slice pitch. + /// If true, the data will be copied to the async execution task instead of using the input pointer provided. + /// Created async task or null if cannot. + GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, int32 rowPitch, int32 slicePitch, bool copyData = false); /// /// Stops current thread execution to gather texture data from the GPU. diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index 0d7062e93..0290048fc 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -316,7 +316,7 @@ private: public: StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex) - : GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span(nullptr, 0), false) + : GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span(nullptr, 0), 0, 0, false) , _streamingTexture(texture) , _dataLock(_streamingTexture->GetOwner()->LockData()) { diff --git a/Source/Engine/Render2D/FontTextureAtlas.cpp b/Source/Engine/Render2D/FontTextureAtlas.cpp index 9595d5c79..88bc47e0d 100644 --- a/Source/Engine/Render2D/FontTextureAtlas.cpp +++ b/Source/Engine/Render2D/FontTextureAtlas.cpp @@ -210,7 +210,9 @@ void FontTextureAtlas::Flush() // Upload data to the GPU BytesContainer data; data.Link(_data); - _texture->UploadMipMapAsync(data, 0)->Start(); + auto task = _texture->UploadMipMapAsync(data, 0); + if (task) + task->Start(); // Clear dirty flag _isDirty = false; diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 28d103f05..f4320400c 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -1627,7 +1627,9 @@ bool TerrainPatch::ModifySplatMap(int32 index, const Color32* samples, const Int LOG(Warning, "Failed to update splatmap texture. It's not allocated."); continue; } - t->UploadMipMapAsync(dataSplatmap->Mips[mipIndex].Data, mipIndex)->Start(); + auto task = t->UploadMipMapAsync(dataSplatmap->Mips[mipIndex].Data, mipIndex); + if (task) + task->Start(); } } else @@ -1745,7 +1747,9 @@ bool TerrainPatch::UpdateHeightData(const TerrainDataUpdateInfo& info, const Int // Update terrain texture (on a GPU) for (int32 mipIndex = 0; mipIndex < _dataHeightmap->Mips.Count(); mipIndex++) { - texture->UploadMipMapAsync(_dataHeightmap->Mips[mipIndex].Data, mipIndex)->Start(); + auto task = texture->UploadMipMapAsync(_dataHeightmap->Mips[mipIndex].Data, mipIndex); + if (task) + task->Start(); } #if 1