From f79b179ba9ad4eeee6b269eba56f4465a77ab06c Mon Sep 17 00:00:00 2001 From: Alexander Lobakin Date: Thu, 26 Feb 2026 21:26:49 +0100 Subject: [PATCH] Allow ScratchImage to be non-owning ScratchImage is very convenient as it automatically compute the number of images, pitches etc. On the other hand, when the user has an already loaded texture, it still always allocate own buffer and perform copying, which might be suboptimal, especially with large textures. Extend ScratchImage::Initialize() with an additional argument (with the default value that behaves the same way as before) to allow it to be non-owning. This means that it still computes the number of images, pitches etc., but doesn't allocate the actual buffer. Then, the user can update individual images using ScratchImage::SetNonOwningImagePixels(). The actual ScratchImage doesn't own this memory, doesn't have own buffer and doesn't perform any deallocation on destruction. Memory management and lifetimes are fully on the user's side. A non-owning ScratchImage differs from owning by having a non-zero m_size and m_memory == nullptr. An example of usage: // create ScratchImage using Initialize(meta, cp_flags, false) // set pixels pointer for each image using auto& image = *scratch.SetNonOwningImagePixels(mip, face, depth, ptr); The return value is optional and is the same as ScratchImage.GetImage(mip, face, depth), only to avoid 2 external calls per subimage when needed. For example, if the user has a contiguous buffer with a mipmapped 2D image: for (size_t i{0}, off{0}; i < scratch.GetMetadata().mipLevels; ++i) off += scratch.SetNonOwningImagePixels(i, 0, 0, ptr + off)->slicePitch; A non-owning ScratchImage can currently be initialized only using Initialize() for simplicity and to indicate that the option is for advanced users only. Other Initialize*() methods can be expanded later if needed. Signed-off-by: Alexander Lobakin --- DirectXTex/DirectXTex.h | 3 ++- DirectXTex/DirectXTexImage.cpp | 41 +++++++++++++++++++++++++--------- DirectXTex/DirectXTexP.h | 3 ++- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h index b3e4a7a4..37b3129c 100644 --- a/DirectXTex/DirectXTex.h +++ b/DirectXTex/DirectXTex.h @@ -458,7 +458,7 @@ namespace DirectX ScratchImage(const ScratchImage&) = delete; ScratchImage& operator=(const ScratchImage&) = delete; - HRESULT __cdecl Initialize(_In_ const TexMetadata& mdata, _In_ CP_FLAGS flags = CP_FLAGS_NONE) noexcept; + HRESULT __cdecl Initialize(_In_ const TexMetadata& mdata, _In_ CP_FLAGS flags = CP_FLAGS_NONE, _In_ bool owning = true) noexcept; HRESULT __cdecl Initialize1D(_In_ DXGI_FORMAT fmt, _In_ size_t length, _In_ size_t arraySize, _In_ size_t mipLevels, _In_ CP_FLAGS flags = CP_FLAGS_NONE) noexcept; HRESULT __cdecl Initialize2D(_In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t arraySize, _In_ size_t mipLevels, _In_ CP_FLAGS flags = CP_FLAGS_NONE) noexcept; @@ -476,6 +476,7 @@ namespace DirectX const TexMetadata& __cdecl GetMetadata() const noexcept { return m_metadata; } const Image* __cdecl GetImage(_In_ size_t mip, _In_ size_t item, _In_ size_t slice) const noexcept; + const Image* __cdecl SetNonOwningImagePixels(_In_ size_t mip, _In_ size_t item, _In_ size_t slice, _In_ uint8_t* pixels) const noexcept; const Image* __cdecl GetImages() const noexcept { return m_image; } size_t __cdecl GetImageCount() const noexcept { return m_nimages; } diff --git a/DirectXTex/DirectXTexImage.cpp b/DirectXTex/DirectXTexImage.cpp index d2c7d408..bd42e7d1 100644 --- a/DirectXTex/DirectXTexImage.cpp +++ b/DirectXTex/DirectXTexImage.cpp @@ -148,9 +148,10 @@ bool DirectX::Internal::SetupImageArray( const TexMetadata& metadata, CP_FLAGS cpFlags, Image* images, - size_t nImages) noexcept + size_t nImages, + bool owning) noexcept { - assert(pMemory); + assert(pMemory || !owning); assert(pixelSize > 0); assert(nImages > 0); @@ -191,7 +192,7 @@ bool DirectX::Internal::SetupImageArray( images[index].format = metadata.format; images[index].rowPitch = rowPitch; images[index].slicePitch = slicePitch; - images[index].pixels = pixels; + images[index].pixels = owning ? pixels : nullptr; ++index; pixels += slicePitch; @@ -240,7 +241,7 @@ bool DirectX::Internal::SetupImageArray( images[index].format = metadata.format; images[index].rowPitch = rowPitch; images[index].slicePitch = slicePitch; - images[index].pixels = pixels; + images[index].pixels = owning ? pixels : nullptr; ++index; pixels += slicePitch; @@ -297,7 +298,7 @@ ScratchImage& ScratchImage::operator= (ScratchImage&& moveFrom) noexcept // Methods //------------------------------------------------------------------------------------- _Use_decl_annotations_ -HRESULT ScratchImage::Initialize(const TexMetadata& mdata, CP_FLAGS flags) noexcept +HRESULT ScratchImage::Initialize(const TexMetadata& mdata, CP_FLAGS flags, bool owning) noexcept { if (!IsValid(mdata.format)) return E_INVALIDARG; @@ -367,16 +368,19 @@ HRESULT ScratchImage::Initialize(const TexMetadata& mdata, CP_FLAGS flags) noexc m_nimages = nimages; memset(m_image, 0, sizeof(Image) * nimages); - m_memory = static_cast(_aligned_malloc(pixelSize, 16)); - if (!m_memory) + if (owning) { - Release(); - return E_OUTOFMEMORY; + m_memory = static_cast(_aligned_malloc(pixelSize, 16)); + if (!m_memory) + { + Release(); + return E_OUTOFMEMORY; + } + memset(m_memory, 0, pixelSize); } - memset(m_memory, 0, pixelSize); m_size = pixelSize; - if (!SetupImageArray(m_memory, pixelSize, m_metadata, flags, m_image, nimages)) + if (!SetupImageArray(m_memory, pixelSize, m_metadata, flags, m_image, nimages, owning)) { Release(); return E_FAIL; @@ -797,6 +801,21 @@ const Image* ScratchImage::GetImage(size_t mip, size_t item, size_t slice) const return &m_image[index]; } +_Use_decl_annotations_ +const Image* ScratchImage::SetNonOwningImagePixels(size_t mip, size_t item, size_t slice, uint8_t* pixels) const noexcept +{ + if (!m_size || m_memory) + return nullptr; + + const Image* image = GetImage(mip, item, slice); + if (!image) + return nullptr; + + const_cast(image)->pixels = pixels; + + return image; +} + bool ScratchImage::IsAlphaAllOpaque() const noexcept { if (!m_image) diff --git a/DirectXTex/DirectXTexP.h b/DirectXTex/DirectXTexP.h index 951a3a7b..0fffd8fa 100644 --- a/DirectXTex/DirectXTexP.h +++ b/DirectXTex/DirectXTexP.h @@ -335,7 +335,8 @@ namespace DirectX _Success_(return) bool __cdecl SetupImageArray( _In_reads_bytes_(pixelSize) uint8_t* pMemory, _In_ size_t pixelSize, _In_ const TexMetadata& metadata, _In_ CP_FLAGS cpFlags, - _Out_writes_(nImages) Image* images, _In_ size_t nImages) noexcept; + _Out_writes_(nImages) Image* images, _In_ size_t nImages, + _In_ bool owning = true) noexcept; //--------------------------------------------------------------------------------- // Conversion helper functions