mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-08-04 14:46:03 +00:00
Use only 2 drawcalls for filling compensatory padding
This commit is contained in:
parent
e44a7b9583
commit
ae33c422ff
4 changed files with 174 additions and 59 deletions
19
kitty/gl.c
19
kitty/gl.c
|
|
@ -676,4 +676,23 @@ unmap_vao_buffer(ssize_t vao_idx, size_t bufnum) {
|
|||
unbind_buffer(buf_idx);
|
||||
}
|
||||
|
||||
void
|
||||
copy_vao_buffer_region(ssize_t vao_idx, size_t src_bufnum, GLintptr src_off,
|
||||
size_t dst_bufnum, GLintptr dst_off, GLsizeiptr size) {
|
||||
ssize_t src_buf = vaos[vao_idx].buffers[src_bufnum];
|
||||
ssize_t dst_buf = vaos[vao_idx].buffers[dst_bufnum];
|
||||
glBindBuffer(GL_COPY_READ_BUFFER, buffers[src_buf].id);
|
||||
glBindBuffer(GL_COPY_WRITE_BUFFER, buffers[dst_buf].id);
|
||||
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, src_off, dst_off, size);
|
||||
glBindBuffer(GL_COPY_READ_BUFFER, 0);
|
||||
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
|
||||
}
|
||||
|
||||
const void*
|
||||
map_vao_buffer_for_reading(ssize_t vao_idx, size_t bufnum) {
|
||||
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
||||
bind_buffer(buf_idx);
|
||||
return map_buffer(buf_idx, GL_READ_ONLY);
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ void* map_vao_buffer_for_write_only(ssize_t vao_idx, size_t bufnum, int offset,
|
|||
void bind_program(int program);
|
||||
void bind_vertex_array(ssize_t vao_idx);
|
||||
void bind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index);
|
||||
void copy_vao_buffer_region(ssize_t vao_idx, size_t src_bufnum, GLintptr src_off, size_t dst_bufnum, GLintptr dst_off, GLsizeiptr size);
|
||||
const void* map_vao_buffer_for_reading(ssize_t vao_idx, size_t bufnum);
|
||||
void unbind_vertex_array(void);
|
||||
void unbind_program(void);
|
||||
GLuint compile_shaders(GLenum shader_type, GLsizei count, const GLchar * const * string);
|
||||
|
|
|
|||
162
kitty/shaders.c
162
kitty/shaders.c
|
|
@ -408,7 +408,7 @@ bind_shader_globals_to_current_context(void) {
|
|||
bind_vao_uniform_buffer(shader_globals_vao_idx, BORDER_COLORS_GLOBAL_BUFFER, BORDER_COLORS_BINDING_POINT);
|
||||
}
|
||||
|
||||
#define CELL_BUFFERS enum { cell_data_buffer, selection_buffer, uniform_buffer, color_table_buffer };
|
||||
#define CELL_BUFFERS enum { cell_data_buffer, selection_buffer, uniform_buffer, color_table_buffer, padding_cell_data_buffer, padding_selection_buffer };
|
||||
|
||||
ssize_t
|
||||
create_cell_vao(void) {
|
||||
|
|
@ -431,6 +431,9 @@ create_cell_vao(void) {
|
|||
size_t ctbufnum = add_buffer_to_vao(vao_idx, GL_UNIFORM_BUFFER);
|
||||
alloc_vao_buffer(vao_idx, program_uniform_block(CELL_PROGRAM, "ColorTable").size, ctbufnum, GL_STATIC_DRAW);
|
||||
|
||||
add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); // padding_cell_data_buffer (slot 4)
|
||||
add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); // padding_selection_buffer (slot 5)
|
||||
|
||||
return vao_idx;
|
||||
#undef A
|
||||
#undef A1
|
||||
|
|
@ -1349,9 +1352,9 @@ draw_cells_without_layers(const UIRenderData *ui, ssize_t vao_idx) {
|
|||
static void
|
||||
configure_cell_vao_attributes(ssize_t vao_idx, unsigned int base_cell, unsigned int cell_step) {
|
||||
// (Re)point the instanced cell attributes so that instance i corresponds to
|
||||
// the cell at (base_cell + i*cell_step). Used to draw a single padding strip
|
||||
// from the shared cell VAO, and to restore the canonical layout afterwards
|
||||
// (base_cell=0, cell_step=1). The VAO must be bound before calling this.
|
||||
// the cell at (base_cell + i*cell_step). Used to restore the canonical
|
||||
// layout after padding draws (base_cell=0, cell_step=1). The VAO must be
|
||||
// bound before calling this.
|
||||
CELL_BUFFERS;
|
||||
const GLsizei cell_stride = (GLsizei)(cell_step * sizeof(GPUCell));
|
||||
const uintptr_t cell_base = (uintptr_t)base_cell * sizeof(GPUCell);
|
||||
|
|
@ -1364,24 +1367,20 @@ configure_cell_vao_attributes(ssize_t vao_idx, unsigned int base_cell, unsigned
|
|||
}
|
||||
|
||||
static void
|
||||
draw_padding_strip(
|
||||
ssize_t vao_idx, bool for_final_output, unsigned int is_horizontal, unsigned int count,
|
||||
unsigned int base_cell, unsigned int cell_step, float across0, float across1,
|
||||
float along_start, float along_step, float clamp_lo, float clamp_hi
|
||||
) {
|
||||
if (!count) return;
|
||||
configure_cell_vao_attributes(vao_idx, base_cell, cell_step);
|
||||
#define L(x) program_uniform_location(PADDING_PROGRAM, #x)
|
||||
glUniform1ui(L(is_horizontal), is_horizontal);
|
||||
glUniform1ui(L(along_count), count);
|
||||
glUniform1ui(L(base_instance), base_cell);
|
||||
glUniform1ui(L(instance_step), cell_step);
|
||||
glUniform2f(L(across), across0, across1);
|
||||
glUniform1f(L(along_start), along_start);
|
||||
glUniform1f(L(along_step), along_step);
|
||||
glUniform2f(L(along_clamp), clamp_lo, clamp_hi);
|
||||
#undef L
|
||||
draw_quad(!for_final_output, count);
|
||||
configure_padding_vao_attributes(ssize_t vao_idx) {
|
||||
// Point the instanced cell attributes at the dedicated padding buffers
|
||||
// (base=0, stride=sizeof(GPUCell)), which are pre-packed before each
|
||||
// combined draw. The VAO must be bound before calling this.
|
||||
CELL_BUFFERS;
|
||||
set_vao_attribute(vao_idx, padding_cell_data_buffer,
|
||||
program_attribute_location(CELL_PROGRAM, "sprite_idx"),
|
||||
2, GL_UNSIGNED_INT, sizeof(GPUCell), (void*)offsetof(GPUCell, sprite_idx), 1);
|
||||
set_vao_attribute(vao_idx, padding_cell_data_buffer,
|
||||
program_attribute_location(CELL_PROGRAM, "colors"),
|
||||
3, GL_UNSIGNED_INT, sizeof(GPUCell), (void*)offsetof(GPUCell, fg), 1);
|
||||
set_vao_attribute(vao_idx, padding_selection_buffer,
|
||||
program_attribute_location(CELL_PROGRAM, "is_selected"),
|
||||
1, GL_UNSIGNED_BYTE, sizeof(GLubyte), NULL, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1390,6 +1389,11 @@ draw_window_padding(const UIRenderData *ui, Window *window, ssize_t vao_idx, boo
|
|||
// padding, arising from the window size not being an exact multiple of the
|
||||
// cell size) to match their neighboring cell. The strips lie outside the
|
||||
// per-window cell viewport, so this runs with the full framebuffer viewport.
|
||||
//
|
||||
// Two instanced draws are issued: one covers the horizontal pair (top+bottom)
|
||||
// and one the vertical pair (left+right). Each draw packs its strip cells into
|
||||
// a dedicated buffer so the VAO needs no per-strip reconfiguration. The shader
|
||||
// selects per-strip geometry and cell indices branch-free via lerp.
|
||||
if (!window || OPT(padding_fill_strategy) != PADDING_FILL_NEIGHBORING_CELL) return;
|
||||
const unsigned int cl = window->size_mismatch_padding.left, ct = window->size_mismatch_padding.top,
|
||||
cr = window->size_mismatch_padding.right, cb = window->size_mismatch_padding.bottom;
|
||||
|
|
@ -1402,8 +1406,8 @@ draw_window_padding(const UIRenderData *ui, Window *window, ssize_t vao_idx, boo
|
|||
|
||||
const float fbw = (float)ui->full_framebuffer_width, fbh = (float)ui->full_framebuffer_height;
|
||||
const float cw = (float)ui->cell_width, ch = (float)ui->cell_height;
|
||||
const float L = (float)ui->screen_left, T = (float)ui->screen_top;
|
||||
const float R = L + (float)ui->screen_width, B = T + (float)ui->screen_height;
|
||||
const float fL = (float)ui->screen_left, T = (float)ui->screen_top;
|
||||
const float R = fL + (float)ui->screen_width, B = T + (float)ui->screen_height;
|
||||
#define NX(px) (2.f * (px) / fbw - 1.f)
|
||||
#define NY(px) (1.f - 2.f * (px) / fbh)
|
||||
const float dx = 2.f * cw / fbw, dy = 2.f * ch / fbh;
|
||||
|
|
@ -1415,23 +1419,103 @@ draw_window_padding(const UIRenderData *ui, Window *window, ssize_t vao_idx, boo
|
|||
bind_vao_uniform_buffer(vao_idx, color_table_buffer, COLOR_TABLE_BINDING_POINT);
|
||||
if (for_final_output) glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
|
||||
// Top strip: spans content width, per top-row cell. across selected by cell
|
||||
// corner: top corner -> outer edge (T-ct), bottom corner -> content edge (T).
|
||||
if (ct) draw_padding_strip(vao_idx, for_final_output, 1u, columns, top_row * columns, 1u,
|
||||
NY(T - ct), NY(T), NX(L), dx, NX(L), NX(R));
|
||||
// Bottom strip: top corner -> content edge (B), bottom corner -> outer (B+cb).
|
||||
if (cb) draw_padding_strip(vao_idx, for_final_output, 1u, columns, bottom_row * columns, 1u,
|
||||
NY(B), NY(B + cb), NX(L), dx, NX(L), NX(R));
|
||||
// Left strip: full comp-frame height (corners via along_clamp), per left-column
|
||||
// cell. left corner -> outer (L-cl), right corner -> content edge (L).
|
||||
if (cl) draw_padding_strip(vao_idx, for_final_output, 0u, lines, top_row * columns, columns,
|
||||
NX(L - cl), NX(L), NY(T), -dy, NY(T - ct), NY(B + cb));
|
||||
// Right strip: left corner -> content edge (R), right corner -> outer (R+cr).
|
||||
if (cr) draw_padding_strip(vao_idx, for_final_output, 0u, lines, top_row * columns + (columns - 1u), columns,
|
||||
NX(R), NX(R + cr), NY(T), -dy, NY(T - ct), NY(B + cb));
|
||||
// Point instanced attributes at the padding-specific buffers once for both draws.
|
||||
configure_padding_vao_attributes(vao_idx);
|
||||
|
||||
#define PL(x) program_uniform_location(PADDING_PROGRAM, #x)
|
||||
|
||||
// Horizontal combined draw: top strip (strip 0) + bottom strip (strip 1).
|
||||
// Cell data is packed contiguously via GPU-side copies (no CPU round-trip).
|
||||
if (ct || cb) {
|
||||
const unsigned int nH = (ct ? 1u : 0u) + (cb ? 1u : 0u);
|
||||
alloc_vao_buffer(vao_idx, (GLsizeiptr)(nH * columns * sizeof(GPUCell)), padding_cell_data_buffer, GL_DYNAMIC_DRAW);
|
||||
alloc_vao_buffer(vao_idx, (GLsizeiptr)(nH * columns * sizeof(GLubyte)), padding_selection_buffer, GL_DYNAMIC_DRAW);
|
||||
|
||||
unsigned int sidx = 0u;
|
||||
if (ct) {
|
||||
copy_vao_buffer_region(vao_idx, cell_data_buffer, (GLintptr)(top_row * columns * sizeof(GPUCell)),
|
||||
padding_cell_data_buffer, (GLintptr)(sidx * columns * sizeof(GPUCell)), (GLsizeiptr)(columns * sizeof(GPUCell)));
|
||||
copy_vao_buffer_region(vao_idx, selection_buffer, (GLintptr)(top_row * columns * sizeof(GLubyte)),
|
||||
padding_selection_buffer, (GLintptr)(sidx * columns * sizeof(GLubyte)), (GLsizeiptr)(columns * sizeof(GLubyte)));
|
||||
sidx++;
|
||||
}
|
||||
if (cb) {
|
||||
copy_vao_buffer_region(vao_idx, cell_data_buffer, (GLintptr)(bottom_row * columns * sizeof(GPUCell)),
|
||||
padding_cell_data_buffer, (GLintptr)(sidx * columns * sizeof(GPUCell)), (GLsizeiptr)(columns * sizeof(GPUCell)));
|
||||
copy_vao_buffer_region(vao_idx, selection_buffer, (GLintptr)(bottom_row * columns * sizeof(GLubyte)),
|
||||
padding_selection_buffer, (GLintptr)(sidx * columns * sizeof(GLubyte)), (GLsizeiptr)(columns * sizeof(GLubyte)));
|
||||
}
|
||||
|
||||
// Strip-0 is top (or bottom when only bottom exists); strip-1 is bottom.
|
||||
// For a single-strip draw base_instance2/across2 equal strip-0 values so
|
||||
// the shader lerp is a no-op for all instances (strip_f is always 0.0).
|
||||
const unsigned int base0 = (ct ? top_row : bottom_row) * columns;
|
||||
const unsigned int base1 = bottom_row * columns;
|
||||
glUniform1ui(PL(is_horizontal), 1u);
|
||||
glUniform1ui(PL(along_count), columns);
|
||||
glUniform1ui(PL(instance_step), 1u);
|
||||
glUniform1ui(PL(base_instance), base0);
|
||||
glUniform1ui(PL(base_instance2), base1);
|
||||
glUniform2f(PL(across), ct ? NY(T - ct) : NY(B), ct ? NY(T) : NY(B + cb));
|
||||
glUniform2f(PL(across2), NY(B), NY(B + cb));
|
||||
glUniform1f(PL(along_start), NX(fL));
|
||||
glUniform1f(PL(along_step), dx);
|
||||
glUniform2f(PL(along_clamp), NX(fL), NX(R));
|
||||
draw_quad(!for_final_output, nH * columns);
|
||||
}
|
||||
|
||||
// Vertical combined draw: left strip (strip 0) + right strip (strip 1).
|
||||
// Column cells are non-contiguous in the cell buffer so we gather them
|
||||
// CPU-side by mapping the buffer for reading, then upload in one shot.
|
||||
if (cl || cr) {
|
||||
const unsigned int nV = (cl ? 1u : 0u) + (cr ? 1u : 0u);
|
||||
// VLA sizes: lines is bounded by the window height / cell height (~<500).
|
||||
GPUCell gathered_cells[2 * lines];
|
||||
GLubyte gathered_sel[2 * lines];
|
||||
|
||||
const unsigned int strip0_col = cl ? 0u : (columns - 1u);
|
||||
const unsigned int strip1_col = columns - 1u;
|
||||
|
||||
const GPUCell *cells = (const GPUCell*)map_vao_buffer_for_reading(vao_idx, cell_data_buffer);
|
||||
for (unsigned int i = 0; i < lines; i++) {
|
||||
gathered_cells[i] = cells[(top_row + i) * columns + strip0_col];
|
||||
if (nV == 2u) gathered_cells[lines + i] = cells[(top_row + i) * columns + strip1_col];
|
||||
}
|
||||
unmap_vao_buffer(vao_idx, cell_data_buffer);
|
||||
|
||||
const GLubyte *sel = (const GLubyte*)map_vao_buffer_for_reading(vao_idx, selection_buffer);
|
||||
for (unsigned int i = 0; i < lines; i++) {
|
||||
gathered_sel[i] = sel[(top_row + i) * columns + strip0_col];
|
||||
if (nV == 2u) gathered_sel[lines + i] = sel[(top_row + i) * columns + strip1_col];
|
||||
}
|
||||
unmap_vao_buffer(vao_idx, selection_buffer);
|
||||
|
||||
void *dst = alloc_and_map_vao_buffer(vao_idx, (GLsizeiptr)(nV * lines * sizeof(GPUCell)), padding_cell_data_buffer, false);
|
||||
memcpy(dst, gathered_cells, nV * lines * sizeof(GPUCell));
|
||||
unmap_vao_buffer(vao_idx, padding_cell_data_buffer);
|
||||
|
||||
dst = alloc_and_map_vao_buffer(vao_idx, (GLsizeiptr)(nV * lines * sizeof(GLubyte)), padding_selection_buffer, false);
|
||||
memcpy(dst, gathered_sel, nV * lines * sizeof(GLubyte));
|
||||
unmap_vao_buffer(vao_idx, padding_selection_buffer);
|
||||
|
||||
const unsigned int base0 = top_row * columns + (cl ? 0u : (columns - 1u));
|
||||
const unsigned int base1 = top_row * columns + (columns - 1u);
|
||||
glUniform1ui(PL(is_horizontal), 0u);
|
||||
glUniform1ui(PL(along_count), lines);
|
||||
glUniform1ui(PL(instance_step), columns);
|
||||
glUniform1ui(PL(base_instance), base0);
|
||||
glUniform1ui(PL(base_instance2), base1);
|
||||
glUniform2f(PL(across), cl ? NX(fL - cl) : NX(R), cl ? NX(fL) : NX(R + cr));
|
||||
glUniform2f(PL(across2), NX(R), NX(R + cr));
|
||||
glUniform1f(PL(along_start), NY(T));
|
||||
glUniform1f(PL(along_step), -dy);
|
||||
glUniform2f(PL(along_clamp), NY(T - ct), NY(B + cb));
|
||||
draw_quad(!for_final_output, nV * lines);
|
||||
}
|
||||
|
||||
#undef PL
|
||||
if (for_final_output) glDisable(GL_FRAMEBUFFER_SRGB);
|
||||
// Restore the canonical cell attribute layout so cell rendering is unaffected.
|
||||
// Restore the canonical cell attribute layout so subsequent cell rendering is unaffected.
|
||||
configure_cell_vao_attributes(vao_idx, 0u, 1u);
|
||||
unbind_program();
|
||||
#undef NX
|
||||
|
|
|
|||
|
|
@ -14,11 +14,11 @@ import background;
|
|||
// the cell VAO (colors/sprite_idx/is_selected instanced attributes) and the
|
||||
// background module's padding_background_premul() to compute the color.
|
||||
//
|
||||
// It is invoked once per edge (a "strip") with the cell VAO attributes
|
||||
// re-pointed so that instance i corresponds to the i'th cell along that strip.
|
||||
// The geometry of the quad is computed entirely from uniforms in
|
||||
// framebuffer-NDC, since the padding lies outside the per-window cell viewport
|
||||
// and is therefore drawn with the full framebuffer viewport.
|
||||
// Each draw call covers one or two parallel strips (e.g. top+bottom or
|
||||
// left+right). The cell VAO attributes are pre-packed into a dedicated
|
||||
// padding buffer so instance i reads strip-0 cells for i < along_count and
|
||||
// strip-1 cells for i >= along_count. All per-strip selection (geometry and
|
||||
// cell index) is done branch-free via lerp on strip_f ∈ {0.0, 1.0}.
|
||||
|
||||
// Corner indicator per quad vertex, matching cell_pos_map in background.slang.
|
||||
// .x: 0 -> left, 1 -> right ; .y: 0 -> top, 1 -> bottom
|
||||
|
|
@ -41,19 +41,15 @@ VertexOutput vertex_main(
|
|||
[[vk::location(2)]] uint is_selected,
|
||||
uint vertex_id : SV_VertexID,
|
||||
uint instance_id : SV_InstanceID,
|
||||
// The true cell index (into the cell grid) of this strip cell, used only to
|
||||
// reconstruct the row/column for the shared background computation. It is
|
||||
// base_instance + instance_id * instance_step and matches the offset/stride
|
||||
// used to re-point the VAO attributes on the C side.
|
||||
// The true cell index (into the cell grid) for strip 0's first instance.
|
||||
uniform uint base_instance,
|
||||
uniform uint instance_step,
|
||||
// 1 for the top/bottom strips (the strip runs along x), 0 for left/right.
|
||||
uniform uint is_horizontal,
|
||||
// Number of cells along the strip (== instance count).
|
||||
// Number of cells per strip (== instance count for a single-strip draw;
|
||||
// total instances == along_count * num_strips).
|
||||
uniform uint along_count,
|
||||
// The two NDC values of the thin (across) dimension of the strip, selected
|
||||
// by the quad corner: .x for the near-content corner mapping, .y for the
|
||||
// other. The C side assigns these per edge.
|
||||
// The two NDC values of the thin (across) dimension for strip 0.
|
||||
uniform float2 across,
|
||||
// NDC of the along dimension for the first cell's near corner, and the step
|
||||
// per cell (signed; negative for the vertical strips as NDC y decreases
|
||||
|
|
@ -61,15 +57,29 @@ VertexOutput vertex_main(
|
|||
uniform float along_start,
|
||||
uniform float along_step,
|
||||
// NDC bounds used to extend the first/last cell so the strip fills the
|
||||
// corners (a no-op when set to the natural strip extent).
|
||||
// corners (shared by both strips of a combined draw).
|
||||
uniform float2 along_clamp,
|
||||
// Strip-1 parameters. Equal to strip-0 values for single-strip draws so
|
||||
// the lerp below is always correct regardless of strip count.
|
||||
uniform uint base_instance2,
|
||||
uniform float2 across2,
|
||||
) {
|
||||
VertexOutput vo;
|
||||
uint real_id = base_instance + instance_id * instance_step;
|
||||
|
||||
// Branchless strip selector: 0.0 for strip 0, 1.0 for strip 1.
|
||||
float strip_f = float(instance_id / along_count);
|
||||
uint strip_i = instance_id % along_count;
|
||||
|
||||
// Select the base cell and across extents for whichever strip this
|
||||
// instance belongs to, using lerp so no divergent branch is emitted.
|
||||
float fbase = lerp(float(base_instance), float(base_instance2), strip_f);
|
||||
uint real_id = uint(fbase) + strip_i * instance_step;
|
||||
float2 chosen_across = lerp(across, across2, strip_f);
|
||||
|
||||
vo.color_premul = padding_background_premul(colors, sprite_idx, is_selected, real_id);
|
||||
|
||||
uint2 p = pad_pos_map[vertex_id];
|
||||
float along_lo = along_start + float(instance_id) * along_step;
|
||||
float along_lo = along_start + float(strip_i) * along_step;
|
||||
float along_hi = along_lo + along_step;
|
||||
|
||||
// Branch-free selection via lerp(). h selects the strip orientation, s is the
|
||||
|
|
@ -77,16 +87,16 @@ VertexOutput vertex_main(
|
|||
float h = float(is_horizontal);
|
||||
float s = lerp(float(p.y), float(p.x), h);
|
||||
float a = lerp(float(p.x), float(p.y), h);
|
||||
// 1 when this is the first/last cell along the strip, else 0.
|
||||
float is_first = 1.0 - min(float(instance_id), 1.0);
|
||||
float is_last = 1.0 - min(float(along_count - 1u - instance_id), 1.0);
|
||||
// 1 when this is the first/last instance within its strip, else 0.
|
||||
float is_first = 1.0 - min(float(strip_i), 1.0);
|
||||
float is_last = 1.0 - min(float(along_count - 1u - strip_i), 1.0);
|
||||
|
||||
float along = lerp(along_lo, along_hi, s);
|
||||
// Extend the first cell's near corner and the last cell's far corner out to
|
||||
// along_clamp so the strip fills the corners.
|
||||
along = lerp(along, along_clamp.x, is_first * (1.0 - s));
|
||||
along = lerp(along, along_clamp.y, is_last * s);
|
||||
float across_v = lerp(across.x, across.y, a);
|
||||
float across_v = lerp(chosen_across.x, chosen_across.y, a);
|
||||
|
||||
// Horizontal strip -> position is (along, across); vertical -> (across, along).
|
||||
vo.position = float4(lerp(across_v, along, h), lerp(along, across_v, h), 0.0, 1.0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue