diff --git a/Source/Engine/Graphics/Textures/GPUTexture.cpp b/Source/Engine/Graphics/Textures/GPUTexture.cpp index b7b1fd68a..127cf2f5b 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.cpp +++ b/Source/Engine/Graphics/Textures/GPUTexture.cpp @@ -697,7 +697,7 @@ bool GPUTexture::DownloadData(TextureData& result) PROFILE_CPU(); // Use faster path for staging resources - if (IsStaging()) + if (IsStaging()) // TODO: what about chips with unified memory? if rendering is not active then we can access GPU memory from CPU directly (eg. mobile, integrated GPUs and some consoles) { const auto arraySize = ArraySize(); const auto mipLevels = MipLevels(); @@ -736,7 +736,11 @@ bool GPUTexture::DownloadData(TextureData& result) return false; } +#if GPU_ENABLE_RESOURCE_NAMING const auto name = ToString(); +#else + const StringView name = TEXT("Texture"); +#endif // Ensure not running on main thread - we support DownloadData from textures only on a worker threads (Thread Pool Workers or Content Loaders) if (IsInMainThread()) diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h index dbe839014..977723f26 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.h +++ b/Source/Engine/Graphics/Textures/GPUTexture.h @@ -517,11 +517,11 @@ public: 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. + /// Downloads the texture data to be accessible from CPU. For frequent access, use staging textures, otherwise current thread will be stalled to wait for the GPU frame to copy data into staging buffer. /// - /// The result data. + /// The destination texture data container. /// True if cannot download data, otherwise false. - bool DownloadData(TextureData& result); + API_FUNCTION() bool DownloadData(TextureData& result); /// /// Creates GPU async task that will gather texture data from the GPU. diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index fc9530e1a..81b5f159c 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -202,6 +202,43 @@ void TextureMipData::Copy(void* data, uint32 dataRowPitch, uint32 dataDepthPitch } } +int32 TextureData::GetArraySize() const +{ + return Items.Count(); +} + +int32 TextureData::GetMipLevels() const +{ + return Items.HasItems() ? Items[0].Mips.Count() : 0; +} + +void TextureData::Clear() +{ + Items.Resize(0, false); +} + +bool TextureData::GetPixels(Array& pixels, int32 mipIndex, int32 arrayIndex) +{ + if (Items.IsValidIndex(arrayIndex) && Items.Get()[arrayIndex].Mips.IsValidIndex(mipIndex)) + { + const int32 mipWidth = Math::Max(1, Width >> mipIndex); + const int32 mipHeight = Math::Max(1, Height >> mipIndex); + return Items.Get()[arrayIndex].Mips.Get()[mipIndex].GetPixels(pixels, mipWidth, mipHeight, Format); + } + return true; +} + +bool TextureData::GetPixels(Array& pixels, int32 mipIndex, int32 arrayIndex) +{ + if (Items.IsValidIndex(arrayIndex) && Items.Get()[arrayIndex].Mips.IsValidIndex(mipIndex)) + { + const int32 mipWidth = Math::Max(1, Width >> mipIndex); + const int32 mipHeight = Math::Max(1, Height >> mipIndex); + return Items.Get()[arrayIndex].Mips.Get()[mipIndex].GetPixels(pixels, mipWidth, mipHeight, Format); + } + return true; +} + REGISTER_BINARY_ASSET_ABSTRACT(TextureBase, "FlaxEngine.TextureBase"); TextureBase::TextureBase(const SpawnParams& params, const AssetInfo* info) diff --git a/Source/Engine/Graphics/Textures/TextureBase.cs b/Source/Engine/Graphics/Textures/TextureBase.cs index 69aeaddd3..4c353f872 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cs +++ b/Source/Engine/Graphics/Textures/TextureBase.cs @@ -5,6 +5,20 @@ using System.Runtime.InteropServices; namespace FlaxEngine { + partial class GPUTexture + { + /// + /// Downloads the texture data to be accessible from CPU. For frequent access, use staging textures, otherwise current thread will be stalled to wait for the GPU frame to copy data into staging buffer. + /// + /// Downloaded texture data container, or nul if failed. + [Unmanaged] + public TextureData DownloadData() + { + var result = new TextureData(); + return DownloadData(result) ? null : result; + } + } + partial class TextureBase { /// diff --git a/Source/Engine/Graphics/Textures/TextureData.h b/Source/Engine/Graphics/Textures/TextureData.h index 868f5c428..248ca977c 100644 --- a/Source/Engine/Graphics/Textures/TextureData.h +++ b/Source/Engine/Graphics/Textures/TextureData.h @@ -44,8 +44,9 @@ public: /// /// Texture data container (used to keep data downloaded from the GPU). /// -class FLAXENGINE_API TextureData +API_CLASS() class FLAXENGINE_API TextureData : public ScriptingObject { + DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(TextureData, ScriptingObject); public: /// /// Single entry of the texture array. Contains collection of mip maps. @@ -60,39 +61,24 @@ public: public: /// - /// Init + /// Top level texture surface width (in pixels). /// - TextureData() - { - } + API_FIELD(ReadOnly) int32 Width = 0; /// - /// Destructor + /// Top level texture surface height (in pixels). /// - ~TextureData() - { - } - -public: - /// - /// Top level texture width (in pixels). - /// - int32 Width = 0; + API_FIELD(ReadOnly) int32 Height = 0; /// - /// Top level texture height (in pixels). + /// Top level texture surface depth (in pixels). /// - int32 Height = 0; - - /// - /// Top level texture depth (in pixels). - /// - int32 Depth = 0; + API_FIELD(ReadOnly) int32 Depth = 0; /// /// The texture data format. /// - PixelFormat Format = PixelFormat::Unknown; + API_FIELD(ReadOnly) PixelFormat Format = PixelFormat::Unknown; /// /// The items collection (depth slices or array slices). @@ -123,26 +109,38 @@ public: } /// - /// Gets amount of textures in the array + /// Gets amount of texture slices in the array. /// - int32 GetArraySize() const - { - return Items.Count(); - } + API_PROPERTY() int32 GetArraySize() const; /// - /// Gets amount of mip maps in the textures + /// Gets amount of mip maps in the texture. /// - int32 GetMipLevels() const - { - return Items.HasItems() ? Items[0].Mips.Count() : 0; - } + API_PROPERTY() int32 GetMipLevels() const; /// - /// Clear data + /// Clear allocated memory. /// - void Clear() - { - Items.Resize(0, false); - } + API_FUNCTION() void Clear(); + +public: + /// + /// Gets the texture pixels as Color32 array. + /// + /// Supported only for 'basic' texture formats (uncompressed, single plane). + /// The result texture pixels array. + /// The mip index (zero-based). + /// The array or depth slice index (zero-based). + /// True if failed, otherwise false. + API_FUNCTION() bool GetPixels(API_PARAM(Out) Array& pixels, int32 mipIndex = 0, int32 arrayIndex = 0); + + /// + /// Gets the texture pixels as Color array. + /// + /// Supported only for 'basic' texture formats (uncompressed, single plane). + /// The result texture pixels array. + /// The mip index (zero-based). + /// The array or depth slice index (zero-based). + /// True if failed, otherwise false. + API_FUNCTION() bool GetPixels(API_PARAM(Out) Array& pixels, int32 mipIndex = 0, int32 arrayIndex = 0); }; diff --git a/Source/Engine/Scripting/Internal/InternalCalls.h b/Source/Engine/Scripting/Internal/InternalCalls.h index 6dbebb21d..b72993234 100644 --- a/Source/Engine/Scripting/Internal/InternalCalls.h +++ b/Source/Engine/Scripting/Internal/InternalCalls.h @@ -92,3 +92,11 @@ extern "C" FLAXENGINE_API void mono_add_internal_call(const char* name, const vo #define INTERNAL_CALL_CHECK_EXP_RETURN(expression, defaultValue) #endif + +template +T& InternalGetReference(T* obj) +{ + if (!obj) + DebugLog::ThrowNullReference(); + return *obj; +} diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index fb24d1be0..2f251932f 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -875,6 +875,12 @@ namespace Flax.Build.Bindings type = "MObject*"; return "(" + typeInfo.Type + "*)ScriptingObject::ToNative({0})"; } + else if (apiType.IsScriptingObject && typeInfo.IsRef && !typeInfo.IsPtr) + { + // Scripting Object is passed as pointer from C# but C++ uses reference + type = typeInfo.ToString(false) + '*'; + return $"InternalGetReference({{0}})"; + } // Nested type (namespace prefix is required) if (!(apiType.Parent is FileInfo))