Bug 1459526 - Handle full-range video in Webrender. r=gw,lsalzman

+ Begin to add video tests to ensure we ratchet towards correctness.
+ Test rec709 x (yuv420p, yuv420p10, gbrp) x (tv, pc) x codecs.
+ Just mark fuzziness for now. Better would be e.g. 16_127_233 'bad
  references'.

Differential Revision: https://phabricator.services.mozilla.com/D115298
This commit is contained in:
Jeff Gilbert 2021-06-17 01:12:18 +00:00
Родитель c0d7347c6c
Коммит 72024fcc23
74 изменённых файлов: 1303 добавлений и 306 удалений

Просмотреть файл

@ -72,6 +72,7 @@ already_AddRefed<Image> RemoteImageHolder::DeserializeImage(
pData.mStereoMode = descriptor.stereoMode();
pData.mColorDepth = descriptor.colorDepth();
pData.mYUVColorSpace = descriptor.yUVColorSpace();
pData.mColorRange = descriptor.colorRange();
pData.mYChannel = ImageDataSerializer::GetYChannel(buffer, descriptor);
pData.mCbChannel = ImageDataSerializer::GetCbChannel(buffer, descriptor);
pData.mCrChannel = ImageDataSerializer::GetCrChannel(buffer, descriptor);

Двоичные данные
dom/media/test/reftest/color_quads/720p.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.5 KiB

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,71 @@
# Reference image generated via https://jdashg.github.io/misc/colors/color-quads-16-127-235.html
# Test videos encoded via ../gen_combos.py --write color_quads/720p.png
# We're sort of testing two things here:
# 1. Does a av1.webm video into the actual values we expect?
# 2. Do other similar videos decode the same was as av1.webm?
# We have this split because while each platform/compositor has its own inaccuracies,
# each platform/compositor will have the *same* inaccuracies regardless of video.
# So, we just need to first check if e.g. av1.webm decodes to what we expect,
# and then we have generally trivially compare other codecs/containers to that.
# -
# yuv420p
fuzzy(16-50,5234-5621) fuzzy-if(swgl,32-38,5462-91746) fuzzy-if(!webrender,16-38,5234-94640) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png
fuzzy-if(Android,254-255,273680-273807) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
== ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
fuzzy(1-2,75-225) fuzzy-if(Android,254-255,273680-273807) fuzzy-if(!Android&&!webrender,1-2,75-94070) fuzzy-if(OSX&&webrender,32-32,187407-187407) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
fuzzy-if(Android,254-255,273680-273807) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p.av1.webm
skip-if(!webrender||Android) fuzzy(16-48,8349-8818) fuzzy-if(winWidget&&swgl,38-38,184080-184080) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm ../reftest_img.html?src=color_quads/720p.png
skip-if(!webrender||Android) fuzzy-if(Android,255-255,273726-273726) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
skip-if(!webrender||Android) skip-if(winWidget&&swgl) fuzzy-if(Android,255-255,273726-273726) fuzzy-if(OSX||winWidget,2-34,184281-187407) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
skip-if(!webrender||Android) fuzzy-if(Android,255-255,273726-273726) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p.av1.webm
# -
# yuv420p10
skip-if(!webrender||Android) fuzzy(33-49,2499-2579) fuzzy-if(swgl,34-52,270527-270528) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm ../reftest_img.html?src=color_quads/720p.png
skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm
skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm
#[2] skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm
skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.yuv420p10.av1.webm
skip-if(!webrender||Android) fuzzy(33-49,174932-175092) fuzzy-if(swgl&&!winWidget,37-52,11553-11554) fuzzy-if(swgl&&winWidget,40-40,187200-187200) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.av1.webm ../reftest_img.html?src=color_quads/720p.png
skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.av1.webm
skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.av1.webm
#[2] skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.av1.webm
skip-if(!webrender||Android) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.yuv420p10.av1.webm
# Android is really broken in a variety of ways for p10.
#[2]: yuv420p10 broken in h264.mp4: https://bugzilla.mozilla.org/show_bug.cgi?id=1711812
# -
# gbrp
# Note: tv-gbrp doesn't really make sense, and we should consider dropping it.
# Specifically, we should probably do (gbrp, ...(tv,pc)x(yuv,yuv10)) instead of (tv,pc)x(gbrp,yuv,yuv10)
# That said, we should probably test a couple combos, at least. (But then again, why not all!)
# !webrender does not support gbr
skip-if(!webrender) skip-if(winWidget&&swgl) fuzzy(0-1,0-3600) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.av1.webm ../reftest_img.html?src=color_quads/720p.png
skip-if(!webrender) skip-if(winWidget&&swgl) fuzzy(0-1,0-7200) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.gbrp.av1.webm ../reftest_img.html?src=color_quads/720p.png
skip-if(!webrender) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.av1.webm
skip-if(!webrender) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.gbrp.av1.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.gbrp.av1.webm
# Our h264.mp4 doesn't handle gbrp, but *also* doesn't error properly.
skip-if(!webrender) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.h264.mp4 ../reftest_video.html?src=timeout
skip-if(!webrender) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.gbrp.h264.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.h264.mp4
# Our vp9 support doesn't handle gbrp
skip-if(!webrender||OSX||winWidget) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.vp9.webm ../reftest_video.html?src=timeout
skip-if(!webrender||!OSX) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.vp9.webm ../reftest_video.html?src=none
skip-if(!webrender||!winWidget) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.vp9.webm ../reftest_video.html?src=none
skip-if(!webrender) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.gbrp.vp9.webm ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.vp9.webm
skip-if(!webrender) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.vp9.webm
skip-if(!webrender) == ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.pc.gbrp.vp9.mp4 ../reftest_video.html?src=color_quads/720p.png.bt709.bt709.tv.gbrp.vp9.webm

Просмотреть файл

@ -0,0 +1,250 @@
#!/usr/bin/env python3
import concurrent.futures
import pathlib
import subprocess
import sys
ARGS = sys.argv
SRC_PATH = pathlib.Path(ARGS.pop())
DIR = SRC_PATH.parent
# crossCombine([{a:false},{a:5}], [{},{b:5}])
# [{a:false}, {a:true}, {a:false,b:5}, {a:true,b:5}]
def cross_combine(*args):
args = list(args)
def cross_combine2(listA, listB):
listC = []
for a in listA:
for b in listB:
c = dict()
c.update(a)
c.update(b)
listC.append(c)
return listC
res = [dict()]
while True:
try:
next = args.pop(0)
except IndexError:
break
res = cross_combine2(res, next)
return res
def keyed_combiner(key, vals):
res = []
for v in vals:
d = dict()
d[key] = v
res.append(d)
return res
# -
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
# -
OGG = []
WEBM_CODECS = ["av1", "vp9"]
if "--all" in ARGS:
OGG = cross_combine(
[{"ext": "ogg"}], keyed_combiner("vcodec", ["theora", "vp8", "vp9"])
)
WEBM_CODECS += ["vp8"]
MP4 = cross_combine([{"ext": "mp4"}], keyed_combiner("vcodec", ["av1", "h264", "vp9"]))
WEBM = cross_combine([{"ext": "webm"}], keyed_combiner("vcodec", WEBM_CODECS))
# -
FORMAT_LIST = set(
[
"yuv420p",
"yuv420p10",
# 'yuv420p12',
# 'yuv420p16be',
# 'yuv420p16le',
"gbrp",
]
)
if "--all" in ARGS:
FORMAT_LIST |= set(
[
"yuv420p",
"yuv420p10",
"yuv420p12",
"yuv420p16be",
"yuv420p16le",
"yuv422p",
"yuv422p10",
"yuv422p12",
"yuv422p16be",
"yuv422p16le",
"yuv444p",
"yuv444p10",
"yuv444p12",
"yuv444p16be",
"yuv444p16le",
"yuv411p",
"yuv410p",
"yuyv422",
"uyvy422",
"rgb24",
"bgr24",
"rgb8",
"bgr8",
"rgb444be",
"rgb444le",
"bgr444be",
"bgr444le",
# 'nv12', # Encoding not different than yuv420p?
# 'nv21', # Encoding not different than yuv420p?
"gbrp",
"gbrp9be",
"gbrp9le",
"gbrp10be",
"gbrp10le",
"gbrp12be",
"gbrp12le",
"gbrp14be",
"gbrp14le",
"gbrp16be",
"gbrp16le",
]
)
FORMATS = keyed_combiner("format", list(FORMAT_LIST))
RANGE = keyed_combiner("range", ["tv", "pc"])
CSPACE_LIST = set(
[
"bt709",
# 'bt2020',
]
)
if "--all" in ARGS:
CSPACE_LIST |= set(
[
"bt709",
"bt2020",
"bt601-6-525", # aka smpte170m NTSC
"bt601-6-625", # aka bt470bg PAL
]
)
CSPACE_LIST = list(CSPACE_LIST)
# -
COMBOS = cross_combine(
WEBM + MP4 + OGG,
FORMATS,
RANGE,
keyed_combiner("src_cspace", CSPACE_LIST),
keyed_combiner("dst_cspace", CSPACE_LIST),
)
# -
print(f"{len(COMBOS)} combinations...")
todo = []
for c in COMBOS:
dst_name = ".".join(
[
SRC_PATH.name,
c["src_cspace"],
c["dst_cspace"],
c["range"],
c["format"],
c["vcodec"],
c["ext"],
]
)
src_cspace = c["src_cspace"]
vf = f"scale=out_range={c['range']}"
vf += f",colorspace=all={c['dst_cspace']}"
vf += f":iall={src_cspace}"
args = [
"ffmpeg",
"-y",
# For input:
"-color_primaries",
src_cspace,
"-color_trc",
src_cspace,
"-colorspace",
src_cspace,
"-i",
SRC_PATH.as_posix(),
# For output:
"-bitexact", # E.g. don't use true random uuids
"-vf",
vf,
"-pix_fmt",
c["format"],
"-vcodec",
c["vcodec"],
"-crf",
"1", # Not-quite-lossless
(DIR / dst_name).as_posix(),
]
if "-v" in ARGS or "-vv" in ARGS:
print("$ " + " ".join(args))
else:
print(" " + args[-1])
todo.append(args)
# -
with open(DIR / "reftest.list", "r") as f:
reftest_list_text = f.read()
for args in todo:
vid_name = pathlib.Path(args[-1]).name
if vid_name not in reftest_list_text:
print(f"WARNING: Not in reftest.list: {vid_name}")
# -
if "--write" not in ARGS:
print("Use --write to write. Exiting...")
exit(0)
# -
def run_cmd(args):
dest = None
if "-vv" not in ARGS:
dest = subprocess.DEVNULL
subprocess.run(args, stderr=dest)
with concurrent.futures.ThreadPoolExecutor() as pool:
fs = []
for cur_args in todo:
f = pool.submit(run_cmd, cur_args)
fs.append(f)
done = 0
for f in concurrent.futures.as_completed(fs):
f.result() # Raise if it raised
done += 1
sys.stdout.write(f"\rEncoded {done}/{len(todo)}")

Просмотреть файл

@ -15,5 +15,8 @@ window.addEventListener("MozReftestInvalidate", doTest);
</head>
<body>
<video id="v1" style="position:absolute; left:0; top:0; filter:hue-rotate(90deg);"></video>
<script>
//doTest();
</script>
</body>
</html>

Просмотреть файл

@ -15,5 +15,8 @@ window.addEventListener("MozReftestInvalidate", doTest);
</head>
<body>
<video id="v1" style="position:absolute; left:0; top:0; filter:hue-rotate(90deg);"></video>
<script>
//doTest();
</script>
</body>
</html>

Просмотреть файл

@ -1,9 +1,9 @@
skip-if(Android) fuzzy-if(OSX,0-80,0-76800) fuzzy-if(appleSilicon,92-92,76799-76799) fuzzy-if(winWidget,0-62,0-76799) fuzzy-if(gtkWidget&&layersGPUAccelerated,0-70,0-644) HTTP(..) == short.mp4.firstframe.html short.mp4.firstframe-ref.html
skip-if(Android) fuzzy-if(OSX,0-87,0-76797) fuzzy-if(appleSilicon,83-83,76797-76797) fuzzy-if(winWidget,0-60,0-76797) fuzzy-if(gtkWidget&&layersGPUAccelerated,0-60,0-1810) HTTP(..) == short.mp4.lastframe.html short.mp4.lastframe-ref.html
skip-if(Android) skip-if(cocoaWidget) skip-if(winWidget) fuzzy-if(gtkWidget&&layersGPUAccelerated,0-57,0-4281) fuzzy-if(OSX,55-80,4173-4417) HTTP(..) == bipbop_300_215kbps.mp4.lastframe.html bipbop_300_215kbps.mp4.lastframe-ref.html
skip-if(Android) fuzzy-if(OSX,0-80,0-76800) fuzzy-if(appleSilicon,92-92,76799-76799) fuzzy-if(winWidget,0-62,0-76799) fuzzy-if(gtkWidget&&layersGPUAccelerated,0-70,0-2032) fuzzy-if(swgl,62-69,588-76737) HTTP(..) == short.mp4.firstframe.html short.mp4.firstframe-ref.html
skip-if(Android) fuzzy-if(OSX,0-87,0-76797) fuzzy-if(appleSilicon,83-83,76797-76797) fuzzy-if(winWidget,0-60,0-76797) fuzzy-if(gtkWidget&&layersGPUAccelerated,0-60,0-6070) fuzzy-if(swgl,55-76,1698-76545) HTTP(..) == short.mp4.lastframe.html short.mp4.lastframe-ref.html
skip-if(Android) skip-if(cocoaWidget) skip-if(winWidget) fuzzy-if(gtkWidget&&layersGPUAccelerated,0-57,0-4282) fuzzy-if(OSX,55-80,4173-4417) fuzzy-if(swgl,54-54,31270-31270) HTTP(..) == bipbop_300_215kbps.mp4.lastframe.html bipbop_300_215kbps.mp4.lastframe-ref.html
skip-if(Android) fuzzy-if(OSX,0-25,0-175921) fuzzy-if(appleSilicon,49-49,176063-176063) fuzzy-if(winWidget,0-71,0-179198) fuzzy-if((/^Windows\x20NT\x2010\.0/.test(http.oscpu))&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI)),0-255,0-179500) HTTP(..) == gizmo.mp4.seek.html gizmo.mp4.55thframe-ref.html
skip-if(Android) skip-if(MinGW) skip-if((/^Windows\x20NT\x2010\.0/.test(http.oscpu))&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI))) fuzzy(0-10,0-778236) == image-10bits-rendering-video.html image-10bits-rendering-ref.html
skip-if(Android) skip-if(MinGW) skip-if((/^Windows\x20NT\x2010\.0/.test(http.oscpu))&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI))) fuzzy(0-10,0-778536) == image-10bits-rendering-90-video.html image-10bits-rendering-90-ref.html
skip-if(Android) fuzzy(0-26,0-567562) fuzzy-if(appleSilicon,46-46,575885-575885) == image-10bits-rendering-720-video.html image-10bits-rendering-720-ref.html
skip-if(Android) fuzzy(0-27,0-573106) fuzzy-if(appleSilicon,46-46,575885-575885) == image-10bits-rendering-720-video.html image-10bits-rendering-720-ref.html
skip-if(Android) fuzzy(0-31,0-573249) == image-10bits-rendering-720-90-video.html image-10bits-rendering-720-90-ref.html
skip-if(Android) skip-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fuzzy(0-84,0-771156) fails-if(useDrawSnapshot) == uneven_frame_duration_video.html uneven_frame_duration_video-ref.html # Skip on Windows 7 as the resolution of the video is too high for test machines and will fail in the decoder.

