This commit is contained in:
2026-01-01 22:01:58 -06:00
parent b61b85c3a8
commit 249b78d141
11 changed files with 2379 additions and 634 deletions

View File

@@ -787,8 +787,8 @@ function _build_sprite_vertices(sprites, camera) {
var vertices_per_sprite = 4
var indices_per_sprite = 6
var vertex_data = new blob_mod(sprites.length * vertices_per_sprite * floats_per_vertex * 32)
var index_data = geometry.make_quad_indices(sprites.length)
var vertex_data = new blob_mod(sprites.length * vertices_per_sprite * floats_per_vertex * 4)
var index_data = new blob_mod(sprites.length * indices_per_sprite * 2)
var vertex_count = 0
@@ -813,9 +813,56 @@ function _build_sprite_vertices(sprites, camera) {
var u1 = s.uv_rect ? (s.uv_rect.x + s.uv_rect.width) : 1
var v1 = s.uv_rect ? (s.uv_rect.y + s.uv_rect.height) : 1
// call to implement
var verts = geometry.sprite_vertices({x,y,w,h,u0,v0,u1,v1,c})
vertex_data.write_blob(verts)
// Quad vertices (bottom-left, bottom-right, top-right, top-left)
// v0: bottom-left
vertex_data.wf(x)
vertex_data.wf(y)
vertex_data.wf(u0)
vertex_data.wf(v1) // Flip V
vertex_data.wf(c.r)
vertex_data.wf(c.g)
vertex_data.wf(c.b)
vertex_data.wf(c.a)
// v1: bottom-right
vertex_data.wf(x + w)
vertex_data.wf(y)
vertex_data.wf(u1)
vertex_data.wf(v1) // Flip V
vertex_data.wf(c.r)
vertex_data.wf(c.g)
vertex_data.wf(c.b)
vertex_data.wf(c.a)
// v2: top-right
vertex_data.wf(x + w)
vertex_data.wf(y + h)
vertex_data.wf(u1)
vertex_data.wf(v0) // Flip V
vertex_data.wf(c.r)
vertex_data.wf(c.g)
vertex_data.wf(c.b)
vertex_data.wf(c.a)
// v3: top-left
vertex_data.wf(x)
vertex_data.wf(y + h)
vertex_data.wf(u0)
vertex_data.wf(v0) // Flip V
vertex_data.wf(c.r)
vertex_data.wf(c.g)
vertex_data.wf(c.b)
vertex_data.wf(c.a)
// Indices (two triangles)
index_data.w16(vertex_count + 0)
index_data.w16(vertex_count + 1)
index_data.w16(vertex_count + 2)
index_data.w16(vertex_count + 0)
index_data.w16(vertex_count + 2)
index_data.w16(vertex_count + 3)
vertex_count += 4
})
return {
@@ -916,16 +963,16 @@ function _build_ortho_matrix(left, right, bottom, top, near, far) {
}
function _build_camera_matrix(camera, target_width, target_height) {
var pos = camera.pos || [0, 0]
var pos = camera.pos || {x: 0, y: 0}
var cam_width = camera.width || target_width
var cam_height = camera.height || target_height
var anchor = camera.anchor || [0.5, 0.5]
var left = pos[0] - cam_width * anchor[0]
var right = pos[0] + cam_width * (1 - anchor[0])
var bottom = pos[1] - cam_height * anchor[1]
var top = pos[1] + cam_height * (1 - anchor[1])
var anchor = camera.anchor || {x: 0.5, y: 0.5}
var left = pos.x - cam_width * anchor.x
var right = pos.x + cam_width * (1 - anchor.x)
var bottom = pos.y - cam_height * anchor.y
var top = pos.y + cam_height * (1 - anchor.y)
return _build_ortho_matrix(left, right, bottom, top, -1, 1)
}
@@ -1068,7 +1115,11 @@ function _execute_commands(commands, window_size) {
case 'draw_text':
pending_draws.push(cmd)
break
case 'draw_texture_ref':
pending_draws.push(cmd)
break
case 'blit':
// Flush pending draws first
if (current_pass && pending_draws.length > 0) {
@@ -1258,12 +1309,19 @@ function _flush_draws(cmd_buffer, pass, draws, camera, target) {
// Flush current batch
if (current_batch) _render_batch(cmd_buffer, pass, current_batch, camera, target)
current_batch = null
// Render text immediately
_render_text(cmd_buffer, pass, draw.drawable, camera, target)
} else if (draw.cmd == 'draw_texture_ref') {
// Flush current batch
if (current_batch) _render_batch(cmd_buffer, pass, current_batch, camera, target)
current_batch = null
// Render pre-rendered effect texture
_render_texture_ref(cmd_buffer, pass, draw.drawable, camera, target)
}
}
// Flush final batch
if (current_batch) _render_batch(cmd_buffer, pass, current_batch, camera, target)
}
@@ -1311,6 +1369,68 @@ function _render_batch(cmd_buffer, pass, batch, camera, target) {
}
}
// Render a pre-rendered texture from an effect group
function _render_texture_ref(cmd_buffer, pass, drawable, camera, target) {
var tex_target = drawable.texture_target
if (!tex_target) return
// The texture_target is a compositor target reference - resolve it
// It should have already been rendered to and we just need to blit it
var pos = drawable.pos || {x: 0, y: 0}
var width = drawable.width || target.width
var height = drawable.height || target.height
// Build a single sprite for the texture reference
var sprites = [{
pos: pos,
width: width,
height: height,
anchor_x: 0,
anchor_y: 0,
color: {r: 1, g: 1, b: 1, a: 1}
}]
var geom = _build_sprite_vertices(sprites, camera)
// Upload geometry
var vb_size = geom.vertices.length / 8
var ib_size = geom.indices.length / 8
var vb = new gpu_mod.buffer(_gpu, {size: vb_size, vertex: true})
var ib = new gpu_mod.buffer(_gpu, {size: ib_size, index: true})
var vb_transfer = new gpu_mod.transfer_buffer(_gpu, {size: vb_size, usage: "upload"})
var ib_transfer = new gpu_mod.transfer_buffer(_gpu, {size: ib_size, usage: "upload"})
vb_transfer.copy_blob(_gpu, geom.vertices)
ib_transfer.copy_blob(_gpu, geom.indices)
var copy_cmd = _gpu.acquire_cmd_buffer()
var copy = copy_cmd.copy_pass()
copy.upload_to_buffer({transfer_buffer: vb_transfer, offset: 0}, {buffer: vb, offset: 0, size: vb_size}, false)
copy.upload_to_buffer({transfer_buffer: ib_transfer, offset: 0}, {buffer: ib, offset: 0, size: ib_size}, false)
copy.end()
copy_cmd.submit()
// Build camera matrix
var proj = _build_camera_matrix(camera, target.width, target.height)
// Select pipeline based on blend mode
var blend = drawable.blend || 'over'
var pipeline = blend == 'add' ? _pipelines.sprite_add : _pipelines.sprite_alpha
// The texture_target has a .texture property from the target pool
var tex = tex_target.texture || tex_target
if (!tex) return
pass.bind_pipeline(pipeline)
pass.bind_vertex_buffers(0, [{buffer: vb, offset: 0}])
pass.bind_index_buffer({buffer: ib, offset: 0}, 16)
pass.bind_fragment_samplers(0, [{texture: tex, sampler: _sampler_linear}])
cmd_buffer.push_vertex_uniform_data(0, proj)
pass.draw_indexed(geom.index_count, 1, 0, 0, 0)
}
function _render_text(cmd_buffer, pass, drawable, camera, target) {
// Get font - support mode tag: 'bitmap', 'sdf', 'msdf'
var font_path = drawable.font
@@ -1666,6 +1786,9 @@ function _do_shader_pass(cmd_buffer, cmd, get_swapchain_tex) {
case 'accumulator':
pipeline = _pipelines.accumulator
break
case 'mask':
pipeline = _pipelines.mask
break
default:
log.console(`sdl_gpu: Unknown shader: ${shader}`)
return
@@ -1762,7 +1885,7 @@ function _do_shader_pass(cmd_buffer, cmd, get_swapchain_tex) {
function _build_shader_uniforms(shader, uniforms) {
var data = new blob_mod(64) // 16 floats max
switch (shader) {
case 'threshold':
data.wf(uniforms.threshold || 0.8)
@@ -1771,21 +1894,21 @@ function _build_shader_uniforms(shader, uniforms) {
data.wf(0) // padding
break
case 'blur':
var dir = uniforms.direction || [1, 0]
var texel = uniforms.texel_size || [0.001, 0.001]
data.wf(dir[0])
data.wf(dir[1])
data.wf(texel[0])
data.wf(texel[1])
var dir = uniforms.direction || {x: 1, y: 0}
var texel = uniforms.texel_size || {x: 0.001, y: 0.001}
data.wf(dir.x)
data.wf(dir.y)
data.wf(texel.x)
data.wf(texel.y)
break
case 'crt':
data.wf(uniforms.curvature || 0.1)
data.wf(uniforms.scanline_intensity || 0.3)
data.wf(uniforms.vignette || 0.2)
data.wf(0) // padding
var res = uniforms.resolution || [1280, 720]
data.wf(res[0])
data.wf(res[1])
var res = uniforms.resolution || {width: 1280, height: 720}
data.wf(res.width)
data.wf(res.height)
data.wf(0) // padding
data.wf(0) // padding
break
@@ -1795,10 +1918,18 @@ function _build_shader_uniforms(shader, uniforms) {
data.wf(0) // padding
data.wf(0) // padding
break
case 'mask':
// channel: 0=alpha, 1=luminance
// invert: 0=normal, 1=inverted
data.wf(uniforms.channel != null ? uniforms.channel : 0)
data.wf(uniforms.invert != null ? uniforms.invert : 0)
data.wf(0) // padding
data.wf(0) // padding
break
default:
return null
}
return stone(data)
}