Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulkan: fix ReadPixels with 2-component formats. #5692

Merged
merged 1 commit into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ A new header is inserted each time a *tag* is created.
- gltf_viewer: Exercise picking functionality.
- OpenGL: add WebGL support for ReadPixels
- Vulkan: add assert and error message for OOM (debug builds)
- Vulkan: fix crash with picking and 2-component ReadPixels.

## v1.23.2

Expand Down
34 changes: 12 additions & 22 deletions filament/backend/src/DataReshaper.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,16 @@ class DataReshaper {
}
}

// Converts a 4-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
template<typename dstComponentType, typename srcComponentType>
static void reshapeImage(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
size_t dstBytesPerRow, size_t dstChannelCount, size_t height, bool swizzle, bool flip) {
const size_t srcChannelCount = 4;
size_t srcChannelCount, size_t dstBytesPerRow, size_t dstChannelCount,
size_t width, size_t height, bool swizzle) {
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
const size_t width = (srcBytesPerRow / sizeof(srcComponentType)) / srcChannelCount;
const size_t minChannelCount = filament::math::min(srcChannelCount, dstChannelCount);
assert_invariant(minChannelCount <= 4);
const int inds[4] = {swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3};

int srcStride;
if (flip) {
src += srcBytesPerRow * (height - 1);
srcStride = -srcBytesPerRow;
} else {
srcStride = srcBytesPerRow;
}

for (size_t row = 0; row < height; ++row) {
const srcComponentType* in = (const srcComponentType*) src;
dstComponentType* out = (dstComponentType*) dest;
Expand All @@ -97,15 +87,15 @@ class DataReshaper {
in += srcChannelCount;
out += dstChannelCount;
}
src += srcStride;
src += srcBytesPerRow;
dest += dstBytesPerRow;
}
}