Просмотреть файл

@ -0,0 +1,20 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<head>
<meta charset='utf-8'>
</head>
<body>
<img id="e_img" style="position:absolute; left:0; top:0; max-width:100%">
<script>
(async () => {
const params = new URLSearchParams(window.location.search);
const src = params.get('src');
src.defined;
e_img.src = src;
await e_img.decode()
document.documentElement.removeAttribute('class');
})();
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<head>
<meta charset='utf-8'>
</head>
<body>
<video id="e_video" style="position:absolute; left:0; top:0; max-width:100%">
<script>
const TIMEOUT_MS = 2000;
// -
function sleepPromise(ms) {
return new Promise(go => {
setTimeout(go, ms);
});
}
(async () => {
await sleepPromise(TIMEOUT_MS);
if (!document.documentElement.hasAttribute('class')) return;
const div = document.body.appendChild(document.createElement('div'));
div.textContent = `Timed out after ${TIMEOUT_MS}ms`;
console.log(div.textContent);
document.documentElement.removeAttribute('class');
})();
// -
// Test
(async () => {
const params = new URLSearchParams(window.location.search);
const src = params.get('src');
src.defined;
if (src == 'none') {
console.log('Show blank.');
document.documentElement.removeAttribute('class');
return;
}
if (src == 'timeout') {
console.log('Deliberate timeout.');
return;
}
e_video.src = src;
e_video.muted = true;
const p = e_video.play();
p.defined;
try {
await p;
console.log('e_video.play() accepted');
} catch (e) {
const div = document.body.appendChild(document.createElement('div'));
div.textContent = `Error: ${JSON.stringify(e)}`;
console.log(div.textContent);
}
document.documentElement.removeAttribute('class');
})();
</script>
</body>
</html>

Просмотреть файл

@ -111,6 +111,10 @@ class MacIOSurface final
bool IsFullRange() const {
return GetPixelFormat() == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
}
mozilla::gfx::ColorRange GetColorRange() const {
if (IsFullRange()) return mozilla::gfx::ColorRange::FULL;
return mozilla::gfx::ColorRange::LIMITED;
}
// We would like to forward declare NSOpenGLContext, but it is an @interface
// and this file is also used from c++, so we use a void *.

Просмотреть файл

