Start work on gradients

WIP. Most of the GPU-side work should be done (though it's not tested
end-to-end and it's certainly possible I missed something), but still
needs work on encoding side.
This commit is contained in:
Raph Levien 2021-06-23 11:50:51 -07:00
parent 379fb1caaa
commit 6f707c4c62
24 changed files with 466 additions and 108 deletions

View file

@ -104,7 +104,6 @@ pub struct PipelineBuilder {
bindings: Vec<vk::DescriptorSetLayoutBinding>, bindings: Vec<vk::DescriptorSetLayoutBinding>,
binding_flags: Vec<vk::DescriptorBindingFlags>, binding_flags: Vec<vk::DescriptorBindingFlags>,
max_textures: u32, max_textures: u32,
has_descriptor_indexing: bool,
} }
pub struct DescriptorSetBuilder { pub struct DescriptorSetBuilder {
@ -644,7 +643,6 @@ impl crate::backend::Device for VkDevice {
bindings: Vec::new(), bindings: Vec::new(),
binding_flags: Vec::new(), binding_flags: Vec::new(),
max_textures: 0, max_textures: 0,
has_descriptor_indexing: self.gpu_info.has_descriptor_indexing,
} }
} }
@ -1090,23 +1088,21 @@ impl crate::backend::PipelineBuilder<VkDevice> for PipelineBuilder {
} }
} }
fn add_textures(&mut self, max_textures: u32) { fn add_textures(&mut self, n_images: u32) {
let start = self.bindings.len() as u32; let start = self.bindings.len() as u32;
for i in 0..n_images {
self.bindings.push( self.bindings.push(
vk::DescriptorSetLayoutBinding::builder() vk::DescriptorSetLayoutBinding::builder()
.binding(start) .binding(start + i)
.descriptor_type(vk::DescriptorType::STORAGE_IMAGE) .descriptor_type(vk::DescriptorType::STORAGE_IMAGE)
.descriptor_count(max_textures) .descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::COMPUTE) .stage_flags(vk::ShaderStageFlags::COMPUTE)
.build(), .build(),
); );
let flags = if self.has_descriptor_indexing { self.binding_flags
vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT .push(vk::DescriptorBindingFlags::default());
} else { }
Default::default() self.max_textures += n_images;
};
self.binding_flags.push(flags);
self.max_textures += max_textures;
} }
unsafe fn create_compute_pipeline( unsafe fn create_compute_pipeline(
@ -1195,11 +1191,11 @@ impl crate::backend::DescriptorSetBuilder<VkDevice> for DescriptorSetBuilder {
.build(), .build(),
); );
} }
if pipeline.max_textures > 0 { if !self.textures.is_empty() {
descriptor_pool_sizes.push( descriptor_pool_sizes.push(
vk::DescriptorPoolSize::builder() vk::DescriptorPoolSize::builder()
.ty(vk::DescriptorType::STORAGE_IMAGE) .ty(vk::DescriptorType::STORAGE_IMAGE)
.descriptor_count(pipeline.max_textures) .descriptor_count(self.textures.len() as u32)
.build(), .build(),
); );
} }
@ -1211,15 +1207,11 @@ impl crate::backend::DescriptorSetBuilder<VkDevice> for DescriptorSetBuilder {
)?; )?;
let descriptor_set_layouts = [pipeline.descriptor_set_layout]; let descriptor_set_layouts = [pipeline.descriptor_set_layout];
let counts = &[pipeline.max_textures];
let variable_info = vk::DescriptorSetVariableDescriptorCountAllocateInfo::builder()
.descriptor_counts(counts);
let descriptor_sets = device let descriptor_sets = device
.allocate_descriptor_sets( .allocate_descriptor_sets(
&vk::DescriptorSetAllocateInfo::builder() &vk::DescriptorSetAllocateInfo::builder()
.descriptor_pool(descriptor_pool) .descriptor_pool(descriptor_pool)
.set_layouts(&descriptor_set_layouts) .set_layouts(&descriptor_set_layouts),
.push_next(&mut variable_info.build()),
) )
.unwrap(); .unwrap();
let mut binding = 0; let mut binding = 0;
@ -1240,6 +1232,7 @@ impl crate::backend::DescriptorSetBuilder<VkDevice> for DescriptorSetBuilder {
); );
binding += 1; binding += 1;
} }
// maybe chain images and textures together; they're basically identical now
for image in &self.images { for image in &self.images {
device.update_descriptor_sets( device.update_descriptor_sets(
&[vk::WriteDescriptorSet::builder() &[vk::WriteDescriptorSet::builder()
@ -1256,28 +1249,21 @@ impl crate::backend::DescriptorSetBuilder<VkDevice> for DescriptorSetBuilder {
); );
binding += 1; binding += 1;
} }
if !self.textures.is_empty() { for image in &self.textures {
let infos = self
.textures
.iter()
.map(|texture| {
vk::DescriptorImageInfo::builder()
.sampler(self.sampler)
.image_view(*texture)
.image_layout(vk::ImageLayout::GENERAL)
.build()
})
.collect::<Vec<_>>();
device.update_descriptor_sets( device.update_descriptor_sets(
&[vk::WriteDescriptorSet::builder() &[vk::WriteDescriptorSet::builder()
.dst_set(descriptor_sets[0]) .dst_set(descriptor_sets[0])
.dst_binding(binding) .dst_binding(binding)
.descriptor_type(vk::DescriptorType::STORAGE_IMAGE) .descriptor_type(vk::DescriptorType::STORAGE_IMAGE)
.image_info(&infos) .image_info(&[vk::DescriptorImageInfo::builder()
.sampler(vk::Sampler::null())
.image_view(*image)
.image_layout(vk::ImageLayout::GENERAL)
.build()])
.build()], .build()],
&[], &[],
); );
//binding += 1; binding += 1;
} }
Ok(DescriptorSet { Ok(DescriptorSet {
descriptor_set: descriptor_sets[0], descriptor_set: descriptor_sets[0],

View file

@ -17,6 +17,15 @@ piet_gpu! {
linewidth: f32, linewidth: f32,
rgba_color: u32, rgba_color: u32,
} }
struct AnnoLinGradient {
bbox: [f32; 4],
// For stroked fills.
linewidth: f32,
index: u32,
line_x: f32,
line_y: f32,
line_c: f32,
}
struct AnnoBeginClip { struct AnnoBeginClip {
bbox: [f32; 4], bbox: [f32; 4],
linewidth: f32, linewidth: f32,
@ -27,6 +36,7 @@ piet_gpu! {
enum Annotated { enum Annotated {
Nop, Nop,
Color(TagFlags, AnnoColor), Color(TagFlags, AnnoColor),
LinGradient(TagFlags, AnnoLinGradient),
Image(TagFlags, AnnoImage), Image(TagFlags, AnnoImage),
BeginClip(TagFlags, AnnoBeginClip), BeginClip(TagFlags, AnnoBeginClip),
EndClip(AnnoEndClip), EndClip(AnnoEndClip),

View file

@ -17,6 +17,13 @@ piet_gpu! {
struct CmdColor { struct CmdColor {
rgba_color: u32, rgba_color: u32,
} }
struct CmdLinGrad {
index: u32,
// line equation for gradient
line_x: f32,
line_y: f32,
line_c: f32,
}
struct CmdImage { struct CmdImage {
index: u32, index: u32,
offset: [i16; 2], offset: [i16; 2],
@ -34,6 +41,7 @@ piet_gpu! {
Solid, Solid,
Alpha(CmdAlpha), Alpha(CmdAlpha),
Color(CmdColor), Color(CmdColor),
LinGrad(CmdLinGrad),
Image(CmdImage), Image(CmdImage),
BeginClip, BeginClip,
EndClip, EndClip,

View file

@ -25,6 +25,11 @@ piet_gpu! {
struct FillColor { struct FillColor {
rgba_color: u32, rgba_color: u32,
} }
struct FillLinGradient {
index: u32,
p0: [f32; 2],
p1: [f32; 2],
}
struct FillImage { struct FillImage {
index: u32, index: u32,
offset: [i16; 2], offset: [i16; 2],
@ -51,11 +56,12 @@ piet_gpu! {
Cubic(CubicSeg), Cubic(CubicSeg),
FillColor(FillColor), FillColor(FillColor),
FillLinGradient(FillLinGradient),
FillImage(FillImage),
SetLineWidth(SetLineWidth), SetLineWidth(SetLineWidth),
Transform(Transform), Transform(Transform),
BeginClip(Clip), BeginClip(Clip),
EndClip(Clip), EndClip(Clip),
FillImage(FillImage),
SetFillMode(SetFillMode), SetFillMode(SetFillMode),
} }
} }

View file

@ -4,7 +4,7 @@
//! //!
//! Requires the [cargo-apk] tool. //! Requires the [cargo-apk] tool.
//! [cargo-apk]: https://crates.io/crates/cargo-apk //! [cargo-apk]: https://crates.io/crates/cargo-apk
/*
use raw_window_handle::android::AndroidHandle; use raw_window_handle::android::AndroidHandle;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
@ -175,3 +175,5 @@ impl GfxState {
} }
} }
} }
*/
fn main() {}

View file

@ -10,6 +10,10 @@ struct AnnoColorRef {
uint offset; uint offset;
}; };
struct AnnoLinGradientRef {
uint offset;
};
struct AnnoBeginClipRef { struct AnnoBeginClipRef {
uint offset; uint offset;
}; };
@ -47,6 +51,21 @@ AnnoColorRef AnnoColor_index(AnnoColorRef ref, uint index) {
return AnnoColorRef(ref.offset + index * AnnoColor_size); return AnnoColorRef(ref.offset + index * AnnoColor_size);
} }
struct AnnoLinGradient {
vec4 bbox;
float linewidth;
uint index;
float line_x;
float line_y;
float line_c;
};
#define AnnoLinGradient_size 36
AnnoLinGradientRef AnnoLinGradient_index(AnnoLinGradientRef ref, uint index) {
return AnnoLinGradientRef(ref.offset + index * AnnoLinGradient_size);
}
struct AnnoBeginClip { struct AnnoBeginClip {
vec4 bbox; vec4 bbox;
float linewidth; float linewidth;
@ -70,10 +89,11 @@ AnnoEndClipRef AnnoEndClip_index(AnnoEndClipRef ref, uint index) {
#define Annotated_Nop 0 #define Annotated_Nop 0
#define Annotated_Color 1 #define Annotated_Color 1
#define Annotated_Image 2 #define Annotated_LinGradient 2
#define Annotated_BeginClip 3 #define Annotated_Image 3
#define Annotated_EndClip 4 #define Annotated_BeginClip 4
#define Annotated_size 32 #define Annotated_EndClip 5
#define Annotated_size 40
AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) { AnnotatedRef Annotated_index(AnnotatedRef ref, uint index) {
return AnnotatedRef(ref.offset + index * Annotated_size); return AnnotatedRef(ref.offset + index * Annotated_size);
@ -137,6 +157,40 @@ void AnnoColor_write(Alloc a, AnnoColorRef ref, AnnoColor s) {
write_mem(a, ix + 5, s.rgba_color); write_mem(a, ix + 5, s.rgba_color);
} }
AnnoLinGradient AnnoLinGradient_read(Alloc a, AnnoLinGradientRef ref) {
uint ix = ref.offset >> 2;
uint raw0 = read_mem(a, ix + 0);
uint raw1 = read_mem(a, ix + 1);
uint raw2 = read_mem(a, ix + 2);
uint raw3 = read_mem(a, ix + 3);
uint raw4 = read_mem(a, ix + 4);
uint raw5 = read_mem(a, ix + 5);
uint raw6 = read_mem(a, ix + 6);
uint raw7 = read_mem(a, ix + 7);
uint raw8 = read_mem(a, ix + 8);
AnnoLinGradient s;
s.bbox = vec4(uintBitsToFloat(raw0), uintBitsToFloat(raw1), uintBitsToFloat(raw2), uintBitsToFloat(raw3));
s.linewidth = uintBitsToFloat(raw4);
s.index = raw5;
s.line_x = uintBitsToFloat(raw6);
s.line_y = uintBitsToFloat(raw7);
s.line_c = uintBitsToFloat(raw8);
return s;
}
void AnnoLinGradient_write(Alloc a, AnnoLinGradientRef ref, AnnoLinGradient s) {
uint ix = ref.offset >> 2;
write_mem(a, ix + 0, floatBitsToUint(s.bbox.x));
write_mem(a, ix + 1, floatBitsToUint(s.bbox.y));
write_mem(a, ix + 2, floatBitsToUint(s.bbox.z));
write_mem(a, ix + 3, floatBitsToUint(s.bbox.w));
write_mem(a, ix + 4, floatBitsToUint(s.linewidth));
write_mem(a, ix + 5, s.index);
write_mem(a, ix + 6, floatBitsToUint(s.line_x));
write_mem(a, ix + 7, floatBitsToUint(s.line_y));
write_mem(a, ix + 8, floatBitsToUint(s.line_c));
}
AnnoBeginClip AnnoBeginClip_read(Alloc a, AnnoBeginClipRef ref) { AnnoBeginClip AnnoBeginClip_read(Alloc a, AnnoBeginClipRef ref) {
uint ix = ref.offset >> 2; uint ix = ref.offset >> 2;
uint raw0 = read_mem(a, ix + 0); uint raw0 = read_mem(a, ix + 0);
@ -187,6 +241,10 @@ AnnoColor Annotated_Color_read(Alloc a, AnnotatedRef ref) {
return AnnoColor_read(a, AnnoColorRef(ref.offset + 4)); return AnnoColor_read(a, AnnoColorRef(ref.offset + 4));
} }
AnnoLinGradient Annotated_LinGradient_read(Alloc a, AnnotatedRef ref) {
return AnnoLinGradient_read(a, AnnoLinGradientRef(ref.offset + 4));
}
AnnoImage Annotated_Image_read(Alloc a, AnnotatedRef ref) { AnnoImage Annotated_Image_read(Alloc a, AnnotatedRef ref) {
return AnnoImage_read(a, AnnoImageRef(ref.offset + 4)); return AnnoImage_read(a, AnnoImageRef(ref.offset + 4));
} }
@ -208,6 +266,11 @@ void Annotated_Color_write(Alloc a, AnnotatedRef ref, uint flags, AnnoColor s) {
AnnoColor_write(a, AnnoColorRef(ref.offset + 4), s); AnnoColor_write(a, AnnoColorRef(ref.offset + 4), s);
} }
void Annotated_LinGradient_write(Alloc a, AnnotatedRef ref, uint flags, AnnoLinGradient s) {
write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_LinGradient);
AnnoLinGradient_write(a, AnnoLinGradientRef(ref.offset + 4), s);
}
void Annotated_Image_write(Alloc a, AnnotatedRef ref, uint flags, AnnoImage s) { void Annotated_Image_write(Alloc a, AnnotatedRef ref, uint flags, AnnoImage s) {
write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_Image); write_mem(a, ref.offset >> 2, (flags << 16) | Annotated_Image);
AnnoImage_write(a, AnnoImageRef(ref.offset + 4), s); AnnoImage_write(a, AnnoImageRef(ref.offset + 4), s);

Binary file not shown.

Binary file not shown.

View file

@ -57,6 +57,7 @@ void main() {
int x0 = 0, y0 = 0, x1 = 0, y1 = 0; int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
switch (tag) { switch (tag) {
case Annotated_Color: case Annotated_Color:
case Annotated_LinGradient:
case Annotated_Image: case Annotated_Image:
case Annotated_BeginClip: case Annotated_BeginClip:
case Annotated_EndClip: case Annotated_EndClip:

Binary file not shown.

View file

@ -24,6 +24,3 @@ build backdrop_lg.spv: glsl backdrop.comp | annotated.h tile.h setup.h
build coarse.spv: glsl coarse.comp | annotated.h bins.h ptcl.h setup.h build coarse.spv: glsl coarse.comp | annotated.h bins.h ptcl.h setup.h
build kernel4.spv: glsl kernel4.comp | ptcl.h setup.h build kernel4.spv: glsl kernel4.comp | ptcl.h setup.h
build kernel4_idx.spv: glsl kernel4.comp | ptcl.h setup.h
flags = -DENABLE_IMAGE_INDICES

View file

@ -229,6 +229,7 @@ void main() {
switch (tag) { switch (tag) {
case Annotated_Color: case Annotated_Color:
case Annotated_Image: case Annotated_Image:
case Annotated_LinGradient:
case Annotated_BeginClip: case Annotated_BeginClip:
case Annotated_EndClip: case Annotated_EndClip:
// We have one "path" for each element, even if the element isn't // We have one "path" for each element, even if the element isn't
@ -344,6 +345,23 @@ void main() {
Cmd_Color_write(cmd_alloc, cmd_ref, CmdColor(fill.rgba_color)); Cmd_Color_write(cmd_alloc, cmd_ref, CmdColor(fill.rgba_color));
cmd_ref.offset += 4 + CmdColor_size; cmd_ref.offset += 4 + CmdColor_size;
break; break;
case Annotated_LinGradient:
// TODO: process and write linear gradient
tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix]
+ (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size));
AnnoLinGradient lin = Annotated_LinGradient_read(conf.anno_alloc, ref);
if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) {
break;
}
write_fill(cmd_alloc, cmd_ref, tag.flags, tile, fill.linewidth);
CmdLinGrad cmd_lin;
cmd_lin.index = lin.index;
cmd_lin.line_x = lin.line_x;
cmd_lin.line_y = lin.line_y;
cmd_lin.line_c = lin.line_c;
Cmd_LinGrad_write(cmd_alloc, cmd_ref, cmd_lin);
cmd_ref.offset += 4 + CmdLinGrad_size;
break;
case Annotated_Image: case Annotated_Image:
tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix] tile = Tile_read(read_tile_alloc(element_ref_ix, mem_ok), TileRef(sh_tile_base[element_ref_ix]
+ (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size)); + (sh_tile_stride[element_ref_ix] * tile_y + tile_x) * Tile_size));

Binary file not shown.

View file

@ -363,6 +363,31 @@ void main() {
AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size); AnnotatedRef out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
Annotated_Color_write(conf.anno_alloc, out_ref, fill_mode, anno_fill); Annotated_Color_write(conf.anno_alloc, out_ref, fill_mode, anno_fill);
break; break;
case Element_FillLinGradient:
FillLinGradient lin = Element_FillLinGradient_read(this_ref);
AnnoLinGradient anno_lin;
anno_lin.index = lin.index;
vec2 p0 = st.mat.xy * lin.p0.x + st.mat.zw * lin.p0.y + st.translate;
vec2 p1 = st.mat.xy * lin.p1.x + st.mat.zw * lin.p1.y + st.translate;
vec2 dxy = p1 - p0;
float scale = inversesqrt(dxy.x * dxy.x + dxy.y * dxy.y);
float line_x = dxy.x * scale;
float line_y = dxy.y * scale;
anno_lin.line_x = line_x;
anno_lin.line_y = line_y;
anno_lin.line_c = -(p0.x * line_x + p0.y * line_y);
// TODO: consider consolidating bbox calculation
if (is_stroke) {
vec2 lw = get_linewidth(st);
anno_lin.bbox = st.bbox + vec4(-lw, lw);
anno_lin.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z));
} else {
anno_lin.bbox = st.bbox;
anno_lin.linewidth = 0.0;
}
out_ref = AnnotatedRef(conf.anno_alloc.offset + (st.path_count - 1) * Annotated_size);
Annotated_LinGradient_write(conf.anno_alloc, out_ref, fill_mode, anno_lin);
break;
case Element_FillImage: case Element_FillImage:
FillImage fill_img = Element_FillImage_read(this_ref); FillImage fill_img = Element_FillImage_read(this_ref);
AnnoImage anno_img; AnnoImage anno_img;

Binary file not shown.

View file

@ -28,11 +28,9 @@ layout(set = 0, binding = 1) restrict readonly buffer ConfigBuf {
layout(rgba8, set = 0, binding = 2) uniform restrict writeonly image2D image; layout(rgba8, set = 0, binding = 2) uniform restrict writeonly image2D image;
#ifdef ENABLE_IMAGE_INDICES layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D image_atlas;
layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D images[];
#else layout(rgba8, set = 0, binding = 4) uniform restrict readonly image2D gradients;
layout(rgba8, set = 0, binding = 3) uniform restrict readonly image2D images[1];
#endif
#include "ptcl.h" #include "ptcl.h"
#include "tile.h" #include "tile.h"
@ -74,11 +72,7 @@ mediump vec4[CHUNK] fillImage(uvec2 xy, CmdImage cmd_img) {
for (uint i = 0; i < CHUNK; i++) { for (uint i = 0; i < CHUNK; i++) {
ivec2 uv = ivec2(xy + chunk_offset(i)) + cmd_img.offset; ivec2 uv = ivec2(xy + chunk_offset(i)) + cmd_img.offset;
mediump vec4 fg_rgba; mediump vec4 fg_rgba;
#ifdef ENABLE_IMAGE_INDICES fg_rgba = imageLoad(image_atlas, uv);
fg_rgba = imageLoad(images[cmd_img.index], uv);
#else
fg_rgba = imageLoad(images[0], uv);
#endif
fg_rgba.rgb = fromsRGB(fg_rgba.rgb); fg_rgba.rgb = fromsRGB(fg_rgba.rgb);
rgba[i] = fg_rgba; rgba[i] = fg_rgba;
} }
@ -99,18 +93,6 @@ void main() {
mediump vec4 rgba[CHUNK]; mediump vec4 rgba[CHUNK];
for (uint i = 0; i < CHUNK; i++) { for (uint i = 0; i < CHUNK; i++) {
rgba[i] = vec4(0.0); rgba[i] = vec4(0.0);
// TODO: remove this debug image support when the actual image method is plumbed.
#ifdef DEBUG_IMAGES
#ifdef ENABLE_IMAGE_INDICES
if (xy_uint.x < 1024 && xy_uint.y < 1024) {
rgba[i] = imageLoad(images[gl_WorkGroupID.x / 64], ivec2(xy_uint + chunk_offset(i))/4);
}
#else
if (xy_uint.x < 1024 && xy_uint.y < 1024) {
rgb[i] = imageLoad(images[0], ivec2(xy_uint + chunk_offset(i))/4).rgb;
}
#endif
#endif
} }
mediump float area[CHUNK]; mediump float area[CHUNK];
@ -198,6 +180,19 @@ void main() {
} }
cmd_ref.offset += 4 + CmdColor_size; cmd_ref.offset += 4 + CmdColor_size;
break; break;
case Cmd_LinGrad:
CmdLinGrad lin = Cmd_LinGrad_read(cmd_alloc, cmd_ref);
float d = lin.line_x * float(xy.x) + lin.line_y * float(xy.y) + lin.line_c;
for (uint k = 0; k < CHUNK; k++) {
vec2 chunk_xy = vec2(chunk_offset(k));
float my_d = d + lin.line_x * chunk_xy.x + lin.line_y * chunk_xy.y;
int x = int(round(clamp(my_d, 0.0, 1.0) * float(GRADIENT_WIDTH - 1)));
mediump vec4 fg_rgba = imageLoad(gradients, ivec2(x, int(lin.index)));
fg_rgba.rgb = fromsRGB(fg_rgba.rgb);
rgba[k] = fg_rgba;
}
cmd_ref.offset += 4 + CmdLinGrad_size;
break;
case Cmd_Image: case Cmd_Image:
CmdImage fill_img = Cmd_Image_read(cmd_alloc, cmd_ref); CmdImage fill_img = Cmd_Image_read(cmd_alloc, cmd_ref);
mediump vec4 img[CHUNK] = fillImage(xy_uint, fill_img); mediump vec4 img[CHUNK] = fillImage(xy_uint, fill_img);

Binary file not shown.

View file

@ -14,6 +14,10 @@ struct CmdColorRef {
uint offset; uint offset;
}; };
struct CmdLinGradRef {
uint offset;
};
struct CmdImageRef { struct CmdImageRef {
uint offset; uint offset;
}; };
@ -62,6 +66,19 @@ CmdColorRef CmdColor_index(CmdColorRef ref, uint index) {
return CmdColorRef(ref.offset + index * CmdColor_size); return CmdColorRef(ref.offset + index * CmdColor_size);
} }
struct CmdLinGrad {
uint index;
float line_x;
float line_y;
float line_c;
};
#define CmdLinGrad_size 16
CmdLinGradRef CmdLinGrad_index(CmdLinGradRef ref, uint index) {
return CmdLinGradRef(ref.offset + index * CmdLinGrad_size);
}
struct CmdImage { struct CmdImage {
uint index; uint index;
ivec2 offset; ivec2 offset;
@ -99,11 +116,12 @@ CmdJumpRef CmdJump_index(CmdJumpRef ref, uint index) {
#define Cmd_Solid 3 #define Cmd_Solid 3
#define Cmd_Alpha 4 #define Cmd_Alpha 4
#define Cmd_Color 5 #define Cmd_Color 5
#define Cmd_Image 6 #define Cmd_LinGrad 6
#define Cmd_BeginClip 7 #define Cmd_Image 7
#define Cmd_EndClip 8 #define Cmd_BeginClip 8
#define Cmd_Jump 9 #define Cmd_EndClip 9
#define Cmd_size 12 #define Cmd_Jump 10
#define Cmd_size 20
CmdRef Cmd_index(CmdRef ref, uint index) { CmdRef Cmd_index(CmdRef ref, uint index) {
return CmdRef(ref.offset + index * Cmd_size); return CmdRef(ref.offset + index * Cmd_size);
@ -159,6 +177,28 @@ void CmdColor_write(Alloc a, CmdColorRef ref, CmdColor s) {
write_mem(a, ix + 0, s.rgba_color); write_mem(a, ix + 0, s.rgba_color);
} }
CmdLinGrad CmdLinGrad_read(Alloc a, CmdLinGradRef ref) {
uint ix = ref.offset >> 2;
uint raw0 = read_mem(a, ix + 0);
uint raw1 = read_mem(a, ix + 1);
uint raw2 = read_mem(a, ix + 2);
uint raw3 = read_mem(a, ix + 3);
CmdLinGrad s;
s.index = raw0;
s.line_x = uintBitsToFloat(raw1);
s.line_y = uintBitsToFloat(raw2);
s.line_c = uintBitsToFloat(raw3);
return s;
}
void CmdLinGrad_write(Alloc a, CmdLinGradRef ref, CmdLinGrad s) {
uint ix = ref.offset >> 2;
write_mem(a, ix + 0, s.index);
write_mem(a, ix + 1, floatBitsToUint(s.line_x));
write_mem(a, ix + 2, floatBitsToUint(s.line_y));
write_mem(a, ix + 3, floatBitsToUint(s.line_c));
}
CmdImage CmdImage_read(Alloc a, CmdImageRef ref) { CmdImage CmdImage_read(Alloc a, CmdImageRef ref) {
uint ix = ref.offset >> 2; uint ix = ref.offset >> 2;
uint raw0 = read_mem(a, ix + 0); uint raw0 = read_mem(a, ix + 0);
@ -222,6 +262,10 @@ CmdColor Cmd_Color_read(Alloc a, CmdRef ref) {
return CmdColor_read(a, CmdColorRef(ref.offset + 4)); return CmdColor_read(a, CmdColorRef(ref.offset + 4));
} }
CmdLinGrad Cmd_LinGrad_read(Alloc a, CmdRef ref) {
return CmdLinGrad_read(a, CmdLinGradRef(ref.offset + 4));
}
CmdImage Cmd_Image_read(Alloc a, CmdRef ref) { CmdImage Cmd_Image_read(Alloc a, CmdRef ref) {
return CmdImage_read(a, CmdImageRef(ref.offset + 4)); return CmdImage_read(a, CmdImageRef(ref.offset + 4));
} }
@ -258,6 +302,11 @@ void Cmd_Color_write(Alloc a, CmdRef ref, CmdColor s) {
CmdColor_write(a, CmdColorRef(ref.offset + 4), s); CmdColor_write(a, CmdColorRef(ref.offset + 4), s);
} }
void Cmd_LinGrad_write(Alloc a, CmdRef ref, CmdLinGrad s) {
write_mem(a, ref.offset >> 2, Cmd_LinGrad);
CmdLinGrad_write(a, CmdLinGradRef(ref.offset + 4), s);
}
void Cmd_Image_write(Alloc a, CmdRef ref, CmdImage s) { void Cmd_Image_write(Alloc a, CmdRef ref, CmdImage s) {
write_mem(a, ref.offset >> 2, Cmd_Image); write_mem(a, ref.offset >> 2, Cmd_Image);
CmdImage_write(a, CmdImageRef(ref.offset + 4), s); CmdImage_write(a, CmdImageRef(ref.offset + 4), s);

View file

@ -18,6 +18,10 @@ struct FillColorRef {
uint offset; uint offset;
}; };
struct FillLinGradientRef {
uint offset;
};
struct FillImageRef { struct FillImageRef {
uint offset; uint offset;
}; };
@ -88,6 +92,18 @@ FillColorRef FillColor_index(FillColorRef ref, uint index) {
return FillColorRef(ref.offset + index * FillColor_size); return FillColorRef(ref.offset + index * FillColor_size);
} }
struct FillLinGradient {
uint index;
vec2 p0;
vec2 p1;
};
#define FillLinGradient_size 20
FillLinGradientRef FillLinGradient_index(FillLinGradientRef ref, uint index) {
return FillLinGradientRef(ref.offset + index * FillLinGradient_size);
}
struct FillImage { struct FillImage {
uint index; uint index;
ivec2 offset; ivec2 offset;
@ -145,12 +161,13 @@ SetFillModeRef SetFillMode_index(SetFillModeRef ref, uint index) {
#define Element_Quad 2 #define Element_Quad 2
#define Element_Cubic 3 #define Element_Cubic 3
#define Element_FillColor 4 #define Element_FillColor 4
#define Element_SetLineWidth 5 #define Element_FillLinGradient 5
#define Element_Transform 6 #define Element_FillImage 6
#define Element_BeginClip 7 #define Element_SetLineWidth 7
#define Element_EndClip 8 #define Element_Transform 8
#define Element_FillImage 9 #define Element_BeginClip 9
#define Element_SetFillMode 10 #define Element_EndClip 10
#define Element_SetFillMode 11
#define Element_size 36 #define Element_size 36
ElementRef Element_index(ElementRef ref, uint index) { ElementRef Element_index(ElementRef ref, uint index) {
@ -215,6 +232,20 @@ FillColor FillColor_read(FillColorRef ref) {
return s; return s;
} }
FillLinGradient FillLinGradient_read(FillLinGradientRef ref) {
uint ix = ref.offset >> 2;
uint raw0 = scene[ix + 0];
uint raw1 = scene[ix + 1];
uint raw2 = scene[ix + 2];
uint raw3 = scene[ix + 3];
uint raw4 = scene[ix + 4];
FillLinGradient s;
s.index = raw0;
s.p0 = vec2(uintBitsToFloat(raw1), uintBitsToFloat(raw2));
s.p1 = vec2(uintBitsToFloat(raw3), uintBitsToFloat(raw4));
return s;
}
FillImage FillImage_read(FillImageRef ref) { FillImage FillImage_read(FillImageRef ref) {
uint ix = ref.offset >> 2; uint ix = ref.offset >> 2;
uint raw0 = scene[ix + 0]; uint raw0 = scene[ix + 0];
@ -287,6 +318,14 @@ FillColor Element_FillColor_read(ElementRef ref) {
return FillColor_read(FillColorRef(ref.offset + 4)); return FillColor_read(FillColorRef(ref.offset + 4));
} }
FillLinGradient Element_FillLinGradient_read(ElementRef ref) {
return FillLinGradient_read(FillLinGradientRef(ref.offset + 4));
}
FillImage Element_FillImage_read(ElementRef ref) {
return FillImage_read(FillImageRef(ref.offset + 4));
}
SetLineWidth Element_SetLineWidth_read(ElementRef ref) { SetLineWidth Element_SetLineWidth_read(ElementRef ref) {
return SetLineWidth_read(SetLineWidthRef(ref.offset + 4)); return SetLineWidth_read(SetLineWidthRef(ref.offset + 4));
} }
@ -303,10 +342,6 @@ Clip Element_EndClip_read(ElementRef ref) {
return Clip_read(ClipRef(ref.offset + 4)); return Clip_read(ClipRef(ref.offset + 4));
} }
FillImage Element_FillImage_read(ElementRef ref) {
return FillImage_read(FillImageRef(ref.offset + 4));
}
SetFillMode Element_SetFillMode_read(ElementRef ref) { SetFillMode Element_SetFillMode_read(ElementRef ref) {
return SetFillMode_read(SetFillModeRef(ref.offset + 4)); return SetFillMode_read(SetFillModeRef(ref.offset + 4));
} }

View file

@ -28,6 +28,8 @@
#define LG_N_TILE (7 + LG_WG_FACTOR) #define LG_N_TILE (7 + LG_WG_FACTOR)
#define N_SLICE (N_TILE / 32) #define N_SLICE (N_TILE / 32)
#define GRADIENT_WIDTH 512
struct Config { struct Config {
uint n_elements; // paths uint n_elements; // paths
uint n_pathseg; uint n_pathseg;

Binary file not shown.

145
piet-gpu/src/gradient.rs Normal file
View file

@ -0,0 +1,145 @@
// Copyright 2021 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
//! Implementation of gradients.
use std::collections::hash_map::{Entry, HashMap};
use piet::{Color, FixedLinearGradient, GradientStop};
#[derive(Clone)]
pub struct BakedGradient {
ramp: Vec<u32>,
}
#[derive(Clone)]
pub struct LinearGradient {
start: [f32; 2],
end: [f32; 2],
ramp_id: u32,
}
#[derive(Default)]
pub struct RampCache {
ramps: Vec<GradientRamp>,
map: HashMap<GradientRamp, usize>,
}
#[derive(Clone, Hash, PartialEq, Eq)]
struct GradientRamp(Vec<u32>);
pub const N_SAMPLES: usize = 512;
// TODO: make this dynamic
pub const N_GRADIENTS: usize = 256;
#[derive(Clone, Copy)]
struct PremulRgba([f64; 4]);
impl PremulRgba {
fn from_color(c: &Color) -> PremulRgba {
let rgba = c.as_rgba();
let a = rgba.3;
// TODO: sRGB nonlinearity? This is complicated.
PremulRgba([rgba.0 * a, rgba.1 * a, rgba.2 * a, a])
}
fn to_u32(&self) -> u32 {
let z = self.0;
Color::rgba(z[0], z[1], z[2], z[3]).as_rgba_u32()
}
fn lerp(&self, other: PremulRgba, t: f64) -> PremulRgba {
fn l(a: f64, b: f64, t: f64) -> f64 {
a * (1.0 - t) + b * t
}
let a = self.0;
let b = other.0;
PremulRgba([l(a[0], b[0], t), l(a[1], b[1], t), l(a[2], b[2], t), l(a[2], b[3], t)])
}
}
impl GradientRamp {
fn from_stops(stops: &[GradientStop]) -> GradientRamp {
let mut last_u = 0.0;
let mut last_c = PremulRgba::from_color(&stops[0].color);
let mut this_u = last_u;
let mut this_c = last_c;
let mut j = 0;
let v = (0..N_SAMPLES).map(|i| {
let u = (i as f64) / 255.0;
while u > this_u {
last_u = this_u;
last_c = this_c;
if let Some(s) = stops.get(j + 1) {
this_u = s.pos as f64;
this_c = PremulRgba::from_color(&s.color);
j += 1;
} else {
break;
}
}
let du = this_u - last_u;
let c = if du < 1e-9 {
this_c
} else {
last_c.lerp(this_c, (u - last_u) / du)
};
c.to_u32()
}).collect();
GradientRamp(v)
}
}
impl RampCache {
/// Add a gradient ramp to the cache.
///
/// Currently there is no eviction, so if the gradient is animating, there may
/// be resource leaks. In order to support lifetime management, the signature
/// should probably change so it returns a ref-counted handle, so that eviction
/// is deferred until the last handle is dropped.
///
/// This function is pretty expensive, but the result is lightweight.
fn add_ramp(&mut self, ramp: &[GradientStop]) -> usize {
let ramp = GradientRamp::from_stops(ramp);
match self.map.entry(ramp) {
Entry::Occupied(o) => *o.get(),
Entry::Vacant(v) => {
let idx = self.ramps.len();
self.ramps.push(v.key().clone());
v.insert(idx);
idx
}
}
}
pub fn add_linear_gradient(&mut self, lin: &FixedLinearGradient) -> LinearGradient {
let ramp_id = self.add_ramp(&lin.stops);
LinearGradient {
ramp_id: ramp_id as u32,
start: crate::render_ctx::to_f32_2(lin.start),
end: crate::render_ctx::to_f32_2(lin.end),
}
}
}
#[cfg(test)]
mod test {
#[test]
fn it_works() {
println!("it works!");
}
}

View file

@ -1,3 +1,4 @@
mod gradient;
mod pico_svg; mod pico_svg;
mod render_ctx; mod render_ctx;
mod text; mod text;
@ -248,6 +249,8 @@ pub struct Renderer {
// Keep a reference to the image so that it is not destroyed. // Keep a reference to the image so that it is not destroyed.
_bg_image: Image, _bg_image: Image,
gradients: Image,
} }
impl Renderer { impl Renderer {
@ -351,22 +354,14 @@ impl Renderer {
let bg_image = Self::make_test_bg_image(&session); let bg_image = Self::make_test_bg_image(&session);
let k4_code = if session.gpu_info().has_descriptor_indexing { let gradients = Self::make_gradient_image(&session);
ShaderCode::Spv(include_bytes!("../shader/kernel4_idx.spv"))
} else { let k4_code = ShaderCode::Spv(include_bytes!("../shader/kernel4.spv"));
println!("doing non-indexed k4"); // This is a bit of a stand-in for future development. For now, we assume one
ShaderCode::Spv(include_bytes!("../shader/kernel4.spv")) // atlas image for all images, and another image for the gradients. In the future,
}; // on GPUs that support it, we will probably want to go to descriptor indexing in
// This is an arbitrary limit on the number of textures that can be referenced by // order to cut down on allocation and copying for the atlas image.
// the fine rasterizer. To set it for real, we probably want to pay attention both let max_textures = 2;
// to the device limit (maxDescriptorSetSampledImages) but also to the number of
// images encoded (I believe there's an cost when allocating descriptor pools). If
// it can't be satisfied, then for compatibility we'll probably want to fall back
// to an atlasing approach.
//
// However, we're adding only one texture for now. Avoid a harmless Vulkan validation
// error by using a tight bound.
let max_textures = 1;
let k4_pipeline = session let k4_pipeline = session
.pipeline_builder() .pipeline_builder()
.add_buffers(2) .add_buffers(2)
@ -377,7 +372,7 @@ impl Renderer {
.descriptor_set_builder() .descriptor_set_builder()
.add_buffers(&[&memory_buf_dev, &config_buf]) .add_buffers(&[&memory_buf_dev, &config_buf])
.add_images(&[&image_dev]) .add_images(&[&image_dev])
.add_textures(&[&bg_image]) .add_textures(&[&bg_image, &gradients])
.build(&session, &k4_pipeline)?; .build(&session, &k4_pipeline)?;
Ok(Renderer { Ok(Renderer {
@ -405,6 +400,7 @@ impl Renderer {
n_paths, n_paths,
n_pathseg, n_pathseg,
_bg_image: bg_image, _bg_image: bg_image,
gradients: gradients,
}) })
} }
@ -417,6 +413,12 @@ impl Renderer {
ImageLayout::Undefined, ImageLayout::Undefined,
ImageLayout::General, ImageLayout::General,
); );
// TODO: make gradient upload optional, only if it's changed
cmd_buf.image_barrier(
&self.gradients,
ImageLayout::Undefined,
ImageLayout::General,
);
cmd_buf.reset_query_pool(&query_pool); cmd_buf.reset_query_pool(&query_pool);
cmd_buf.write_timestamp(&query_pool, 0); cmd_buf.write_timestamp(&query_pool, 0);
cmd_buf.dispatch( cmd_buf.dispatch(
@ -492,9 +494,7 @@ impl Renderer {
if format != ImageFormat::RgbaPremul { if format != ImageFormat::RgbaPremul {
return Err("unsupported image format".into()); return Err("unsupported image format".into());
} }
let host_upload = BufferUsage::MAP_WRITE | BufferUsage::COPY_SRC; let buffer = session.create_buffer_init(&buf, BufferUsage::COPY_SRC)?;
let mut buffer = session.create_buffer(buf.len() as u64, host_upload)?;
buffer.write(buf)?;
let image = session.create_image2d(width.try_into()?, height.try_into()?)?; let image = session.create_image2d(width.try_into()?, height.try_into()?)?;
let mut cmd_buf = session.cmd_buf()?; let mut cmd_buf = session.cmd_buf()?;
cmd_buf.begin(); cmd_buf.begin();
@ -528,4 +528,10 @@ impl Renderer {
} }
Self::make_image(session, WIDTH, HEIGHT, &buf, ImageFormat::RgbaPremul).unwrap() Self::make_image(session, WIDTH, HEIGHT, &buf, ImageFormat::RgbaPremul).unwrap()
} }
fn make_gradient_image(session: &Session) -> Image {
unsafe {
session.create_image2d(gradient::N_SAMPLES as u32, gradient::N_GRADIENTS as u32).unwrap()
}
}
} }

View file

@ -14,6 +14,7 @@ use piet_gpu_types::scene::{
Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetFillMode, SetLineWidth, Transform, Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetFillMode, SetLineWidth, Transform,
}; };
use crate::gradient::{LinearGradient, RampCache};
use crate::text::Font; use crate::text::Font;
pub use crate::text::{PathEncoder, PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder}; pub use crate::text::{PathEncoder, PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder};
@ -38,12 +39,14 @@ pub struct PietGpuRenderContext {
cur_transform: Affine, cur_transform: Affine,
state_stack: Vec<State>, state_stack: Vec<State>,
clip_stack: Vec<ClipElement>, clip_stack: Vec<ClipElement>,
ramp_cache: RampCache,
} }
#[derive(Clone)] #[derive(Clone)]
pub enum PietGpuBrush { pub enum PietGpuBrush {
Solid(u32), Solid(u32),
Gradient, LinGradient(LinearGradient),
} }
#[derive(Default)] #[derive(Default)]
@ -92,6 +95,7 @@ impl PietGpuRenderContext {
cur_transform: Affine::default(), cur_transform: Affine::default(),
state_stack: Vec::new(), state_stack: Vec::new(),
clip_stack: Vec::new(), clip_stack: Vec::new(),
ramp_cache: RampCache::default(),
} }
} }
@ -148,8 +152,14 @@ impl RenderContext for PietGpuRenderContext {
PietGpuBrush::Solid(premul.as_rgba_u32()) PietGpuBrush::Solid(premul.as_rgba_u32())
} }
fn gradient(&mut self, _gradient: impl Into<FixedGradient>) -> Result<Self::Brush, Error> { fn gradient(&mut self, gradient: impl Into<FixedGradient>) -> Result<Self::Brush, Error> {
Ok(Self::Brush::Gradient) match gradient.into() {
FixedGradient::Linear(lin) => {
let lin = self.ramp_cache.add_linear_gradient(&lin);
Ok(PietGpuBrush::LinGradient(lin))
}
_ => todo!("don't do radial gradients yet"),
}
} }
fn clear(&mut self, _color: Color) {} fn clear(&mut self, _color: Color) {}