0

HDR images: Propagate headroom to gpu::raster::RasterDecoderImpl

HDR images have their tonemapping "baked in" during image decode.
This means that if the HDR headroom changes (which it does), we have
to re-decode the image. This also significantly complicates the
implementation of the dynamic-range-limit CSS property.

Fix this situation by having the tonemapping be applied at raster
time, on the service side.

In order to perform tonemapping, the RasterDecoder needs to know the
HDR headroom. Plumb that information through the following path:
- cc::TileManager::CreateRasterTask
  - already has the parameter)
- cc::RasterSource::PlaybackSettings
  - add new member)
- gpu::raster::RasterInterface::BeginRasterCHROMIUM
- (command buffer glue)
- gpu::raster::RasterDecoderImpl::DoBeginRasterCHROMIUM

In the next patch, this will be used to apply tonemapping on the
service side.

Add tests for the client side of the propagation. End-to-end testing
will require the tonemapping change (which will be in the next patch).

Bug: 1483235
Change-Id: I3a687083859d1d9ccbec6ef12956705ea89da935
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4909841
Reviewed-by: Khushal Sagar <khushalsagar@chromium.org>
Commit-Queue: ccameron chromium <ccameron@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1205543}
This commit is contained in:
Christopher Cameron
2023-10-04 23:12:42 +00:00
committed by Chromium LUCI CQ
parent 12d607016c
commit 060d382e7a
22 changed files with 97 additions and 23 deletions

@@ -358,7 +358,7 @@ void HeadsUpDisplayLayerImpl::UpdateHudTexture(
ri->BeginRasterCHROMIUM(background_color, needs_clear, msaa_sample_count,
gpu::raster::kNoMSAA, can_use_lcd_text,
/*visible=*/true, gfx::ColorSpace::CreateSRGB(),
backing->mailbox.name);
/*hdr_headroom=*/1.f, backing->mailbox.name);
constexpr gfx::Vector2dF post_translate(0.f, 0.f);
constexpr gfx::Vector2dF post_scale(1.f, 1.f);
DummyImageProvider image_provider;

@@ -216,7 +216,7 @@ class OopPixelTest : public testing::Test,
/*needs_clear=*/options.preclear, options.msaa_sample_count,
msaa_mode, options.use_lcd_text,
/*visible=*/true, options.target_color_params.color_space,
mailbox.name);
options.target_color_params.hdr_max_luminance_relative, mailbox.name);
ri->EndRasterCHROMIUM();
}
@@ -229,7 +229,7 @@ class OopPixelTest : public testing::Test,
/*needs_clear=*/!options.preclear, options.msaa_sample_count, msaa_mode,
options.use_lcd_text,
/*visible=*/true, options.target_color_params.color_space,
mailbox.name);
options.target_color_params.hdr_max_luminance_relative, mailbox.name);
size_t max_op_size_limit =
gpu::raster::RasterInterface::kDefaultMaxOpSizeHint;
ri->RasterCHROMIUM(display_item_list.get(), &image_provider,

@@ -414,7 +414,8 @@ void GpuRasterBufferProvider::RasterBufferImpl::RasterizeSource(
ri->BeginRasterCHROMIUM(
raster_source->background_color(), mailbox_needs_clear,
playback_settings.msaa_sample_count, msaa_mode, use_lcd_text,
playback_settings.visible, color_space_, backing_->mailbox.name);
playback_settings.visible, color_space_, playback_settings.hdr_headroom,
backing_->mailbox.name);
gfx::Vector2dF recording_to_raster_scale = transform.scale();
recording_to_raster_scale.InvScale(raster_source->recording_scale_factor());

@@ -55,6 +55,9 @@ class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
// Visible hint, GPU may use it as a hint to schedule raster tasks.
bool visible = false;
// The HDR headroom to use when tone mapping content.
float hdr_headroom = 1.f;
raw_ptr<ImageProvider> image_provider = nullptr;
};
constexpr static int kDefault = 1;