@ -225,6 +225,87 @@ enum class ColorRange : uint8_t {
_Last = FULL,
};
// Really "YcbcrColorSpace"
enum class YUVRangedColorSpace : uint8_t {
BT601_Narrow = 0,
BT601_Full,
BT709_Narrow,
BT709_Full,
BT2020_Narrow,
BT2020_Full,
GbrIdentity,
_First = BT601_Narrow,
_Last = GbrIdentity,
Default = BT709_Narrow,
};
struct FromYUVRangedColorSpaceT final {
const YUVColorSpace space;
const ColorRange range;
};
inline FromYUVRangedColorSpaceT FromYUVRangedColorSpace(
const YUVRangedColorSpace s) {
switch (s) {
case YUVRangedColorSpace::BT601_Narrow:
return {YUVColorSpace::BT601, ColorRange::LIMITED};
case YUVRangedColorSpace::BT601_Full:
return {YUVColorSpace::BT601, ColorRange::FULL};
case YUVRangedColorSpace::BT709_Narrow:
return {YUVColorSpace::BT709, ColorRange::LIMITED};
case YUVRangedColorSpace::BT709_Full:
return {YUVColorSpace::BT709, ColorRange::FULL};
case YUVRangedColorSpace::BT2020_Narrow:
return {YUVColorSpace::BT2020, ColorRange::LIMITED};
case YUVRangedColorSpace::BT2020_Full:
return {YUVColorSpace::BT2020, ColorRange::FULL};
case YUVRangedColorSpace::GbrIdentity:
return {YUVColorSpace::Identity, ColorRange::FULL};
}
MOZ_CRASH("bad YUVRangedColorSpace");
}
// Todo: This should go in the CPP.
inline YUVRangedColorSpace ToYUVRangedColorSpace(const YUVColorSpace space,
const ColorRange range) {
bool narrow;
switch (range) {
case ColorRange::FULL:
narrow = false;
break;
case ColorRange::LIMITED:
narrow = true;
break;
}
switch (space) {
case YUVColorSpace::Identity:
MOZ_ASSERT(range == ColorRange::FULL);
return YUVRangedColorSpace::GbrIdentity;
case YUVColorSpace::BT601:
return narrow ? YUVRangedColorSpace::BT601_Narrow
: YUVRangedColorSpace::BT601_Full;
case YUVColorSpace::BT709:
return narrow ? YUVRangedColorSpace::BT709_Narrow
: YUVRangedColorSpace::BT709_Full;
case YUVColorSpace::BT2020:
return narrow ? YUVRangedColorSpace::BT2020_Narrow
: YUVRangedColorSpace::BT2020_Full;
}
}
template <typename DescriptorT>
inline YUVRangedColorSpace GetYUVRangedColorSpace(const DescriptorT& d) {
return ToYUVRangedColorSpace(d.yUVColorSpace(), d.colorRange());
}
static inline SurfaceFormat SurfaceFormatForColorDepth(ColorDepth aColorDepth) {
SurfaceFormat format = SurfaceFormat::A8;
switch (aColorDepth) {

Просмотреть файл

@ -738,6 +738,13 @@ struct ParamTraits<mozilla::gfx::YUVColorSpace>
mozilla::gfx::YUVColorSpace, mozilla::gfx::YUVColorSpace::_First,
mozilla::gfx::YUVColorSpace::_Last> {};
template <>
struct ParamTraits<mozilla::gfx::YUVRangedColorSpace>
: public ContiguousEnumSerializerInclusive<
mozilla::gfx::YUVRangedColorSpace,
mozilla::gfx::YUVRangedColorSpace::_First,
mozilla::gfx::YUVRangedColorSpace::_Last> {};
template <>
struct ParamTraits<mozilla::StereoMode>
: public ContiguousEnumSerializer<mozilla::StereoMode,

Просмотреть файл

@ -757,6 +757,7 @@ bool DCSurfaceVideo::CreateVideoSwapChain(RenderTextureHost* aTexture) {
return true;
}
// TODO: Replace with YUVRangedColorSpace
static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
const gfx::YUVColorSpace aYUVColorSpace,
const gfx::ColorRange aColorRange) {
@ -785,14 +786,20 @@ static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
return Nothing();
}
static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
const gfx::YUVRangedColorSpace aYUVColorSpace) {
const auto info = FromYUVRangedColorSpace(aYUVColorSpace);
return GetSourceDXGIColorSpace(info.space, info.range);
}
bool DCSurfaceVideo::CallVideoProcessorBlt(RenderTextureHost* aTexture) {
HRESULT hr;
const auto videoDevice = mDCLayerTree->GetVideoDevice();
const auto videoContext = mDCLayerTree->GetVideoContext();
const auto texture = aTexture->AsRenderDXGITextureHost();
Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace = GetSourceDXGIColorSpace(
texture->GetYUVColorSpace(), texture->GetColorRange());
Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace =
GetSourceDXGIColorSpace(texture->GetYUVColorSpace());
if (sourceColorSpace.isNothing()) {
gfxCriticalNote << "Unsupported color space";
return false;

Просмотреть файл

@ -46,9 +46,6 @@ class RenderAndroidSurfaceTextureHost final : public RenderTextureHostSWGL {
bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) override;
void UnmapPlanes() override;
gfx::YUVColorSpace GetYUVColorSpace() const override {
return gfx::YUVColorSpace::Default;
}
RenderAndroidSurfaceTextureHost* AsRenderAndroidSurfaceTextureHost()
override {

Просмотреть файл

@ -183,12 +183,12 @@ gfx::ColorDepth RenderBufferTextureHost::GetColorDepth() const {
}
}
gfx::YUVColorSpace RenderBufferTextureHost::GetYUVColorSpace() const {
gfx::YUVRangedColorSpace RenderBufferTextureHost::GetYUVColorSpace() const {
switch (mDescriptor.type()) {
case layers::BufferDescriptor::TYCbCrDescriptor:
return mDescriptor.get_YCbCrDescriptor().yUVColorSpace();
return gfx::GetYUVRangedColorSpace(mDescriptor.get_YCbCrDescriptor());
default:
return gfx::YUVColorSpace::Default;
return gfx::YUVRangedColorSpace::Default;
}
}

Просмотреть файл

@ -42,7 +42,7 @@ class RenderBufferTextureHost final : public RenderTextureHostSWGL {
gfx::ColorDepth GetColorDepth() const override;
gfx::YUVColorSpace GetYUVColorSpace() const override;
gfx::YUVRangedColorSpace GetYUVColorSpace() const override;
bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) override;

Просмотреть файл

@ -97,9 +97,10 @@ void RenderCompositorD3D11SWGL::HandleExternalImage(
if (host->GetFormat() == SurfaceFormat::NV12 ||
host->GetFormat() == SurfaceFormat::P010 ||
host->GetFormat() == SurfaceFormat::P016) {
const auto yuv = FromYUVRangedColorSpace(host->GetYUVColorSpace());
texturedEffect =
new EffectNV12(layer, host->GetYUVColorSpace(), host->GetColorRange(),
host->GetColorDepth(), aFrameSurface.mFilter);
new EffectNV12(layer, yuv.space, yuv.range, host->GetColorDepth(),
aFrameSurface.mFilter);
} else {
MOZ_ASSERT(host->GetFormat() == SurfaceFormat::B8G8R8X8 ||
host->GetFormat() == SurfaceFormat::B8G8R8A8);
@ -122,9 +123,10 @@ void RenderCompositorD3D11SWGL::HandleExternalImage(
GetDevice(), SurfaceFormat::A8, host->GetD3D11Texture2D(2));
u->SetNextSibling(v);
const auto yuv = FromYUVRangedColorSpace(host->GetYUVColorSpace());
texturedEffect =
new EffectYCbCr(layer, host->GetYUVColorSpace(), host->GetColorRange(),
host->GetColorDepth(), aFrameSurface.mFilter);
new EffectYCbCr(layer, yuv.space, yuv.range, host->GetColorDepth(),
aFrameSurface.mFilter);
size = host->GetSize(0);
host->LockInternal();
}

Просмотреть файл

@ -57,8 +57,8 @@ class RenderDXGITextureHost final : public RenderTextureHostSWGL {
bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) override;
void UnmapPlanes() override;
gfx::YUVColorSpace GetYUVColorSpace() const override {
return mYUVColorSpace;
gfx::YUVRangedColorSpace GetYUVColorSpace() const override {
return ToYUVRangedColorSpace(mYUVColorSpace, GetColorRange());
}
bool EnsureD3D11Texture2D(ID3D11Device* aDevice);
@ -146,8 +146,8 @@ class RenderDXGIYCbCrTextureHost final : public RenderTextureHostSWGL {
bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) override;
void UnmapPlanes() override;
gfx::YUVColorSpace GetYUVColorSpace() const override {
return mYUVColorSpace;
gfx::YUVRangedColorSpace GetYUVColorSpace() const override {
return ToYUVRangedColorSpace(mYUVColorSpace, GetColorRange());
}
bool EnsureD3D11Texture2D(ID3D11Device* aDevice);

Просмотреть файл

@ -213,12 +213,12 @@ gfx::ColorDepth RenderExternalTextureHost::GetColorDepth() const {
}
}
gfx::YUVColorSpace RenderExternalTextureHost::GetYUVColorSpace() const {
gfx::YUVRangedColorSpace RenderExternalTextureHost::GetYUVColorSpace() const {
switch (mDescriptor.type()) {
case layers::BufferDescriptor::TYCbCrDescriptor:
return mDescriptor.get_YCbCrDescriptor().yUVColorSpace();
return gfx::GetYUVRangedColorSpace(mDescriptor.get_YCbCrDescriptor());
default:
return gfx::YUVColorSpace::Default;
return gfx::YUVRangedColorSpace::Default;
}
}

Просмотреть файл

@ -39,7 +39,7 @@ class RenderExternalTextureHost final : public RenderTextureHostSWGL {
gfx::ColorDepth GetColorDepth() const override;
gfx::YUVColorSpace GetYUVColorSpace() const override;
gfx::YUVRangedColorSpace GetYUVColorSpace() const override;
bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) override;

Просмотреть файл

@ -145,8 +145,10 @@ gfx::ColorDepth RenderMacIOSurfaceTextureHost::GetColorDepth() const {
return gfx::ColorDepth::COLOR_8;
}
gfx::YUVColorSpace RenderMacIOSurfaceTextureHost::GetYUVColorSpace() const {
return mSurface->GetYUVColorSpace();
gfx::YUVRangedColorSpace RenderMacIOSurfaceTextureHost::GetYUVColorSpace()
const {
return ToYUVRangedColorSpace(mSurface->GetYUVColorSpace(),
mSurface->GetColorRange());
}
bool RenderMacIOSurfaceTextureHost::MapPlane(RenderCompositor* aCompositor,

Просмотреть файл

@ -42,7 +42,7 @@ class RenderMacIOSurfaceTextureHost final : public RenderTextureHostSWGL {
size_t GetPlaneCount() const override;
gfx::SurfaceFormat GetFormat() const override;
gfx::ColorDepth GetColorDepth() const override;
gfx::YUVColorSpace GetYUVColorSpace() const override;
gfx::YUVRangedColorSpace GetYUVColorSpace() const override;
bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) override;
void UnmapPlanes() override;

Просмотреть файл

@ -167,7 +167,7 @@ bool RenderTextureHostSWGL::LockSWGLCompositeSurface(
case gfx::SurfaceFormat::YUV422: {
aInfo->yuv_planes = mPlanes.size();
auto colorSpace = GetYUVColorSpace();
aInfo->color_space = ToWrYuvColorSpace(colorSpace);
aInfo->color_space = ToWrYuvRangedColorSpace(colorSpace);
auto colorDepth = GetColorDepth();
aInfo->color_depth = ToWrColorDepth(colorDepth);
break;

Просмотреть файл

@ -33,8 +33,8 @@ class RenderTextureHostSWGL : public RenderTextureHost {
return gfx::ColorDepth::COLOR_8;
}
virtual gfx::YUVColorSpace GetYUVColorSpace() const {
return gfx::YUVColorSpace::Default;
virtual gfx::YUVRangedColorSpace GetYUVColorSpace() const {
return gfx::YUVRangedColorSpace::Default;
}
struct PlaneInfo {

Просмотреть файл

@ -111,11 +111,11 @@ gfx::ColorDepth RenderTextureHostWrapper::GetColorDepth() const {
return gfx::ColorDepth::COLOR_8;
}
gfx::YUVColorSpace RenderTextureHostWrapper::GetYUVColorSpace() const {
gfx::YUVRangedColorSpace RenderTextureHostWrapper::GetYUVColorSpace() const {
if (RenderTextureHostSWGL* swglHost = EnsureRenderTextureHostSWGL()) {
return swglHost->GetYUVColorSpace();
}
return gfx::YUVColorSpace::Default;
return gfx::YUVRangedColorSpace::Default;
}
bool RenderTextureHostWrapper::MapPlane(RenderCompositor* aCompositor,

Просмотреть файл

@ -41,7 +41,7 @@ class RenderTextureHostWrapper final : public RenderTextureHostSWGL {
size_t GetPlaneCount() const override;
gfx::SurfaceFormat GetFormat() const override;
gfx::ColorDepth GetColorDepth() const override;
gfx::YUVColorSpace GetYUVColorSpace() const override;
gfx::YUVRangedColorSpace GetYUVColorSpace() const override;
bool MapPlane(RenderCompositor* aCompositor, uint8_t aChannelIndex,
PlaneInfo& aPlaneInfo) override;
void UnmapPlanes() override;

Просмотреть файл

@ -787,6 +787,31 @@ static inline wr::WrYuvColorSpace ToWrYuvColorSpace(
return wr::WrYuvColorSpace::Rec601;
}
// TODO: Use YUVRangedColorSpace instead of assuming ColorRange::LIMITED.
static inline wr::YuvRangedColorSpace ToWrYuvRangedColorSpace(
gfx::YUVRangedColorSpace aFrom) {
switch (aFrom) {
case gfx::YUVRangedColorSpace::BT601_Narrow:
return wr::YuvRangedColorSpace::Rec601Narrow;
case gfx::YUVRangedColorSpace::BT601_Full:
return wr::YuvRangedColorSpace::Rec601Full;
case gfx::YUVRangedColorSpace::BT709_Narrow:
return wr::YuvRangedColorSpace::Rec709Narrow;
case gfx::YUVRangedColorSpace::BT709_Full:
return wr::YuvRangedColorSpace::Rec709Full;
case gfx::YUVRangedColorSpace::BT2020_Narrow:
return wr::YuvRangedColorSpace::Rec2020Narrow;
case gfx::YUVRangedColorSpace::BT2020_Full:
return wr::YuvRangedColorSpace::Rec2020Full;
case gfx::YUVRangedColorSpace::GbrIdentity:
break;
default:
MOZ_ASSERT_UNREACHABLE("Tried to convert invalid YUVColorSpace.");
break;
}
return wr::YuvRangedColorSpace::GbrIdentity;
}
static inline wr::WrColorDepth ToWrColorDepth(gfx::ColorDepth aColorDepth) {
switch (aColorDepth) {
case gfx::ColorDepth::COLOR_8:

Просмотреть файл

@ -2260,7 +2260,10 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr {
match (lhs.ty.kind, rhs.ty.kind) {
(TypeKind::Mat2, TypeKind::Vec2) |
(TypeKind::Mat3, TypeKind::Vec3) |
(TypeKind::Mat3, TypeKind::Mat3) |
(TypeKind::Mat3, TypeKind::Mat43) |
(TypeKind::Mat4, TypeKind::Vec4) => rhs.ty.clone(),
(TypeKind::Mat43, TypeKind::Vec4) => Type::new(TypeKind::Vec3),
(TypeKind::Mat2, TypeKind::Float) |
(TypeKind::Mat3, TypeKind::Float) |
(TypeKind::Mat4, TypeKind::Float) => lhs.ty.clone(),
@ -2551,7 +2554,7 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr {
.fields
.iter()
.find(|x| &x.name == i)
.expect("missing field");
.expect(&format!("missing field `{}` in `{}`", i, sym.name));
Expr {
kind: ExprKind::Dot(e, i.clone()),
ty: field.ty.clone(),
@ -3043,6 +3046,13 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio
Type::new(Vec4),
vec![Type::new(Vec4)],
);
declare_function(
state,
"vec4",
Some("make_vec4"),
Type::new(Vec4),
vec![Type::new(IVec4)],
);
declare_function(
state,
@ -3228,6 +3238,35 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio
Type::new(Float),
],
);
declare_function(
state,
"mat3x4",
Some("make_mat3x4"),
Type::new(Mat34),
vec![
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
Type::new(Float),
],
);
declare_function(
state,
"transpose",
None,
Type::new(Mat43),
vec![Type::new(Mat34)],
);
declare_function(
state,
"mat4",
@ -4127,7 +4166,7 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio
None,
Type::new(Void),
vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(Int), Type::new(Int)],
Type::new(Vec3), Type::new(Mat3), Type::new(Int)],
);
declare_function(
state,
@ -4136,7 +4175,7 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio
Type::new(Void),
vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(Int), Type::new(Int)],
Type::new(Vec3), Type::new(Mat3), Type::new(Int)],
);
declare_function(
state,
@ -4146,7 +4185,7 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio
vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(Int), Type::new(Int)],
Type::new(Vec3), Type::new(Mat3), Type::new(Int)],
);
declare_function(
state,
@ -4154,7 +4193,8 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio
None,
Type::new(Void),
vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(Int), Type::new(Int), Type::new(Float)],
Type::new(Vec3), Type::new(Mat3), Type::new(Int),
Type::new(Float)],
);
declare_function(
state,
@ -4163,7 +4203,8 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio
Type::new(Void),
vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(Int), Type::new(Int), Type::new(Float)],
Type::new(Vec3), Type::new(Mat3), Type::new(Int),
Type::new(Float)],
);
declare_function(
state,
@ -4173,7 +4214,8 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio
vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(*s), Type::new(Vec2), Type::new(Vec4),
Type::new(Int), Type::new(Int), Type::new(Float)],
Type::new(Vec3), Type::new(Mat3), Type::new(Int),
Type::new(Float)],
);
}

Просмотреть файл

@ -501,94 +501,123 @@ struct YUVMatrix {
// These constants are loaded off the "this" pointer via relative addressing
// modes and should be about as quick to load as directly addressed SIMD
// constant memory.
V8<int16_t> rbCoeffs;
V8<int16_t> gCoeffs;
V8<uint16_t> yScale;
V8<int16_t> yBias;
V8<int16_t> uvBias;
V8<int16_t> brMask;
// Set the coefficients to cancel out and pass through YUV as GBR. All biases
// are set to zero and the BR-mask is set to remove the contribution of Y to
// the BR channels. Scales are set such that the shift by 6 in convert is
// balanced.
YUVMatrix()
: rbCoeffs(1 << 6),
gCoeffs(0),
yScale(1 << (6 + 1)),
yBias(0),
uvBias(0),
brMask(0) {}
V8<int16_t> br_uvCoeffs; // biased by 6 bits [b_from_u, r_from_v, repeats]
V8<int16_t> gg_uvCoeffs; // biased by 6 bits [g_from_u, g_from_v, repeats]
V8<uint16_t> yCoeffs; // biased by 7 bits
V8<int16_t> yBias; // 0 or 16
V8<int16_t> uvBias; // 128
V8<int16_t> br_yMask;
// E.g. rec709-narrow:
// [ 1.16, 0, 1.79, -0.97 ]
// [ 1.16, -0.21, -0.53, 0.30 ]
// [ 1.16, 2.11, 0, -1.13 ]
// =
// [ yScale, 0, r_from_v ] ([Y ] )
// [ yScale, g_from_u, g_from_v ] x ([cb] - ycbcr_bias )
// [ yScale, b_from_u, 0 ] ([cr] )
static YUVMatrix From(const vec3_scalar& ycbcr_bias,
const mat3_scalar& rgb_from_debiased_ycbcr) {
assert(ycbcr_bias.z == ycbcr_bias.y);
const auto rgb_from_y = rgb_from_debiased_ycbcr[0].y;
assert(rgb_from_debiased_ycbcr[0].x == rgb_from_debiased_ycbcr[0].z);
int16_t br_from_y_mask = -1;
if (rgb_from_debiased_ycbcr[0].x == 0.0) {
// gbr-identity matrix?
assert(rgb_from_debiased_ycbcr[0].x == 0);
assert(rgb_from_debiased_ycbcr[0].y == 1);
assert(rgb_from_debiased_ycbcr[0].z == 0);
assert(rgb_from_debiased_ycbcr[1].x == 0);
assert(rgb_from_debiased_ycbcr[1].y == 0);
assert(rgb_from_debiased_ycbcr[1].z == 1);
assert(rgb_from_debiased_ycbcr[2].x == 1);
assert(rgb_from_debiased_ycbcr[2].y == 0);
assert(rgb_from_debiased_ycbcr[2].z == 0);
assert(ycbcr_bias.x == 0);
assert(ycbcr_bias.y == 0);
assert(ycbcr_bias.z == 0);
br_from_y_mask = 0;
} else {
assert(rgb_from_debiased_ycbcr[0].x == rgb_from_y);
}
assert(rgb_from_debiased_ycbcr[1].x == 0.0);
const auto g_from_u = rgb_from_debiased_ycbcr[1].y;
const auto b_from_u = rgb_from_debiased_ycbcr[1].z;
const auto r_from_v = rgb_from_debiased_ycbcr[2].x;
const auto g_from_v = rgb_from_debiased_ycbcr[2].y;
assert(rgb_from_debiased_ycbcr[2].z == 0.0);
return YUVMatrix({ycbcr_bias.x, ycbcr_bias.y}, rgb_from_y, br_from_y_mask,
r_from_v, g_from_u, g_from_v, b_from_u);
}
// Convert matrix coefficients to fixed-point representation.
YUVMatrix(double rv, double gu, double gv, double bu)
: rbCoeffs(
zip(I16(int16_t(bu * 64.0 + 0.5)), I16(int16_t(rv * 64.0 + 0.5)))),
gCoeffs(zip(I16(-int16_t(gu * -64.0 + 0.5)),
I16(-int16_t(gv * -64.0 + 0.5)))),
yScale(2 * 74 + 1),
yBias(int16_t(-16 * 74.5) + (1 << 5)),
uvBias(-128),
brMask(-1) {}
YUVMatrix(vec2_scalar yuv_bias, double yCoeff, int16_t br_yMask_, double rv,
double gu, double gv, double bu)
: br_uvCoeffs(zip(I16(int16_t(bu * (1 << 6) + 0.5)),
I16(int16_t(rv * (1 << 6) + 0.5)))),
gg_uvCoeffs(zip(I16(-int16_t(-gu * (1 << 6) +
0.5)), // These are negative coeffs, so
// round them away from zero
I16(-int16_t(-gv * (1 << 6) + 0.5)))),
yCoeffs(uint16_t(yCoeff * (1 << (6 + 1)) + 0.5)),
// (E.g. 1.16 => 74.5<<1)
yBias(int16_t(yCoeff * yuv_bias.x * 255 * (1<<6) + 0.5)),
uvBias(int16_t(yuv_bias.y * 255 + 0.5)),
br_yMask(br_yMask_) {
assert(yuv_bias.x >= 0);
assert(yuv_bias.y >= 0);
assert(yCoeff > 0);
assert(br_yMask_ == 0 || br_yMask_ == -1);
assert(bu > 0);
assert(rv > 0);
assert(gu <= 0);
assert(gv <= 0);
}
ALWAYS_INLINE PackedRGBA8 convert(V8<int16_t> yy, V8<int16_t> uv) const {
// Bias Y values by -16 and multiply by 74.5. Add 2^5 offset to round to
// nearest 2^6. Note that we have to use an unsigned multiply with a 2x
// scale to represent a fractional scale and to avoid shifting with the sign
// bit.
yy = bit_cast<V8<int16_t>>((bit_cast<V8<uint16_t>>(yy) * yScale) >> 1) +
yBias;
// We gave ourselves an extra bit (7 instead of 6) of bias to give us some
// extra precision for the more-sensitive y scaling.
// Note that we have to use an unsigned multiply with a 2x scale to
// represent a fractional scale and to avoid shifting with the sign bit.
// Bias U/V values by -128.
uv += uvBias;
// Note: if you subtract the bias before multiplication, we see more
// underflows. This could be fixed by an unsigned subsat.
yy = bit_cast<V8<int16_t>>((bit_cast<V8<uint16_t>>(yy) * yCoeffs) >> 1);
yy -= yBias;
// Compute (R, B) = (74.5*Y + rv*V, 74.5*Y + bu*U)
auto br = rbCoeffs * uv;
br = addsat(yy & brMask, br);
// Compute [B] = [yCoeff*Y + bu*U + 0*V]
// [R] [yCoeff*Y + 0*U + rv*V]
uv -= uvBias;
auto br = br_uvCoeffs * uv;
br = addsat(yy & br_yMask, br);
br >>= 6;
// Compute G = 74.5*Y + -gu*U + -gv*V
auto gg = gCoeffs * uv;
gg = addsat(
yy,
addsat(gg, bit_cast<V8<int16_t>>(bit_cast<V4<uint32_t>>(gg) >> 16)));
// Compute G = yCoeff*Y + gu*U + gv*V
// First calc [gu*U, gv*V, ...]:
auto gg = gg_uvCoeffs * uv;
// Then cross the streams to get `gu*U + gv*V`:
gg = addsat(gg, bit_cast<V8<int16_t>>(bit_cast<V4<uint32_t>>(gg) >> 16));
// Add the other parts:
gg = addsat(yy, gg); // This is the part that needs the most headroom
// usually. In particular, ycbcr(255,255,255) hugely
// saturates.
gg >>= 6;
// Interleave B/R and G values. Force alpha to opaque.
// Interleave B/R and G values. Force alpha (high-gg half) to opaque.
return packYUV(gg, br);
}
};
enum YUVColorSpace { REC_601 = 0, REC_709, REC_2020, IDENTITY };
static const YUVMatrix yuvMatrix[IDENTITY + 1] = {
// clang-format off
// From Rec601:
// [R] [1.1643835616438356, 0.0, 1.5960267857142858 ] [Y - 16]
// [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708 ] x [U - 128]
// [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [V - 128]
{1.5960267857142858, -0.3917622900949137, -0.8129676472377708, 2.017232142857143},
// From Rec709:
// [R] [1.1643835616438356, 0.0, 1.7927410714285714] [Y - 16]
// [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444 ] x [U - 128]
// [B] [1.1643835616438356, 2.1124017857142854, 0.0 ] [V - 128]
{1.7927410714285714, -0.21324861427372963, -0.532909328559444, 2.1124017857142854},
// From Re2020:
// [R] [1.16438356164384, 0.0, 1.678674107142860 ] [Y - 16]
// [G] = [1.16438356164384, -0.187326104219343, -0.650424318505057 ] x [U - 128]
// [B] [1.16438356164384, 2.14177232142857, 0.0 ] [V - 128]
{1.678674107142860, -0.187326104219343, -0.650424318505057, 2.14177232142857},
// Identity
// [R] [V]
// [G] = [Y]
// [B] [U]
{},
// clang-format on
};
// Helper function for textureLinearRowR8 that samples horizontal taps and
// combines them based on Y fraction with next row.
template <typename S>
@ -963,7 +992,7 @@ static void linear_row_yuv(uint32_t* dest, int span, sampler2DRect samplerY,
}
static void linear_convert_yuv(Texture& ytex, Texture& utex, Texture& vtex,
YUVColorSpace colorSpace, int colorDepth,
const YUVMatrix& rgbFromYcbcr, int colorDepth,
const IntRect& srcReq, Texture& dsttex,
const IntRect& dstReq, bool invertY,
const IntRect& clipRect) {
@ -1011,13 +1040,136 @@ static void linear_convert_yuv(Texture& ytex, Texture& utex, Texture& vtex,
for (int rows = dstBounds.height(); rows > 0; rows--) {
linear_row_yuv((uint32_t*)dest, span, &sampler[0], srcUV, srcDUV.x,
&sampler[1], &sampler[2], chromaUV, chromaDUV.x, colorDepth,
yuvMatrix[colorSpace]);
rgbFromYcbcr);
dest += destStride;
srcUV.y += srcDUV.y;
chromaUV.y += chromaDUV.y;
}
}
// -
// This section must match gfx/2d/Types.h
enum class YUVRangedColorSpace : uint8_t {
BT601_Narrow = 0,
BT601_Full,
BT709_Narrow,
BT709_Full,
BT2020_Narrow,
BT2020_Full,
GbrIdentity,
};
// -
// This section must match yuv.glsl
vec4_scalar get_ycbcr_zeros_ones(const YUVRangedColorSpace color_space,
const GLuint color_depth) {
// For SWGL's 8bpc-only pipeline, our extra care here probably doesn't matter.
// However, technically e.g. 10-bit achromatic zero for cb and cr is
// (128 << 2) / ((1 << 10) - 1) = 512 / 1023, which != 128 / 255, and affects
// our matrix values subtly. Maybe not enough to matter? But it's the most
// correct thing to do.
// Unlike the glsl version, our texture samples are u8([0,255]) not
// u16([0,1023]) though.
switch (color_space) {
case YUVRangedColorSpace::BT601_Narrow:
case YUVRangedColorSpace::BT709_Narrow:
case YUVRangedColorSpace::BT2020_Narrow: {
auto extra_bit_count = color_depth - 8;
vec4_scalar zo = {
float(16 << extra_bit_count),
float(128 << extra_bit_count),
float(235 << extra_bit_count),
float(240 << extra_bit_count),
};
float all_bits = (1 << color_depth) - 1;
zo /= all_bits;
return zo;
}
case YUVRangedColorSpace::BT601_Full:
case YUVRangedColorSpace::BT709_Full:
case YUVRangedColorSpace::BT2020_Full: {
const auto narrow =
get_ycbcr_zeros_ones(YUVRangedColorSpace::BT601_Narrow, color_depth);
return {0.0, narrow.y, 1.0, 1.0};
}
case YUVRangedColorSpace::GbrIdentity:
break;
}
return {0.0, 0.0, 1.0, 1.0};
}
constexpr mat3_scalar RgbFromYuv_Rec601 = {
{1.00000, 1.00000, 1.00000},
{0.00000, -0.17207, 0.88600},
{0.70100, -0.35707, 0.00000},
};
constexpr mat3_scalar RgbFromYuv_Rec709 = {
{1.00000, 1.00000, 1.00000},
{0.00000, -0.09366, 0.92780},
{0.78740, -0.23406, 0.00000},
};
constexpr mat3_scalar RgbFromYuv_Rec2020 = {
{1.00000, 1.00000, 1.00000},
{0.00000, -0.08228, 0.94070},
{0.73730, -0.28568, 0.00000},
};
constexpr mat3_scalar RgbFromYuv_GbrIdentity = {
{0, 1, 0},
{0, 0, 1},
{1, 0, 0},
};
inline mat3_scalar get_rgb_from_yuv(const YUVRangedColorSpace color_space) {
switch (color_space) {
case YUVRangedColorSpace::BT601_Narrow:
case YUVRangedColorSpace::BT601_Full:
return RgbFromYuv_Rec601;
case YUVRangedColorSpace::BT709_Narrow:
case YUVRangedColorSpace::BT709_Full:
return RgbFromYuv_Rec709;
case YUVRangedColorSpace::BT2020_Narrow:
case YUVRangedColorSpace::BT2020_Full:
return RgbFromYuv_Rec2020;
case YUVRangedColorSpace::GbrIdentity:
break;
}
return RgbFromYuv_GbrIdentity;
}
struct YcbcrInfo final {
vec3_scalar ycbcr_bias;
mat3_scalar rgb_from_debiased_ycbcr;
};
inline YcbcrInfo get_ycbcr_info(const YUVRangedColorSpace color_space,
GLuint color_depth) {
// SWGL always does 8bpc math, so don't scale the matrix for 10bpc!
color_depth = 8;
const auto zeros_ones = get_ycbcr_zeros_ones(color_space, color_depth);
const auto zeros = vec2_scalar{zeros_ones.x, zeros_ones.y};
const auto ones = vec2_scalar{zeros_ones.z, zeros_ones.w};
const auto scale = 1.0f / (ones - zeros);
const auto rgb_from_yuv = get_rgb_from_yuv(color_space);
const mat3_scalar yuv_from_debiased_ycbcr = {
{scale.x, 0, 0},
{0, scale.y, 0},
{0, 0, scale.y},
};
YcbcrInfo ret;
ret.ycbcr_bias = {zeros.x, zeros.y, zeros.y};
ret.rgb_from_debiased_ycbcr = rgb_from_yuv * yuv_from_debiased_ycbcr;
return ret;
}
// -
extern "C" {
// Extension for compositing a YUV surface represented by separate YUV planes
@ -1025,7 +1177,7 @@ extern "C" {
// transform from YUV to BGRA after sampling.
void CompositeYUV(LockedTexture* lockedDst, LockedTexture* lockedY,
LockedTexture* lockedU, LockedTexture* lockedV,
YUVColorSpace colorSpace, GLuint colorDepth, GLint srcX,
YUVRangedColorSpace colorSpace, GLuint colorDepth, GLint srcX,
GLint srcY, GLsizei srcWidth, GLsizei srcHeight, GLint dstX,
GLint dstY, GLsizei dstWidth, GLsizei dstHeight,
GLboolean flip, GLint clipX, GLint clipY, GLsizei clipWidth,
@ -1033,10 +1185,14 @@ void CompositeYUV(LockedTexture* lockedDst, LockedTexture* lockedY,
if (!lockedDst || !lockedY || !lockedU || !lockedV) {
return;
}
if (colorSpace > IDENTITY) {
if (colorSpace > YUVRangedColorSpace::GbrIdentity) {
assert(false);
return;
}
const auto ycbcrInfo = get_ycbcr_info(colorSpace, colorDepth);
const auto rgbFromYcbcr =
YUVMatrix::From(ycbcrInfo.ycbcr_bias, ycbcrInfo.rgb_from_debiased_ycbcr);
Texture& ytex = *lockedY;
Texture& utex = *lockedU;
Texture& vtex = *lockedV;
@ -1062,7 +1218,7 @@ void CompositeYUV(LockedTexture* lockedDst, LockedTexture* lockedY,
// For now, always use a linear filter path that would be required for
// scaling. Further fast-paths for non-scaled video might be desirable in the
// future.
linear_convert_yuv(ytex, utex, vtex, colorSpace, colorDepth, srcReq, dsttex,
linear_convert_yuv(ytex, utex, vtex, rgbFromYcbcr, colorDepth, srcReq, dsttex,
dstReq, flip, clipRect);
}

Просмотреть файл

@ -343,6 +343,7 @@ bvec2 make_bvec2(const X& x, const Y& y) {
return bvec2(x, y);
}
struct vec3_scalar;
struct vec4_scalar;
struct vec2_scalar {
@ -371,6 +372,7 @@ struct vec2_scalar {
vec2_scalar sel(XYZW c1, XYZW c2) {
return vec2_scalar(select(c1), select(c2));
}
vec3_scalar sel(XYZW c1, XYZW c2, XYZW c3);
vec4_scalar sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4);
friend bool operator==(const vec2_scalar& l, const vec2_scalar& r) {
@ -1073,6 +1075,9 @@ struct ivec4_scalar {
friend ivec4_scalar operator&(int32_t a, ivec4_scalar b) {
return ivec4_scalar{a & b.x, a & b.y, a & b.z, a & b.w};
}
friend ivec4_scalar operator<<(ivec4_scalar a, int32_t b) {
return ivec4_scalar{a.x << b, a.y << b, a.z << b, a.w << b};
}
int32_t& operator[](int index) {
switch (index) {
@ -1524,6 +1529,9 @@ struct vec3 {
friend vec3 operator/(vec3 a, Float b) {
return vec3(a.x / b, a.y / b, a.z / b);
}
friend vec3 operator/(vec3 a, vec3 b) {
return vec3(a.x / b.x, a.y / b.y, a.z / b.z);
}
friend I32 operator==(const vec3& l, const vec3& r) {
return l.x == r.x && l.y == r.y && l.z == r.z;
@ -1768,6 +1776,9 @@ struct vec4_scalar {
}
};
vec3_scalar vec2_scalar::sel(XYZW c1, XYZW c2, XYZW c3) {
return {select(c1), select(c2), select(c3)};
}
vec4_scalar vec2_scalar::sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) {
return vec4_scalar{select(c1), select(c2), select(c3), select(c4)};
}
@ -2041,6 +2052,10 @@ vec4 make_vec4(const X& x, const Y& y, const Z& z, const W& w) {
return vec4(x, y, z, w);
}
vec4_scalar make_vec4(const ivec4_scalar& v) {
return vec4_scalar{float(v.x), float(v.y), float(v.z), float(v.w)};
}
ALWAYS_INLINE vec3::vec3(vec4 v) : x(v.x), y(v.y), z(v.z) {}
SI ivec4 roundfast(vec4 v, Float scale) {
@ -2370,6 +2385,17 @@ struct mat3_scalar {
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
return u;
}
friend auto operator*(mat3_scalar a, mat3_scalar b) {
mat3_scalar r;
for (int c = 0; c < 3; c++) {
const auto& v = b[c];
r[c].x = a[0].x * v.x + a[1].x * v.y + a[2].x * v.z;
r[c].y = a[0].y * v.x + a[1].y * v.y + a[2].y * v.z;
r[c].z = a[0].z * v.x + a[1].z * v.y + a[2].z * v.z;
}
return r;
}
};
struct mat3 {
@ -2442,6 +2468,87 @@ mat3 make_mat3(const X& x, const Y& y, const Z& z) {
return mat3(x, y, z);
}
struct mat3x4_scalar {
vec4_scalar data[3];
mat3x4_scalar() = default;
constexpr mat3x4_scalar(vec4_scalar a, vec4_scalar b, vec4_scalar c) {
data[0] = a;
data[1] = b;
data[2] = c;
}
auto& operator[](int index) { return data[index]; }
constexpr auto operator[](int index) const { return data[index]; }
friend auto operator*(mat3x4_scalar m, vec3_scalar v) {
vec4_scalar u;
u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z;
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
u.w = m[0].w * v.x + m[1].w * v.y + m[2].w * v.z;
return u;
}
friend auto operator*(mat3x4_scalar m, vec3 v) {
vec4 u;
u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z;
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
u.w = m[0].w * v.x + m[1].w * v.y + m[2].w * v.z;
return u;
}
};
constexpr mat3x4_scalar make_mat3x4(float m0, float m1, float m2, float m3,
float m4, float m5, float m6, float m7,
float m8, float m9, float m10, float m11) {
return mat3x4_scalar{
{m0, m1, m2, m3},
{m4, m5, m6, m7},
{m8, m9, m10, m11},
};
}
struct mat4x3_scalar {
vec3_scalar data[4];
mat4x3_scalar() = default;
constexpr mat4x3_scalar(vec3_scalar a, vec3_scalar b, vec3_scalar c,
vec3_scalar d) {
data[0] = a;
data[1] = b;
data[2] = c;
data[3] = d;
}
auto& operator[](int index) { return data[index]; }
constexpr auto operator[](int index) const { return data[index]; }
friend auto operator*(mat4x3_scalar m, vec4_scalar v) {
vec3_scalar u;
u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z + m[3].x * v.w;
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z + m[3].y * v.w;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z + m[3].z * v.w;
return u;
}
friend auto operator*(mat4x3_scalar m, vec4 v) {
vec3 u;
u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z + m[3].x * v.w;
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z + m[3].y * v.w;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z + m[3].z * v.w;
return u;
}
};
constexpr mat4x3_scalar transpose(const mat3x4_scalar m) {
return {{m[0].x, m[1].x, m[2].x},
{m[0].y, m[1].y, m[2].y},
{m[0].z, m[1].z, m[2].z},
{m[0].w, m[1].w, m[2].w}};
}
struct mat4_scalar {
vec4_scalar data[4];

Просмотреть файл

@ -981,27 +981,27 @@ static int blendGaussianBlur(S sampler, vec2 uv, const vec4_scalar& uv_rect,
swgl_commitGaussianBlur(R8, s, p, uv_rect, hori, radius, coeffs)
// Convert and pack planar YUV samples to RGB output using a color space
static ALWAYS_INLINE PackedRGBA8 convertYUV(int colorSpace, U16 y, U16 u,
U16 v) {
static ALWAYS_INLINE PackedRGBA8 convertYUV(const YUVMatrix& rgb_from_ycbcr,
U16 y, U16 u, U16 v) {
auto yy = V8<int16_t>(zip(y, y));
auto uv = V8<int16_t>(zip(u, v));
return yuvMatrix[colorSpace].convert(yy, uv);
return rgb_from_ycbcr.convert(yy, uv);
}
// Helper functions to sample from planar YUV textures before converting to RGB
template <typename S0>
static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0,
int colorSpace,
const YUVMatrix& rgb_from_ycbcr,
UNUSED int rescaleFactor) {
switch (sampler0->format) {
case TextureFormat::RGBA8: {
auto planar = textureLinearPlanarRGBA8(sampler0, uv0);
return convertYUV(colorSpace, highHalf(planar.rg), lowHalf(planar.rg),
return convertYUV(rgb_from_ycbcr, highHalf(planar.rg), lowHalf(planar.rg),
lowHalf(planar.ba));
}
case TextureFormat::YUV422: {
auto planar = textureLinearPlanarYUV422(sampler0, uv0);
return convertYUV(colorSpace, planar.y, planar.u, planar.v);
return convertYUV(rgb_from_ycbcr, planar.y, planar.u, planar.v);
}
default:
assert(false);
@ -1011,18 +1011,21 @@ static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0,
template <bool BLEND, typename S0, typename P, typename C = NoColor>
static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0,
const vec4_scalar& uv_rect0, int colorSpace,
const vec4_scalar& uv_rect0, const vec3_scalar& ycbcr_bias,
const mat3_scalar& rgb_from_debiased_ycbcr,
int rescaleFactor, C color = C()) {
if (!swgl_isTextureLinear(sampler0)) {
return 0;
}
LINEAR_QUANTIZE_UV(sampler0, uv0, uv_step0, uv_rect0, min_uv0, max_uv0);
const auto rgb_from_ycbcr =
YUVMatrix::From(ycbcr_bias, rgb_from_debiased_ycbcr);
auto c = packColor(buf, color);
auto* end = buf + span;
for (; buf < end; buf += swgl_StepSize, uv0 += uv_step0) {
commit_blend_span<BLEND>(
buf, applyColor(sampleYUV(sampler0, ivec2(clamp(uv0, min_uv0, max_uv0)),
colorSpace, rescaleFactor),
rgb_from_ycbcr, rescaleFactor),
c));
}
return span;
@ -1030,20 +1033,23 @@ static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0,
template <typename S0, typename S1>
static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0, S1 sampler1,
ivec2 uv1, int colorSpace,
ivec2 uv1,
const YUVMatrix& rgb_from_ycbcr,
UNUSED int rescaleFactor) {
switch (sampler1->format) {
case TextureFormat::RG8: {
assert(sampler0->format == TextureFormat::R8);
auto y = textureLinearUnpackedR8(sampler0, uv0);
auto planar = textureLinearPlanarRG8(sampler1, uv1);
return convertYUV(colorSpace, y, lowHalf(planar.rg), highHalf(planar.rg));
return convertYUV(rgb_from_ycbcr, y, lowHalf(planar.rg),
highHalf(planar.rg));
}
case TextureFormat::RGBA8: {
assert(sampler0->format == TextureFormat::R8);
auto y = textureLinearUnpackedR8(sampler0, uv0);
auto planar = textureLinearPlanarRGBA8(sampler1, uv1);
return convertYUV(colorSpace, y, lowHalf(planar.ba), highHalf(planar.rg));
return convertYUV(rgb_from_ycbcr, y, lowHalf(planar.ba),
highHalf(planar.rg));
}
default:
assert(false);
@ -1055,20 +1061,23 @@ template <bool BLEND, typename S0, typename S1, typename P,
typename C = NoColor>
static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0,
const vec4_scalar& uv_rect0, S1 sampler1, vec2 uv1,
const vec4_scalar& uv_rect1, int colorSpace,
const vec4_scalar& uv_rect1, const vec3_scalar& ycbcr_bias,
const mat3_scalar& rgb_from_debiased_ycbcr,
int rescaleFactor, C color = C()) {
if (!swgl_isTextureLinear(sampler0) || !swgl_isTextureLinear(sampler1)) {
return 0;
}
LINEAR_QUANTIZE_UV(sampler0, uv0, uv_step0, uv_rect0, min_uv0, max_uv0);
LINEAR_QUANTIZE_UV(sampler1, uv1, uv_step1, uv_rect1, min_uv1, max_uv1);
const auto rgb_from_ycbcr =
YUVMatrix::From(ycbcr_bias, rgb_from_debiased_ycbcr);
auto c = packColor(buf, color);
auto* end = buf + span;
for (; buf < end; buf += swgl_StepSize, uv0 += uv_step0, uv1 += uv_step1) {
commit_blend_span<BLEND>(
buf, applyColor(sampleYUV(sampler0, ivec2(clamp(uv0, min_uv0, max_uv0)),
sampler1, ivec2(clamp(uv1, min_uv1, max_uv1)),
colorSpace, rescaleFactor),
rgb_from_ycbcr, rescaleFactor),
c));
}
return span;
@ -1077,7 +1086,8 @@ static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0,
template <typename S0, typename S1, typename S2>
static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0, S1 sampler1,
ivec2 uv1, S2 sampler2, ivec2 uv2,
int colorSpace, int rescaleFactor) {
const YUVMatrix& rgb_from_ycbcr,
int rescaleFactor) {
assert(sampler0->format == sampler1->format &&
sampler0->format == sampler2->format);
switch (sampler0->format) {
@ -1085,7 +1095,7 @@ static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0, S1 sampler1,
auto y = textureLinearUnpackedR8(sampler0, uv0);
auto u = textureLinearUnpackedR8(sampler1, uv1);
auto v = textureLinearUnpackedR8(sampler2, uv2);
return convertYUV(colorSpace, y, u, v);
return convertYUV(rgb_from_ycbcr, y, u, v);
}
case TextureFormat::R16: {
// The rescaling factor represents how many bits to add to renormalize the
@ -1100,7 +1110,7 @@ static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0, S1 sampler1,
auto y = textureLinearUnpackedR16(sampler0, uv0) >> rescaleBits;
auto u = textureLinearUnpackedR16(sampler1, uv1) >> rescaleBits;
auto v = textureLinearUnpackedR16(sampler2, uv2) >> rescaleBits;
return convertYUV(colorSpace, U16(y), U16(u), U16(v));
return convertYUV(rgb_from_ycbcr, U16(y), U16(u), U16(v));
}
default:
assert(false);
@ -1118,15 +1128,18 @@ static void blendYUVFallback(P* buf, int span, S0 sampler0, vec2 uv0,
vec2_scalar uv_step1, vec2_scalar min_uv1,
vec2_scalar max_uv1, S2 sampler2, vec2 uv2,
vec2_scalar uv_step2, vec2_scalar min_uv2,
vec2_scalar max_uv2, int colorSpace,
vec2_scalar max_uv2, const vec3_scalar& ycbcr_bias,
const mat3_scalar& rgb_from_debiased_ycbcr,
int rescaleFactor, C color) {
const auto rgb_from_ycbcr =
YUVMatrix::From(ycbcr_bias, rgb_from_debiased_ycbcr);
for (auto* end = buf + span; buf < end; buf += swgl_StepSize, uv0 += uv_step0,
uv1 += uv_step1, uv2 += uv_step2) {
commit_blend_span<BLEND>(
buf, applyColor(sampleYUV(sampler0, ivec2(clamp(uv0, min_uv0, max_uv0)),
sampler1, ivec2(clamp(uv1, min_uv1, max_uv1)),
sampler2, ivec2(clamp(uv2, min_uv2, max_uv2)),
colorSpace, rescaleFactor),
rgb_from_ycbcr, rescaleFactor),
color));
}
}
@ -1136,7 +1149,8 @@ template <bool BLEND, typename S0, typename S1, typename S2, typename P,
static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0,
const vec4_scalar& uv_rect0, S1 sampler1, vec2 uv1,
const vec4_scalar& uv_rect1, S2 sampler2, vec2 uv2,
const vec4_scalar& uv_rect2, int colorSpace,
const vec4_scalar& uv_rect2, const vec3_scalar& ycbcr_bias,
const mat3_scalar& rgb_from_debiased_ycbcr,
int rescaleFactor, C color = C()) {
if (!swgl_isTextureLinear(sampler0) || !swgl_isTextureLinear(sampler1) ||
!swgl_isTextureLinear(sampler2)) {
@ -1148,8 +1162,8 @@ static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0,
auto c = packColor(buf, color);
blendYUVFallback<BLEND>(buf, span, sampler0, uv0, uv_step0, min_uv0, max_uv0,
sampler1, uv1, uv_step1, min_uv1, max_uv1, sampler2,
uv2, uv_step2, min_uv2, max_uv2, colorSpace,
rescaleFactor, c);
uv2, uv_step2, min_uv2, max_uv2, ycbcr_bias,
rgb_from_debiased_ycbcr, rescaleFactor, c);
return span;
}
@ -1166,7 +1180,8 @@ static int blendYUV(uint32_t* buf, int span, sampler2DRect sampler0, vec2 uv0,
const vec4_scalar& uv_rect0, sampler2DRect sampler1,
vec2 uv1, const vec4_scalar& uv_rect1,
sampler2DRect sampler2, vec2 uv2,
const vec4_scalar& uv_rect2, int colorSpace,
const vec4_scalar& uv_rect2, const vec3_scalar& ycbcr_bias,
const mat3_scalar& rgb_from_debiased_ycbcr,
int rescaleFactor, NoColor noColor = NoColor()) {
if (!swgl_isTextureLinear(sampler0) || !swgl_isTextureLinear(sampler1) ||
!swgl_isTextureLinear(sampler2)) {
@ -1192,10 +1207,11 @@ static int blendYUV(uint32_t* buf, int span, sampler2DRect sampler0, vec2 uv0,
(min_uv1.x - uv1.x.x) / uv_step1.x))),
(end - buf) / swgl_StepSize);
if (outside > 0) {
blendYUVFallback<BLEND>(
buf, outside * swgl_StepSize, sampler0, uv0, uv_step0, min_uv0,
max_uv0, sampler1, uv1, uv_step1, min_uv1, max_uv1, sampler2, uv2,
uv_step2, min_uv2, max_uv2, colorSpace, rescaleFactor, noColor);
blendYUVFallback<BLEND>(buf, outside * swgl_StepSize, sampler0, uv0,
uv_step0, min_uv0, max_uv0, sampler1, uv1,
uv_step1, min_uv1, max_uv1, sampler2, uv2,
uv_step2, min_uv2, max_uv2, ycbcr_bias,
rgb_from_debiased_ycbcr, rescaleFactor, noColor);
buf += outside * swgl_StepSize;
uv0.x += outside * uv_step0.x;
uv1.x += outside * uv_step1.x;
@ -1213,10 +1229,12 @@ static int blendYUV(uint32_t* buf, int span, sampler2DRect sampler0, vec2 uv0,
int colorDepth =
(sampler0->format == TextureFormat::R16 ? 16 : 8) - rescaleFactor;
// Finally, call the inner loop of CompositeYUV.
const auto rgb_from_ycbcr =
YUVMatrix::From(ycbcr_bias, rgb_from_debiased_ycbcr);
linear_row_yuv<BLEND>(
buf, inside * swgl_StepSize, sampler0, force_scalar(uv0),
uv_step0.x / swgl_StepSize, sampler1, sampler2, force_scalar(uv1),
uv_step1.x / swgl_StepSize, colorDepth, yuvMatrix[colorSpace]);
uv_step1.x / swgl_StepSize, colorDepth, rgb_from_ycbcr);
// Now that we're done, advance past the processed inside portion.
buf += inside * swgl_StepSize;
uv0.x += inside * uv_step0.x;
@ -1229,8 +1247,8 @@ static int blendYUV(uint32_t* buf, int span, sampler2DRect sampler0, vec2 uv0,
// left of the span.
blendYUVFallback<BLEND>(buf, end - buf, sampler0, uv0, uv_step0, min_uv0,
max_uv0, sampler1, uv1, uv_step1, min_uv1, max_uv1,
sampler2, uv2, uv_step2, min_uv2, max_uv2, colorSpace,
rescaleFactor, noColor);
sampler2, uv2, uv_step2, min_uv2, max_uv2, ycbcr_bias,
rgb_from_debiased_ycbcr, rescaleFactor, noColor);
return span;
}

Просмотреть файл

@ -296,7 +296,7 @@ extern "C" {
locked_y: *mut LockedTexture,
locked_u: *mut LockedTexture,
locked_v: *mut LockedTexture,
color_space: YUVColorSpace,
color_space: YuvRangedColorSpace,
color_depth: GLuint,
src_x: GLint,
src_y: GLint,
@ -2295,12 +2295,15 @@ pub struct LockedResource(*mut LockedTexture);
unsafe impl Send for LockedResource {}
unsafe impl Sync for LockedResource {}
#[repr(C)]
pub enum YUVColorSpace {
Rec601 = 0,
Rec709,
Rec2020,
Identity,
#[repr(u8)]
pub enum YuvRangedColorSpace {
Rec601Narrow = 0,
Rec601Full,
Rec709Narrow,
Rec709Full,
Rec2020Narrow,
Rec2020Full,
GbrIdentity,
}
impl LockedResource {
@ -2355,7 +2358,7 @@ impl LockedResource {
locked_y: &LockedResource,
locked_u: &LockedResource,
locked_v: &LockedResource,
color_space: YUVColorSpace,
color_space: YuvRangedColorSpace,
color_depth: GLuint,
src_x: GLint,
src_y: GLint,

Просмотреть файл

@ -15,26 +15,23 @@ flat varying vec4 vUvBounds_U;
varying vec2 vUv_V;
flat varying vec4 vUvBounds_V;
flat varying mat3 vYuvColorMatrix;
flat varying vec4 vYuvOffsetVector_Coefficient;
YUV_PRECISION flat varying vec3 vYcbcrBias;
YUV_PRECISION flat varying mat3 vRgbFromDebiasedYcbcr;
flat varying int vFormat;
#ifdef SWGL_DRAW_SPAN
flat varying int vYuvColorSpace;
flat varying int vRescaleFactor;
#endif
#ifdef WR_VERTEX_SHADER
struct YuvPrimitive {
float coefficient;
int color_space;
int yuv_format;
};
YuvPrimitive fetch_yuv_primitive(int address) {
vec4 data = fetch_from_gpu_cache_1(address);
return YuvPrimitive(data.x, int(data.y), int(data.z));
// From YuvImageData.write_prim_gpu_blocks:
int channel_bit_depth = int(data.x);
int color_space = int(data.y);
int yuv_format = int(data.z);
return YuvPrimitive(channel_bit_depth, color_space, yuv_format);
}
void brush_vs(
@ -52,19 +49,26 @@ void brush_vs(
vec2 f = (vi.local_pos - local_rect.p0) / rect_size(local_rect);
YuvPrimitive prim = fetch_yuv_primitive(prim_address);
vYuvOffsetVector_Coefficient.w = prim.coefficient;
vYuvColorMatrix = get_yuv_color_matrix(prim.color_space);
vYuvOffsetVector_Coefficient.xyz = get_yuv_offset_vector(prim.color_space);
vFormat = prim.yuv_format;
#ifdef SWGL_DRAW_SPAN
// swgl_commitTextureLinearYUV needs to know the color space specifier and
// also needs to know how many bits of scaling are required to normalize
// HDR textures.
vYuvColorSpace = prim.color_space;
vRescaleFactor = int(log2(prim.coefficient));
vRescaleFactor = 0;
if (prim.channel_bit_depth > 8) {
vRescaleFactor = 16 - prim.channel_bit_depth;
}
// Since SWGL rescales filtered YUV values to 8bpc before yuv->rgb
// conversion, don't embed a 10bpc channel multiplier into the yuv matrix.
prim.channel_bit_depth = 8;
#endif
YuvColorMatrixInfo mat_info = get_rgb_from_ycbcr_info(prim);
vYcbcrBias = mat_info.ycbcr_bias;
vRgbFromDebiasedYcbcr = mat_info.rgb_from_debiased_ycbrc;
vFormat = prim.yuv_format;
// The additional test for 99 works around a gen6 shader compiler bug: 1708937
if (vFormat == YUV_FORMAT_PLANAR || vFormat == 99) {
ImageSource res_y = fetch_image_source(prim_user_data.x);
@ -90,9 +94,8 @@ void brush_vs(
Fragment brush_fs() {
vec4 color = sample_yuv(
vFormat,
vYuvColorMatrix,
vYuvOffsetVector_Coefficient.xyz,
vYuvOffsetVector_Coefficient.w,
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vUv_Y,
vUv_U,
vUv_V,
@ -105,6 +108,9 @@ Fragment brush_fs() {
color *= antialias_brush();
#endif
//color.r = float(100+vFormat) / 255.0;
//color.g = vYcbcrBias.x;
//color.b = vYcbcrBias.y;
return Fragment(color);
}
@ -114,14 +120,20 @@ void swgl_drawSpanRGBA8() {
swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y,
sColor1, vUv_U, vUvBounds_U,
sColor2, vUv_V, vUvBounds_V,
vYuvColorSpace, vRescaleFactor);
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
} else if (vFormat == YUV_FORMAT_NV12) {
swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y,
sColor1, vUv_U, vUvBounds_U,
vYuvColorSpace, vRescaleFactor);
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
} else if (vFormat == YUV_FORMAT_INTERLEAVED) {
swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y,
vYuvColorSpace, vRescaleFactor);
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
}
}
#endif

Просмотреть файл

@ -16,12 +16,11 @@
#endif
#ifdef WR_FEATURE_YUV
flat varying mat3 vYuvColorMatrix;
flat varying vec3 vYuvOffsetVector;
flat varying float vYuvCoefficient;
YUV_PRECISION flat varying vec3 vYcbcrBias;
YUV_PRECISION flat varying mat3 vRgbFromDebiasedYcbcr;
flat varying int vYuvFormat;
#ifdef SWGL_DRAW_SPAN
flat varying int vYuvColorSpace;
flat varying int vRescaleFactor;
#endif
varying vec2 vUV_y;
@ -59,6 +58,16 @@ PER_INSTANCE attribute vec4 aUvRect2;
PER_INSTANCE attribute vec4 aUvRect0;
#endif
#ifdef WR_FEATURE_YUV
YuvPrimitive fetch_yuv_primitive() {
// From ExternalSurfaceDependency::Yuv:
int color_space = int(aParams.y);
int yuv_format = int(aParams.z);
int channel_bit_depth = int(aParams.w);
return YuvPrimitive(channel_bit_depth, color_space, yuv_format);
}
#endif
void main(void) {
// Get world position
vec2 world_pos = mix(aDeviceRect.xy, aDeviceRect.zw, aPosition.xy);
@ -70,23 +79,27 @@ void main(void) {
vec2 uv = (clipped_world_pos - aDeviceRect.xy) / (aDeviceRect.zw - aDeviceRect.xy);
#ifdef WR_FEATURE_YUV
int yuv_color_space = int(aParams.y);
int yuv_format = int(aParams.z);
float yuv_coefficient = aParams.w;
vYuvColorMatrix = get_yuv_color_matrix(yuv_color_space);
vYuvOffsetVector = get_yuv_offset_vector(yuv_color_space);
vYuvCoefficient = yuv_coefficient;
vYuvFormat = yuv_format;
YuvPrimitive prim = fetch_yuv_primitive();
#ifdef SWGL_DRAW_SPAN
// swgl_commitTextureLinearYUV needs to know the color space specifier and
// also needs to know how many bits of scaling are required to normalize
// HDR textures.
vYuvColorSpace = yuv_color_space;
vRescaleFactor = int(log2(yuv_coefficient));
vRescaleFactor = 0;
if (prim.channel_bit_depth > 8) {
vRescaleFactor = 16 - prim.channel_bit_depth;
}
// Since SWGL rescales filtered YUV values to 8bpc before yuv->rgb
// conversion, don't embed a 10bpc channel multiplier into the yuv matrix.
prim.channel_bit_depth = 8;
#endif
YuvColorMatrixInfo mat_info = get_rgb_from_ycbcr_info(prim);
vYcbcrBias = mat_info.ycbcr_bias;
vRgbFromDebiasedYcbcr = mat_info.rgb_from_debiased_ycbrc;
vYuvFormat = prim.yuv_format;
write_uv_rect(
aUvRect0.xy,
aUvRect0.zw,
@ -153,9 +166,8 @@ void main(void) {
#ifdef WR_FEATURE_YUV
vec4 color = sample_yuv(
vYuvFormat,
vYuvColorMatrix,
vYuvOffsetVector,
vYuvCoefficient,
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vUV_y,
vUV_u,
vUV_v,
@ -188,14 +200,20 @@ void swgl_drawSpanRGBA8() {
swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y,
sColor1, vUV_u, vUVBounds_u,
sColor2, vUV_v, vUVBounds_v,
vYuvColorSpace, vRescaleFactor);
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
} else if (vYuvFormat == YUV_FORMAT_NV12) {
swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y,
sColor1, vUV_u, vUVBounds_u,
vYuvColorSpace, vRescaleFactor);
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
} else if (vYuvFormat == YUV_FORMAT_INTERLEAVED) {
swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y,
vYuvColorSpace, vRescaleFactor);
vYcbcrBias,
vRgbFromDebiasedYcbcr,
vRescaleFactor);
}
#else
#ifdef WR_FEATURE_FAST_PATH

Просмотреть файл

@ -8,6 +8,9 @@
#define YUV_FORMAT_PLANAR 1
#define YUV_FORMAT_INTERLEAVED 2
//#define YUV_PRECISION mediump
#define YUV_PRECISION
#ifdef WR_VERTEX_SHADER
#ifdef WR_FEATURE_TEXTURE_RECT
@ -16,81 +19,140 @@
#define TEX_SIZE_YUV(sampler) vec2(TEX_SIZE(sampler).xy)
#endif
#define YUV_COLOR_SPACE_REC601 0
#define YUV_COLOR_SPACE_REC709 1
#define YUV_COLOR_SPACE_REC2020 2
#define YUV_COLOR_SPACE_IDENTITY 3
// `YuvRangedColorSpace`
#define YUV_COLOR_SPACE_REC601_NARROW 0
#define YUV_COLOR_SPACE_REC601_FULL 1
#define YUV_COLOR_SPACE_REC709_NARROW 2
#define YUV_COLOR_SPACE_REC709_FULL 3
#define YUV_COLOR_SPACE_REC2020_NARROW 4
#define YUV_COLOR_SPACE_REC2020_FULL 5
#define YUV_COLOR_SPACE_GBR_IDENTITY 6
// The constants added to the Y, U and V components are applied in the fragment shader.
// From Rec601:
// [R] [1.1643835616438356, 0.0, 1.5960267857142858 ] [Y - 16]
// [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708 ] x [U - 128]
// [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [V - 128]
//
// For the range [0,1] instead of [0,255].
//
// `rgbFromYuv` from https://jdashg.github.io/misc/colors/from-coeffs.html
// The matrix is stored in column-major.
const mat3 YuvColorMatrixRec601 = mat3(
1.16438, 1.16438, 1.16438,
0.0, -0.39176, 2.01723,
1.59603, -0.81297, 0.0
const mat3 RgbFromYuv_Rec601 = mat3(
1.00000, 1.00000, 1.00000,
0.00000,-0.17207, 0.88600,
0.70100,-0.35707, 0.00000
);
// From Rec709:
// [R] [1.1643835616438356, 0.0, 1.7927410714285714] [Y - 16]
// [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444 ] x [U - 128]
// [B] [1.1643835616438356, 2.1124017857142854, 0.0 ] [V - 128]
//
// For the range [0,1] instead of [0,255]:
//
// The matrix is stored in column-major.
const mat3 YuvColorMatrixRec709 = mat3(
1.16438, 1.16438, 1.16438,
0.0 , -0.21325, 2.11240,
1.79274, -0.53291, 0.0
const mat3 RgbFromYuv_Rec709 = mat3(
1.00000, 1.00000, 1.00000,
0.00000,-0.09366, 0.92780,
0.78740,-0.23406, 0.00000
);
// From Re2020:
// [R] [1.16438356164384, 0.0, 1.678674107142860 ] [Y - 16]
// [G] = [1.16438356164384, -0.187326104219343, -0.650424318505057 ] x [U - 128]
// [B] [1.16438356164384, 2.14177232142857, 0.0 ] [V - 128]
//
// For the range [0,1] instead of [0,255]:
//
// The matrix is stored in column-major.
const mat3 YuvColorMatrixRec2020 = mat3(
1.16438356164384 , 1.164383561643840, 1.16438356164384,
0.0 , -0.187326104219343, 2.14177232142857,
1.67867410714286 , -0.650424318505057, 0.0
const mat3 RgbFromYuv_Rec2020 = mat3(
1.00000, 1.00000, 1.00000,
0.00000,-0.08228, 0.94070,
0.73730,-0.28568, 0.00000
);
// The matrix is stored in column-major.
// Identity is stored as GBR
const mat3 IdentityColorMatrix = mat3(
const mat3 RgbFromYuv_GbrIdentity = mat3(
0.0 , 1.0, 0.0,
0.0 , 0.0, 1.0,
1.0 , 0.0, 0.0
);
mat3 get_yuv_color_matrix(int color_space) {
if (color_space == YUV_COLOR_SPACE_REC601) {
return YuvColorMatrixRec601;
} else if (color_space == YUV_COLOR_SPACE_REC709) {
return YuvColorMatrixRec709;
} else if (color_space == YUV_COLOR_SPACE_IDENTITY) {
return IdentityColorMatrix;
// -
struct YuvPrimitive {
int channel_bit_depth;
int color_space;
int yuv_format;
};
struct YuvColorSamplingInfo {
mat3 rgb_from_yuv;
vec4 packed_zero_one_vals;
};
struct YuvColorMatrixInfo {
vec3 ycbcr_bias;
mat3 rgb_from_debiased_ycbrc;
};
// -
vec4 yuv_channel_zero_one_identity(int bit_depth) {
int channel_depth = 8;
if (bit_depth > 8) {
// For >8bpc, we get the low bits, not the high bits:
// 10bpc(1.0): 0b0000_0011_1111_1111
channel_depth = 16;
}
float all_ones_normalized = float((1 << bit_depth) - 1) / float((1 << channel_depth) - 1);
return vec4(0.0, 0.0, all_ones_normalized, all_ones_normalized);
}
vec4 yuv_channel_zero_one_narrow_range(int bit_depth) {
// Note: 512/1023 != 128/255
ivec4 zero_one_ints = ivec4(16, 128, 235, 240) << (bit_depth - 8);
int channel_depth = 8;
if (bit_depth > 8) {
// For >8bpc, we get the low bits, not the high bits:
// 10bpc(1.0): 0b0000_0011_1111_1111
channel_depth = 16;
}
return vec4(zero_one_ints) / float((1 << channel_depth) - 1);
}
vec4 yuv_channel_zero_one_full_range(int bit_depth) {
vec4 narrow = yuv_channel_zero_one_narrow_range(bit_depth);
vec4 identity = yuv_channel_zero_one_identity(bit_depth);
return vec4(0.0, narrow.y, identity.z, identity.w);
}
YuvColorSamplingInfo get_yuv_color_info(YuvPrimitive prim) {
if (prim.color_space == YUV_COLOR_SPACE_REC601_NARROW) {
return YuvColorSamplingInfo(RgbFromYuv_Rec601,
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth));
} else if (prim.color_space == YUV_COLOR_SPACE_REC601_FULL) {
return YuvColorSamplingInfo(RgbFromYuv_Rec601,
yuv_channel_zero_one_full_range(prim.channel_bit_depth));
} else if (prim.color_space == YUV_COLOR_SPACE_REC709_NARROW) {
return YuvColorSamplingInfo(RgbFromYuv_Rec709,
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth));
} else if (prim.color_space == YUV_COLOR_SPACE_REC709_FULL) {
return YuvColorSamplingInfo(RgbFromYuv_Rec709,
yuv_channel_zero_one_full_range(prim.channel_bit_depth));
} else if (prim.color_space == YUV_COLOR_SPACE_REC2020_NARROW) {
return YuvColorSamplingInfo(RgbFromYuv_Rec2020,
yuv_channel_zero_one_narrow_range(prim.channel_bit_depth));
} else if (prim.color_space == YUV_COLOR_SPACE_REC2020_FULL) {
return YuvColorSamplingInfo(RgbFromYuv_Rec2020,
yuv_channel_zero_one_full_range(prim.channel_bit_depth));
} else {
return YuvColorMatrixRec2020;
// Identity
return YuvColorSamplingInfo(RgbFromYuv_GbrIdentity,
yuv_channel_zero_one_identity(prim.channel_bit_depth));
}
}
vec3 get_yuv_offset_vector(int color_space) {
if (color_space == YUV_COLOR_SPACE_IDENTITY) {
return vec3(0.0, 0.0, 0.0);
} else {
return vec3(0.06275, 0.50196, 0.50196);
}
YuvColorMatrixInfo get_rgb_from_ycbcr_info(YuvPrimitive prim) {
YuvColorSamplingInfo info = get_yuv_color_info(prim);
vec2 zero = info.packed_zero_one_vals.xy;
vec2 one = info.packed_zero_one_vals.zw;
// Such that yuv_value = (ycbcr_sample - zero) / (one - zero)
vec2 scale = 1.0 / (one - zero);
YuvColorMatrixInfo mat_info;
mat_info.ycbcr_bias = zero.xyy;
mat3 yuv_from_debiased_ycbcr = mat3(scale.x, 0.0, 0.0,
0.0, scale.y, 0.0,
0.0, 0.0, scale.y);
mat_info.rgb_from_debiased_ycbrc = info.rgb_from_yuv * yuv_from_debiased_ycbcr;
return mat_info;
}
void write_uv_rect(
@ -116,9 +178,8 @@ void write_uv_rect(
vec4 sample_yuv(
int format,
mat3 yuv_color_matrix,
vec3 yuv_offset_vector,
float coefficient,
YUV_PRECISION vec3 ycbcr_bias,
YUV_PRECISION mat3 rgb_from_debiased_ycbrc,
vec2 in_uv_y,
vec2 in_uv_u,
vec2 in_uv_v,
@ -126,7 +187,7 @@ vec4 sample_yuv(
vec4 uv_bounds_u,
vec4 uv_bounds_v
) {
vec3 yuv_value;
YUV_PRECISION vec3 ycbcr_sample;
switch (format) {
case YUV_FORMAT_PLANAR:
@ -135,9 +196,9 @@ vec4 sample_yuv(
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
vec2 uv_u = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw);
vec2 uv_v = clamp(in_uv_v, uv_bounds_v.xy, uv_bounds_v.zw);
yuv_value.x = TEX_SAMPLE(sColor0, uv_y).r;
yuv_value.y = TEX_SAMPLE(sColor1, uv_u).r;
yuv_value.z = TEX_SAMPLE(sColor2, uv_v).r;
ycbcr_sample.x = TEX_SAMPLE(sColor0, uv_y).r;
ycbcr_sample.y = TEX_SAMPLE(sColor1, uv_u).r;
ycbcr_sample.z = TEX_SAMPLE(sColor2, uv_v).r;
}
break;
@ -145,8 +206,8 @@ vec4 sample_yuv(
{
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
vec2 uv_uv = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw);
yuv_value.x = TEX_SAMPLE(sColor0, uv_y).r;
yuv_value.yz = TEX_SAMPLE(sColor1, uv_uv).rg;
ycbcr_sample.x = TEX_SAMPLE(sColor0, uv_y).r;
ycbcr_sample.yz = TEX_SAMPLE(sColor1, uv_uv).rg;
}
break;
@ -156,25 +217,24 @@ vec4 sample_yuv(
// the existing green, blue and red color channels."
// https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt
vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw);
yuv_value = TEX_SAMPLE(sColor0, uv_y).gbr;
ycbcr_sample = TEX_SAMPLE(sColor0, uv_y).gbr;
}
break;
default:
yuv_value = vec3(0.0);
ycbcr_sample = vec3(0.0);
break;
}
//if (true) return vec4(ycbcr_sample, 1.0);
// See the YuvColorMatrix definition for an explanation of where the constants come from.
vec3 yuv = yuv_value * coefficient - yuv_offset_vector;
vec3 rgb = yuv_color_matrix * yuv;
YUV_PRECISION vec3 rgb = rgb_from_debiased_ycbrc * (ycbcr_sample - ycbcr_bias);
#if defined(WR_FEATURE_ALPHA_PASS) && defined(SWGL_CLIP_MASK)
// Avoid out-of-range RGB values that can mess with blending. These occur due to invalid
// YUV values outside the mappable space that never the less can be generated.
rgb = clamp(rgb, 0.0, 1.0);
#endif
vec4 color = vec4(rgb, 1.0);
return color;
return vec4(rgb, 1.0);
}
#endif

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ColorF, YuvColorSpace, YuvFormat, ImageRendering, ExternalImageId, ImageBufferKind};
use api::{ColorF, YuvRangedColorSpace, YuvFormat, ImageRendering, ExternalImageId, ImageBufferKind};
use api::units::*;
use api::ColorDepth;
use crate::image_source::resolve_image;
@ -155,9 +155,9 @@ pub fn tile_kind(surface: &CompositeTileSurface, is_opaque: bool) -> TileKind {
pub enum ExternalSurfaceDependency {
Yuv {
image_dependencies: [ImageDependency; 3],
color_space: YuvColorSpace,
color_space: YuvRangedColorSpace,
format: YuvFormat,
rescale: f32,
channel_bit_depth: u32,
},
Rgb {
image_dependency: ImageDependency,
@ -221,9 +221,9 @@ pub enum ResolvedExternalSurfaceColorData {
// YUV specific information
image_dependencies: [ImageDependency; 3],
planes: [ExternalPlaneDescriptor; 3],
color_space: YuvColorSpace,
color_space: YuvRangedColorSpace,
format: YuvFormat,
rescale: f32,
channel_bit_depth: u32,
},
Rgb {
image_dependency: ImageDependency,
@ -844,7 +844,7 @@ impl CompositeState {
});
match external_surface.dependency {
ExternalSurfaceDependency::Yuv{ color_space, format, rescale, .. } => {
ExternalSurfaceDependency::Yuv{ color_space, format, channel_bit_depth, .. } => {
let image_buffer_kind = planes[0].texture.image_buffer_kind();
@ -854,8 +854,8 @@ impl CompositeState {
planes,
color_space,
format,
rescale,
},
channel_bit_depth,
},
image_buffer_kind,
update_params,
});
@ -1134,7 +1134,7 @@ pub struct SWGLCompositeSurfaceInfo {
/// Textures for planes of the surface, or 0 if not applicable.
pub textures: [u32; 3],
/// Color space of surface if using a YUV format.
pub color_space: YuvColorSpace,
pub color_space: YuvRangedColorSpace,
/// Color depth of surface if using a YUV format.
pub color_depth: ColorDepth,
/// The actual source surface size before transformation.

Просмотреть файл

@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicBool, AtomicI8, AtomicIsize, AtomicPtr, AtomicU32,
use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use std::thread;
use crate::{
api::units::*, api::ColorDepth, api::ExternalImageId, api::ImageRendering, api::YuvColorSpace, Compositor,
api::units::*, api::ColorDepth, api::ExternalImageId, api::ImageRendering, api::YuvRangedColorSpace, Compositor,
CompositorCapabilities, CompositorSurfaceTransform, NativeSurfaceId, NativeSurfaceInfo, NativeTileId,
profiler, MappableCompositor, SWGLCompositeSurfaceInfo,
};
@ -178,7 +178,7 @@ enum SwCompositeSource {
swgl::LockedResource,
swgl::LockedResource,
swgl::LockedResource,
YuvColorSpace,
YuvRangedColorSpace,
ColorDepth,
),
}
@ -242,10 +242,13 @@ impl SwCompositeJob {
}
SwCompositeSource::YUV(ref y, ref u, ref v, color_space, color_depth) => {
let swgl_color_space = match color_space {
YuvColorSpace::Rec601 => swgl::YUVColorSpace::Rec601,
YuvColorSpace::Rec709 => swgl::YUVColorSpace::Rec709,
YuvColorSpace::Rec2020 => swgl::YUVColorSpace::Rec2020,
YuvColorSpace::Identity => swgl::YUVColorSpace::Identity,
YuvRangedColorSpace::Rec601Narrow => swgl::YuvRangedColorSpace::Rec601Narrow,
YuvRangedColorSpace::Rec601Full => swgl::YuvRangedColorSpace::Rec601Full,
YuvRangedColorSpace::Rec709Narrow => swgl::YuvRangedColorSpace::Rec709Narrow,
YuvRangedColorSpace::Rec709Full => swgl::YuvRangedColorSpace::Rec709Full,
YuvRangedColorSpace::Rec2020Narrow => swgl::YuvRangedColorSpace::Rec2020Narrow,
YuvRangedColorSpace::Rec2020Full => swgl::YuvRangedColorSpace::Rec2020Full,
YuvRangedColorSpace::GbrIdentity => swgl::YuvRangedColorSpace::GbrIdentity,
};
self.locked_dst.composite_yuv(
y,
@ -1004,7 +1007,7 @@ impl SwCompositor {
let mut info = SWGLCompositeSurfaceInfo {
yuv_planes: 0,
textures: [0; 3],
color_space: YuvColorSpace::Identity,
color_space: YuvRangedColorSpace::GbrIdentity,
color_depth: ColorDepth::Color8,
size: DeviceIntSize::zero(),
};

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, PremultipliedColorF, YuvFormat, YuvColorSpace};
use api::{AlphaType, PremultipliedColorF, YuvFormat, YuvRangedColorSpace};
use api::units::*;
use crate::composite::CompositeFeatures;
use crate::segment::EdgeAaSegmentMask;
@ -254,7 +254,7 @@ pub struct CompositeInstance {
color_space_or_uv_type: f32, // YuvColorSpace for YUV;
// UV coordinate space for RGB
yuv_format: f32, // YuvFormat
yuv_rescale: f32,
yuv_channel_bit_depth: f32,
// UV rectangles (pixel space) for color / yuv texture planes
uv_rects: [TexelRect; 3],
@ -275,7 +275,7 @@ impl CompositeInstance {
z_id: z_id.0 as f32,
color_space_or_uv_type: pack_as_float(UV_TYPE_NORMALIZED),
yuv_format: 0.0,
yuv_rescale: 0.0,
yuv_channel_bit_depth: 0.0,
uv_rects: [uv, uv, uv],
}
}
@ -294,7 +294,7 @@ impl CompositeInstance {
z_id: z_id.0 as f32,
color_space_or_uv_type: pack_as_float(UV_TYPE_UNNORMALIZED),
yuv_format: 0.0,
yuv_rescale: 0.0,
yuv_channel_bit_depth: 0.0,
uv_rects: [uv_rect, uv_rect, uv_rect],
}
}
@ -303,9 +303,9 @@ impl CompositeInstance {
rect: DeviceRect,
clip_rect: DeviceRect,
z_id: ZBufferId,
yuv_color_space: YuvColorSpace,
yuv_color_space: YuvRangedColorSpace,
yuv_format: YuvFormat,
yuv_rescale: f32,
yuv_channel_bit_depth: u32,
uv_rects: [TexelRect; 3],
) -> Self {
CompositeInstance {
@ -315,7 +315,7 @@ impl CompositeInstance {
z_id: z_id.0 as f32,
color_space_or_uv_type: pack_as_float(yuv_color_space as u32),
yuv_format: pack_as_float(yuv_format as u32),
yuv_rescale,
yuv_channel_bit_depth: pack_as_float(yuv_channel_bit_depth),
uv_rects,
}
}

Просмотреть файл

@ -97,7 +97,7 @@
use api::{MixBlendMode, PremultipliedColorF, FilterPrimitiveKind};
use api::{PropertyBinding, PropertyBindingId, FilterPrimitive};
use api::{DebugFlags, ImageKey, ColorF, ColorU, PrimitiveFlags};
use api::{ImageRendering, ColorDepth, YuvColorSpace, YuvFormat, AlphaType};
use api::{ImageRendering, ColorDepth, YuvRangedColorSpace, YuvFormat, AlphaType};
use api::units::*;
use crate::batch::BatchFilter;
use crate::box_shadow::BLUR_SAMPLE_SCALE;
@ -2983,7 +2983,7 @@ impl TileCacheInstance {
gpu_cache: &mut GpuCache,
image_rendering: ImageRendering,
color_depth: ColorDepth,
color_space: YuvColorSpace,
color_space: YuvRangedColorSpace,
format: YuvFormat,
) -> bool {
for &key in api_keys {
@ -3011,7 +3011,7 @@ impl TileCacheInstance {
image_dependencies: *image_dependencies,
color_space,
format,
rescale: color_depth.rescaling_factor(),
channel_bit_depth: color_depth.bit_depth(),
},
api_keys,
resource_cache,
@ -3592,7 +3592,7 @@ impl TileCacheInstance {
gpu_cache,
prim_data.kind.image_rendering,
prim_data.kind.color_depth,
prim_data.kind.color_space,
prim_data.kind.color_space.with_range(prim_data.kind.color_range),
prim_data.kind.format,
);
}

Просмотреть файл

@ -603,9 +603,10 @@ impl YuvImageData {
}
pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) {
let ranged_color_space = self.color_space.with_range(self.color_range);
request.push([
self.color_depth.rescaling_factor(),
pack_as_float(self.color_space as u32),
pack_as_float(self.color_depth.bit_depth()),
pack_as_float(ranged_color_space as u32),
pack_as_float(self.format as u32),
0.0
]);

Просмотреть файл

@ -2968,7 +2968,7 @@ impl Renderer {
let ( textures, instance ) = match surface.color_data {
ResolvedExternalSurfaceColorData::Yuv{
ref planes, color_space, format, rescale, .. } => {
ref planes, color_space, format, channel_bit_depth, .. } => {
// Bind an appropriate YUV shader for the texture format kind
self.shaders
@ -3009,7 +3009,7 @@ impl Renderer {
ZBufferId(0),
color_space,
format,
rescale,
channel_bit_depth,
uv_rects,
);
@ -3142,7 +3142,7 @@ impl Renderer {
let surface = &external_surfaces[external_surface_index.0];
match surface.color_data {
ResolvedExternalSurfaceColorData::Yuv{ ref planes, color_space, format, rescale, .. } => {
ResolvedExternalSurfaceColorData::Yuv{ ref planes, color_space, format, channel_bit_depth, .. } => {
let textures = BatchTextures::composite_yuv(
planes[0].texture,
planes[1].texture,
@ -3167,7 +3167,7 @@ impl Renderer {
tile.z_id,
color_space,
format,
rescale,
channel_bit_depth,
uv_rects,
),
textures,

Просмотреть файл

@ -1344,7 +1344,7 @@ pub enum YuvColorSpace {
Rec601 = 0,
Rec709 = 1,
Rec2020 = 2,
Identity = 3, // aka RGB as per ISO/IEC 23091-2:2019
Identity = 3, // aka GBR as per ISO/IEC 23091-2:2019
}
#[repr(u8)]
@ -1354,6 +1354,44 @@ pub enum ColorRange {
Full = 1,
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
pub enum YuvRangedColorSpace {
Rec601Narrow = 0,
Rec601Full = 1,
Rec709Narrow = 2,
Rec709Full = 3,
Rec2020Narrow = 4,
Rec2020Full = 5,
GbrIdentity = 6,
}
impl YuvColorSpace {
pub fn with_range(self, range: ColorRange) -> YuvRangedColorSpace {
match self {
YuvColorSpace::Identity => YuvRangedColorSpace::GbrIdentity,
YuvColorSpace::Rec601 => {
match range {
ColorRange::Limited => YuvRangedColorSpace::Rec601Narrow,
ColorRange::Full => YuvRangedColorSpace::Rec601Full,
}
}
YuvColorSpace::Rec709 => {
match range {
ColorRange::Limited => YuvRangedColorSpace::Rec709Narrow,
ColorRange::Full => YuvRangedColorSpace::Rec709Full,
}
}
YuvColorSpace::Rec2020 => {
match range {
ColorRange::Limited => YuvRangedColorSpace::Rec2020Narrow,
ColorRange::Full => YuvRangedColorSpace::Rec2020Full,
}
}
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)]
pub enum YuvData {
NV12(ImageKey, ImageKey), // (Y channel, CbCr interleaved channel)
@ -1729,6 +1767,7 @@ impl_default_for_enums! {
ImageRendering => Auto,
AlphaType => Alpha,
YuvColorSpace => Rec601,
YuvRangedColorSpace => Rec601Narrow,
ColorRange => Limited,
YuvData => NV12(ImageKey::default(), ImageKey::default()),
YuvFormat => NV12,

Просмотреть файл

@ -451,4 +451,5 @@ include display-list/reftest.list
# Media
include ../../dom/media/test/reftest/reftest.list
include ../../dom/media/test/reftest/color_quads/reftest.list
include ../../dom/media/webvtt/test/reftest/reftest.list

Просмотреть файл

@ -603,7 +603,7 @@ function update_pixel_difference_text() {
}
// Disable this for now, because per bug 1633504, the numbers may be
// inaccurate and dependent on the browser's configuration.
// ID("pixel-differences").textContent = differenceText;
ID("pixel-differences").textContent = differenceText;
}
function get_pixel_differences() {