// Converts a 4-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
static bool reshapeImage(PixelBufferDescriptor* dst, PixelDataType srcType,
const uint8_t* srcBytes, int srcBytesPerRow, int width, int height, bool swizzle,
bool flip) {
uint32_t srcChannelCount, const uint8_t* srcBytes, int srcBytesPerRow, int width,
int height, bool swizzle) {
size_t dstChannelCount;
switch (dst->format) {
case PixelDataFormat::R_INTEGER: dstChannelCount = 1; break;
Expand All @@ -118,8 +108,9 @@ class DataReshaper {
case PixelDataFormat::RGBA: dstChannelCount = 4; break;
default: return false;
}
void (*reshaper)(uint8_t*, const uint8_t*, size_t, size_t, size_t, size_t, bool, bool)
= nullptr;
void (*reshaper)(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
size_t srcChannelCount, size_t dstBytesPerRow, size_t dstChannelCount,
size_t width, size_t height, bool swizzle) = nullptr;
constexpr auto UBYTE = PixelDataType::UBYTE, FLOAT = PixelDataType::FLOAT,
UINT = PixelDataType::UINT, INT = PixelDataType::INT;
switch (dst->type) {
Expand Down Expand Up @@ -165,11 +156,10 @@ class DataReshaper {
uint8_t* dstBytes = (uint8_t*) dst->buffer;
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(dst->format, dst->type,
dst->stride ? dst->stride : width, 1, dst->alignment);
reshaper(dstBytes, srcBytes, srcBytesPerRow, dstBytesPerRow, dstChannelCount, height,
swizzle, flip);
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount, dstBytesPerRow,
dstChannelCount, width, height, swizzle);
return true;
}

};

template<> inline float getMaxValue() { return 1.0f; }
Expand Down
4 changes: 2 additions & 2 deletions filament/backend/src/vulkan/VulkanDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1639,8 +1639,8 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
vkMapMemory(device, stagingMemory, 0, VK_WHOLE_SIZE, 0, (void**) &srcPixels);
srcPixels += subResourceLayout.offset;

if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), srcPixels,
subResourceLayout.rowPitch, width, height, swizzle, false)) {
if (!DataReshaper::reshapeImage(&pbd, getComponentType(srcFormat), getComponentCount(srcFormat),
srcPixels, subResourceLayout.rowPitch, width, height, swizzle)) {
utils::slog.e << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
}

Expand Down
103 changes: 103 additions & 0 deletions filament/backend/src/vulkan/VulkanUtility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,109 @@ PixelDataType getComponentType(VkFormat format) {
return {};
}

uint32_t getComponentCount(VkFormat format) {
switch (format) {
case VK_FORMAT_R8_UNORM:
case VK_FORMAT_R8_SNORM:
case VK_FORMAT_R8_USCALED:
case VK_FORMAT_R8_SSCALED:
case VK_FORMAT_R8_UINT:
case VK_FORMAT_R8_SINT:
case VK_FORMAT_R8_SRGB:
case VK_FORMAT_R16_UNORM:
case VK_FORMAT_R16_SNORM:
case VK_FORMAT_R16_USCALED:
case VK_FORMAT_R16_SSCALED:
case VK_FORMAT_R16_UINT:
case VK_FORMAT_R16_SINT:
case VK_FORMAT_R16_SFLOAT:
case VK_FORMAT_R32_UINT:
case VK_FORMAT_R32_SINT:
case VK_FORMAT_R32_SFLOAT:
return 1;

case VK_FORMAT_R8G8_UNORM:
case VK_FORMAT_R8G8_SNORM:
case VK_FORMAT_R8G8_USCALED:
case VK_FORMAT_R8G8_SSCALED:
case VK_FORMAT_R8G8_UINT:
case VK_FORMAT_R8G8_SINT:
case VK_FORMAT_R8G8_SRGB:
case VK_FORMAT_R16G16_UNORM:
case VK_FORMAT_R16G16_SNORM:
case VK_FORMAT_R16G16_USCALED:
case VK_FORMAT_R16G16_SSCALED:
case VK_FORMAT_R16G16_UINT:
case VK_FORMAT_R16G16_SINT:
case VK_FORMAT_R16G16_SFLOAT:
case VK_FORMAT_R32G32_UINT:
case VK_FORMAT_R32G32_SINT:
case VK_FORMAT_R32G32_SFLOAT:
return 2;

case VK_FORMAT_R8G8B8_UNORM:
case VK_FORMAT_R8G8B8_SNORM:
case VK_FORMAT_R8G8B8_USCALED:
case VK_FORMAT_R8G8B8_SSCALED:
case VK_FORMAT_R8G8B8_UINT:
case VK_FORMAT_R8G8B8_SINT:
case VK_FORMAT_R8G8B8_SRGB:
case VK_FORMAT_B8G8R8_UNORM:
case VK_FORMAT_B8G8R8_SNORM:
case VK_FORMAT_B8G8R8_USCALED:
case VK_FORMAT_B8G8R8_SSCALED:
case VK_FORMAT_B8G8R8_UINT:
case VK_FORMAT_B8G8R8_SINT:
case VK_FORMAT_B8G8R8_SRGB:
case VK_FORMAT_R16G16B16_UNORM:
case VK_FORMAT_R16G16B16_SNORM:
case VK_FORMAT_R16G16B16_USCALED:
case VK_FORMAT_R16G16B16_SSCALED:
case VK_FORMAT_R16G16B16_UINT:
case VK_FORMAT_R16G16B16_SINT:
case VK_FORMAT_R16G16B16_SFLOAT:
case VK_FORMAT_R32G32B32_UINT:
case VK_FORMAT_R32G32B32_SINT:
case VK_FORMAT_R32G32B32_SFLOAT:
return 3;

case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SNORM:
case VK_FORMAT_R8G8B8A8_USCALED:
case VK_FORMAT_R8G8B8A8_SSCALED:
case VK_FORMAT_R8G8B8A8_UINT:
case VK_FORMAT_R8G8B8A8_SINT:
case VK_FORMAT_R8G8B8A8_SRGB:
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SNORM:
case VK_FORMAT_B8G8R8A8_USCALED:
case VK_FORMAT_B8G8R8A8_SSCALED:
case VK_FORMAT_B8G8R8A8_UINT:
case VK_FORMAT_B8G8R8A8_SINT:
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
case VK_FORMAT_R16G16B16A16_UNORM:
case VK_FORMAT_R16G16B16A16_SNORM:
case VK_FORMAT_R16G16B16A16_USCALED:
case VK_FORMAT_R16G16B16A16_SSCALED:
case VK_FORMAT_R16G16B16A16_UINT:
case VK_FORMAT_R16G16B16A16_SINT:
case VK_FORMAT_R16G16B16A16_SFLOAT:
case VK_FORMAT_R32G32B32A32_UINT:
case VK_FORMAT_R32G32B32A32_SINT:
case VK_FORMAT_R32G32B32A32_SFLOAT:
return 4;
default: assert_invariant(false && "Unknown data type, conversion is not supported.");
}
return {};
}

VkComponentMapping getSwizzleMap(TextureSwizzle swizzle[4]) {
VkComponentMapping map;
VkComponentSwizzle* dst = &map.r;
Expand Down
1 change: 1 addition & 0 deletions filament/backend/src/vulkan/VulkanUtility.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ VkBlendFactor getBlendFactor(BlendFunction mode);
VkCullModeFlags getCullMode(CullingMode mode);
VkFrontFace getFrontFace(bool inverseFrontFaces);
PixelDataType getComponentType(VkFormat format);
uint32_t getComponentCount(VkFormat format);
VkComponentMapping getSwizzleMap(TextureSwizzle swizzle[4]);
VkImageViewType getImageViewType(SamplerType target);
VkImageLayout getDefaultImageLayout(TextureUsage usage);
Expand Down