Render surfaces correctly when encoded in 420YpCbCr8BiPlanarFullRange

This commit is contained in:
Antonio Scandurra 2022-09-08 15:39:15 +02:00
parent 4e0380c9fb
commit ca618b02b6
4 changed files with 202 additions and 121 deletions

View File

@ -28,6 +28,7 @@ pub struct Renderer {
shadow_pipeline_state: metal::RenderPipelineState, shadow_pipeline_state: metal::RenderPipelineState,
sprite_pipeline_state: metal::RenderPipelineState, sprite_pipeline_state: metal::RenderPipelineState,
image_pipeline_state: metal::RenderPipelineState, image_pipeline_state: metal::RenderPipelineState,
surface_pipeline_state: metal::RenderPipelineState,
path_atlas_pipeline_state: metal::RenderPipelineState, path_atlas_pipeline_state: metal::RenderPipelineState,
underline_pipeline_state: metal::RenderPipelineState, underline_pipeline_state: metal::RenderPipelineState,
unit_vertices: metal::Buffer, unit_vertices: metal::Buffer,
@ -116,6 +117,14 @@ impl Renderer {
"image_fragment", "image_fragment",
pixel_format, pixel_format,
); );
let surface_pipeline_state = build_pipeline_state(
&device,
&library,
"surface",
"surface_vertex",
"surface_fragment",
pixel_format,
);
let path_atlas_pipeline_state = build_path_atlas_pipeline_state( let path_atlas_pipeline_state = build_path_atlas_pipeline_state(
&device, &device,
&library, &library,
@ -141,6 +150,7 @@ impl Renderer {
shadow_pipeline_state, shadow_pipeline_state,
sprite_pipeline_state, sprite_pipeline_state,
image_pipeline_state, image_pipeline_state,
surface_pipeline_state,
path_atlas_pipeline_state, path_atlas_pipeline_state,
underline_pipeline_state, underline_pipeline_state,
unit_vertices, unit_vertices,
@ -798,14 +808,14 @@ impl Renderer {
return; return;
} }
command_encoder.set_render_pipeline_state(&self.image_pipeline_state); command_encoder.set_render_pipeline_state(&self.surface_pipeline_state);
command_encoder.set_vertex_buffer( command_encoder.set_vertex_buffer(
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexVertices as u64, shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexVertices as u64,
Some(&self.unit_vertices), Some(&self.unit_vertices),
0, 0,
); );
command_encoder.set_vertex_bytes( command_encoder.set_vertex_bytes(
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexViewportSize as u64, shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexViewportSize as u64,
mem::size_of::<shaders::vector_float2>() as u64, mem::size_of::<shaders::vector_float2>() as u64,
[drawable_size.to_float2()].as_ptr() as *const c_void, [drawable_size.to_float2()].as_ptr() as *const c_void,
); );
@ -817,64 +827,71 @@ impl Renderer {
surface.image_buffer.height() as i32, surface.image_buffer.height() as i32,
); );
let target_size = surface.bounds.size() * scale_factor; let target_size = surface.bounds.size() * scale_factor;
let pixel_format = if surface.image_buffer.pixel_format_type()
== core_video::kCVPixelFormatType_32BGRA
{
MTLPixelFormat::BGRA8Unorm
} else {
MTLPixelFormat::R8Unorm
};
let texture = self assert_eq!(
surface.image_buffer.pixel_format_type(),
core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
);
let y_texture = self
.cv_texture_cache .cv_texture_cache
.create_texture_from_image( .create_texture_from_image(
surface.image_buffer.as_concrete_TypeRef(), surface.image_buffer.as_concrete_TypeRef(),
ptr::null(), ptr::null(),
pixel_format, MTLPixelFormat::R8Unorm,
source_size.x() as usize, surface.image_buffer.plane_width(0),
source_size.y() as usize, surface.image_buffer.plane_height(0),
0, 0,
) )
.unwrap(); .unwrap();
let cb_cr_texture = self
.cv_texture_cache
.create_texture_from_image(
surface.image_buffer.as_concrete_TypeRef(),
ptr::null(),
MTLPixelFormat::RG8Unorm,
surface.image_buffer.plane_width(1),
surface.image_buffer.plane_height(1),
1,
)
.unwrap();
align_offset(offset); align_offset(offset);
let next_offset = *offset + mem::size_of::<shaders::GPUIImage>(); let next_offset = *offset + mem::size_of::<shaders::GPUISurface>();
assert!( assert!(
next_offset <= INSTANCE_BUFFER_SIZE, next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted" "instance buffer exhausted"
); );
command_encoder.set_vertex_buffer( command_encoder.set_vertex_buffer(
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexImages as u64, shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexSurfaces as u64,
Some(&self.instances), Some(&self.instances),
*offset as u64, *offset as u64,
); );
command_encoder.set_vertex_bytes( command_encoder.set_vertex_bytes(
shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexAtlasSize as u64, shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexAtlasSize as u64,
mem::size_of::<shaders::vector_float2>() as u64, mem::size_of::<shaders::vector_float2>() as u64,
[source_size.to_float2()].as_ptr() as *const c_void, [source_size.to_float2()].as_ptr() as *const c_void,
); );
command_encoder.set_fragment_texture( command_encoder.set_fragment_texture(
shaders::GPUIImageFragmentInputIndex_GPUIImageFragmentInputIndexAtlas as u64, shaders::GPUISurfaceFragmentInputIndex_GPUISurfaceFragmentInputIndexYAtlas as u64,
Some(texture.as_texture_ref()), Some(y_texture.as_texture_ref()),
);
command_encoder.set_fragment_texture(
shaders::GPUISurfaceFragmentInputIndex_GPUISurfaceFragmentInputIndexCbCrAtlas
as u64,
Some(cb_cr_texture.as_texture_ref()),
); );
unsafe { unsafe {
let buffer_contents = let buffer_contents = (self.instances.contents() as *mut u8).add(*offset)
(self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIImage; as *mut shaders::GPUISurface;
std::ptr::write( std::ptr::write(
buffer_contents, buffer_contents,
shaders::GPUIImage { shaders::GPUISurface {
origin: origin.to_float2(), origin: origin.to_float2(),
target_size: target_size.to_float2(), target_size: target_size.to_float2(),
source_size: source_size.to_float2(), source_size: source_size.to_float2(),
atlas_origin: Default::default(),
border_top: Default::default(),
border_right: Default::default(),
border_bottom: Default::default(),
border_left: Default::default(),
border_color: Default::default(),
corner_radius: Default::default(),
}, },
); );
} }

View File

@ -1,122 +1,125 @@
#include <simd/simd.h> #include <simd/simd.h>
typedef struct typedef struct {
{ vector_float2 viewport_size;
vector_float2 viewport_size;
} GPUIUniforms; } GPUIUniforms;
typedef enum typedef enum {
{ GPUIQuadInputIndexVertices = 0,
GPUIQuadInputIndexVertices = 0, GPUIQuadInputIndexQuads = 1,
GPUIQuadInputIndexQuads = 1, GPUIQuadInputIndexUniforms = 2,
GPUIQuadInputIndexUniforms = 2,
} GPUIQuadInputIndex; } GPUIQuadInputIndex;
typedef struct typedef struct {
{ vector_float2 origin;
vector_float2 origin; vector_float2 size;
vector_float2 size; vector_uchar4 background_color;
vector_uchar4 background_color; float border_top;
float border_top; float border_right;
float border_right; float border_bottom;
float border_bottom; float border_left;
float border_left; vector_uchar4 border_color;
vector_uchar4 border_color; float corner_radius;
float corner_radius;
} GPUIQuad; } GPUIQuad;
typedef enum typedef enum {
{ GPUIShadowInputIndexVertices = 0,
GPUIShadowInputIndexVertices = 0, GPUIShadowInputIndexShadows = 1,
GPUIShadowInputIndexShadows = 1, GPUIShadowInputIndexUniforms = 2,
GPUIShadowInputIndexUniforms = 2,
} GPUIShadowInputIndex; } GPUIShadowInputIndex;
typedef struct typedef struct {
{ vector_float2 origin;
vector_float2 origin; vector_float2 size;
vector_float2 size; float corner_radius;
float corner_radius; float sigma;
float sigma; vector_uchar4 color;
vector_uchar4 color;
} GPUIShadow; } GPUIShadow;
typedef enum typedef enum {
{ GPUISpriteVertexInputIndexVertices = 0,
GPUISpriteVertexInputIndexVertices = 0, GPUISpriteVertexInputIndexSprites = 1,
GPUISpriteVertexInputIndexSprites = 1, GPUISpriteVertexInputIndexViewportSize = 2,
GPUISpriteVertexInputIndexViewportSize = 2, GPUISpriteVertexInputIndexAtlasSize = 3,
GPUISpriteVertexInputIndexAtlasSize = 3,
} GPUISpriteVertexInputIndex; } GPUISpriteVertexInputIndex;
typedef enum typedef enum {
{ GPUISpriteFragmentInputIndexAtlas = 0,
GPUISpriteFragmentInputIndexAtlas = 0,
} GPUISpriteFragmentInputIndex; } GPUISpriteFragmentInputIndex;
typedef struct typedef struct {
{ vector_float2 origin;
vector_float2 origin; vector_float2 target_size;
vector_float2 target_size; vector_float2 source_size;
vector_float2 source_size; vector_float2 atlas_origin;
vector_float2 atlas_origin; vector_uchar4 color;
vector_uchar4 color; uint8_t compute_winding;
uint8_t compute_winding;
} GPUISprite; } GPUISprite;
typedef enum typedef enum {
{ GPUIPathAtlasVertexInputIndexVertices = 0,
GPUIPathAtlasVertexInputIndexVertices = 0, GPUIPathAtlasVertexInputIndexAtlasSize = 1,
GPUIPathAtlasVertexInputIndexAtlasSize = 1,
} GPUIPathAtlasVertexInputIndex; } GPUIPathAtlasVertexInputIndex;
typedef struct typedef struct {
{ vector_float2 xy_position;
vector_float2 xy_position; vector_float2 st_position;
vector_float2 st_position; vector_float2 clip_rect_origin;
vector_float2 clip_rect_origin; vector_float2 clip_rect_size;
vector_float2 clip_rect_size;
} GPUIPathVertex; } GPUIPathVertex;
typedef enum typedef enum {
{ GPUIImageVertexInputIndexVertices = 0,
GPUIImageVertexInputIndexVertices = 0, GPUIImageVertexInputIndexImages = 1,
GPUIImageVertexInputIndexImages = 1, GPUIImageVertexInputIndexViewportSize = 2,
GPUIImageVertexInputIndexViewportSize = 2, GPUIImageVertexInputIndexAtlasSize = 3,
GPUIImageVertexInputIndexAtlasSize = 3,
} GPUIImageVertexInputIndex; } GPUIImageVertexInputIndex;
typedef enum typedef enum {
{ GPUIImageFragmentInputIndexAtlas = 0,
GPUIImageFragmentInputIndexAtlas = 0,
} GPUIImageFragmentInputIndex; } GPUIImageFragmentInputIndex;
typedef struct typedef struct {
{ vector_float2 origin;
vector_float2 origin; vector_float2 target_size;
vector_float2 target_size; vector_float2 source_size;
vector_float2 source_size; vector_float2 atlas_origin;
vector_float2 atlas_origin; float border_top;
float border_top; float border_right;
float border_right; float border_bottom;
float border_bottom; float border_left;
float border_left; vector_uchar4 border_color;
vector_uchar4 border_color; float corner_radius;
float corner_radius;
} GPUIImage; } GPUIImage;
typedef enum typedef enum {
{ GPUISurfaceVertexInputIndexVertices = 0,
GPUIUnderlineInputIndexVertices = 0, GPUISurfaceVertexInputIndexSurfaces = 1,
GPUIUnderlineInputIndexUnderlines = 1, GPUISurfaceVertexInputIndexViewportSize = 2,
GPUIUnderlineInputIndexUniforms = 2, GPUISurfaceVertexInputIndexAtlasSize = 3,
} GPUISurfaceVertexInputIndex;
typedef enum {
GPUISurfaceFragmentInputIndexYAtlas = 0,
GPUISurfaceFragmentInputIndexCbCrAtlas = 1,
} GPUISurfaceFragmentInputIndex;
typedef struct {
vector_float2 origin;
vector_float2 target_size;
vector_float2 source_size;
} GPUISurface;
typedef enum {
GPUIUnderlineInputIndexVertices = 0,
GPUIUnderlineInputIndexUnderlines = 1,
GPUIUnderlineInputIndexUniforms = 2,
} GPUIUnderlineInputIndex; } GPUIUnderlineInputIndex;
typedef struct typedef struct {
{ vector_float2 origin;
vector_float2 origin; vector_float2 size;
vector_float2 size; float thickness;
float thickness; vector_uchar4 color;
vector_uchar4 color; uint8_t squiggly;
uint8_t squiggly;
} GPUIUnderline; } GPUIUnderline;

View File

@ -263,6 +263,54 @@ fragment float4 image_fragment(
return quad_sdf(input); return quad_sdf(input);
} }
vertex QuadFragmentInput surface_vertex(
uint unit_vertex_id [[vertex_id]],
uint image_id [[instance_id]],
constant float2 *unit_vertices [[buffer(GPUISurfaceVertexInputIndexVertices)]],
constant GPUISurface *images [[buffer(GPUISurfaceVertexInputIndexSurfaces)]],
constant float2 *viewport_size [[buffer(GPUISurfaceVertexInputIndexViewportSize)]],
constant float2 *atlas_size [[buffer(GPUISurfaceVertexInputIndexAtlasSize)]]
) {
float2 unit_vertex = unit_vertices[unit_vertex_id];
GPUISurface image = images[image_id];
float2 position = unit_vertex * image.target_size + image.origin;
float4 device_position = to_device_position(position, *viewport_size);
float2 atlas_position = (unit_vertex * image.source_size) / *atlas_size;
return QuadFragmentInput {
device_position,
atlas_position,
image.origin,
image.target_size,
float4(0.),
0.,
0.,
0.,
0.,
float4(0.),
0.,
};
}
fragment float4 surface_fragment(
QuadFragmentInput input [[stage_in]],
texture2d<float> y_atlas [[ texture(GPUISurfaceFragmentInputIndexYAtlas) ]],
texture2d<float> cb_cr_atlas [[ texture(GPUISurfaceFragmentInputIndexCbCrAtlas) ]]
) {
constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear);
const float4x4 ycbcrToRGBTransform = float4x4(
float4(+1.0000f, +1.0000f, +1.0000f, +0.0000f),
float4(+0.0000f, -0.3441f, +1.7720f, +0.0000f),
float4(+1.4020f, -0.7141f, +0.0000f, +0.0000f),
float4(-0.7010f, +0.5291f, -0.8860f, +1.0000f)
);
float4 ycbcr = float4(y_atlas.sample(atlas_sampler, input.atlas_position).r,
cb_cr_atlas.sample(atlas_sampler, input.atlas_position).rg, 1.0);
input.background_color = ycbcrToRGBTransform * ycbcr;
return quad_sdf(input);
}
struct PathAtlasVertexOutput { struct PathAtlasVertexOutput {
float4 position [[position]]; float4 position [[position]];
float2 st_position; float2 st_position;

View File

@ -31,7 +31,10 @@ pub mod core_video {
#![allow(non_snake_case)] #![allow(non_snake_case)]
use super::*; use super::*;
pub use crate::bindings::kCVPixelFormatType_32BGRA; pub use crate::bindings::{
kCVPixelFormatType_32BGRA, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, kCVPixelFormatType_420YpCbCr8Planar,
};
use crate::bindings::{kCVReturnSuccess, CVReturn, OSType}; use crate::bindings::{kCVReturnSuccess, CVReturn, OSType};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use core_foundation::{ use core_foundation::{
@ -68,6 +71,14 @@ pub mod core_video {
unsafe { CVPixelBufferGetHeight(self.as_concrete_TypeRef()) } unsafe { CVPixelBufferGetHeight(self.as_concrete_TypeRef()) }
} }
pub fn plane_width(&self, plane: usize) -> usize {
unsafe { CVPixelBufferGetWidthOfPlane(self.as_concrete_TypeRef(), plane) }
}
pub fn plane_height(&self, plane: usize) -> usize {
unsafe { CVPixelBufferGetHeightOfPlane(self.as_concrete_TypeRef(), plane) }
}
pub fn pixel_format_type(&self) -> OSType { pub fn pixel_format_type(&self) -> OSType {
unsafe { CVPixelBufferGetPixelFormatType(self.as_concrete_TypeRef()) } unsafe { CVPixelBufferGetPixelFormatType(self.as_concrete_TypeRef()) }
} }
@ -79,6 +90,8 @@ pub mod core_video {
fn CVPixelBufferGetIOSurface(buffer: CVImageBufferRef) -> IOSurfaceRef; fn CVPixelBufferGetIOSurface(buffer: CVImageBufferRef) -> IOSurfaceRef;
fn CVPixelBufferGetWidth(buffer: CVImageBufferRef) -> usize; fn CVPixelBufferGetWidth(buffer: CVImageBufferRef) -> usize;
fn CVPixelBufferGetHeight(buffer: CVImageBufferRef) -> usize; fn CVPixelBufferGetHeight(buffer: CVImageBufferRef) -> usize;
fn CVPixelBufferGetWidthOfPlane(buffer: CVImageBufferRef, plane: usize) -> usize;
fn CVPixelBufferGetHeightOfPlane(buffer: CVImageBufferRef, plane: usize) -> usize;
fn CVPixelBufferGetPixelFormatType(buffer: CVImageBufferRef) -> OSType; fn CVPixelBufferGetPixelFormatType(buffer: CVImageBufferRef) -> OSType;
} }