@@ -80,6 +80,12 @@ void FakeLayerTreeHostImpl::NotifyTileStateChanged(const Tile* tile) {
notify_tile_state_changed_called_ = true;
}
TargetColorParams FakeLayerTreeHostImpl::GetTargetColorParams(
gfx::ContentColorUsage content_color_usage) const {
return target_color_params_.value_or(
LayerTreeHostImpl::GetTargetColorParams(content_color_usage));
}
const viz::BeginFrameArgs& FakeLayerTreeHostImpl::CurrentBeginFrameArgs()
const {
return current_begin_frame_tracker_.DangerousMethodCurrentOrLast();

@@ -45,6 +45,8 @@ class FakeLayerTreeHostImpl : public LayerTreeHostImpl {
void NotifyTileStateChanged(const Tile* tile) override;
const viz::BeginFrameArgs& CurrentBeginFrameArgs() const override;
void AdvanceToNextFrame(base::TimeDelta advance_by);
TargetColorParams GetTargetColorParams(
gfx::ContentColorUsage content_color_usage) const override;
using LayerTreeHostImpl::ActivateSyncTree;
using LayerTreeHostImpl::prepare_tiles_needed;
@@ -57,6 +59,10 @@ class FakeLayerTreeHostImpl : public LayerTreeHostImpl {
void set_notify_tile_state_changed_called(bool called) {
notify_tile_state_changed_called_ = called;
}
void set_target_color_params(
absl::optional<TargetColorParams> target_color_params) {
target_color_params_ = target_color_params;
}
AnimationHost* animation_host() const;
@@ -66,6 +72,7 @@ class FakeLayerTreeHostImpl : public LayerTreeHostImpl {
FakeLayerTreeHostImplClient client_;
FakeRenderingStatsInstrumentation stats_instrumentation_;
bool notify_tile_state_changed_called_;
absl::optional<TargetColorParams> target_color_params_;
};
} // namespace cc

@@ -1409,6 +1409,8 @@ scoped_refptr<TileTask> TileManager::CreateRasterTask(
playback_settings.msaa_sample_count = msaa_sample_count;
playback_settings.visible =
tile->required_for_activation() || tile->required_for_draw();
playback_settings.hdr_headroom =
target_color_params.hdr_max_luminance_relative;
// Create and queue all image decode tasks that this tile depends on. Note
// that we need to store the images for decode tasks in

@@ -97,15 +97,24 @@ class SynchronousSimpleTaskRunner : public base::TestSimpleTaskRunner {
class FakeRasterBuffer : public RasterBuffer {
public:
FakeRasterBuffer() = default;
explicit FakeRasterBuffer(float expected_hdr_headroom)
: expected_hdr_headroom_(expected_hdr_headroom) {}
void Playback(const RasterSource* raster_source,
const gfx::Rect& raster_full_rect,
const gfx::Rect& raster_dirty_rect,
uint64_t new_content_id,
const gfx::AxisTransform2d& transform,
const RasterSource::PlaybackSettings& playback_settings,
const GURL& url) override {}
const GURL& url) override {
EXPECT_EQ(expected_hdr_headroom_, playback_settings.hdr_headroom);
}
bool SupportsBackgroundThreadPriority() const override { return true; }
private:
const float expected_hdr_headroom_ = 1.f;
};
class TileManagerTilePriorityQueueTest : public TestLayerTreeHostBase {
@@ -2566,6 +2575,10 @@ class MockReadyToDrawRasterBufferProviderImpl
base::OnceClosure callback,
uint64_t pending_callback_id));
void set_expected_hdr_headroom(float expected_hdr_headroom) {
expected_hdr_headroom_ = expected_hdr_headroom;
}
std::unique_ptr<RasterBuffer> AcquireBufferForRaster(
const ResourcePool::InUsePoolResource& resource,
uint64_t resource_content_id,
@@ -2575,8 +2588,11 @@ class MockReadyToDrawRasterBufferProviderImpl
bool depends_on_hardware_accelerated_webp_candidates) override {
if (!resource.software_backing())
resource.set_software_backing(std::make_unique<TestSoftwareBacking>());
return std::make_unique<FakeRasterBuffer>();
return std::make_unique<FakeRasterBuffer>(expected_hdr_headroom_);
}
private:
float expected_hdr_headroom_ = 1.f;
};
class TileManagerReadyToDrawTest : public TileManagerTest {
@@ -2711,6 +2727,25 @@ TEST_F(TileManagerReadyToDrawTest, NonSmoothActivationDoesNotWaitOnCallback) {
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, HdrHeadroomPropagated) {
constexpr float kTestHdrHeadroom = 4.f;
TargetColorParams target_color_params;
target_color_params.hdr_max_luminance_relative = kTestHdrHeadroom;
host_impl()->set_target_color_params(target_color_params);
mock_raster_buffer_provider()->set_expected_hdr_headroom(kTestHdrHeadroom);
SetupTreesWithPendingTreeTiles();
base::RunLoop run_loop;
host_impl()->tile_manager()->PrepareTiles(host_impl()->global_tile_state());
EXPECT_CALL(MockHostImpl(), NotifyReadyToActivate())
.WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
run_loop.Run();
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToDraw());
EXPECT_TRUE(host_impl()->tile_manager()->IsReadyToActivate());
}
TEST_F(TileManagerReadyToDrawTest, SmoothDrawWaitsOnCallback) {
host_impl()->SetTreePriority(SMOOTHNESS_TAKES_PRIORITY);
SetupTreesWithActiveTreeTiles();

@@ -116,6 +116,7 @@ class TestRasterInterface : public gpu::raster::RasterInterface {
GLboolean can_use_lcd_text,
GLboolean visible,
const gfx::ColorSpace& color_space,
float hdr_headroom,
const GLbyte* mailbox) override {}
void RasterCHROMIUM(const cc::DisplayItemList* list,
cc::ImageProvider* provider,

@@ -97,6 +97,7 @@ void BeginRasterCHROMIUMImmediate(GLfloat r,
gpu::raster::MsaaMode msaa_mode,
GLboolean can_use_lcd_text,
GLboolean visible,
GLfloat hdr_headroom,
const GLbyte* mailbox) {
const uint32_t size =
raster::cmds::BeginRasterCHROMIUMImmediate::ComputeSize();
@@ -105,7 +106,7 @@ void BeginRasterCHROMIUMImmediate(GLfloat r,
size);
if (c) {
c->Init(r, g, b, a, needs_clear, msaa_sample_count, msaa_mode,
can_use_lcd_text, visible, mailbox);
can_use_lcd_text, visible, hdr_headroom, mailbox);
}
}

@@ -1406,13 +1406,14 @@ void RasterImplementation::BeginRasterCHROMIUM(
GLboolean can_use_lcd_text,
GLboolean visible,
const gfx::ColorSpace& color_space,
float hdr_headroom,
const GLbyte* mailbox) {
DCHECK(!raster_properties_);
helper_->BeginRasterCHROMIUMImmediate(
sk_color_4f.fR, sk_color_4f.fG, sk_color_4f.fB, sk_color_4f.fA,
needs_clear, msaa_sample_count, msaa_mode, can_use_lcd_text, visible,
mailbox);
hdr_headroom, mailbox);
raster_properties_.emplace(sk_color_4f, can_use_lcd_text,
color_space.ToSkColorSpace());

@@ -161,6 +161,7 @@ class RASTER_EXPORT RasterImplementation : public RasterInterface,
GLboolean can_use_lcd_text,
GLboolean visible,
const gfx::ColorSpace& color_space,
float hdr_headroom,
const GLbyte* mailbox) override;
void RasterCHROMIUM(const cc::DisplayItemList* list,
cc::ImageProvider* provider,

@@ -328,6 +328,7 @@ void RasterImplementationGLES::BeginRasterCHROMIUM(
GLboolean can_use_lcd_text,
GLboolean visible,
const gfx::ColorSpace& color_space,
float hdr_headroom,
const GLbyte* mailbox) {
NOTREACHED();
}

@@ -105,6 +105,7 @@ class RASTER_EXPORT RasterImplementationGLES : public RasterInterface {
GLboolean can_use_lcd_text,
GLboolean visible,
const gfx::ColorSpace& color_space,
float hdr_headroom,
const GLbyte* mailbox) override;
void RasterCHROMIUM(const cc::DisplayItemList* list,
cc::ImageProvider* provider,

@@ -116,6 +116,7 @@ class RasterInterface : public InterfaceBase {
GLboolean can_use_lcd_text,
GLboolean visible,
const gfx::ColorSpace& color_space,
float hdr_headroom,
const GLbyte* mailbox) = 0;
// Heuristic decided on UMA data. This covers 85% of the cases where we need

@@ -395,6 +395,7 @@ struct BeginRasterCHROMIUMImmediate {
gpu::raster::MsaaMode _msaa_mode,
GLboolean _can_use_lcd_text,
GLboolean _visible,
GLfloat _hdr_headroom,
const GLbyte* _mailbox) {
SetHeader();
r = _r;
@@ -406,6 +407,7 @@ struct BeginRasterCHROMIUMImmediate {
msaa_mode = _msaa_mode;
can_use_lcd_text = _can_use_lcd_text;
visible = _visible;
hdr_headroom = _hdr_headroom;
memcpy(ImmediateDataAddress(this), _mailbox, ComputeDataSize());
}
@@ -419,10 +421,11 @@ struct BeginRasterCHROMIUMImmediate {
gpu::raster::MsaaMode _msaa_mode,
GLboolean _can_use_lcd_text,
GLboolean _visible,
GLfloat _hdr_headroom,
const GLbyte* _mailbox) {
static_cast<ValueType*>(cmd)->Init(_r, _g, _b, _a, _needs_clear,
_msaa_sample_count, _msaa_mode,
_can_use_lcd_text, _visible, _mailbox);
static_cast<ValueType*>(cmd)->Init(
_r, _g, _b, _a, _needs_clear, _msaa_sample_count, _msaa_mode,
_can_use_lcd_text, _visible, _hdr_headroom, _mailbox);
const uint32_t size = ComputeSize();
return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size);
}
@@ -437,10 +440,11 @@ struct BeginRasterCHROMIUMImmediate {
uint32_t msaa_mode;
uint32_t can_use_lcd_text;
uint32_t visible;
float hdr_headroom;
};
static_assert(sizeof(BeginRasterCHROMIUMImmediate) == 40,
"size of BeginRasterCHROMIUMImmediate should be 40");
static_assert(sizeof(BeginRasterCHROMIUMImmediate) == 44,
"size of BeginRasterCHROMIUMImmediate should be 44");
static_assert(offsetof(BeginRasterCHROMIUMImmediate, header) == 0,
"offset of BeginRasterCHROMIUMImmediate header should be 0");
static_assert(offsetof(BeginRasterCHROMIUMImmediate, r) == 4,
@@ -464,6 +468,9 @@ static_assert(
"offset of BeginRasterCHROMIUMImmediate can_use_lcd_text should be 32");
static_assert(offsetof(BeginRasterCHROMIUMImmediate, visible) == 36,
"offset of BeginRasterCHROMIUMImmediate visible should be 36");
static_assert(
offsetof(BeginRasterCHROMIUMImmediate, hdr_headroom) == 40,
"offset of BeginRasterCHROMIUMImmediate hdr_headroom should be 40");
struct RasterCHROMIUM {
typedef RasterCHROMIUM ValueType;

@@ -158,12 +158,12 @@ TEST_F(RasterFormatTest, BeginRasterCHROMIUMImmediate) {
};
cmds::BeginRasterCHROMIUMImmediate& cmd =
*GetBufferAs<cmds::BeginRasterCHROMIUMImmediate>();
void* next_cmd =
cmd.Set(&cmd, static_cast<GLfloat>(11), static_cast<GLfloat>(12),
static_cast<GLfloat>(13), static_cast<GLfloat>(14),
static_cast<GLboolean>(15), static_cast<GLuint>(16),
static_cast<gpu::raster::MsaaMode>(17),
static_cast<GLboolean>(18), static_cast<GLboolean>(19), data);
void* next_cmd = cmd.Set(
&cmd, static_cast<GLfloat>(11), static_cast<GLfloat>(12),
static_cast<GLfloat>(13), static_cast<GLfloat>(14),
static_cast<GLboolean>(15), static_cast<GLuint>(16),
static_cast<gpu::raster::MsaaMode>(17), static_cast<GLboolean>(18),
static_cast<GLboolean>(19), static_cast<GLfloat>(20), data);
EXPECT_EQ(static_cast<uint32_t>(cmds::BeginRasterCHROMIUMImmediate::kCmdId),
cmd.header.command);
EXPECT_EQ(sizeof(cmd) + RoundSizeToMultipleOfEntries(sizeof(data)),
@@ -177,6 +177,7 @@ TEST_F(RasterFormatTest, BeginRasterCHROMIUMImmediate) {
EXPECT_EQ(static_cast<gpu::raster::MsaaMode>(17), cmd.msaa_mode);
EXPECT_EQ(static_cast<GLboolean>(18), cmd.can_use_lcd_text);
EXPECT_EQ(static_cast<GLboolean>(19), cmd.visible);
EXPECT_EQ(static_cast<GLfloat>(20), cmd.hdr_headroom);
CheckBytesWrittenMatchesExpectedSize(
next_cmd, sizeof(cmd) + RoundSizeToMultipleOfEntries(sizeof(data)));
}

@@ -23,7 +23,7 @@ GL_APICALL void GL_APIENTRY glLoseContextCHROMIUM (GLenumResetStatus cur
GL_APICALL GLenum GL_APIENTRY glGetGraphicsResetStatusKHR (void);
// Extension CHROMIUM_raster_transport
GL_APICALL void GL_APIENTRY glBeginRasterCHROMIUM (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLboolean needs_clear, GLuint msaa_sample_count, gpu::raster::MsaaMode msaa_mode, GLboolean can_use_lcd_text, GLboolean visible, const GLbyte* mailbox);
GL_APICALL void GL_APIENTRY glBeginRasterCHROMIUM (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLboolean needs_clear, GLuint msaa_sample_count, gpu::raster::MsaaMode msaa_mode, GLboolean can_use_lcd_text, GLboolean visible, GLfloat hdr_headroom, const GLbyte* mailbox);
GL_APICALL void GL_APIENTRY glRasterCHROMIUM (GLuint raster_shm_id, GLuint raster_shm_offset, GLuint raster_shm_size, GLuint font_shm_id, GLuint font_shm_offset, GLuint font_shm_size);
GL_APICALL void GL_APIENTRY glEndRasterCHROMIUM (void);
GL_APICALL void GL_APIENTRY glCreateTransferCacheEntryINTERNAL (GLuint entry_type, GLuint entry_id, GLuint handle_shm_id, GLuint handle_shm_offset, GLuint data_shm_id, GLuint data_shm_offset, GLuint data_size);

@@ -746,6 +746,7 @@ class RasterDecoderImpl final : public RasterDecoder,
MsaaMode msaa_mode,
GLboolean can_use_lcd_text,
GLboolean visible,
GLfloat hdr_headroom,
const volatile GLbyte* key);
void DoRasterCHROMIUM(GLuint raster_shm_id,
GLuint raster_shm_offset,
@@ -2888,6 +2889,7 @@ void RasterDecoderImpl::DoBeginRasterCHROMIUM(GLfloat r,
MsaaMode msaa_mode,
GLboolean can_use_lcd_text,
GLboolean visible,
GLfloat hdr_headroom,
const volatile GLbyte* key) {
// Workaround for https://crbug.com/906453: Flush before BeginRaster (the
// commands between BeginRaster and EndRaster will not flush).
@@ -3021,6 +3023,7 @@ void RasterDecoderImpl::DoBeginRasterCHROMIUM(GLfloat r,
}
sk_surface_ = scoped_shared_image_write_->surface();
// TODO(https://crbug.com/1483235): Save `hdr_headroom`.
if (!begin_semaphores.empty()) {
bool result =

@@ -121,6 +121,7 @@ error::Error RasterDecoderImpl::HandleBeginRasterCHROMIUMImmediate(
static_cast<gpu::raster::MsaaMode>(c.msaa_mode);
GLboolean can_use_lcd_text = static_cast<GLboolean>(c.can_use_lcd_text);
GLboolean visible = static_cast<GLboolean>(c.visible);
GLfloat hdr_headroom = static_cast<GLfloat>(c.hdr_headroom);
uint32_t mailbox_size;
if (!gles2::GLES2Util::ComputeDataSize<GLbyte, 16>(1, &mailbox_size)) {
return error::kOutOfBounds;
@@ -135,7 +136,7 @@ error::Error RasterDecoderImpl::HandleBeginRasterCHROMIUMImmediate(
return error::kOutOfBounds;
}
DoBeginRasterCHROMIUM(r, g, b, a, needs_clear, msaa_sample_count, msaa_mode,
can_use_lcd_text, visible, mailbox);
can_use_lcd_text, visible, hdr_headroom, mailbox);
return error::kNoError;
}

@@ -99,7 +99,8 @@ TEST_F(RasterInProcessCommandBufferTest, AllowedBetweenBeginEndRasterCHROMIUM) {
ri_->BeginRasterCHROMIUM(
/*sk_color_4f=*/{0, 0, 0, 0}, /*needs_clear=*/true,
/*msaa_sample_count=*/0, gpu::raster::kNoMSAA,
/*can_use_lcd_text=*/false, /*visible=*/true, color_space, mailbox.name);
/*can_use_lcd_text=*/false, /*visible=*/true, color_space,
/*hdr_headroom=*/1.f, mailbox.name);
EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), ri_->GetError());
// Should flag an error this command is not allowed between a Begin and

@@ -1559,7 +1559,7 @@ void CanvasResourceProvider::RasterRecordOOP(cc::PaintRecord last_recording,
oopr_uses_dmsaa_ ? gpu::raster::MsaaMode::kDMSAA
: gpu::raster::MsaaMode::kNoMSAA,
can_use_lcd_text, /*visible=*/true, GetColorSpace(),
mailbox.name);
/*hdr_headroom=*/1.f, mailbox.name);
ri->RasterCHROMIUM(list.get(), GetOrCreateCanvasImageProvider(), size,
full_raster_rect, playback_rect, post_translate,