Skip to content

Commit

Permalink
[Misc] Replaced RenderSystem::CopyTextureImageData() with ConvertImag…
Browse files Browse the repository at this point in the history
…eBuffer().

- Deprecated RenderSystem::CopyTextureImageData(): Use global ConvertImageBuffer() function instead.
- Deprecated RenderSystem::AssertImageDataSize(): Use LLGL_VERIFY() macro instead.
- Fixed global function ConvertImageBuffer(): The depth-stencil case did not return a value.
- Allow ConvertImageBuffer() function to also copy the source to destination buffer as is if requested.
- Make ConvertImageBuffer() return the number of bytes that have been written to.
- Simplified secondary version of ConvertImageBuffer() by utilizing the primary version.
  • Loading branch information
LukasBanana committed Feb 12, 2025
1 parent da14b3b commit 521bf73
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 190 deletions.
24 changes: 19 additions & 5 deletions include/LLGL/ImageFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,28 +201,36 @@ struct LLGL_DEPRECATED("LLGL::DstImageDescriptor is deprecated since 0.04b; Use

/**
\brief Converts the image format and data type of the source image (only uncompressed color formats).
\param[in] srcImageView Specifies the source image view.
\param[out] dstImageView Specifies the destination image view.
\param[in] extent Specifies the extent of the image. This is required
\param[in] threadCount Specifies the number of threads to use for conversion.
If this is less than 2, no multi-threading is used. If this is equal to \c LLGL_MAX_THREAD_COUNT,
the maximal count of threads the system supports will be used (e.g. 4 on a quad-core processor). By default 0.
\return True if any conversion was necessary. Otherwise, no conversion was necessary and the destination buffer is not modified!
\param[in] copyUnchangedImage Specifies whether to copy the source buffer into the destination buffer if no conversion was necessary. By default false.
\return Number of bytes that have been written to the destination buffer.
If this is 0, no conversion was necessary and the destination buffer is not modified.
\note Compressed images and depth-stencil images cannot be converted with this function.
\throw std::invalid_argument If a compressed image format is specified either as source or destination.
\throw std::invalid_argument If a depth-stencil format is specified either as source or destination.
\throw std::invalid_argument If the source buffer size is not a multiple of the source data type size times the image format size.
\throw std::invalid_argument If the source buffer is a null pointer.
\throw std::invalid_argument If the destination buffer size does not match the required output buffer size.
\throw std::invalid_argument If the destination buffer is a null pointer.
\see LLGL_MAX_THREAD_COUNT
\see GetMemoryFootprint
*/
LLGL_EXPORT bool ConvertImageBuffer(
LLGL_EXPORT std::size_t ConvertImageBuffer(
const ImageView& srcImageView,
const MutableImageView& dstImageView,
const Extent3D& extent,
unsigned threadCount = 0
unsigned threadCount = 0,
bool copyUnchangedImage = false
);

/**
Expand All @@ -231,28 +239,34 @@ LLGL_EXPORT bool ConvertImageBuffer(
This must only be used for tightly packed image buffer, i.e. with a row stride of zero.
\see ConvertImageBuffer(const ImageView&, const MutableImageView&, const Extent3D&, unsigned)
*/
LLGL_EXPORT bool ConvertImageBuffer(
LLGL_EXPORT std::size_t ConvertImageBuffer(
const ImageView& srcImageView,
const MutableImageView& dstImageView,
unsigned threadCount = 0
unsigned threadCount = 0,
bool copyUnchangedImage = false
);

/**
\brief Converst the image format and data type of the source image (only uncompressed color formats) and returns the new generated image buffer.
\param[in] srcImageView Specifies the source image view.
\param[in] dstFormat Specifies the destination image format.
\param[in] dstDataType Specifies the destination image data type.
\param[in] extent Specifies the extent of the image. This is required
\param[in] threadCount Specifies the number of threads to use for conversion.
If this is less than 2, no multi-threading is used. If this is equal to \c LLGL_MAX_THREAD_COUNT,
the maximal count of threads the system supports will be used (e.g. 4 on a quad-core processor). By default 0.
\return Byte buffer with the converted image data or null if no conversion is necessary.
This can be casted to the respective target data type (e.g. <code>unsigned char</code>, <code>int</code>, <code>float</code> etc.).
\note Compressed images and depth-stencil images cannot be converted.
\throw std::invalid_argument If a compressed image format is specified either as source or destination.
\throw std::invalid_argument If a depth-stencil format is specified either as source or destination.
\throw std::invalid_argument If the source buffer size is not a multiple of the source data type size times the image format size.
\throw std::invalid_argument If the source buffer is a null pointer.
\see LLGL_MAX_THREAD_COUNT
\see GetMemoryFootprint
*/
Expand Down
21 changes: 8 additions & 13 deletions include/LLGL/RenderSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,15 +191,15 @@ class LLGL_EXPORT RenderSystem : public Interface

/**
\brief Returns basic renderer information.
\remarks This is not a constant member function because the first call will invoke the query,
while subsequent calls with return the cached information.
\remarks This is not a constant member function because the first call invokes the query,
while subsequent calls return the cached information.
*/
const RendererInfo& GetRendererInfo();

/**
\brief Returns the rendering capabilities.
\remarks This is not a constant member function because the first call will invoke the query,
while subsequent calls with return the cached information.
\remarks This is not a constant member function because the first call invokes the query,
while subsequent calls return the cached information.
*/
const RenderingCapabilities& GetRenderingCaps();

Expand Down Expand Up @@ -672,11 +672,11 @@ class LLGL_EXPORT RenderSystem : public Interface
void Errorf(const char* format, ...);

//! \deprecated Since 0.04b; Implement QueryRendererDetails() instead!
LLGL_DEPRECATED("RenderSystem::SetRendererInfo is deprecated since 0.04b; Implement QueryRendererDetails() instead!")
LLGL_DEPRECATED("RenderSystem::SetRendererInfo() is deprecated since 0.04b; Implement QueryRendererDetails() instead!")
void SetRendererInfo(const RendererInfo& info);

//! \deprecated Since 0.04b; Implement QueryRendererDetails() instead!
LLGL_DEPRECATED("RenderSystem::SetRendererInfo is deprecated since 0.04b; Implement QueryRendererDetails() instead!")
LLGL_DEPRECATED("RenderSystem::SetRendererInfo() is deprecated since 0.04b; Implement QueryRendererDetails() instead!")
void SetRenderingCaps(const RenderingCapabilities& caps);

protected:
Expand All @@ -701,15 +701,10 @@ class LLGL_EXPORT RenderSystem : public Interface
//! Validates the specified shader descriptor.
static void AssertCreateShader(const ShaderDescriptor& shaderDesc);

//! Validates the specified image data size against the required size (in bytes).
LLGL_DEPRECATED("RenderSystem::AssertImageDataSize() is deprecated since 0.04b; Use LLGL_VERIFY() macro instead!")
static void AssertImageDataSize(std::size_t dataSize, std::size_t requiredDataSize, const char* useCase = nullptr);

/**
\brief Copies the specified source image to the destination image.
\remarks This function also performs image conversion if there is a mismatch between source and destination format.
\returns The number of bytes that have been written into the destination image buffer.
\see ConvertImageBuffer
*/
LLGL_DEPRECATED("RenderSystem::CopyTextureImageData() is deprecated since 0.04b; Use ConvertOrCopyImageBuffer() instead!")
static std::size_t CopyTextureImageData(
const MutableImageView& dstImageView,
const ImageView& srcImageView,
Expand Down
2 changes: 1 addition & 1 deletion include/LLGL/RenderSystemFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ struct RenderSystemFlags
/**
\brief Renderer identification number enumeration.
\remarks There are several IDs for reserved future renderers, which are currently not supported (and maybe never supported).
You can use an ID greater than 'RendererID::Reserved' (which has a value of 0x000000ff) for your own renderer.
You can use an ID greater than \c RendererID::Reserved (which has a value of 0x000000FF) for your own renderer.
Or use one of the pre-defined IDs if you want to implement your own OpenGL/ Direct3D or whatever renderer.
\see RendererInfo::rendererID
*/
Expand Down
130 changes: 51 additions & 79 deletions sources/Core/ImageFlags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ static void ConvertImageBufferDataTypeWorker(
}
}

static void ConvertImageBufferDataType(
static std::size_t ConvertImageBufferDataType(
const ImageView& srcImageView,
const MutableImageView& dstImageView,
const Extent3D& extent,
Expand Down Expand Up @@ -270,6 +270,8 @@ static void ConvertImageBufferDataType(
numPixels,
threadCount
);

return requiredDstBufferSize;
}

static void SetVariantMinMax(DataType dataType, Variant& var, bool setMin)
Expand Down Expand Up @@ -586,7 +588,7 @@ static void ConvertImageBufferFormatWorker(
}
}

static void ConvertImageBufferFormat(
static std::size_t ConvertImageBufferFormat(
const ImageView& srcImageView,
const MutableImageView& dstImageView,
const Extent3D& extent,
Expand Down Expand Up @@ -618,6 +620,8 @@ static void ConvertImageBufferFormat(
numPixels,
threadCount
);

return requiredDstBufferSize;
}

static void ValidateSourceImageView(const ImageView& imageView)
Expand Down Expand Up @@ -652,19 +656,13 @@ static void ValidateImageConversionParams(

/* ----- Public functions ----- */

LLGL_EXPORT bool ConvertImageBuffer(
LLGL_EXPORT std::size_t ConvertImageBuffer(
const ImageView& srcImageView,
const MutableImageView& dstImageView,
const Extent3D& extent,
unsigned threadCount)
unsigned threadCount,
bool copyUnchangedImage)
{
if (srcImageView.format == dstImageView.format &&
srcImageView.dataType == dstImageView.dataType &&
srcImageView.rowStride == 0)
{
return false;
}

/* Validate input parameters */
ValidateSourceImageView(srcImageView);
ValidateDestinationImageView(dstImageView);
Expand All @@ -676,7 +674,7 @@ LLGL_EXPORT bool ConvertImageBuffer(
if (IsDepthOrStencilFormat(srcImageView.format))
{
/* Convert depth-stencil image format */
ConvertImageBufferFormat(srcImageView, dstImageView, extent, threadCount);
return ConvertImageBufferFormat(srcImageView, dstImageView, extent, threadCount);
}
else if (srcImageView.dataType != dstImageView.dataType && srcImageView.format != dstImageView.format)
{
Expand Down Expand Up @@ -705,21 +703,17 @@ LLGL_EXPORT bool ConvertImageBuffer(
};

/* Convert image format */
ConvertImageBufferFormat(intermediateSrcImageView, dstImageView, extent, threadCount);

return true;
return ConvertImageBufferFormat(intermediateSrcImageView, dstImageView, extent, threadCount);
}
else if (srcImageView.dataType != dstImageView.dataType)
{
/* Convert image data type */
ConvertImageBufferDataType(srcImageView, dstImageView, extent, threadCount);
return true;
return ConvertImageBufferDataType(srcImageView, dstImageView, extent, threadCount);
}
else if (srcImageView.format != dstImageView.format)
{
/* Convert image format */
ConvertImageBufferFormat(srcImageView, dstImageView, extent, threadCount);
return true;
return ConvertImageBufferFormat(srcImageView, dstImageView, extent, threadCount);
}
else if (srcImageView.rowStride != 0)
{
Expand All @@ -737,17 +731,30 @@ LLGL_EXPORT bool ConvertImageBuffer(
srcImageView.rowStride,
0
);
return true;
const std::size_t numPixels = (extent.width * extent.height * extent.depth);
return (numPixels * bpp);
}
}

return false;
/* Copy data directly into destination buffer if no conversion was necessary */
if (copyUnchangedImage)
{
const std::size_t numPixels = (extent.width * extent.height * extent.depth);
const std::size_t requiredImageSize = GetMemoryFootprint(dstImageView.format, dstImageView.dataType, numPixels);
LLGL_ASSERT(dstImageView.dataSize >= requiredImageSize);
LLGL_ASSERT(srcImageView.dataSize >= requiredImageSize);
::memcpy(dstImageView.data, srcImageView.data, requiredImageSize);
return requiredImageSize;
}

return 0;
}

LLGL_EXPORT bool ConvertImageBuffer(
LLGL_EXPORT std::size_t ConvertImageBuffer(
const ImageView& srcImageView,
const MutableImageView& dstImageView,
unsigned threadCount)
unsigned threadCount,
bool copyUnchangedImage)
{
LLGL_ASSERT(
srcImageView.rowStride == 0,
Expand All @@ -762,7 +769,7 @@ LLGL_EXPORT bool ConvertImageBuffer(
);

const Extent3D extent1D{ static_cast<std::uint32_t>(srcImageView.dataSize / bpp), 1u, 1u };
return ConvertImageBuffer(srcImageView, dstImageView, extent1D, threadCount);
return ConvertImageBuffer(srcImageView, dstImageView, extent1D, threadCount, copyUnchangedImage);
}

LLGL_EXPORT DynamicByteArray ConvertImageBuffer(
Expand All @@ -772,69 +779,28 @@ LLGL_EXPORT DynamicByteArray ConvertImageBuffer(
const Extent3D& extent,
unsigned threadCount)
{
if (srcImageView.format == dstFormat && srcImageView.dataType == dstDataType)
if (srcImageView.format == dstFormat &&
srcImageView.dataType == dstDataType &&
srcImageView.rowStride == 0)
{
return nullptr;
}

/* Validate input parameters */
ValidateSourceImageView(srcImageView);
ValidateImageConversionParams(srcImageView, dstFormat, dstDataType);

if (threadCount == LLGL_MAX_THREAD_COUNT)
threadCount = std::thread::hardware_concurrency();

/* Initialize destination image descriptor */
const std::size_t numPixels = extent.width * extent.height * extent.depth;
const std::size_t dstImageSize = GetMemoryFootprint(dstFormat, dstDataType, numPixels);

DynamicByteArray dstImage{ dstImageSize, UninitializeTag{} };

const MutableImageView dstImageView{ dstFormat, dstDataType, dstImage.get(), dstImageSize };

if (IsDepthOrStencilFormat(srcImageView.format))
{
/* Convert depth-stencil image format */
ConvertImageBufferFormat(srcImageView, dstImageView, extent, threadCount);
}
else if (srcImageView.dataType != dstDataType && srcImageView.format != dstFormat)
{
/* Convert image data type with intermediate buffer that is tightly packed */
const std::size_t intermediateBufferSize = GetMemoryFootprint(srcImageView.format, dstDataType, numPixels);
DynamicByteArray intermediateBuffer = DynamicByteArray{ intermediateBufferSize, UninitializeTag{} };
if (ConvertImageBuffer(srcImageView, dstImageView, extent, threadCount) > 0)
return dstImage;

const MutableImageView intermediateDstImageView
{
srcImageView.format,
dstDataType,
intermediateBuffer.get(),
intermediateBufferSize,
};

ConvertImageBufferDataType(srcImageView, intermediateDstImageView, extent, threadCount);

/* Set new source buffer and source data type */
const ImageView intermediateSrcImageView
{
srcImageView.format,
dstDataType,
intermediateBuffer.get(),
intermediateBufferSize
};

/* Convert image format */
ConvertImageBufferFormat(intermediateSrcImageView, dstImageView, extent, threadCount);
}
else if (srcImageView.dataType != dstDataType)
{
/* Convert image data type */
ConvertImageBufferDataType(srcImageView, dstImageView, extent, threadCount);
}
else if (srcImageView.format != dstFormat)
{
/* Convert image format */
ConvertImageBufferFormat(srcImageView, dstImageView, extent, threadCount);
}

return dstImage;
return nullptr;
}

LLGL_EXPORT DynamicByteArray ConvertImageBuffer(
Expand Down Expand Up @@ -933,24 +899,30 @@ LLGL_EXPORT void CopyImageBufferRegion(
ValidateSourceImageView(srcImageView);
ValidateDestinationImageView(dstImageView);

if (srcImageView.format != dstImageView.format || srcImageView.dataType != dstImageView.dataType)
LLGL_TRAP("cannot copy image buffer region with source and destination images having different format or data type");
LLGL_ASSERT(
srcImageView.format == dstImageView.format && srcImageView.dataType == dstImageView.dataType,
"LLGL::CopyImageBufferRegion() only supports source and destination buffers of equal format and type"
);

const std::uint32_t bpp = static_cast<std::uint32_t>(GetMemoryFootprint(dstImageView.format, dstImageView.dataType, 1));

/* Validate destination image boundaries */
const std::size_t dstPos = GetFlattenedImageBufferPos(dstOffset.x, dstOffset.y, dstOffset.z, dstRowStride, dstLayerStride, bpp);
const std::size_t dstPosEnd = GetFlattenedImageBufferPosEnd(dstOffset, extent, dstRowStride, dstLayerStride, bpp);

if (dstPosEnd > dstImageView.dataSize)
LLGL_TRAP("destination image buffer region out of range");
LLGL_ASSERT(
dstImageView.dataSize >= dstPosEnd,
"destination image buffer size is too small for copy operation"
);

/* Validate source image boundaries */
const std::size_t srcPos = GetFlattenedImageBufferPos(srcOffset.x, srcOffset.y, srcOffset.z, srcRowStride, srcLayerStride, bpp);
const std::size_t srcPosEnd = GetFlattenedImageBufferPosEnd(srcOffset, extent, srcRowStride, srcLayerStride, bpp);

if (srcPosEnd > srcImageView.dataSize)
LLGL_TRAP("source image buffer region out of range");
LLGL_ASSERT(
srcImageView.dataSize >= srcPosEnd,
"source image buffer size is too small for copy operation"
);

/* Copy image buffer region */
BitBlit(
Expand Down
Loading

0 comments on commit 521bf73

Please sign in to comment.