Loading libs/renderengine/include/renderengine/DisplaySettings.h +4 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,10 @@ struct DisplaySettings { // dataspace, in non-linear space. mat4 colorTransform = mat4(); // If true, and colorTransform is non-identity, most client draw calls can // ignore it. Some draws (e.g. screen decorations) may need it, though. bool deviceHandlesColorTransform = false; // An additional orientation flag to be applied after clipping the output. // By way of example, this may be used for supporting fullscreen screenshot // capture of a device in landscape while the buffer is in portrait Loading libs/renderengine/skia/SkiaGLRenderEngine.cpp +34 −8 Original line number Diff line number Diff line Loading @@ -792,7 +792,7 @@ void SkiaGLRenderEngine::drawLayersInternal( // setup color filter if necessary sk_sp<SkColorFilter> displayColorTransform; if (display.colorTransform != mat4()) { if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)); } const bool ctModifiesAlpha = Loading Loading @@ -1107,11 +1107,37 @@ void SkiaGLRenderEngine::drawLayersInternal( if (imageTextureRef->colorType() == kAlpha_8_SkColorType) { LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8"); float matrix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1 }; paint.setColorFilter(SkColorFilters::Matrix(matrix)); // SysUI creates the alpha layer as a coverage layer, which is // appropriate for the DPU. Use a color matrix to convert it to // a mask. // TODO (b/219525258): Handle input as a mask. // // The color matrix will convert A8 pixels with no alpha to // black, as described by this vector. If the display handles // the color transform, we need to invert it to find the color // that will result in black after the DPU applies the transform. SkV4 black{0.0f, 0.0f, 0.0f, 1.0f}; // r, g, b, a if (display.colorTransform != mat4() && display.deviceHandlesColorTransform) { SkM44 colorSpaceMatrix = getSkM44(display.colorTransform); if (colorSpaceMatrix.invert(&colorSpaceMatrix)) { black = colorSpaceMatrix * black; } else { // We'll just have to use 0,0,0 as black, which should // be close to correct. ALOGI("Could not invert colorTransform!"); } } SkColorMatrix colorMatrix(0, 0, 0, 0, black[0], 0, 0, 0, 0, black[1], 0, 0, 0, 0, black[2], 0, 0, 0, -1, 1); if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { // On the other hand, if the device doesn't handle it, we // have to apply it ourselves. colorMatrix.postConcat(toSkColorMatrix(display.colorTransform)); } paint.setColorFilter(SkColorFilters::Matrix(colorMatrix)); } } else { ATRACE_NAME("DrawColor"); Loading @@ -1134,8 +1160,8 @@ void SkiaGLRenderEngine::drawLayersInternal( paint.setBlendMode(SkBlendMode::kSrc); } // A color filter will have been set for an A8 buffer. Do not replace // it with the displayColorTransform, which shouldn't affect A8. // An A8 buffer will already have the proper color filter attached to // its paint, including the displayColorTransform as needed. if (!paint.getColorFilter()) { paint.setColorFilter(displayColorTransform); } Loading libs/renderengine/tests/RenderEngineTest.cpp +139 −0 Original line number Diff line number Diff line Loading @@ -2627,6 +2627,7 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { const auto r8Buffer = allocateR8Buffer(2, 1); if (!r8Buffer) { GTEST_SKIP() << "Test is only necessary on devices that support r8"; return; } { Loading Loading @@ -2677,6 +2678,144 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 255); expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255); } TEST_P(RenderEngineTest, r8_respects_color_transform) { if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { return; } initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); if (!r8Buffer) { GTEST_SKIP() << "Test is only necessary on devices that support r8"; return; } { uint8_t* pixels; r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&pixels)); pixels[0] = 0; pixels[1] = 255; r8Buffer->getBuffer()->unlock(); } const auto rect = Rect(0, 0, 2, 1); const renderengine::DisplaySettings display{ .physicalDisplay = rect, .clip = rect, .outputDataspace = ui::Dataspace::SRGB, // Verify that the R8 layer respects the color transform when // deviceHandlesColorTransform is false. This transform converts // pure red to pure green. That will occur when the R8 buffer is // 255. When the R8 buffer is 0, it will still change to black, as // with r8_behaves_as_mask. .colorTransform = mat4(0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), .deviceHandlesColorTransform = false, }; const auto redBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(255, 0, 0, 255)); const renderengine::LayerSettings redLayer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = redBuffer, }, }, .alpha = 1.0f, }; const renderengine::LayerSettings r8Layer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = r8Buffer, }, }, .alpha = 1.0f, }; std::vector<renderengine::LayerSettings> layers{redLayer, r8Layer}; invokeDraw(display, layers); expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 255); expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255); } TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { return; } initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); if (!r8Buffer) { GTEST_SKIP() << "Test is only necessary on devices that support r8"; return; } { uint8_t* pixels; r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&pixels)); pixels[0] = 0; pixels[1] = 255; r8Buffer->getBuffer()->unlock(); } const auto rect = Rect(0, 0, 2, 1); const renderengine::DisplaySettings display{ .physicalDisplay = rect, .clip = rect, .outputDataspace = ui::Dataspace::SRGB, // If deviceHandlesColorTransform is true, pixels where the A8 // buffer is opaque are unaffected. If the colorTransform is // invertible, pixels where the A8 buffer are transparent have the // inverse applied to them so that the DPU will convert them back to // black. Test with an arbitrary, invertible matrix. .colorTransform = mat4(1, 0, 0, 2, 3, 1, 2, 5, 0, 5, 3, 0, 0, 1, 0, 2), .deviceHandlesColorTransform = true, }; const auto redBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(255, 0, 0, 255)); const renderengine::LayerSettings redLayer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = redBuffer, }, }, .alpha = 1.0f, }; const renderengine::LayerSettings r8Layer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = r8Buffer, }, }, .alpha = 1.0f, }; std::vector<renderengine::LayerSettings> layers{redLayer, r8Layer}; invokeDraw(display, layers); expectBufferColor(Rect(1, 0, 2, 1), 255, 0, 0, 255); // Still red. expectBufferColor(Rect(0, 0, 1, 1), 0, 70, 0, 255); } } // namespace renderengine } // namespace android Loading services/surfaceflinger/CompositionEngine/src/Output.cpp +3 −3 Original line number Diff line number Diff line Loading @@ -1079,9 +1079,9 @@ std::optional<base::unique_fd> Output::composeSurfaces( clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits; // Compute the global color transform matrix. if (!outputState.usesDeviceComposition && !getSkipColorTransform()) { clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; } clientCompositionDisplay.deviceHandlesColorTransform = outputState.usesDeviceComposition || getSkipColorTransform(); // Generate the client composition requests for the layers on this output. std::vector<LayerFE*> clientCompositionLayersFE; Loading services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +10 −4 Original line number Diff line number Diff line Loading @@ -3487,7 +3487,8 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposi .maxLuminance = kDefaultMaxLuminance, .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = mat4(), .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = true, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3505,7 +3506,8 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, .maxLuminance = kDefaultMaxLuminance, .currentLuminanceNits = kDisplayLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = mat4(), .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = true, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3522,7 +3524,8 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComp .maxLuminance = kDefaultMaxLuminance, .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = mat4(), .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = true, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3540,6 +3543,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientCo .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = false, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3557,6 +3561,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClien .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = false, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3574,7 +3579,8 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, .maxLuminance = kDefaultMaxLuminance, .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = mat4(), .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = true, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading Loading
libs/renderengine/include/renderengine/DisplaySettings.h +4 −0 Original line number Diff line number Diff line Loading @@ -54,6 +54,10 @@ struct DisplaySettings { // dataspace, in non-linear space. mat4 colorTransform = mat4(); // If true, and colorTransform is non-identity, most client draw calls can // ignore it. Some draws (e.g. screen decorations) may need it, though. bool deviceHandlesColorTransform = false; // An additional orientation flag to be applied after clipping the output. // By way of example, this may be used for supporting fullscreen screenshot // capture of a device in landscape while the buffer is in portrait Loading
libs/renderengine/skia/SkiaGLRenderEngine.cpp +34 −8 Original line number Diff line number Diff line Loading @@ -792,7 +792,7 @@ void SkiaGLRenderEngine::drawLayersInternal( // setup color filter if necessary sk_sp<SkColorFilter> displayColorTransform; if (display.colorTransform != mat4()) { if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)); } const bool ctModifiesAlpha = Loading Loading @@ -1107,11 +1107,37 @@ void SkiaGLRenderEngine::drawLayersInternal( if (imageTextureRef->colorType() == kAlpha_8_SkColorType) { LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8"); float matrix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1 }; paint.setColorFilter(SkColorFilters::Matrix(matrix)); // SysUI creates the alpha layer as a coverage layer, which is // appropriate for the DPU. Use a color matrix to convert it to // a mask. // TODO (b/219525258): Handle input as a mask. // // The color matrix will convert A8 pixels with no alpha to // black, as described by this vector. If the display handles // the color transform, we need to invert it to find the color // that will result in black after the DPU applies the transform. SkV4 black{0.0f, 0.0f, 0.0f, 1.0f}; // r, g, b, a if (display.colorTransform != mat4() && display.deviceHandlesColorTransform) { SkM44 colorSpaceMatrix = getSkM44(display.colorTransform); if (colorSpaceMatrix.invert(&colorSpaceMatrix)) { black = colorSpaceMatrix * black; } else { // We'll just have to use 0,0,0 as black, which should // be close to correct. ALOGI("Could not invert colorTransform!"); } } SkColorMatrix colorMatrix(0, 0, 0, 0, black[0], 0, 0, 0, 0, black[1], 0, 0, 0, 0, black[2], 0, 0, 0, -1, 1); if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { // On the other hand, if the device doesn't handle it, we // have to apply it ourselves. colorMatrix.postConcat(toSkColorMatrix(display.colorTransform)); } paint.setColorFilter(SkColorFilters::Matrix(colorMatrix)); } } else { ATRACE_NAME("DrawColor"); Loading @@ -1134,8 +1160,8 @@ void SkiaGLRenderEngine::drawLayersInternal( paint.setBlendMode(SkBlendMode::kSrc); } // A color filter will have been set for an A8 buffer. Do not replace // it with the displayColorTransform, which shouldn't affect A8. // An A8 buffer will already have the proper color filter attached to // its paint, including the displayColorTransform as needed. if (!paint.getColorFilter()) { paint.setColorFilter(displayColorTransform); } Loading
libs/renderengine/tests/RenderEngineTest.cpp +139 −0 Original line number Diff line number Diff line Loading @@ -2627,6 +2627,7 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { const auto r8Buffer = allocateR8Buffer(2, 1); if (!r8Buffer) { GTEST_SKIP() << "Test is only necessary on devices that support r8"; return; } { Loading Loading @@ -2677,6 +2678,144 @@ TEST_P(RenderEngineTest, r8_behaves_as_mask) { expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 255); expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255); } TEST_P(RenderEngineTest, r8_respects_color_transform) { if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { return; } initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); if (!r8Buffer) { GTEST_SKIP() << "Test is only necessary on devices that support r8"; return; } { uint8_t* pixels; r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&pixels)); pixels[0] = 0; pixels[1] = 255; r8Buffer->getBuffer()->unlock(); } const auto rect = Rect(0, 0, 2, 1); const renderengine::DisplaySettings display{ .physicalDisplay = rect, .clip = rect, .outputDataspace = ui::Dataspace::SRGB, // Verify that the R8 layer respects the color transform when // deviceHandlesColorTransform is false. This transform converts // pure red to pure green. That will occur when the R8 buffer is // 255. When the R8 buffer is 0, it will still change to black, as // with r8_behaves_as_mask. .colorTransform = mat4(0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), .deviceHandlesColorTransform = false, }; const auto redBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(255, 0, 0, 255)); const renderengine::LayerSettings redLayer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = redBuffer, }, }, .alpha = 1.0f, }; const renderengine::LayerSettings r8Layer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = r8Buffer, }, }, .alpha = 1.0f, }; std::vector<renderengine::LayerSettings> layers{redLayer, r8Layer}; invokeDraw(display, layers); expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 255); expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255); } TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) { if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { return; } initializeRenderEngine(); const auto r8Buffer = allocateR8Buffer(2, 1); if (!r8Buffer) { GTEST_SKIP() << "Test is only necessary on devices that support r8"; return; } { uint8_t* pixels; r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast<void**>(&pixels)); pixels[0] = 0; pixels[1] = 255; r8Buffer->getBuffer()->unlock(); } const auto rect = Rect(0, 0, 2, 1); const renderengine::DisplaySettings display{ .physicalDisplay = rect, .clip = rect, .outputDataspace = ui::Dataspace::SRGB, // If deviceHandlesColorTransform is true, pixels where the A8 // buffer is opaque are unaffected. If the colorTransform is // invertible, pixels where the A8 buffer are transparent have the // inverse applied to them so that the DPU will convert them back to // black. Test with an arbitrary, invertible matrix. .colorTransform = mat4(1, 0, 0, 2, 3, 1, 2, 5, 0, 5, 3, 0, 0, 1, 0, 2), .deviceHandlesColorTransform = true, }; const auto redBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(255, 0, 0, 255)); const renderengine::LayerSettings redLayer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = redBuffer, }, }, .alpha = 1.0f, }; const renderengine::LayerSettings r8Layer{ .geometry.boundaries = rect.toFloatRect(), .source = renderengine::PixelSource{ .buffer = renderengine::Buffer{ .buffer = r8Buffer, }, }, .alpha = 1.0f, }; std::vector<renderengine::LayerSettings> layers{redLayer, r8Layer}; invokeDraw(display, layers); expectBufferColor(Rect(1, 0, 2, 1), 255, 0, 0, 255); // Still red. expectBufferColor(Rect(0, 0, 1, 1), 0, 70, 0, 255); } } // namespace renderengine } // namespace android Loading
services/surfaceflinger/CompositionEngine/src/Output.cpp +3 −3 Original line number Diff line number Diff line Loading @@ -1079,9 +1079,9 @@ std::optional<base::unique_fd> Output::composeSurfaces( clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits; // Compute the global color transform matrix. if (!outputState.usesDeviceComposition && !getSkipColorTransform()) { clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; } clientCompositionDisplay.deviceHandlesColorTransform = outputState.usesDeviceComposition || getSkipColorTransform(); // Generate the client composition requests for the layers on this output. std::vector<LayerFE*> clientCompositionLayersFE; Loading
services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +10 −4 Original line number Diff line number Diff line Loading @@ -3487,7 +3487,8 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposi .maxLuminance = kDefaultMaxLuminance, .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = mat4(), .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = true, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3505,7 +3506,8 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, .maxLuminance = kDefaultMaxLuminance, .currentLuminanceNits = kDisplayLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = mat4(), .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = true, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3522,7 +3524,8 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComp .maxLuminance = kDefaultMaxLuminance, .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = mat4(), .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = true, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3540,6 +3543,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientCo .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = false, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3557,6 +3561,7 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClien .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = false, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading @@ -3574,7 +3579,8 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, .maxLuminance = kDefaultMaxLuminance, .currentLuminanceNits = kDefaultMaxLuminance, .outputDataspace = kDefaultOutputDataspace, .colorTransform = mat4(), .colorTransform = kDefaultColorTransformMat, .deviceHandlesColorTransform = true, .orientation = kDefaultOutputOrientationFlags, .targetLuminanceNits = kClientTargetLuminanceNits}) .execute() Loading