blob: adea70cd055f6a118e331475cd09353e2b08ed63 [file] [log] [blame]
/*
* Copyright © 2020 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "gltrace_unpack_compressed.hpp"
#include "gltrace.hpp"
template<typename X, typename Y>
auto _div_round_up(X a, Y b) -> decltype(a / b) {
return (a + b - 1) / b;
}
struct UnpackParams
{
GLint block_size = 0;
GLint block_width = 1;
GLint block_height = 1;
GLint block_depth = 1;
GLint skip_pixels = 0;
GLint row_length = 0;
GLint skip_rows = 0;
GLint image_height = 0;
GLint skip_images = 0;
};
UnpackParams
getUnpackParams(bool compressed) {
UnpackParams unpack_params;
_glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &unpack_params.skip_pixels);
_glGetIntegerv(GL_UNPACK_ROW_LENGTH, &unpack_params.row_length);
_glGetIntegerv(GL_UNPACK_IMAGE_HEIGHT, &unpack_params.image_height);
_glGetIntegerv(GL_UNPACK_SKIP_ROWS, &unpack_params.skip_rows);
_glGetIntegerv(GL_UNPACK_SKIP_IMAGES, &unpack_params.skip_images);
if (compressed) {
_glGetIntegerv(GL_UNPACK_COMPRESSED_BLOCK_SIZE, &unpack_params.block_size);
_glGetIntegerv(GL_UNPACK_COMPRESSED_BLOCK_WIDTH, &unpack_params.block_width);
_glGetIntegerv(GL_UNPACK_COMPRESSED_BLOCK_HEIGHT, &unpack_params.block_height);
_glGetIntegerv(GL_UNPACK_COMPRESSED_BLOCK_DEPTH, &unpack_params.block_depth);
}
return unpack_params;
}
bool
canTakeFastPath(const UnpackParams& unpack_params, GLsizei width, GLsizei height, GLsizei depth) {
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* By default the pixel storage modes UNPACK_ROW_LENGTH, UNPACK_SKIP_ROWS,
* UNPACK_SKIP_PIXELS, UNPACK_IMAGE_HEIGHT and UNPACK_SKIP_IMAGES are ignored
* for compressed images. To enable UNPACK_SKIP_PIXELS and UNPACK_ROW_LENGTH,
* block size and b_w must both be non-zero.
*/
if (unpack_params.block_size == 0 || unpack_params.block_width == 0)
return true;
if (unpack_params.skip_pixels)
return false;
if (width > 0 && unpack_params.row_length > 0 && width < unpack_params.row_length)
return false;
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* To also enable UNPACK_SKIP_ROWS and UNPACK_IMAGE_HEIGHT, b_h must be non-zero.
*
* If zero - other paramters won't matter.
*/
if (height == 0 || unpack_params.block_height == 0)
return true;
if (unpack_params.skip_rows)
return false;
if (unpack_params.image_height > 0 && height < unpack_params.image_height)
return false;
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* And to also enable UNPACK_SKIP_IMAGES, b_d must be non-zero.
*/
if (depth == 0 || unpack_params.block_depth == 0)
return true;
if(unpack_params.skip_images)
return false;
return true;
}
void
writeCompressedTex(const void * data, GLenum format, GLsizei width, GLsizei height, GLsizei depth,
GLsizei imageSize, GLboolean has_unpack_subimage, std::function<void(const void*, GLsizei)> callback)
{
/* imageSize may not be the amount of bytes we must copy from user's ptr
* if the pixel storage modes were set by user. Supplied imageSize​ has to be
* GL_UNPACK_COMPRESSED_BLOCK_SIZE * (width / GL_UNPACK_COMPRESSED_BLOCK_WIDTH) *
* (height / GL_UNPACK_COMPRESSED_BLOCK_HEIGHT) * (depth / GL_UNPACK_COMPRESSED_BLOCK_DEPTH)
* which doesn't account storage modes. However storage mods do affect how
* much bytes we should copy.
*/
if (!has_unpack_subimage) {
callback(data, imageSize);
return;
}
UnpackParams unpack_params = getUnpackParams(true);
if (canTakeFastPath(unpack_params, width, height, depth)) {
callback(data, imageSize);
return;
}
size_t blocks_in_row = _div_round_up(width, unpack_params.block_width);
size_t copy_bytes_per_row= blocks_in_row * unpack_params.block_size;
size_t total_bytes_per_row = copy_bytes_per_row;
size_t copy_rows_per_slice = height > 0 ? _div_round_up(height, unpack_params.block_height) : 1;
size_t total_rows_per_slice = copy_rows_per_slice;
size_t copy_slices = depth > 0 ? _div_round_up(depth, unpack_params.block_depth) : 1;
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* Before obtaining the first compressed image block from memory,
* the data pointer is advanced by:
* (UNPACK_SKIP_PIXELS / b_w) * n + (UNPACK_SKIP_ROWS / b_h) * k
* elements.
*
* Where n = 1 and k is blocks in a row.
* This is "(UNPACK_SKIP_PIXELS / b_w) * n" part
*/
size_t skip_bytes = unpack_params.skip_pixels / unpack_params.block_width * 1 * unpack_params.block_size;
/* OpenGL 4.6, 8.4.4.1 Unpacking:
*
* If the value of UNPACK_ROW_LENGTH is zero, then the number of groups in a row
* is width; otherwise the number of groups is the value of UNPACK_ROW_LENGTH.
*/
if (unpack_params.row_length) {
total_bytes_per_row = unpack_params.block_size * _div_round_up(unpack_params.row_length, unpack_params.block_width);
}
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* To also enable UNPACK_SKIP_ROWS and UNPACK_IMAGE_HEIGHT, b_h must be non-zero.
*/
if (height > 0 && unpack_params.block_height > 0) {
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* + (UNPACK_SKIP_ROWS / b_h) * k
*/
skip_bytes += unpack_params.skip_rows / unpack_params.block_height * total_bytes_per_row;
if (unpack_params.image_height) {
total_rows_per_slice = _div_round_up(unpack_params.image_height, unpack_params.block_height);
}
}
/* OpenGL 4.6, 8.5 Texture Image Specification:
*
* And to also enable UNPACK_SKIP_IMAGES, b_d must be non-zero.
*/
if (depth > 0 && unpack_params.block_depth > 0) {
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* For three-dimensional compressed images the pointer is advanced by
* UNPACK_SKIP_IMAGES / b_d times the number of elements in one two-dimensional
* image before obtaining the first group from memory.
*/
skip_bytes += unpack_params.skip_images * total_bytes_per_row * total_rows_per_slice / unpack_params.block_depth;
}
size_t alloc_size = skip_bytes + depth * total_bytes_per_row * total_rows_per_slice / std::max(unpack_params.block_depth, 1);
alloc_size += total_bytes_per_row * copy_rows_per_slice;
std::vector<uint8_t> copied_data(alloc_size, 0);
uint8_t* dst_pointer = copied_data.data();
const uint8_t* src_pointer = reinterpret_cast<const uint8_t*>(data);
src_pointer += skip_bytes;
dst_pointer += skip_bytes;
for (size_t slice = 0; slice < copy_slices; slice++) {
assert(dst_pointer < (copied_data.data() + copied_data.size()));
if (total_bytes_per_row == copy_bytes_per_row) {
assert(dst_pointer < (copied_data.data() + copied_data.size()));
// No skips between rows so we can get away with one copy
size_t to_copy = copy_bytes_per_row * copy_rows_per_slice;
memcpy(reinterpret_cast<void*>(dst_pointer), src_pointer, to_copy);
src_pointer += to_copy;
dst_pointer += to_copy;
} else {
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* Then width / b_w blocks are obtained from contiguous blocks in memory (without advancing the pointer),
* after which the pointer is advanced by k elements.
* height / b_h sets of width / b_w blocks are obtained this way.
*/
for (size_t row = 0; row < copy_rows_per_slice; row++) {
assert(dst_pointer < (copied_data.data() + copied_data.size()));
memcpy(reinterpret_cast<void*>(dst_pointer), src_pointer, copy_bytes_per_row);
src_pointer += total_bytes_per_row;
dst_pointer += total_bytes_per_row;
}
}
/* OpenGL 4.6, 8.7 Compressed Texture Images:
*
* Then after height rows are obtained, if UNPACK_IMAGE_HEIGHT is positive,
* the pointer skips over the (UNPACK_IMAGE_HEIGHT - height) / b_h rows,
* before obtaining the next set of blocks.
*
* Pointer will be right at the start of the sub-image in the next slice.
*/
size_t advance = total_bytes_per_row * (total_rows_per_slice - copy_rows_per_slice);
src_pointer += advance;
dst_pointer += advance;
}
callback(copied_data.data(), copied_data.size());
}