This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/BKE_pbvh_pixels.hh
Jeroen Bakker 37533cd6cb ImBuf: Optimize GPU memory by using 1 component format for grayscale images
This is done by checking the number of bitplanes from the image buffer.
We assume that for float buffer to use the same bitplanes as it was a
byte buffer.

Then, the data of the image buffer is packed at the start of the `rect` or
`float_rect` before upload.

**Statistics - einar.v004.blend **

Note that not all grayscale textures have been stored as BW images so the
amount of memory that can be reduced would be more.

Without patch
```
104 Textures - 3294.99 MB (3294.47 MB over 32x32), 37 RTs - 192.52 MB.
Avg. tex dimension: 2201.88x1253.51 (2283.53x2202.13 over 32x32)
464 Buffers - 25.01 MB total 1.24 MB IBs 23.50 MB VBs.
3512.52 MB - Grand total GPU buffer + texture load
```

Patch applied
```
104 Textures - 2917.66 MB (2917.14 MB over 32x32), 39 RTs - 215.45 MB.
Avg. tex dimension: 2221.38x1252.75 (2323.28x2253.47 over 32x32)
467 Buffers - 25.01 MB total 1.24 MB IBs 23.51 MB VBs.
3158.13 MB - Grand total GPU buffer + texture load.
```

Reviewed By: fclem

Differential Revision: https://developer.blender.org/D15484
2022-08-23 14:35:39 +02:00

218 lines
5.1 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
#pragma once
#include "BLI_math.h"
#include "BLI_math_vec_types.hh"
#include "BLI_rect.h"
#include "BLI_vector.hh"
#include "DNA_image_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_image.h"
#include "BKE_image_wrappers.hh"
#include "IMB_imbuf_types.h"
namespace blender::bke::pbvh::pixels {
struct TrianglePaintInput {
int3 vert_indices;
/**
* Delta barycentric coordinates between 2 neighboring UV's in the U direction.
*
* Only the first two coordinates are stored. The third should be recalculated
*/
float2 delta_barycentric_coord_u;
/**
* Initially only the vert indices are known.
*
* delta_barycentric_coord_u is initialized in a later stage as it requires image tile
* dimensions.
*/
TrianglePaintInput(const int3 vert_indices)
: vert_indices(vert_indices), delta_barycentric_coord_u(0.0f, 0.0f)
{
}
};
/**
* Data shared between pixels that belong to the same triangle.
*
* Data is stored as a list of structs, grouped by usage to improve performance (improves CPU
* cache prefetching).
*/
struct Triangles {
/** Data accessed by the inner loop of the painting brush. */
Vector<TrianglePaintInput> paint_input;
public:
void append(const int3 vert_indices)
{
this->paint_input.append(TrianglePaintInput(vert_indices));
}
TrianglePaintInput &get_paint_input(const int index)
{
return paint_input[index];
}
const TrianglePaintInput &get_paint_input(const int index) const
{
return paint_input[index];
}
void clear()
{
paint_input.clear();
}
uint64_t size() const
{
return paint_input.size();
}
uint64_t mem_size() const
{
return paint_input.size() * sizeof(TrianglePaintInput);
}
};
/**
* Encode sequential pixels to reduce memory footprint.
*/
struct PackedPixelRow {
/** Barycentric coordinate of the first pixel. */
float2 start_barycentric_coord;
/** Image coordinate starting of the first pixel. */
ushort2 start_image_coordinate;
/** Number of sequential pixels encoded in this package. */
ushort num_pixels;
/** Reference to the pbvh triangle index. */
ushort triangle_index;
};
/**
* Node pixel data containing the pixels for a single UDIM tile.
*/
struct UDIMTilePixels {
/** UDIM Tile number. */
short tile_number;
struct {
bool dirty : 1;
} flags;
/* Dirty region of the tile in image space. */
rcti dirty_region;
Vector<PackedPixelRow> pixel_rows;
UDIMTilePixels()
{
flags.dirty = false;
BLI_rcti_init_minmax(&dirty_region);
}
void mark_dirty(const PackedPixelRow &pixel_row)
{
int2 start_image_coord(pixel_row.start_image_coordinate.x, pixel_row.start_image_coordinate.y);
BLI_rcti_do_minmax_v(&dirty_region, start_image_coord);
BLI_rcti_do_minmax_v(&dirty_region, start_image_coord + int2(pixel_row.num_pixels + 1, 0));
flags.dirty = true;
}
void clear_dirty()
{
BLI_rcti_init_minmax(&dirty_region);
flags.dirty = false;
}
};
struct UDIMTileUndo {
short tile_number;
rcti region;
UDIMTileUndo(short tile_number, rcti &region) : tile_number(tile_number), region(region)
{
}
};
struct NodeData {
struct {
bool dirty : 1;
} flags;
Vector<UDIMTilePixels> tiles;
Vector<UDIMTileUndo> undo_regions;
Triangles triangles;
NodeData()
{
flags.dirty = false;
}
UDIMTilePixels *find_tile_data(const image::ImageTileWrapper &image_tile)
{
for (UDIMTilePixels &tile : tiles) {
if (tile.tile_number == image_tile.get_tile_number()) {
return &tile;
}
}
return nullptr;
}
void rebuild_undo_regions()
{
undo_regions.clear();
for (UDIMTilePixels &tile : tiles) {
rcti region;
BLI_rcti_init_minmax(&region);
for (PackedPixelRow &pixel_row : tile.pixel_rows) {
BLI_rcti_do_minmax_v(
&region, int2(pixel_row.start_image_coordinate.x, pixel_row.start_image_coordinate.y));
BLI_rcti_do_minmax_v(&region,
int2(pixel_row.start_image_coordinate.x + pixel_row.num_pixels + 1,
pixel_row.start_image_coordinate.y + 1));
}
undo_regions.append(UDIMTileUndo(tile.tile_number, region));
}
}
void mark_region(Image &image, const image::ImageTileWrapper &image_tile, ImBuf &image_buffer)
{
UDIMTilePixels *tile = find_tile_data(image_tile);
if (tile && tile->flags.dirty) {
if (image_buffer.planes == 8) {
image_buffer.planes = 32;
BKE_image_partial_update_mark_full_update(&image);
}
else {
BKE_image_partial_update_mark_region(
&image, image_tile.image_tile, &image_buffer, &tile->dirty_region);
}
tile->clear_dirty();
}
}
void clear_data()
{
tiles.clear();
triangles.clear();
}
static void free_func(void *instance)
{
NodeData *node_data = static_cast<NodeData *>(instance);
MEM_delete(node_data);
}
};
NodeData &BKE_pbvh_pixels_node_data_get(PBVHNode &node);
void BKE_pbvh_pixels_mark_image_dirty(PBVHNode &node, Image &image, ImageUser &image_user);
} // namespace blender::bke::pbvh::pixels