working gpu renderer
This commit is contained in:
@@ -6,9 +6,42 @@ var video = use('sdl_video')
|
|||||||
var surface = use('surface')
|
var surface = use('surface')
|
||||||
var sdl_gpu = use('sdl_gpu')
|
var sdl_gpu = use('sdl_gpu')
|
||||||
var io = use('io')
|
var io = use('io')
|
||||||
|
var geometry = use('geometry')
|
||||||
|
|
||||||
var os = use('os')
|
var os = use('os')
|
||||||
|
|
||||||
|
// suppose your render‐surface is W×H pixels, and
|
||||||
|
// your camera.viewport = { x:0, y:0, width:1, height:1 };
|
||||||
|
// so vpW = W*1, vpH = H*1
|
||||||
|
|
||||||
|
def vpW = 640
|
||||||
|
def vpH = 360
|
||||||
|
|
||||||
|
// how many world‑units from center to left/right?
|
||||||
|
// if zoom = 1 means 1 world‑unit == 1 pixel:
|
||||||
|
def halfW = vpW * 0.5;
|
||||||
|
def halfH = vpH * 0.5;
|
||||||
|
|
||||||
|
// define your clipping‐volume in camera‑space:
|
||||||
|
def l = -halfW, r = +halfW;
|
||||||
|
def b = -halfH, t = +halfH;
|
||||||
|
def n = 0, f = 1;
|
||||||
|
|
||||||
|
// Metal wants z in [0,1], so we use the “zero‐to‐one” variant:
|
||||||
|
function makeOrthoMetal(l,r,b,t,n,f){
|
||||||
|
return [
|
||||||
|
2/(r-l), 0, 0, 0,
|
||||||
|
0, 2/(t-b), 0, 0,
|
||||||
|
0, 0, 1/(f-n), 0,
|
||||||
|
-(r+l)/(r-l), -(t+b)/(t-b), -n/(f-n), 1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
def P = makeOrthoMetal(l,r,b,t,n,f);
|
||||||
|
|
||||||
|
var Pblob = geometry.array_blob(P)
|
||||||
|
log.console(Pblob.length)
|
||||||
|
|
||||||
var driver = "vulkan"
|
var driver = "vulkan"
|
||||||
switch(os.platform()) {
|
switch(os.platform()) {
|
||||||
case "Linux":
|
case "Linux":
|
||||||
@@ -63,7 +96,7 @@ var default_window = {
|
|||||||
// Basic properties
|
// Basic properties
|
||||||
title: "Prosperon Window",
|
title: "Prosperon Window",
|
||||||
width: 640,
|
width: 640,
|
||||||
height: 480,
|
height: 360,
|
||||||
|
|
||||||
// Position - can be numbers or "centered"
|
// Position - can be numbers or "centered"
|
||||||
x: null, // SDL_WINDOWPOS_null by default
|
x: null, // SDL_WINDOWPOS_null by default
|
||||||
@@ -122,6 +155,7 @@ var device = new sdl_gpu.gpu({
|
|||||||
})
|
})
|
||||||
device.claim_window(window)
|
device.claim_window(window)
|
||||||
device.set_swapchain(window, 'sdr', 'vsync')
|
device.set_swapchain(window, 'sdr', 'vsync')
|
||||||
|
|
||||||
var shader_type = device.shader_format()[0]
|
var shader_type = device.shader_format()[0]
|
||||||
shader_type = 'msl'
|
shader_type = 'msl'
|
||||||
var std_sampler = new sdl_gpu.sampler(device, default_sampler)
|
var std_sampler = new sdl_gpu.sampler(device, default_sampler)
|
||||||
@@ -130,6 +164,25 @@ var std_sampler = new sdl_gpu.sampler(device, default_sampler)
|
|||||||
var shader_cache = {}
|
var shader_cache = {}
|
||||||
var pipeline_cache = {}
|
var pipeline_cache = {}
|
||||||
|
|
||||||
|
function upload(copypass, buffer, toblob)
|
||||||
|
{
|
||||||
|
var trans = new sdl_gpu.transfer_buffer(device, {
|
||||||
|
size: toblob.length/8,
|
||||||
|
usage:"upload"
|
||||||
|
})
|
||||||
|
|
||||||
|
trans.copy_blob(device, toblob)
|
||||||
|
|
||||||
|
copypass.upload_to_buffer({
|
||||||
|
transfer_buffer: trans,
|
||||||
|
offset:0
|
||||||
|
}, {
|
||||||
|
buffer: buffer,
|
||||||
|
offset: 0,
|
||||||
|
size: toblob.length/8
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function make_shader(sh_file)
|
function make_shader(sh_file)
|
||||||
{
|
{
|
||||||
var file = `shaders/${shader_type}/${sh_file}.${shader_type}`
|
var file = `shaders/${shader_type}/${sh_file}.${shader_type}`
|
||||||
@@ -147,210 +200,26 @@ function make_shader(sh_file)
|
|||||||
entrypoint: shader_type == "msl" ? "main0" : "main"
|
entrypoint: shader_type == "msl" ? "main0" : "main"
|
||||||
}
|
}
|
||||||
|
|
||||||
shader.gpu = context.make_shader(shader)
|
shader.gpu = new sdl_gpu.shader(device, shader)
|
||||||
shader.reflection = refl;
|
shader.reflection = refl;
|
||||||
shader_cache[file] = shader
|
shader_cache[file] = shader
|
||||||
shader.file = sh_file
|
shader.file = sh_file
|
||||||
return shader
|
return shader
|
||||||
}
|
}
|
||||||
|
|
||||||
function load_shader(vert, frag, pipeline_config) {
|
var usepipe
|
||||||
// Default to sprite pipeline config if not provided
|
function load_pipeline(config)
|
||||||
pipeline_config = pipeline_config || sprite_pipeline_config
|
{
|
||||||
|
log.console(usepipe)
|
||||||
// Check cache first
|
if (usepipe) return usepipe
|
||||||
var cache_key = `${vert}:${frag}:${json.encode(pipeline_config)}`
|
config.vertex = make_shader(config.vertex).gpu
|
||||||
if (pipeline_cache[cache_key])
|
config.fragment = make_shader(config.fragment).gpu
|
||||||
return pipeline_cache[cache_key]
|
log.console(json.encode(config))
|
||||||
|
log.console("ANOTHER NEW PIPELINE")
|
||||||
// Load shader reflections
|
log.console(config)
|
||||||
var vert_reflection = load_shader_reflection(vert, 'vert')
|
usepipe = new sdl_gpu.graphics_pipeline(device, config)
|
||||||
var frag_reflection = load_shader_reflection(frag, 'frag')
|
log.console(usepipe)
|
||||||
|
return usepipe
|
||||||
if (!vert_reflection || !frag_reflection)
|
|
||||||
return null
|
|
||||||
|
|
||||||
// Load shaders based on platform
|
|
||||||
var vert_path = `accio/shaders/${shader_type}/${vert}.vert.${shader_type}`
|
|
||||||
var frag_path = `accio/shaders/${shader_type}/${frag}.frag.${shader_type}`
|
|
||||||
|
|
||||||
if (!io.exists(vert_path) || !io.exists(frag_path)) {
|
|
||||||
log.error(`Shaders not found: ${vert_path}, ${frag_path}`)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
var vert_code = io.slurpbytes(vert_path)
|
|
||||||
var frag_code = io.slurpbytes(frag_path)
|
|
||||||
|
|
||||||
// Extract shader info from reflection
|
|
||||||
var vert_shader_info = {
|
|
||||||
stage: "vertex",
|
|
||||||
code: vert_code,
|
|
||||||
entrypoint: "main0",
|
|
||||||
format: shader_type,
|
|
||||||
num_samplers: 0,
|
|
||||||
num_textures: 0,
|
|
||||||
num_storage_buffers: 0,
|
|
||||||
num_uniform_buffers: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
log.console(json.encode(vert_reflection))
|
|
||||||
|
|
||||||
// Count fragment shader resources
|
|
||||||
var num_samplers = 0
|
|
||||||
var num_textures = 0
|
|
||||||
|
|
||||||
if (frag_reflection.separate_images)
|
|
||||||
num_textures = frag_reflection.separate_images.length
|
|
||||||
if (frag_reflection.separate_samplers)
|
|
||||||
num_samplers = frag_reflection.separate_samplers.length
|
|
||||||
|
|
||||||
var frag_shader_info = {
|
|
||||||
stage: "fragment",
|
|
||||||
code: frag_code,
|
|
||||||
entrypoint: "main0",
|
|
||||||
format: shader_type,
|
|
||||||
num_samplers: num_samplers,
|
|
||||||
num_textures: num_textures,
|
|
||||||
num_storage_buffers: 0,
|
|
||||||
num_uniform_buffers: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check shader cache
|
|
||||||
var vert_cache_key = `${vert}:vert`
|
|
||||||
var frag_cache_key = `${frag}:frag`
|
|
||||||
|
|
||||||
var vert_shader = shader_cache[vert_cache_key]
|
|
||||||
if (!vert_shader) {
|
|
||||||
log.console(json.encode(vert_shader_info))
|
|
||||||
vert_shader = new sdl_gpu.shader(device, vert_shader_info)
|
|
||||||
if (!vert_shader) {
|
|
||||||
log.error(`Failed to create vertex shader: ${vert}`)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
shader_cache[vert_cache_key] = vert_shader
|
|
||||||
}
|
|
||||||
|
|
||||||
var frag_shader = shader_cache[frag_cache_key]
|
|
||||||
if (!frag_shader) {
|
|
||||||
frag_shader = new sdl_gpu.shader(device, frag_shader_info)
|
|
||||||
if (!frag_shader) {
|
|
||||||
log.error(`Failed to create fragment shader: ${frag}`)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
shader_cache[frag_cache_key] = frag_shader
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create graphics pipeline using composition
|
|
||||||
var pipeline_info = create_pipeline_config({
|
|
||||||
vertex: vert_shader,
|
|
||||||
fragment: frag_shader,
|
|
||||||
blend: pipeline_config.blend,
|
|
||||||
target: pipeline_config.target,
|
|
||||||
label: pipeline_config.label
|
|
||||||
})
|
|
||||||
|
|
||||||
// Ensure target is properly set
|
|
||||||
if (!pipeline_info.target || !pipeline_info.target.color_targets) {
|
|
||||||
log.error("Pipeline target configuration is missing or invalid")
|
|
||||||
log.console("pipeline_config:", json.encode(pipeline_config))
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up vertex input layout based on reflection
|
|
||||||
var vertex_stride = 0
|
|
||||||
var vertex_attributes = []
|
|
||||||
|
|
||||||
// Build vertex attributes from reflection
|
|
||||||
if (vert_reflection.inputs) {
|
|
||||||
// Add attributes from reflection
|
|
||||||
for (var input of vert_reflection.inputs) {
|
|
||||||
var format = "float2" // default
|
|
||||||
var size = 8
|
|
||||||
|
|
||||||
if (input.type == "vec2") {
|
|
||||||
format = "float2"
|
|
||||||
size = 8
|
|
||||||
} else if (input.type == "vec4") {
|
|
||||||
format = "float4"
|
|
||||||
size = 16
|
|
||||||
}
|
|
||||||
|
|
||||||
vertex_attributes.push({
|
|
||||||
location: input.location,
|
|
||||||
buffer_slot: 0,
|
|
||||||
format: format,
|
|
||||||
offset: vertex_stride
|
|
||||||
})
|
|
||||||
vertex_stride += size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeline_info.vertex_buffer_descriptions = [{
|
|
||||||
slot: 0,
|
|
||||||
pitch: vertex_stride,
|
|
||||||
input_rate: "vertex",
|
|
||||||
instance_step_rate: 0
|
|
||||||
}]
|
|
||||||
|
|
||||||
pipeline_info.vertex_attributes = vertex_attributes
|
|
||||||
|
|
||||||
log.console("Creating pipeline with info:")
|
|
||||||
log.console(json.encode(pipeline_info))
|
|
||||||
log.console("Target info:")
|
|
||||||
log.console(json.encode(pipeline_info.target))
|
|
||||||
log.console("Has color_targets?", pipeline_info.target.color_targets ? "yes" : "no")
|
|
||||||
|
|
||||||
var pipeline = new sdl_gpu.graphics_pipeline(device, pipeline_info)
|
|
||||||
if (!pipeline) {
|
|
||||||
log.error(`Failed to create graphics pipeline for ${vert}/${frag}`)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the pipeline
|
|
||||||
pipeline_cache[cache_key] = pipeline
|
|
||||||
|
|
||||||
return pipeline
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load default sprite shaders
|
|
||||||
var sprite_gpu_pipeline = load_shader('sprite', 'sprite')
|
|
||||||
|
|
||||||
// Public function to get shader pipelines
|
|
||||||
prosperon.get_shader_pipeline = function(vert, frag, pipeline_config) {
|
|
||||||
return load_shader(vert, frag, pipeline_config)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export pipeline components for custom configs
|
|
||||||
prosperon.pipeline = {
|
|
||||||
depth_states: {
|
|
||||||
default: default_depth_state,
|
|
||||||
depth_test: {
|
|
||||||
compare: "less",
|
|
||||||
test: true,
|
|
||||||
write: true,
|
|
||||||
bias: 0,
|
|
||||||
bias_slope_scale: 0,
|
|
||||||
bias_clamp: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
blend_states: {
|
|
||||||
disabled: disabled_blend_state,
|
|
||||||
alpha: alpha_blend_state,
|
|
||||||
additive: {
|
|
||||||
enabled: true,
|
|
||||||
src_rgb: "src_alpha",
|
|
||||||
dst_rgb: "one",
|
|
||||||
op_rgb: "add",
|
|
||||||
src_alpha: "one",
|
|
||||||
dst_alpha: "one",
|
|
||||||
op_alpha: "add"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stencil_states: {
|
|
||||||
default: default_stencil_state
|
|
||||||
},
|
|
||||||
create_config: create_pipeline_config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize ImGui with the window and renderer
|
// Initialize ImGui with the window and renderer
|
||||||
@@ -361,7 +230,7 @@ var io = use('io');
|
|||||||
var rasterize = use('rasterize');
|
var rasterize = use('rasterize');
|
||||||
var time = use('time')
|
var time = use('time')
|
||||||
var tilemap = use('tilemap')
|
var tilemap = use('tilemap')
|
||||||
var geometry = use('geometry')
|
|
||||||
var res = use('resources')
|
var res = use('resources')
|
||||||
var input = use('input')
|
var input = use('input')
|
||||||
|
|
||||||
@@ -452,83 +321,6 @@ function create_pipeline_config(options) {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sprite pipeline configuration
|
|
||||||
var sprite_pipeline_config = {
|
|
||||||
blend: alpha_blend_state,
|
|
||||||
target: {
|
|
||||||
color_targets: [{
|
|
||||||
format: "rgba8",
|
|
||||||
blend: alpha_blend_state
|
|
||||||
}],
|
|
||||||
depth: "d32 float s8" // C code expects "depth" not "depth_stencil_format"
|
|
||||||
},
|
|
||||||
label: "sprite pipeline"
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCameraMatrix(cam) {
|
|
||||||
def win_w = logical.width
|
|
||||||
def win_h = logical.height
|
|
||||||
def view_w = (cam.size?.[0] ?? win_w) / cam.zoom
|
|
||||||
def view_h = (cam.size?.[1] ?? win_h) / cam.zoom
|
|
||||||
|
|
||||||
def ox = cam.pos[0] - view_w * (cam.anchor?.[0] ?? 0)
|
|
||||||
def oy = cam.pos[1] - view_h * (cam.anchor?.[1] ?? 0)
|
|
||||||
|
|
||||||
def vx = (cam.viewport?.x ?? 0) * win_w
|
|
||||||
def vy = (cam.viewport?.y ?? 0) * win_h
|
|
||||||
def vw = (cam.viewport?.width ?? 1) * win_w
|
|
||||||
def vh = (cam.viewport?.height ?? 1) * win_h
|
|
||||||
|
|
||||||
def sx = vw / view_w
|
|
||||||
def sy = vh / view_h // flip-Y later
|
|
||||||
|
|
||||||
/* affine matrix that SDL wants (Y going down) */
|
|
||||||
cam.a = sx
|
|
||||||
cam.c = vx - sx * ox
|
|
||||||
cam.e = -sy // <-- minus = flip Y
|
|
||||||
cam.f = vy + vh + sy * oy
|
|
||||||
|
|
||||||
/* convenience inverses */
|
|
||||||
cam.ia = 1 / cam.a
|
|
||||||
cam.ic = -cam.c / cam.a
|
|
||||||
cam.ie = 1 / cam.e
|
|
||||||
cam.if = -cam.f / cam.e
|
|
||||||
|
|
||||||
camera = cam
|
|
||||||
}
|
|
||||||
|
|
||||||
//---- forward transform ----
|
|
||||||
function worldToScreenPoint([x,y], camera) {
|
|
||||||
return {
|
|
||||||
x: camera.a * x + camera.c,
|
|
||||||
y: camera.e * y + camera.f
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//---- inverse transform ----
|
|
||||||
function screenToWorldPoint(pos, camera) {
|
|
||||||
return {
|
|
||||||
x: camera.ia * pos[0] + camera.ic,
|
|
||||||
y: camera.ie * pos[1] + camera.if
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//---- rectangle (two corner) ----
|
|
||||||
function worldToScreenRect({x,y,width,height}, camera) {
|
|
||||||
// map bottom-left and top-right
|
|
||||||
def x1 = camera.a * x + camera.c;
|
|
||||||
def y1 = camera.e * y + camera.f;
|
|
||||||
def x2 = camera.a * (x + width) + camera.c;
|
|
||||||
def y2 = camera.e * (y + height) + camera.f;
|
|
||||||
|
|
||||||
return {
|
|
||||||
x:Math.min(x1,x2),
|
|
||||||
y:Math.min(y1,y2),
|
|
||||||
width:Math.abs(x2-x1),
|
|
||||||
height:Math.abs(y2-y1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var gameactor
|
var gameactor
|
||||||
|
|
||||||
var images = {}
|
var images = {}
|
||||||
@@ -632,94 +424,121 @@ prosperon.input = function(fn)
|
|||||||
poll_input()
|
poll_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sprite_pipeline = {
|
||||||
|
vertex: "sprite.vert",
|
||||||
|
fragment: "sprite.frag",
|
||||||
|
cull: "none",
|
||||||
|
target: {
|
||||||
|
color_targets: [
|
||||||
|
{format: device.swapchain_format(window), blend:disabled_blend_state}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
vertex_buffer_descriptions: [ { slot:0, input_rate: "vertex", instance_step_rate: 0,
|
||||||
|
pitch: 8},
|
||||||
|
{slot:1, input_rate:"vertex", instance_step_rate: 0, pitch: 8},
|
||||||
|
{slot:2, input_rate:"vertex", instance_step_rate: 0, pitch: 16}
|
||||||
|
],
|
||||||
|
vertex_attributes: [
|
||||||
|
{ location: 0, buffer_slot: 0, format: "float2", offset: 0},
|
||||||
|
{ location: 1, buffer_slot: 1, format: "float2", offset: 0},
|
||||||
|
{ location: 2, buffer_slot: 2, format: "float4", offset: 0}
|
||||||
|
],
|
||||||
|
primitive: "triangle",
|
||||||
|
blend: alpha_blend_state
|
||||||
|
}
|
||||||
|
|
||||||
|
var pipey = load_pipeline(sprite_pipeline)
|
||||||
|
|
||||||
prosperon.create_batch = function create_batch(draw_cmds, done) {
|
prosperon.create_batch = function create_batch(draw_cmds, done) {
|
||||||
var cmd_buffer = device.acquire_cmd_buffer()
|
|
||||||
var swapchain_texture = cmd_buffer.acquire_swapchain(window)
|
|
||||||
|
|
||||||
var img = graphics.texture("pockle")
|
var img = graphics.texture("pockle")
|
||||||
var gpu_tex = get_img_gpu(img)
|
var pipeline = pipey
|
||||||
var geom = geometry.sprites_to_data([{
|
|
||||||
pos: {x: 0, y: 0},
|
|
||||||
texture: img,
|
|
||||||
color: {r:1,g:1,b:1,a:1}
|
|
||||||
}])
|
|
||||||
|
|
||||||
device.upload(cmd_buffer, [geom])
|
var geom = geometry.make_rect_quad({x:-320,y:-180,width:640,height:360})
|
||||||
|
geom.indices = geometry.make_quad_indices(1)
|
||||||
|
|
||||||
var pass = cmd_buffer.render_pass({
|
var cmd_buffer = device.acquire_cmd_buffer()
|
||||||
color_targets: [{
|
|
||||||
texture: swapchain_texture,
|
cmd_buffer.push_vertex_uniform_data(0, Pblob)
|
||||||
clear_color: {r:0.1, g:0.1, b:0.15, a:1},
|
|
||||||
load_op: "clear",
|
var pos_buffer = new sdl_gpu.buffer(device, {
|
||||||
store_op: "store"
|
vertex: true,
|
||||||
}]
|
size: geom.xy.length/8
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var uv_buffer = new sdl_gpu.buffer(device, {
|
||||||
|
vertex: true,
|
||||||
|
size: geom.uv.length/8
|
||||||
|
})
|
||||||
|
|
||||||
|
var color_buffer = new sdl_gpu.buffer(device, {
|
||||||
|
vertex: true,
|
||||||
|
size: geom.color.length/8
|
||||||
|
})
|
||||||
|
|
||||||
if (!swapchain_texture) {
|
var index_buffer = new sdl_gpu.buffer(device, {
|
||||||
cmd_buffer.cancel()
|
index: true,
|
||||||
} else {
|
size: geom.indices.length/8
|
||||||
|
})
|
||||||
|
|
||||||
|
var cpy_pass = cmd_buffer.copy_pass()
|
||||||
|
|
||||||
|
upload(cpy_pass, pos_buffer, geom.xy)
|
||||||
|
upload(cpy_pass, uv_buffer, geom.uv)
|
||||||
|
upload(cpy_pass, color_buffer, geom.color)
|
||||||
|
upload(cpy_pass, index_buffer, geom.indices)
|
||||||
|
|
||||||
|
if (!img.gpu) {
|
||||||
|
img.gpu = new sdl_gpu.texture(device, {
|
||||||
|
width: img.width,
|
||||||
|
height: img.height,
|
||||||
|
layers: 1,
|
||||||
|
mip_levels: 1,
|
||||||
|
samples: 0,
|
||||||
|
type: "2d",
|
||||||
|
format: "rgba8",
|
||||||
|
sampler: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
var tbuf = new sdl_gpu.transfer_buffer(device, {
|
||||||
|
size: img.cpu.pixels.length/8,
|
||||||
|
usage: "upload"
|
||||||
|
})
|
||||||
|
|
||||||
|
tbuf.copy_blob(device, img.cpu.pixels)
|
||||||
|
|
||||||
|
cpy_pass.upload_to_texture({
|
||||||
|
transfer_buffer: tbuf,
|
||||||
|
offset: 0,
|
||||||
|
pixels_per_row: img.cpu.width,
|
||||||
|
rows_per_lay: img.cpu.height,
|
||||||
|
}, {
|
||||||
|
texture: img.gpu,
|
||||||
|
mip_level: 0,
|
||||||
|
layer: 0,
|
||||||
|
x: 0, y: 0, z: 0,
|
||||||
|
w: img.cpu.width,
|
||||||
|
h: img.cpu.height,
|
||||||
|
d: 1
|
||||||
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin render pass
|
cpy_pass.end()
|
||||||
var render_pass = cmd_buffer.render_pass({
|
|
||||||
color_targets: [{
|
|
||||||
texture: swapchain_texture,
|
|
||||||
clear_color: {r: 0.1, g: 0.1, b: 0.15, a: 1.0},
|
|
||||||
load_op: "clear",
|
|
||||||
store_op: "store"
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
|
|
||||||
render_pass.bind_pipeline(sprite_gpu_pipeline)
|
var pass = cmd_buffer.swapchain_pass(window)
|
||||||
|
|
||||||
// Set viewport
|
pass.viewport({
|
||||||
render_pass.viewport({
|
|
||||||
x: 0, y: 0,
|
x: 0, y: 0,
|
||||||
w: logical.width,
|
width: 640,
|
||||||
h: logical.height
|
height: 360
|
||||||
})
|
})
|
||||||
|
|
||||||
// Process draw_image commands
|
pass.bind_pipeline(pipeline)
|
||||||
for (var cmd of draw_cmds) {
|
pass.bind_buffers(0, [{buffer:pos_buffer,offset:0}, {buffer:uv_buffer, offset:0}, {buffer:color_buffer, offset:0}])
|
||||||
if (cmd.cmd != "draw_image") continue
|
pass.bind_index_buffer({buffer:index_buffer,offset:0}, 16)
|
||||||
|
pass.bind_samplers(false, 0, [{texture:img.gpu, sampler:std_sampler}])
|
||||||
|
pass.draw_indexed(6, 1, 0, 0, 0)
|
||||||
|
|
||||||
var img = graphics.texture(cmd.image)
|
pass.end()
|
||||||
var gpu_tex = get_img_gpu(img)
|
|
||||||
|
|
||||||
if (!gpu_tex) continue
|
|
||||||
|
|
||||||
// Set up sprite dimensions
|
|
||||||
cmd.rect.width ??= img.width
|
|
||||||
cmd.rect.height ??= img.height
|
|
||||||
cmd.rect.width = cmd.rect.width * (cmd.scale?.x ?? 1)
|
|
||||||
cmd.rect.height = cmd.rect.height * (cmd.scale?.y ?? 1)
|
|
||||||
cmd.rect = worldToScreenRect(cmd.rect, camera)
|
|
||||||
|
|
||||||
var geom = geometry.sprites_to_data([{
|
|
||||||
pos: {x: cmd.rect.x, y: cmd.rect.y},
|
|
||||||
texture: img,
|
|
||||||
color: {r:1,g:1,b:1,a:1}
|
|
||||||
}])
|
|
||||||
|
|
||||||
log.console(json.encode(geom))
|
|
||||||
|
|
||||||
render_pass.bind_buffers(0, [geom], device)
|
|
||||||
|
|
||||||
// Bind texture and sampler
|
|
||||||
render_pass.bind_samplers(false, 0, [{
|
|
||||||
texture: gpu_tex,
|
|
||||||
sampler: std_sampler
|
|
||||||
}])
|
|
||||||
|
|
||||||
// Draw the sprite
|
|
||||||
log.console("DRWA INDEX")
|
|
||||||
render_pass.draw(geom.num_vertices, 1, 0, 0, 0) // 6 vertices, 1 instance
|
|
||||||
log.console("DREW")
|
|
||||||
}
|
|
||||||
|
|
||||||
render_pass.end()
|
|
||||||
cmd_buffer.submit()
|
cmd_buffer.submit()
|
||||||
|
|
||||||
if (done) done()
|
if (done) done()
|
||||||
|
|||||||
@@ -153,8 +153,6 @@ void actor_free(cell_rt *actor)
|
|||||||
JS_FreeAtom(js, actor->actor_sym);
|
JS_FreeAtom(js, actor->actor_sym);
|
||||||
|
|
||||||
SDL_RemoveTimer(actor->ar);
|
SDL_RemoveTimer(actor->ar);
|
||||||
for (int i = 0; i < arrlen(actor->js_swapchains); i++)
|
|
||||||
JS_FreeValue(js, actor->js_swapchains[i]);
|
|
||||||
|
|
||||||
/* Free timer callbacks stored in actor */
|
/* Free timer callbacks stored in actor */
|
||||||
for (int i = 0; i < hmlen(actor->timers); i++) {
|
for (int i = 0; i < hmlen(actor->timers); i++) {
|
||||||
@@ -162,7 +160,6 @@ void actor_free(cell_rt *actor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
hmfree(actor->timers);
|
hmfree(actor->timers);
|
||||||
arrfree(actor->js_swapchains);
|
|
||||||
arrfree(actor->module_registry);
|
arrfree(actor->module_registry);
|
||||||
|
|
||||||
/* Free all letters in the queue */
|
/* Free all letters in the queue */
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ mi_heap_t *heap;
|
|||||||
void *init_wota;
|
void *init_wota;
|
||||||
|
|
||||||
ModuleEntry *module_registry;
|
ModuleEntry *module_registry;
|
||||||
JSValue *js_swapchains;
|
|
||||||
|
|
||||||
/* Protects JSContext usage */
|
/* Protects JSContext usage */
|
||||||
SDL_Mutex *mutex; /* for everything else */
|
SDL_Mutex *mutex; /* for everything else */
|
||||||
|
|||||||
@@ -1436,34 +1436,6 @@ JSC_CCALL(os_power_state,
|
|||||||
return JS_NULL;
|
return JS_NULL;
|
||||||
)
|
)
|
||||||
|
|
||||||
JSC_CCALL(os_cull_sprites,
|
|
||||||
ret = JS_NewArray(js);
|
|
||||||
int n = 0;
|
|
||||||
|
|
||||||
JSValue sprites = argv[0];
|
|
||||||
shader_globals info = {0}; // TODO: get this as a JS object
|
|
||||||
rect camera_rect = {0};
|
|
||||||
camera_rect.x = info.camera_pos_world.x - info.render_size.x/2.0;
|
|
||||||
camera_rect.y = info.camera_pos_world.y - info.render_size.y/2.0;
|
|
||||||
camera_rect.w = info.render_size.x;
|
|
||||||
camera_rect.h = info.render_size.y;
|
|
||||||
|
|
||||||
int len = JS_ArrayLength(js,sprites);
|
|
||||||
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
JSValue sub = JS_GetPropertyUint32(js,sprites,i);
|
|
||||||
transform *t;
|
|
||||||
JS_GETATOM(js,t,sub,transform,transform)
|
|
||||||
|
|
||||||
rect sprite = transform2rect(t);
|
|
||||||
if (SDL_HasRectIntersectionFloat(&sprite, &camera_rect)) {
|
|
||||||
JS_SetPropertyUint32(js,ret,n,JS_DupValue(js,sub));
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
JS_FreeValue(js,sub);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
static const JSCFunctionListEntry js_util_funcs[] = {
|
static const JSCFunctionListEntry js_util_funcs[] = {
|
||||||
MIST_FUNC_DEF(os, guid, 0),
|
MIST_FUNC_DEF(os, guid, 0),
|
||||||
MIST_FUNC_DEF(os, insertion_sort, 2),
|
MIST_FUNC_DEF(os, insertion_sort, 2),
|
||||||
@@ -1524,7 +1496,6 @@ static const JSCFunctionListEntry js_graphics_funcs[] = {
|
|||||||
MIST_FUNC_DEF(os, make_texture, 1),
|
MIST_FUNC_DEF(os, make_texture, 1),
|
||||||
MIST_FUNC_DEF(os, make_gif, 1),
|
MIST_FUNC_DEF(os, make_gif, 1),
|
||||||
MIST_FUNC_DEF(os, make_aseprite, 1),
|
MIST_FUNC_DEF(os, make_aseprite, 1),
|
||||||
MIST_FUNC_DEF(os, cull_sprites, 2),
|
|
||||||
MIST_FUNC_DEF(os, make_font, 2),
|
MIST_FUNC_DEF(os, make_font, 2),
|
||||||
MIST_FUNC_DEF(os, make_line_prim, 5),
|
MIST_FUNC_DEF(os, make_line_prim, 5),
|
||||||
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),
|
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),
|
||||||
|
|||||||
@@ -56,24 +56,6 @@ struct lrtb {
|
|||||||
typedef struct lrtb lrtb;
|
typedef struct lrtb lrtb;
|
||||||
typedef struct text_vert text_vert;
|
typedef struct text_vert text_vert;
|
||||||
|
|
||||||
// Shader globals structure for camera transformations
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
typedef struct shader_globals {
|
|
||||||
HMM_Mat4 world_to_projection;
|
|
||||||
HMM_Mat4 projection_to_world;
|
|
||||||
HMM_Mat4 world_to_view;
|
|
||||||
HMM_Mat4 view_to_projection;
|
|
||||||
HMM_Vec3 camera_pos_world;
|
|
||||||
HMM_Vec3 camera_dir_world;
|
|
||||||
float viewport_min_z;
|
|
||||||
float viewport_max_z;
|
|
||||||
HMM_Vec2 viewport_size;
|
|
||||||
HMM_Vec2 viewport_offset;
|
|
||||||
HMM_Vec2 render_size;
|
|
||||||
float time;
|
|
||||||
} shader_globals;
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
// Common macros for property access
|
// Common macros for property access
|
||||||
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
|
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
|
||||||
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
|
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \
|
||||||
|
|||||||
@@ -971,6 +971,34 @@ JSC_CCALL(geometry_tilemap_to_data,
|
|||||||
free(index_data);
|
free(index_data);
|
||||||
)
|
)
|
||||||
|
|
||||||
|
static void print_buffers(float *xy_data, float *uv_data, SDL_FColor *color_data, uint16_t *index_data, int vertex_count, int index_count)
|
||||||
|
{
|
||||||
|
printf("=== GEOMETRY BUFFERS ===\n");
|
||||||
|
|
||||||
|
printf("XY Data (%d vertices):\n", vertex_count);
|
||||||
|
for (int i = 0; i < vertex_count; i++) {
|
||||||
|
printf(" Vertex %d: x=%.2f, y=%.2f\n", i, xy_data[i*2], xy_data[i*2+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nUV Data:\n");
|
||||||
|
for (int i = 0; i < vertex_count; i++) {
|
||||||
|
printf(" Vertex %d: u=%.4f, v=%.4f\n", i, uv_data[i*2], uv_data[i*2+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nColor Data:\n");
|
||||||
|
for (int i = 0; i < vertex_count; i++) {
|
||||||
|
printf(" Vertex %d: r=%.2f, g=%.2f, b=%.2f, a=%.2f\n",
|
||||||
|
i, color_data[i].r, color_data[i].g, color_data[i].b, color_data[i].a);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nIndex Data (%d indices):\n", index_count);
|
||||||
|
for (int i = 0; i < index_count; i += 3) {
|
||||||
|
printf(" Triangle %d: %d, %d, %d\n", i/3, index_data[i], index_data[i+1], index_data[i+2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("========================\n");
|
||||||
|
}
|
||||||
|
|
||||||
JSC_CCALL(geometry_sprites_to_data,
|
JSC_CCALL(geometry_sprites_to_data,
|
||||||
JSValue sprites_array = argv[0];
|
JSValue sprites_array = argv[0];
|
||||||
if (!JS_IsArray(js, sprites_array)) {
|
if (!JS_IsArray(js, sprites_array)) {
|
||||||
@@ -1318,6 +1346,108 @@ JSC_CCALL(geometry_renderitem_clear,
|
|||||||
return JS_NULL;
|
return JS_NULL;
|
||||||
)
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(geometry_array_blob,
|
||||||
|
JSValue arr = argv[0];
|
||||||
|
size_t len = JS_ArrayLength(js,arr);
|
||||||
|
float data[len];
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
JSValue val = JS_GetPropertyUint32(js,arr,i);
|
||||||
|
data[i] = js2number(js, val);
|
||||||
|
JS_FreeValue(js,val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return js_new_blob_stoned_copy(js, data, sizeof(float)*len);
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(geometry_make_quad_indices,
|
||||||
|
int quads;
|
||||||
|
if (JS_ToInt32(js, &quads, argv[0]) < 0) {
|
||||||
|
return JS_ThrowTypeError(js, "quads must be a number");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quads < 0) {
|
||||||
|
return JS_ThrowTypeError(js, "quads must be >= 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
int index_count = quads * 6;
|
||||||
|
uint16_t *index_data = malloc(index_count * sizeof(uint16_t));
|
||||||
|
|
||||||
|
int index_idx = 0;
|
||||||
|
for (int i = 0; i < quads; i++) {
|
||||||
|
uint16_t base_idx = i * 4;
|
||||||
|
index_data[index_idx++] = base_idx + 0;
|
||||||
|
index_data[index_idx++] = base_idx + 1;
|
||||||
|
index_data[index_idx++] = base_idx + 2;
|
||||||
|
index_data[index_idx++] = base_idx + 1;
|
||||||
|
index_data[index_idx++] = base_idx + 3;
|
||||||
|
index_data[index_idx++] = base_idx + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValue result = js_new_blob_stoned_copy(js, index_data, index_count * sizeof(uint16_t));
|
||||||
|
free(index_data);
|
||||||
|
return result;
|
||||||
|
)
|
||||||
|
|
||||||
|
JSC_CCALL(geometry_make_rect_quad,
|
||||||
|
rect r = js2rect(js, argv[0]);
|
||||||
|
|
||||||
|
// Optional UV rect (default to 0,0,1,1)
|
||||||
|
rect uv = {0, 0, 1, 1};
|
||||||
|
if (argc > 1 && !JS_IsNull(argv[1])) {
|
||||||
|
uv = js2rect(js, argv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional color (default to white)
|
||||||
|
SDL_FColor color = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
if (argc > 2 && !JS_IsNull(argv[2])) {
|
||||||
|
HMM_Vec4 c = js2color(js, argv[2]);
|
||||||
|
color.r = c.r;
|
||||||
|
color.g = c.g;
|
||||||
|
color.b = c.b;
|
||||||
|
color.a = c.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate buffers for 1 quad (4 vertices)
|
||||||
|
int vertex_count = 4;
|
||||||
|
int index_count = 6;
|
||||||
|
|
||||||
|
float xy_data[vertex_count*2];
|
||||||
|
float uv_data[vertex_count*2];
|
||||||
|
SDL_FColor color_data[vertex_count];
|
||||||
|
|
||||||
|
// Set vertex positions (4 corners)
|
||||||
|
xy_data[0] = r.x; xy_data[1] = r.y; // bottom-left
|
||||||
|
xy_data[2] = r.x + r.w; xy_data[3] = r.y; // bottom-right
|
||||||
|
xy_data[4] = r.x; xy_data[5] = r.y + r.h; // top-left
|
||||||
|
xy_data[6] = r.x + r.w; xy_data[7] = r.y + r.h; // top-right
|
||||||
|
|
||||||
|
// Set UV coordinates
|
||||||
|
uv_data[0] = uv.x; uv_data[1] = uv.y + uv.h; // bottom-left
|
||||||
|
uv_data[2] = uv.x + uv.w; uv_data[3] = uv.y + uv.h; // bottom-right
|
||||||
|
uv_data[4] = uv.x; uv_data[5] = uv.y; // top-left
|
||||||
|
uv_data[6] = uv.x + uv.w; uv_data[7] = uv.y; // top-right
|
||||||
|
|
||||||
|
// Set colors
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
color_data[i] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create result object
|
||||||
|
ret = JS_NewObject(js);
|
||||||
|
|
||||||
|
JSValue xy_blob = js_new_blob_stoned_copy(js, xy_data, vertex_count * 2 * sizeof(float));
|
||||||
|
JSValue uv_blob = js_new_blob_stoned_copy(js, uv_data, vertex_count * 2 * sizeof(float));
|
||||||
|
JSValue color_blob = js_new_blob_stoned_copy(js, color_data, vertex_count * sizeof(SDL_FColor));
|
||||||
|
|
||||||
|
JS_SetPropertyStr(js, ret, "xy", xy_blob);
|
||||||
|
JS_SetPropertyStr(js, ret, "xy_stride", JS_NewInt32(js, 2 * sizeof(float)));
|
||||||
|
JS_SetPropertyStr(js, ret, "uv", uv_blob);
|
||||||
|
JS_SetPropertyStr(js, ret, "uv_stride", JS_NewInt32(js, 2 * sizeof(float)));
|
||||||
|
JS_SetPropertyStr(js, ret, "color", color_blob);
|
||||||
|
JS_SetPropertyStr(js, ret, "color_stride", JS_NewInt32(js, sizeof(SDL_FColor)));
|
||||||
|
JS_SetPropertyStr(js, ret, "num_vertices", JS_NewInt32(js, vertex_count));
|
||||||
|
)
|
||||||
|
|
||||||
static const JSCFunctionListEntry js_geometry_funcs[] = {
|
static const JSCFunctionListEntry js_geometry_funcs[] = {
|
||||||
MIST_FUNC_DEF(geometry, rect_intersection, 2),
|
MIST_FUNC_DEF(geometry, rect_intersection, 2),
|
||||||
MIST_FUNC_DEF(geometry, rect_intersects, 2),
|
MIST_FUNC_DEF(geometry, rect_intersects, 2),
|
||||||
@@ -1339,6 +1469,9 @@ static const JSCFunctionListEntry js_geometry_funcs[] = {
|
|||||||
MIST_FUNC_DEF(geometry, renderitem_push, 3),
|
MIST_FUNC_DEF(geometry, renderitem_push, 3),
|
||||||
MIST_FUNC_DEF(geometry, renderitem_sort, 0),
|
MIST_FUNC_DEF(geometry, renderitem_sort, 0),
|
||||||
MIST_FUNC_DEF(geometry, renderitem_clear, 0),
|
MIST_FUNC_DEF(geometry, renderitem_clear, 0),
|
||||||
|
MIST_FUNC_DEF(geometry, array_blob, 2),
|
||||||
|
MIST_FUNC_DEF(geometry, make_quad_indices, 1),
|
||||||
|
MIST_FUNC_DEF(geometry, make_rect_quad, 3),
|
||||||
};
|
};
|
||||||
|
|
||||||
JSValue js_geometry_use(JSContext *js) {
|
JSValue js_geometry_use(JSContext *js) {
|
||||||
|
|||||||
2038
source/qjs_sdl_gpu.c
2038
source/qjs_sdl_gpu.c
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user