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.
  • Loading branch information
LukasBanana committed Feb 12, 2025
1 parent da14b3b commit 7ffce76
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 137 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
65 changes: 38 additions & 27 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,11 +731,22 @@ 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 0;
}

LLGL_EXPORT bool ConvertImageBuffer(
Expand Down Expand Up @@ -933,24 +938,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
17 changes: 15 additions & 2 deletions sources/Core/ImageUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

#include "ImageUtils.h"
#include "Assertion.h"
#include <LLGL/Types.h>
#include <LLGL/Utils/ForRange.h>
#include <cstdint>
Expand Down Expand Up @@ -34,8 +35,20 @@ LLGL_EXPORT void BitBlit(
dstRowStride = std::max(dstRowStride, rowLength);
srcRowStride = std::max(srcRowStride, rowLength);

dstLayerStride = std::max(dstLayerStride, layerLength);
srcLayerStride = std::max(srcLayerStride, layerLength);
const std::uint32_t dstLayerLength = dstRowStride * extent.height;
const std::uint32_t srcLayerLength = srcRowStride * extent.height;

LLGL_ASSERT(
dstLayerStride == 0 || dstLayerStride >= dstLayerLength,
"'dstLayerStride' must be 0 or at least %u, but %u was specified", dstLayerLength, dstLayerStride
);
LLGL_ASSERT(
srcLayerStride == 0 || srcLayerStride >= srcLayerLength,
"'srcLayerStride' must be 0 or at least %u, but %u was specified", srcLayerLength, srcLayerStride
);

dstLayerStride = std::max(dstLayerLength, std::max(dstLayerStride, layerLength));
srcLayerStride = std::max(srcLayerLength, std::max(srcLayerStride, layerLength));

if (srcRowStride == dstRowStride && rowLength == dstRowStride)
{
Expand Down
4 changes: 2 additions & 2 deletions sources/Renderer/Direct3D11/D3D11RenderSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,8 @@ void D3D11RenderSystem::ReadTexture(Texture& texture, const TextureRegion& textu
D3D11ThrowIfFailed(hr, "failed to map D3D11 texture copy resource", textureD3D.GetNative());

/* Copy host visible resource to CPU accessible resource */
const ImageView intermediateSrcView{ formatAttribs.format, formatAttribs.dataType, mappedSubresource.pData, mappedSubresource.DepthPitch };
const std::size_t bytesWritten = RenderSystem::CopyTextureImageData(intermediateDstView, intermediateSrcView, numTexelsPerLayer, extent.width, mappedSubresource.RowPitch);
const ImageView intermediateSrcView{ formatAttribs.format, formatAttribs.dataType, mappedSubresource.pData, mappedSubresource.RowPitch };
const std::size_t bytesWritten = RenderSystem::CopyTextureImageData(intermediateDstView, intermediateSrcView, numTexelsPerLayer, extent.width);

/* Unmap resource */
context_->Unmap(texCopy.Get(), subresource);
Expand Down
5 changes: 2 additions & 3 deletions sources/Renderer/Direct3D12/D3D12RenderSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,13 @@ void D3D12RenderSystem::ReadTexture(Texture& texture, const TextureRegion& textu
const Format format = textureD3D.GetFormat();
const FormatAttributes& formatAttribs = GetFormatAttribs(format);
const Extent3D extent = CalcTextureExtent(textureD3D.GetType(), textureRegion.extent);
const std::uint32_t numTexelsPerLayer = extent.width * extent.height * extent.depth;

void* mappedData = nullptr;
HRESULT hr = readbackBuffer->Map(0, nullptr, &mappedData);
DXThrowIfFailed(hr, "failed to map D3D12 texture copy resource");

const char* srcData = static_cast<const char*>(mappedData);
ImageView intermediateSrcView{ formatAttribs.format, formatAttribs.dataType, srcData, layerStride };
ImageView intermediateSrcView{ formatAttribs.format, formatAttribs.dataType, srcData, layerStride, rowStride };

if (isStencilOnlyFormat)
{
Expand All @@ -278,7 +277,7 @@ void D3D12RenderSystem::ReadTexture(Texture& texture, const TextureRegion& textu
for_range(arrayLayer, textureRegion.subresource.numArrayLayers)
{
/* Copy CPU accessible buffer to output data */
RenderSystem::CopyTextureImageData(intermediateDstView, intermediateSrcView, numTexelsPerLayer, extent.width, rowStride);
ConvertImageBuffer(intermediateSrcView, intermediateDstView, extent, LLGL_MAX_THREAD_COUNT, true);

/* Move destination image pointer to next layer */
intermediateDstView.data = static_cast<char*>(intermediateDstView.data) + layerSize;
Expand Down
Loading

0 comments on commit 7ffce76

Please sign in to comment.