2026-06-16 17:03:24 +00:00
|
|
|
|
#version 460
|
|
|
|
|
|
#extension GL_GOOGLE_include_directive : enable
|
|
|
|
|
|
#include "ui-shared.glsl"
|
|
|
|
|
|
|
|
|
|
|
|
// ─── fused UI uber-kernel (issue #47) ────────────────────────────────────
|
|
|
|
|
|
// One dispatch that composites up to four standard categories in canonical
|
|
|
|
|
|
// back-to-front order — quads → circles → images → text — into a single
|
|
|
|
|
|
// per-pixel register, loading the destination image ONCE and storing it ONCE.
|
|
|
|
|
|
// A run of consecutive Dispatch* calls would instead load+store the image per
|
|
|
|
|
|
// category and drain compute with a VkMemoryBarrier between each; this kernel
|
|
|
|
|
|
// collapses all of that to 1 load + 1 store + 0 inter-pass barriers.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Each category keeps the exact cooperative shared-memory tile-cull + per-pixel
|
|
|
|
|
|
// accumulate of its standalone shader (see ui-quads/circles/images/text), so a
|
|
|
|
|
|
// fused category is pixel-identical to its standalone Dispatch* pass (the only
|
|
|
|
|
|
// difference is that intermediate results stay in full float precision between
|
|
|
|
|
|
// categories instead of round-tripping through the storage image — strictly
|
|
|
|
|
|
// more accurate). Categories run sequentially within each thread, so the VGPR
|
|
|
|
|
|
// high-water mark is ~max(per-category), not the sum.
|
|
|
|
|
|
//
|
|
|
|
|
|
// An absent category (itemCount 0) is a zero-trip, push-constant-uniform loop:
|
|
|
|
|
|
// no divergence, no memory traffic — using DispatchFused for only quads+text
|
|
|
|
|
|
// costs ~nothing for the unused circle/image phases.
|
|
|
|
|
|
//
|
|
|
|
|
|
// This is ADDITIVE: it has its own push-constant layout and does NOT touch the
|
|
|
|
|
|
// frozen 48-byte UIDispatchHeader or the per-element Dispatch* contract.
|
|
|
|
|
|
|
|
|
|
|
|
// Push-constant block. Mirrors Crafter::UIFusedHeader byte-for-byte: every
|
|
|
|
|
|
// member is vec4-aligned, no padding holes, exactly 128 bytes (the guaranteed
|
|
|
|
|
|
// push-constant minimum).
|
|
|
|
|
|
layout(push_constant) uniform PC {
|
|
|
|
|
|
uvec4 itemBuffers; // heap slots: (quads, circles, images, text)
|
|
|
|
|
|
uvec4 itemCounts; // item counts: (quads, circles, images, text)
|
|
|
|
|
|
uvec4 misc; // (outImage, fontTexture, fontSampler, flags)
|
|
|
|
|
|
uvec4 surface; // (surfaceWidth, surfaceHeight, frameIdx, _pad)
|
|
|
|
|
|
vec4 clipQuads;
|
|
|
|
|
|
vec4 clipCircles;
|
|
|
|
|
|
vec4 clipImages;
|
|
|
|
|
|
vec4 clipText;
|
|
|
|
|
|
} pc;
|
|
|
|
|
|
|
|
|
|
|
|
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
|
|
|
|
|
|
|
|
|
|
|
// SDF tuning for the text phase — must match Crafter::FontAtlas constants
|
|
|
|
|
|
// (same values as ui-text.comp.glsl).
|
|
|
|
|
|
const float ON_EDGE = 128.0 / 255.0;
|
|
|
|
|
|
const float DIST_SCALE = 32.0;
|
|
|
|
|
|
|
2026-06-16 17:09:06 +00:00
|
|
|
|
// Per-category clip-active bits packed into pc.misc.w (the fused analogue of
|
|
|
|
|
|
// ui-shared.glsl's UI_FLAG_CLIP). A bit is set only when that category's clip
|
|
|
|
|
|
// rect is narrower than the surface; when clear, the category skips its four
|
|
|
|
|
|
// per-pixel clip compares entirely (the common full-surface case). The branch
|
|
|
|
|
|
// is push-constant-uniform, so it never diverges.
|
|
|
|
|
|
const uint UI_FUSED_CLIP_QUADS = 0x1u;
|
|
|
|
|
|
const uint UI_FUSED_CLIP_CIRCLES = 0x2u;
|
|
|
|
|
|
const uint UI_FUSED_CLIP_IMAGES = 0x4u;
|
|
|
|
|
|
const uint UI_FUSED_CLIP_TEXT = 0x8u;
|
|
|
|
|
|
|
2026-06-16 17:03:24 +00:00
|
|
|
|
// Generic per-chunk cooperative-cull scratch, REUSED across the four phases
|
|
|
|
|
|
// (they composite sequentially with a barrier between, so the storage is free
|
|
|
|
|
|
// once a phase's last read completes). Keeping one shared set instead of four
|
|
|
|
|
|
// keeps the shared-memory footprint — and thus occupancy — at the per-category
|
|
|
|
|
|
// level. Member mapping per phase:
|
|
|
|
|
|
// quads: v0=rect v1=color v2=corners v3=outline
|
|
|
|
|
|
// circles: v0=centerRadius v1=color v2=outline
|
|
|
|
|
|
// images: v0=rect v1=uv v2=tint v4=slots
|
|
|
|
|
|
// text: v0=rect v1=uv v2=color
|
|
|
|
|
|
shared vec4 s_v0[UI_CHUNK];
|
|
|
|
|
|
shared vec4 s_v1[UI_CHUNK];
|
|
|
|
|
|
shared vec4 s_v2[UI_CHUNK];
|
|
|
|
|
|
shared vec4 s_v3[UI_CHUNK];
|
|
|
|
|
|
shared uvec4 s_v4[UI_CHUNK];
|
2026-06-18 13:22:07 +00:00
|
|
|
|
// Per-item constants precomputed once at cooperative-load time, REUSED across
|
|
|
|
|
|
// phases like the s_v* scratch (the per-pixel inner loops read them instead of
|
|
|
|
|
|
// recomputing per pixel × item):
|
|
|
|
|
|
// images: s_inv = 1.0/rect.zw
|
|
|
|
|
|
// text: s_inv = 1.0/rect.zw, s_band = SDF AA scale (a divide + two maxes)
|
|
|
|
|
|
shared vec2 s_inv[UI_CHUNK];
|
|
|
|
|
|
shared float s_band[UI_CHUNK];
|
2026-06-16 17:03:24 +00:00
|
|
|
|
shared uint s_keep[UI_CHUNK];
|
|
|
|
|
|
shared uint s_order[UI_CHUNK];
|
|
|
|
|
|
shared uint s_count;
|
|
|
|
|
|
|
|
|
|
|
|
// Stable in-order compaction of one chunk's survivors. Lane 0 scans s_keep in
|
|
|
|
|
|
// buffer order so the inner per-pixel loop still sees items in draw order.
|
|
|
|
|
|
void uiCompactChunk(uint base, uint count) {
|
|
|
|
|
|
if (gl_LocalInvocationIndex == 0u) {
|
|
|
|
|
|
uint n = 0u;
|
|
|
|
|
|
uint lim = min(UI_CHUNK, count - base);
|
|
|
|
|
|
for (uint k = 0u; k < lim; ++k)
|
|
|
|
|
|
if (s_keep[k] != 0u) s_order[n++] = k;
|
|
|
|
|
|
s_count = n;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
|
// NOTE: do not early-return — every thread must reach the barriers below.
|
|
|
|
|
|
uvec2 pxu = gl_GlobalInvocationID.xy;
|
|
|
|
|
|
bool inSurface = pxu.x < pc.surface.x && pxu.y < pc.surface.y;
|
|
|
|
|
|
ivec2 screenPx = ivec2(pxu);
|
|
|
|
|
|
vec2 sp = vec2(screenPx) + 0.5;
|
|
|
|
|
|
|
|
|
|
|
|
// Single load of the destination for the whole surface.
|
|
|
|
|
|
vec4 dst = vec4(0.0);
|
|
|
|
|
|
if (inSurface) dst = imageLoad(uiImages[pc.misc.x], screenPx);
|
|
|
|
|
|
|
|
|
|
|
|
vec2 tileMin, tileMax;
|
|
|
|
|
|
uiTileBounds(tileMin, tileMax);
|
|
|
|
|
|
uint lid = gl_LocalInvocationIndex;
|
|
|
|
|
|
|
|
|
|
|
|
// ─── QUADS ────────────────────────────────────────────────────────────
|
|
|
|
|
|
{
|
|
|
|
|
|
uint heap = pc.itemBuffers.x;
|
|
|
|
|
|
uint count = pc.itemCounts.x;
|
2026-06-16 17:09:06 +00:00
|
|
|
|
bool inClip = inSurface &&
|
|
|
|
|
|
((pc.misc.w & UI_FUSED_CLIP_QUADS) == 0u || uiPixelInClipRect(pxu, pc.clipQuads));
|
2026-06-16 17:03:24 +00:00
|
|
|
|
for (uint base = 0u; base < count; base += UI_CHUNK) {
|
|
|
|
|
|
uint idx = base + lid;
|
|
|
|
|
|
bool keep = false;
|
|
|
|
|
|
if (idx < count) {
|
|
|
|
|
|
s_v0[lid] = uiQuadHeap[heap].items[idx].rect;
|
|
|
|
|
|
s_v1[lid] = uiQuadHeap[heap].items[idx].color;
|
|
|
|
|
|
s_v2[lid] = uiQuadHeap[heap].items[idx].corners;
|
|
|
|
|
|
s_v3[lid] = uiQuadHeap[heap].items[idx].outline;
|
|
|
|
|
|
keep = uiAabbOverlapsTile(s_v0[lid].xy, s_v0[lid].xy + s_v0[lid].zw,
|
|
|
|
|
|
tileMin, tileMax);
|
|
|
|
|
|
}
|
|
|
|
|
|
s_keep[lid] = keep ? 1u : 0u;
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
uiCompactChunk(base, count);
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
|
|
if (inClip) {
|
|
|
|
|
|
for (uint j = 0u; j < s_count; ++j) {
|
|
|
|
|
|
uint c = s_order[j];
|
|
|
|
|
|
|
|
|
|
|
|
vec2 lo = s_v0[c].xy;
|
|
|
|
|
|
vec2 hi = s_v0[c].xy + s_v0[c].zw;
|
|
|
|
|
|
if (sp.x < lo.x || sp.y < lo.y) continue;
|
|
|
|
|
|
if (sp.x >= hi.x || sp.y >= hi.y) continue;
|
|
|
|
|
|
|
|
|
|
|
|
vec2 halfSize = s_v0[c].zw * 0.5;
|
|
|
|
|
|
vec2 p = sp - (s_v0[c].xy + halfSize);
|
|
|
|
|
|
float d = uiSdRoundRect(p, halfSize, s_v2[c]);
|
|
|
|
|
|
|
|
|
|
|
|
vec4 outline = s_v3[c];
|
|
|
|
|
|
float bodyA = clamp(0.5 - d, 0.0, 1.0);
|
|
|
|
|
|
if (bodyA <= 0.0 && outline.x <= 0.0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
vec4 col = s_v1[c];
|
|
|
|
|
|
vec4 src = vec4(col.rgb, col.a * bodyA);
|
|
|
|
|
|
|
|
|
|
|
|
if (outline.x > 0.0) {
|
|
|
|
|
|
float t = abs(d + outline.x * 0.5) - outline.x * 0.5;
|
|
|
|
|
|
float outlineA = clamp(0.5 - t, 0.0, 1.0);
|
|
|
|
|
|
src.rgb = mix(src.rgb, outline.yzw, outlineA);
|
|
|
|
|
|
src.a = max(src.a, outlineA);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (src.a <= 0.0) continue;
|
|
|
|
|
|
dst = uiBlendOver(dst, src);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
barrier(); // done reading shared for this chunk before reuse
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ─── CIRCLES ──────────────────────────────────────────────────────────
|
|
|
|
|
|
{
|
|
|
|
|
|
uint heap = pc.itemBuffers.y;
|
|
|
|
|
|
uint count = pc.itemCounts.y;
|
2026-06-16 17:09:06 +00:00
|
|
|
|
bool inClip = inSurface &&
|
|
|
|
|
|
((pc.misc.w & UI_FUSED_CLIP_CIRCLES) == 0u || uiPixelInClipRect(pxu, pc.clipCircles));
|
2026-06-16 17:03:24 +00:00
|
|
|
|
for (uint base = 0u; base < count; base += UI_CHUNK) {
|
|
|
|
|
|
uint idx = base + lid;
|
|
|
|
|
|
bool keep = false;
|
|
|
|
|
|
if (idx < count) {
|
|
|
|
|
|
s_v0[lid] = uiCircleHeap[heap].items[idx].centerRadius;
|
|
|
|
|
|
s_v1[lid] = uiCircleHeap[heap].items[idx].color;
|
|
|
|
|
|
s_v2[lid] = uiCircleHeap[heap].items[idx].outline;
|
|
|
|
|
|
float radius = s_v0[lid].z;
|
|
|
|
|
|
if (radius > 0.0) {
|
|
|
|
|
|
vec2 cen = s_v0[lid].xy;
|
|
|
|
|
|
vec2 r = vec2(radius + 1.0);
|
|
|
|
|
|
keep = uiAabbOverlapsTile(cen - r, cen + r, tileMin, tileMax);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
s_keep[lid] = keep ? 1u : 0u;
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
uiCompactChunk(base, count);
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
|
|
if (inClip) {
|
|
|
|
|
|
for (uint j = 0u; j < s_count; ++j) {
|
|
|
|
|
|
uint c = s_order[j];
|
|
|
|
|
|
|
|
|
|
|
|
vec2 center = s_v0[c].xy;
|
|
|
|
|
|
float radius = s_v0[c].z;
|
|
|
|
|
|
if (radius <= 0.0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (abs(sp.x - center.x) > radius + 1.0) continue;
|
|
|
|
|
|
if (abs(sp.y - center.y) > radius + 1.0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
float d = length(sp - center) - radius;
|
|
|
|
|
|
|
|
|
|
|
|
vec4 outline = s_v2[c];
|
|
|
|
|
|
float bodyA = clamp(0.5 - d, 0.0, 1.0);
|
|
|
|
|
|
if (bodyA <= 0.0 && outline.x <= 0.0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
vec4 col = s_v1[c];
|
|
|
|
|
|
vec4 src = vec4(col.rgb, col.a * bodyA);
|
|
|
|
|
|
|
|
|
|
|
|
if (outline.x > 0.0) {
|
|
|
|
|
|
float t = abs(d + outline.x * 0.5) - outline.x * 0.5;
|
|
|
|
|
|
float outlineA = clamp(0.5 - t, 0.0, 1.0);
|
|
|
|
|
|
src.rgb = mix(src.rgb, outline.yzw, outlineA);
|
|
|
|
|
|
src.a = max(src.a, outlineA);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (src.a <= 0.0) continue;
|
|
|
|
|
|
dst = uiBlendOver(dst, src);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ─── IMAGES ───────────────────────────────────────────────────────────
|
|
|
|
|
|
{
|
|
|
|
|
|
uint heap = pc.itemBuffers.z;
|
|
|
|
|
|
uint count = pc.itemCounts.z;
|
2026-06-16 17:09:06 +00:00
|
|
|
|
bool inClip = inSurface &&
|
|
|
|
|
|
((pc.misc.w & UI_FUSED_CLIP_IMAGES) == 0u || uiPixelInClipRect(pxu, pc.clipImages));
|
2026-06-16 17:03:24 +00:00
|
|
|
|
for (uint base = 0u; base < count; base += UI_CHUNK) {
|
|
|
|
|
|
uint idx = base + lid;
|
|
|
|
|
|
bool keep = false;
|
|
|
|
|
|
if (idx < count) {
|
|
|
|
|
|
s_v0[lid] = uiImageHeap[heap].items[idx].rect;
|
|
|
|
|
|
s_v1[lid] = uiImageHeap[heap].items[idx].uv;
|
|
|
|
|
|
s_v2[lid] = uiImageHeap[heap].items[idx].tint;
|
|
|
|
|
|
s_v4[lid] = uiImageHeap[heap].items[idx].slots;
|
2026-06-18 13:22:07 +00:00
|
|
|
|
s_inv[lid] = 1.0 / s_v0[lid].zw;
|
2026-06-16 17:03:24 +00:00
|
|
|
|
keep = uiAabbOverlapsTile(s_v0[lid].xy, s_v0[lid].xy + s_v0[lid].zw,
|
|
|
|
|
|
tileMin, tileMax);
|
|
|
|
|
|
}
|
|
|
|
|
|
s_keep[lid] = keep ? 1u : 0u;
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
uiCompactChunk(base, count);
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
|
|
if (inClip) {
|
|
|
|
|
|
for (uint j = 0u; j < s_count; ++j) {
|
|
|
|
|
|
uint c = s_order[j];
|
|
|
|
|
|
|
|
|
|
|
|
vec2 lo = s_v0[c].xy;
|
|
|
|
|
|
vec2 hi = s_v0[c].xy + s_v0[c].zw;
|
|
|
|
|
|
if (sp.x < lo.x || sp.y < lo.y) continue;
|
|
|
|
|
|
if (sp.x >= hi.x || sp.y >= hi.y) continue;
|
|
|
|
|
|
|
2026-06-18 13:22:07 +00:00
|
|
|
|
vec2 t = (sp - s_v0[c].xy) * s_inv[c];
|
2026-06-16 17:03:24 +00:00
|
|
|
|
vec2 uv = mix(s_v1[c].xy, s_v1[c].zw, t);
|
|
|
|
|
|
|
|
|
|
|
|
uint texSlot = s_v4[c].x;
|
|
|
|
|
|
uint sampSlot = s_v4[c].y;
|
|
|
|
|
|
|
|
|
|
|
|
vec4 sampled = texture(
|
|
|
|
|
|
sampler2D(uiTextures[nonuniformEXT(texSlot)],
|
|
|
|
|
|
uiSamplers[nonuniformEXT(sampSlot)]),
|
|
|
|
|
|
uv
|
|
|
|
|
|
);
|
|
|
|
|
|
vec4 src = sampled * s_v2[c];
|
|
|
|
|
|
if (src.a <= 0.0) continue;
|
|
|
|
|
|
dst = uiBlendOver(dst, src);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ─── TEXT ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
{
|
|
|
|
|
|
uint heap = pc.itemBuffers.w;
|
|
|
|
|
|
uint count = pc.itemCounts.w;
|
2026-06-16 17:09:06 +00:00
|
|
|
|
bool inClip = inSurface &&
|
|
|
|
|
|
((pc.misc.w & UI_FUSED_CLIP_TEXT) == 0u || uiPixelInClipRect(pxu, pc.clipText));
|
2026-06-16 17:03:24 +00:00
|
|
|
|
for (uint base = 0u; base < count; base += UI_CHUNK) {
|
|
|
|
|
|
uint idx = base + lid;
|
|
|
|
|
|
bool keep = false;
|
|
|
|
|
|
if (idx < count) {
|
|
|
|
|
|
s_v0[lid] = uiGlyphHeap[heap].items[idx].rect;
|
|
|
|
|
|
s_v1[lid] = uiGlyphHeap[heap].items[idx].uv;
|
|
|
|
|
|
s_v2[lid] = uiGlyphHeap[heap].items[idx].color;
|
2026-06-18 13:22:07 +00:00
|
|
|
|
s_inv[lid] = 1.0 / s_v0[lid].zw;
|
|
|
|
|
|
// SDF AA band — atlas-px per screen-px, constant per glyph
|
|
|
|
|
|
// (uvSpan * kAtlasSize(1024) / screenSpan), max'd to 1px floor.
|
|
|
|
|
|
vec2 uvSpan = s_v1[lid].zw - s_v1[lid].xy;
|
|
|
|
|
|
vec2 atlasPerScreen = (uvSpan * 1024.0) * s_inv[lid];
|
|
|
|
|
|
float scalePx = max(atlasPerScreen.x, atlasPerScreen.y);
|
|
|
|
|
|
s_band[lid] = max(scalePx, 0.0001);
|
2026-06-16 17:03:24 +00:00
|
|
|
|
keep = uiAabbOverlapsTile(s_v0[lid].xy, s_v0[lid].xy + s_v0[lid].zw,
|
|
|
|
|
|
tileMin, tileMax);
|
|
|
|
|
|
}
|
|
|
|
|
|
s_keep[lid] = keep ? 1u : 0u;
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
uiCompactChunk(base, count);
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
|
|
if (inClip) {
|
|
|
|
|
|
for (uint j = 0u; j < s_count; ++j) {
|
|
|
|
|
|
uint c = s_order[j];
|
|
|
|
|
|
|
|
|
|
|
|
vec2 lo = s_v0[c].xy;
|
|
|
|
|
|
vec2 hi = s_v0[c].xy + s_v0[c].zw;
|
|
|
|
|
|
if (sp.x < lo.x || sp.y < lo.y) continue;
|
|
|
|
|
|
if (sp.x >= hi.x || sp.y >= hi.y) continue;
|
|
|
|
|
|
|
2026-06-18 13:22:07 +00:00
|
|
|
|
vec2 t = (sp - s_v0[c].xy) * s_inv[c];
|
2026-06-16 17:03:24 +00:00
|
|
|
|
vec2 uv = mix(s_v1[c].xy, s_v1[c].zw, t);
|
|
|
|
|
|
|
|
|
|
|
|
// Font slots are push constants — provably dynamically uniform,
|
|
|
|
|
|
// so no nonuniformEXT (same as ui-text.comp.glsl).
|
|
|
|
|
|
float sdf = texture(
|
|
|
|
|
|
sampler2D(uiTextures[pc.misc.y],
|
|
|
|
|
|
uiSamplers[pc.misc.z]),
|
|
|
|
|
|
uv
|
|
|
|
|
|
).r;
|
|
|
|
|
|
|
|
|
|
|
|
float dAtlas = (ON_EDGE - sdf) * DIST_SCALE;
|
|
|
|
|
|
|
2026-06-18 13:22:07 +00:00
|
|
|
|
// band (the AA scale) was precomputed once per glyph at load.
|
|
|
|
|
|
float a = clamp(0.5 - dAtlas / s_band[c], 0.0, 1.0);
|
2026-06-16 17:03:24 +00:00
|
|
|
|
if (a <= 0.0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
vec4 col = s_v2[c];
|
|
|
|
|
|
vec4 src = vec4(col.rgb, col.a * a);
|
|
|
|
|
|
dst = uiBlendOver(dst, src);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
barrier();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (inSurface) imageStore(uiImages[pc.misc.x], screenPx, dst);
|
|
|
|
|
|
}
|