Skip to content

Commit 5a3acb4

Browse files
committed
Add osc::Texture2D::update_pixel_data to optimize oscimgui backend
1 parent 99733f8 commit 5a3acb4

File tree

4 files changed

+56
-12
lines changed

4 files changed

+56
-12
lines changed

liboscar/Graphics/GraphicsImplementation.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1523,6 +1523,11 @@ class osc::Texture2D::Impl final {
15231523
rgs::copy(pixel_components_row_by_row, pixel_data_.begin());
15241524
}
15251525

1526+
void update_pixel_data(const std::function<void(std::span<uint8_t>)>& updater)
1527+
{
1528+
updater(pixel_data_);
1529+
}
1530+
15261531
// non PIMPL method
15271532

15281533
gl::Texture2D& updTexture()
@@ -1781,6 +1786,11 @@ void osc::Texture2D::set_pixel_data(std::span<const uint8_t> pixel_components_ro
17811786
impl_.upd()->set_pixel_data(pixel_components_row_by_row);
17821787
}
17831788

1789+
void osc::Texture2D::update_pixel_data(const std::function<void(std::span<uint8_t>)>& updater)
1790+
{
1791+
impl_.upd()->update_pixel_data(updater);
1792+
}
1793+
17841794
std::ostream& osc::operator<<(std::ostream& o, const Texture2D&)
17851795
{
17861796
return o << "Texture2D()";

liboscar/Graphics/Texture2D.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <liboscar/Utils/CopyOnUpdSharedValue.h>
1111

1212
#include <cstdint>
13+
#include <functional>
1314
#include <iosfwd>
1415
#include <span>
1516
#include <vector>
@@ -127,6 +128,10 @@ namespace osc
127128
std::span<const uint8_t> pixel_data() const;
128129
void set_pixel_data(std::span<const uint8_t>);
129130

131+
// Updates this texture's pixel data in-place. Equivalent to calling `pixel_data`, mutating
132+
// it, and then passing that to `set_pixel_data`.
133+
void update_pixel_data(const std::function<void(std::span<uint8_t>)>& updater);
134+
130135
friend bool operator==(const Texture2D&, const Texture2D&) = default;
131136

132137
class Impl;

liboscar/Graphics/Texture2D.tests.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,35 @@ TEST(Texture2D, set_pixel_data_on_8bit_component_format_clamps_hdr_color_values)
200200
ASSERT_NE(texture_2d.pixels(), hdr_pixels); // because the impl had to convert them
201201
}
202202

203+
TEST(Texture2D, update_pixel_data_calls_callback_with_span_that_is_identical_to_the_one_returned_from_get_pixel_data)
204+
{
205+
Texture2D texture = generate_2x2_texture();
206+
const std::vector<uint8_t> copy(texture.pixel_data().begin(), texture.pixel_data().end());
207+
bool called = false;
208+
texture.update_pixel_data([&copy, &called](std::span<uint8_t> pixels)
209+
{
210+
ASSERT_TRUE(rgs::equal(pixels, copy));
211+
called = true;
212+
});
213+
ASSERT_TRUE(called);
214+
}
215+
216+
TEST(Texture2D, update_pixel_data_makes_subsequent_pixel_data_show_mutated_version)
217+
{
218+
static constexpr uint8_t fill_value = 0x00;
219+
220+
Texture2D texture = generate_2x2_texture();
221+
ASSERT_FALSE(rgs::all_of(texture.pixel_data(), [](uint8_t byte) { return byte == fill_value; }));
222+
223+
bool called = false;
224+
texture.update_pixel_data([&called](std::span<uint8_t> pixels)
225+
{
226+
rgs::fill(pixels, fill_value);
227+
called = true;
228+
});
229+
ASSERT_TRUE(rgs::all_of(texture.pixel_data(), [](uint8_t byte) { return byte == fill_value; }));
230+
}
231+
203232
TEST(Texture2D, set_pixels32_on_an_8bit_texture_performs_no_conversion)
204233
{
205234
const Color32 color32 = {0x77, 0x63, 0x24, 0x76};

liboscar/UI/oscimgui.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -632,21 +632,21 @@ namespace
632632
// Fetch the texture handle from the liboscar backend data
633633
auto* t = bd.lookup_texture(texture_data.GetTexID());
634634
OSC_ASSERT(t and std::holds_alternative<Texture2D>(*t) && "the texture should've been created by ImTextureStatus_WantCreate");
635-
Texture2D& texture = std::get<Texture2D>(*t);
636635

637636
// Update pixel data
638-
std::vector<uint8_t> pixel_data_copy(texture.pixel_data().begin(), texture.pixel_data().end());
639-
for (const ImTextureRect& source_rect : texture_data.Updates) {
640-
uint8_t* source_first_pixel = static_cast<uint8_t*>(texture_data.GetPixelsAt(source_rect.x, source_rect.y));
641-
uint8_t* destination_first_pixel = pixel_data_copy.data() + ((source_rect.x + source_rect.y*texture_data.Width) * texture_data.BytesPerPixel);
642-
for (int row = 0; row < source_rect.h; ++row) {
643-
uint8_t* source_row_begin = source_first_pixel + (row * texture_data.GetPitch());
644-
uint8_t* source_row_end = source_row_begin + (source_rect.w * texture_data.BytesPerPixel);
645-
uint8_t* destination_row_begin = destination_first_pixel + (row * texture_data.GetPitch());
646-
std::copy(source_row_begin, source_row_end, destination_row_begin);
637+
std::get<Texture2D>(*t).update_pixel_data([&texture_data](std::span<uint8_t> pixel_data)
638+
{
639+
for (const ImTextureRect& source_rect : texture_data.Updates) {
640+
uint8_t* source_first_pixel = static_cast<uint8_t*>(texture_data.GetPixelsAt(source_rect.x, source_rect.y));
641+
uint8_t* destination_first_pixel = pixel_data.data() + ((source_rect.x + source_rect.y*texture_data.Width) * texture_data.BytesPerPixel);
642+
for (int row = 0; row < source_rect.h; ++row) {
643+
uint8_t* source_row_begin = source_first_pixel + (row * texture_data.GetPitch());
644+
uint8_t* source_row_end = source_row_begin + (source_rect.w * texture_data.BytesPerPixel);
645+
uint8_t* destination_row_begin = destination_first_pixel + (row * texture_data.GetPitch());
646+
std::copy(source_row_begin, source_row_end, destination_row_begin);
647+
}
647648
}
648-
}
649-
texture.set_pixel_data(pixel_data_copy);
649+
});
650650
texture_data.SetStatus(ImTextureStatus_OK);
651651
}
652652
else if (texture_data.Status == ImTextureStatus_WantDestroy) {

0 commit comments

Comments
 (0)