From 09c3d5cc4e6ee3430c0c307436f6a5dac5483dc4 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 28 Jul 2025 16:34:29 -0500 Subject: [PATCH] sdl 3 gpu --- meson.build | 2 +- prosperon/draw2d.cm | 24 +- prosperon/prosperon.cm | 689 ++++++++++++++------ prosperon/sdl_gpu.cm | 655 ------------------- source/jsffi.c | 7 +- source/qjs_sdl_gpu.c | 1387 ++++++++++++++++++++++++++-------------- source/qjs_sdl_input.c | 2 +- source/qjs_sdl_video.c | 4 + 8 files changed, 1420 insertions(+), 1350 deletions(-) delete mode 100644 prosperon/sdl_gpu.cm diff --git a/meson.build b/meson.build index d81884e5..d4605ba1 100644 --- a/meson.build +++ b/meson.build @@ -328,7 +328,7 @@ sources = [] src += [ 'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c', 'render.c','simplex.c','spline.c', 'transform.c','cell.c', 'wildmatch.c', - 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c', + 'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_input.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_sdl_gpu.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c', 'picohttpparser.c', 'qjs_miniz.c', 'timer.c', 'qjs_socket.c', 'qjs_kim.c', 'qjs_utf8.c', 'qjs_fit.c', 'qjs_text.c', 'qjs_layout.c' ] # quirc src diff --git a/prosperon/draw2d.cm b/prosperon/draw2d.cm index b0f37b64..7ee6286d 100644 --- a/prosperon/draw2d.cm +++ b/prosperon/draw2d.cm @@ -2,11 +2,7 @@ var math = use('math') var color = use('color') var draw = {} -draw[cell.DOC] = ` -A collection of 2D drawing functions that create drawing command lists. -These are pure functions that return plain JavaScript objects representing -drawing operations. No rendering or actor communication happens here. -` + var current_list = [] // Clear current list @@ -43,15 +39,6 @@ var rect_def = { radius: 0 } -var slice9_info = { - tile_top:true, - tile_bottom:true, - tile_left:true, - tile_right:true, - tile_center_x:true, - tile_center_right:true, -} - var image_info = { tile_x: false, tile_y: false, @@ -125,6 +112,15 @@ draw.rectangle = function render_rectangle(rect, defl, material) { }) } +var slice9_info = { + tile_top:true, + tile_bottom:true, + tile_left:true, + tile_right:true, + tile_center_x:true, + tile_center_right:true, +} + draw.slice9 = function slice9(image, rect = [0,0], slice = 0, info = slice9_info, material) { if (!image) throw Error('Need an image to render.') diff --git a/prosperon/prosperon.cm b/prosperon/prosperon.cm index 075cf45c..5103309d 100644 --- a/prosperon/prosperon.cm +++ b/prosperon/prosperon.cm @@ -1,10 +1,63 @@ var prosperon = {} -// This file is hard coded for the SDL renderer case +// This file is hard coded for the SDL GPU case var video = use('sdl_video') -var imgui = use('imgui') var surface = use('surface') +var sdl_gpu = use('sdl_gpu') +var io = use('io') + +var os = use('os') + +var driver = "vulkan" +switch(os.platform()) { + case "Linux": + driver = "vulkan" + break + case "Windows": +// driver = "direct3d12" + driver = "vulkan" + break + case "macOS": + driver = "metal" + break +} + +var default_sampler = { + min_filter: "nearest", + mag_filter: "nearest", + mipmap: "linear", + u: "repeat", + v: "repeat", + w: "repeat", + mip_bias: 0, + max_anisotropy: 0, + compare_op: "none", + min_lod: 0, + max_lod: 0, + anisotropy: false, + compare: false +}; + +var main_color = { + type:"2d", + format: "rgba8", + layers: 1, + mip_levels: 1, + samples: 0, + sampler:true, + color_target:true +}; + +var main_depth = { + type: "2d", + format: "d32 float s8", + layers:1, + mip_levels:1, + samples:0, + sampler:true, + depth_target:true +}; var default_window = { // Basic properties @@ -59,14 +112,251 @@ var default_window = { var win_config = arg[0] || {} win_config.__proto__ = default_window +win_config.metal = true + var window = new video.window(win_config) -var renderer = window.make_renderer() +var device = new sdl_gpu.gpu({ + shaders_msl:true, + shaders_metallib:true, + name: "metal" +}) +device.claim_window(window) +device.set_swapchain(window, 'sdr', 'vsync') +var shader_type = device.shader_format()[0] +shader_type = 'msl' +var std_sampler = new sdl_gpu.sampler(device, default_sampler) + +// Shader and pipeline cache +var shader_cache = {} +var pipeline_cache = {} + +function make_shader(sh_file) +{ + var file = `shaders/${shader_type}/${sh_file}.${shader_type}` + if (shader_cache[file]) return shader_cache[file] + var refl = json.decode(io.slurp(`shaders/reflection/${sh_file}.json`)) + + var shader = { + code: io.slurpbytes(file), + format: shader_type, + stage: sh_file.endsWith("vert") ? "vertex" : "fragment", + num_samplers: refl.separate_samplers ? refl.separate_samplers.length : 0, + num_textures: 0, + num_storage_buffers: refl.separate_storage_buffers ? refl.separate_storage_buffers.length : 0, + num_uniform_buffers: refl.ubos ? refl.ubos.length : 0, + entrypoint: shader_type == "msl" ? "main0" : "main" + } + + shader.gpu = context.make_shader(shader) + shader.reflection = refl; + shader_cache[file] = shader + shader.file = sh_file + return shader +} + +function load_shader(vert, frag, pipeline_config) { + // Default to sprite pipeline config if not provided + pipeline_config = pipeline_config || sprite_pipeline_config + + // Check cache first + var cache_key = `${vert}:${frag}:${json.encode(pipeline_config)}` + if (pipeline_cache[cache_key]) + return pipeline_cache[cache_key] + + // Load shader reflections + var vert_reflection = load_shader_reflection(vert, 'vert') + var frag_reflection = load_shader_reflection(frag, 'frag') + + 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 -imgui.init(window, renderer) -imgui.newframe() +//imgui.init(window, renderer) +//imgui.newframe() -var os = use('os'); var io = use('io'); var rasterize = use('rasterize'); var time = use('time') @@ -79,6 +369,102 @@ var graphics = use('graphics') var camera = {} +prosperon.scissor = function(rect) { + device.scissor(rect) +} + +// Pipeline component definitions +var default_depth_state = { + compare: "always", // never/less/equal/less_equal/greater/not_equal/greater_equal/always + test: false, + write: false, + bias: 0, + bias_slope_scale: 0, + bias_clamp: 0 +} + +var default_stencil_state = { + compare: "always", // never/less/equal/less_equal/greater/neq/greq/always + fail: "keep", // keep/zero/replace/incr_clamp/decr_clamp/invert/incr_wrap/decr_wrap + depth_fail: "keep", + pass: "keep" +} + +var disabled_blend_state = { + enabled: false, + src_rgb: "zero", + dst_rgb: "zero", + op_rgb: "add", + src_alpha: "one", + dst_alpha: "zero", + op_alpha: "add" +} + +var alpha_blend_state = { + enabled: true, + src_rgb: "src_alpha", + dst_rgb: "one_minus_src_alpha", + op_rgb: "add", + src_alpha: "one", + dst_alpha: "zero", + op_alpha: "add" +} + +var default_multisample_state = { + count: 1, + mask: 0xFFFFFFFF, + domask: false +} + +// Helper function to create pipeline config +function create_pipeline_config(options) { + var config = { + vertex: options.vertex, + fragment: options.fragment, + primitive: options.primitive || "triangle", + fill: options.fill ?? true, + depth: options.depth || default_depth_state, + stencil: { + enabled: options.stencil_enabled ?? false, + front: options.stencil_front || default_stencil_state, + back: options.stencil_back || default_stencil_state, + test: options.stencil_test ?? false, + compare_mask: options.stencil_compare_mask ?? 0, + write_mask: options.stencil_write_mask ?? 0 + }, + blend: options.blend || disabled_blend_state, + cull: options.cull || "none", + face: options.face || "cw", + alpha_to_coverage: options.alpha_to_coverage ?? false, + multisample: options.multisample || default_multisample_state, + label: options.label || "pipeline", + target: options.target || {} + } + + // Ensure target has required properties + if (!config.target.color_targets) { + config.target.color_targets = [{ + format: "rgba8", + blend: config.blend + }] + } + + 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 @@ -152,167 +538,12 @@ var renderer_commands = [] var win_size = {width:500,height:500} var logical = {width:500,height:500} -// Convert high-level draw commands to low-level renderer commands -function translate_draw_commands(commands) { - renderer_commands.length = 0 - - commands.forEach(function(cmd) { - if (cmd.material && cmd.material.color && typeof cmd.material.color == 'object') { - renderer.drawColor = cmd.material.color - } - switch(cmd.cmd) { - case "camera": - updateCameraMatrix(cmd.camera, win_size.width, win_size.height) - break - - case "draw_rect": - cmd.rect = worldToScreenRect(cmd.rect, camera) - renderer.fillRect(cmd.rect) - break - - case "draw_line": - var points = cmd.points.map(p => { - var pt = worldToScreenPoint(p, camera) - return[pt.x, pt.y] - }) - renderer.line(points) - break - - case "draw_point": - cmd.pos = worldToScreenPoint(cmd.pos, camera) - renderer.point(cmd.pos) - break - - case "draw_image": - var img = graphics.texture(cmd.image) - if (!img.gpu) { - var surf = new surface(img.cpu) - img.gpu = renderer.load_texture(surf) - } - var gpu = img.gpu - - if (!cmd.scale) cmd.scale = {x:1,y:1} - cmd.rect.width ??= img.width - cmd.rect.height ??= img.height - cmd.rect.width = cmd.rect.width * cmd.scale.x - cmd.rect.height = cmd.rect.height * cmd.scale.y - cmd.rect = worldToScreenRect(cmd.rect, camera) - renderer.texture( - gpu, - img.rect, - cmd.rect, - 0, - {x:0,y:0} - ) - - break - - case "draw_text": - if (!cmd.text) break - if (!cmd.pos) break - - // Get font from the font string (e.g., "smalle.16") - var font = graphics.get_font(cmd.font) - if (!font.gpu) { - var surf = new surface(font.surface) - font.gpu = renderer.load_texture(surf) - } - var gpu = font.gpu - - // Create text geometry buffer - var text_mesh = graphics.make_text_buffer( - cmd.text, - {x: cmd.pos.x, y: cmd.pos.y}, - [cmd.material.color.r, cmd.material.color.g, cmd.material.color.b, cmd.material.color.a], - cmd.wrap || 0, - font - ) - - if (!text_mesh) break - - if (text_mesh.xy.length == 0) break - - // Transform XY coordinates using camera matrix - var camera_params = [camera.a, camera.c, camera.e, camera.f] - var transformed_xy = geometry.transform_xy_blob(text_mesh.xy, camera_params) - - // Create transformed geometry object - var geom = { - xy: transformed_xy, - xy_stride: text_mesh.xy_stride, - uv: text_mesh.uv, - uv_stride: text_mesh.uv_stride, - color: text_mesh.color, - color_stride: text_mesh.color_stride, - indices: text_mesh.indices, - num_vertices: text_mesh.num_vertices, - num_indices: text_mesh.num_indices, - size_indices: text_mesh.size_indices - } - - renderer.geometry_raw(gpu, geom.xy, geom.xy_stride, geom.color, geom.color_stride, geom.uv, geom.uv_stride, geom.num_vertices, geom.indices, geom.num_indices, geom.size_indices) - break - - case "tilemap": - // Get cached geometry commands from tilemap - var geometryCommands = cmd.tilemap.draw() - - // Process each geometry command (one per texture) - for (var geomCmd of geometryCommands) { - var img = graphics.texture(geomCmd.image) - if (!img) continue - - // Get GPU texture following draw_image pattern - if (!img.gpu) { - var surf = new surface(img.cpu) - img.gpu = renderer.load_texture(surf) - } - var gpu = img.gpu - - // Transform geometry through camera and send to renderer - var geom = geomCmd.geometry - - // Transform XY coordinates using camera matrix - var camera_params = [camera.a, camera.c, camera.e, camera.f] - var transformed_xy = geometry.transform_xy_blob(geom.xy, camera_params) - - // Render directly instead of pushing to commands - renderer.geometry_raw(gpu, transformed_xy, geom.xy_stride, geom.color, geom.color_stride, geom.uv, geom.uv_stride, geom.num_vertices, geom.indices, geom.num_indices, geom.size_indices) - } - break - - case "geometry": - var gpu - if (cmd.texture_id) { - // If texture_id provided, assume it's already a GPU texture - gpu = cmd.texture_id - } else { - // Fall back to looking up by image path - var img = graphics.texture(cmd.image) - if (!img) break - - // Get GPU texture following draw_image pattern - if (!img.gpu) { - var surf = new surface(img.cpu) - img.gpu = renderer.load_texture(surf) - } - gpu = img.gpu - } - - // Transform geometry through camera and send to renderer - var geom = cmd.geometry - - // Transform XY coordinates using camera matrix - var camera_params = [camera.a, camera.c, camera.e, camera.f] - var transformed_xy = geometry.transform_xy_blob(geom.xy, camera_params) - - // Render directly instead of pushing to commands - renderer.geometry_raw(gpu, transformed_xy, geom.xy_stride, geom.color, geom.color_stride, geom.uv, geom.uv_stride, geom.num_vertices, geom.indices, geom.num_indices, geom.size_indices) - break - } - }) - - return renderer_commands +function get_img_gpu(img) +{ + if (img.gpu) return img.gpu + var surf = new surface(img.cpu) + img.gpu = device.load_texture(surf, 0) + return img.gpu } ///// input ///// @@ -322,10 +553,12 @@ function poll_input() { var evs = input.get_events() // Filter and transform events - if (renderer && Array.isArray(evs)) { + if (Array.isArray(evs)) { var filteredEvents = [] - var wantMouse = imgui.wantmouse() - var wantKeys = imgui.wantkeys() +// var wantMouse = imgui.wantmouse() +// var wantKeys = imgui.wantkeys() + var wantMouse = false + var wantKeys = false for (var i = 0; i < evs.length; i++) { var event = evs[i] @@ -354,15 +587,15 @@ function poll_input() { event.type == 'mouse_button_up' || event.type == 'mouse_wheel')) { // Convert window coordinates to renderer logical coordinates - var logicalPos = renderer.coordsFromWindow(event.pos) - event.pos = logicalPos +// var logicalPos = renderer.coordsFromWindow(event.pos) +// event.pos = logicalPos } // Handle drop events which also have position if (event.pos && (event.type == 'drop_file' || event.type == 'drop_text' || event.type == 'drop_position')) { - var logicalPos = renderer.coordsFromWindow(event.pos) - event.pos = logicalPos +// var logicalPos = renderer.coordsFromWindow(event.pos) +// event.pos = logicalPos } // Handle window events @@ -399,19 +632,95 @@ prosperon.input = function(fn) poll_input() } -// 2) helper to build & send a batch, then call done() prosperon.create_batch = function create_batch(draw_cmds, done) { - renderer.drawColor = {r:0.1,g:0.1,b:0.15,a:1} - renderer.clear() + var cmd_buffer = device.acquire_cmd_buffer() + var swapchain_texture = cmd_buffer.acquire_swapchain(window) - if (draw_cmds && draw_cmds.length) - var commands = translate_draw_commands(draw_cmds) + var img = graphics.texture("pockle") + var gpu_tex = get_img_gpu(img) + var geom = geometry.sprites_to_data([{ + pos: {x: 0, y: 0}, + texture: img, + color: {r:1,g:1,b:1,a:1} + }]) - renderer.drawColor = {r:1,g:1,b:1,a:1} - imgui.endframe(renderer) - imgui.newframe() + device.upload(cmd_buffer, [geom]) - renderer.present() + var pass = cmd_buffer.render_pass({ + color_targets: [{ + texture: swapchain_texture, + clear_color: {r:0.1, g:0.1, b:0.15, a:1}, + load_op: "clear", + store_op: "store" + }] + }) + + + + if (!swapchain_texture) { + cmd_buffer.cancel() + } else { + } + + // Begin render pass + 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) + + // Set viewport + render_pass.viewport({ + x: 0, y: 0, + w: logical.width, + h: logical.height + }) + + // Process draw_image commands + for (var cmd of draw_cmds) { + if (cmd.cmd != "draw_image") continue + + var img = graphics.texture(cmd.image) + 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() if (done) done() } @@ -462,22 +771,8 @@ prosperon.set_window = function(config) if (window_cmds[c]) window_cmds[c](config[c]) } -var renderer_cmds = { - resolution(e) { - logical.width = e.width - logical.height = e.height - renderer.logicalPresentation = {...e} - } -} - prosperon.set_renderer = function(config) { - for (var c in config) - if (renderer_cmds[c]) renderer_cmds[c](config[c]) -} - -prosperon.init = function() { - // No longer needed since we initialize directly } // Function to load textures directly to the renderer diff --git a/prosperon/sdl_gpu.cm b/prosperon/sdl_gpu.cm deleted file mode 100644 index 39513620..00000000 --- a/prosperon/sdl_gpu.cm +++ /dev/null @@ -1,655 +0,0 @@ -var render = {} - -var io = use('io') -var controller = use('controller') -var tracy = use('tracy') -var graphics = use('graphics') -var imgui = use('imgui') -var transform = use('transform') -var color = use('color') - -var base_pipeline = { - vertex: "sprite.vert", - fragment: "sprite.frag", - primitive: "triangle", // point, line, linestrip, triangle, trianglestrip - fill: true, // false for lines - depth: { - compare: "greater_equal", // never/less/equal/less_equal/greater/not_equal/greater_equal/always - test: false, - write: false, - bias: 0, - bias_slope_scale: 0, - bias_clamp: 0 - }, - stencil: { - enabled: true, - front: { - compare: "equal", // never/less/equal/less_equal/greater/neq/greq/always - fail: "keep", // keep/zero/replace/incr_clamp/decr_clamp/invert/incr_wrap/decr_wrap - depth_fail: "keep", - pass: "keep" - }, - back: { - compare: "equal", // never/less/equal/less_equal/greater/neq/greq/always - fail: "keep", // keep/zero/replace/incr_clamp/decr_clamp/invert/incr_wrap/decr_wrap - depth_fail: "keep", - pass: "keep" - }, - test: true, - compare_mask: 0, - write_mask: 0 - }, - blend: { - enabled: false, - src_rgb: "zero", // zero/one/src_color/one_minus_src_color/dst_color/one_minus_dst_color/src_alpha/one_minus_src_alpha/dst_alpha/one_minus_dst_alpha/constant_color/one_minus_constant_color/src_alpha_saturate - dst_rgb: "zero", - op_rgb: "add", // add/sub/rev_sub/min/max - src_alpha: "one", - dst_alpha: "zero", - op_alpha: "add" - }, - cull: "none", // none/front/back - face: "cw", // cw/ccw - alpha_to_coverage: false, - multisample: { - count: 1, // number of multisamples - mask: 0xFFFFFFFF, - domask: false - }, - label: "scripted pipeline", - target: {} -} - -var sprite_pipeline = Object.create(base_pipeline); -sprite_pipeline.blend = { - enabled:true, - src_rgb: "src_alpha", // zero/one/src_color/one_minus_src_color/dst_color/one_minus_dst_color/src_alpha/one_minus_src_alpha/dst_alpha/one_minus_dst_alpha/constant_color/one_minus_constant_color/src_alpha_saturate - dst_rgb: "one_minus_src_alpha", - op_rgb: "add", // add/sub/rev_sub/min/max - src_alpha: "one", - dst_alpha: "zero", - op_alpha: "add" -}; - -var context; - -sprite_pipeline.target = { - color_targets: [{ - format:"rgba8", - blend:sprite_pipeline.blend - }], - depth: "d32 float s8" -}; - -var driver = "vulkan" -switch(os.platform()) { - case "Linux": - driver = "vulkan" - break - case "Windows": -// driver = "direct3d12" - driver = "vulkan" - break - case "macOS": - driver = "metal" - break -} - -var unit_transform = new transform; - -var cur = {}; -cur.images = []; -cur.samplers = []; - -var tbuffer; -function full_upload(buffers) { - var cmds = context.acquire_cmd_buffer(); - tbuffer = context.upload(cmds, buffers, tbuffer); - cmds.submit(); -} - -function bind_pipeline(pass, pipeline) { - make_pipeline(pipeline) - pass.bind_pipeline(pipeline.gpu) - pass.pipeline = pipeline; -} - -var main_pass; - -var cornflower = [62/255,96/255,113/255,1]; - -function get_pipeline_ubo_slot(pipeline, name) { - if (!pipeline.vertex.reflection.ubos) return; - for (var i = 0; i < pipeline.vertex.reflection.ubos.length; i++) { - var ubo = pipeline.vertex.reflection.ubos[i]; - if (ubo.name.endsWith(name)) - return i; - } - return null; -} - -function transpose4x4(val) { - var out = []; - out[0] = val[0]; out[1] = val[4]; out[2] = val[8]; out[3] = val[12]; - out[4] = val[1]; out[5] = val[5]; out[6] = val[9]; out[7] = val[13]; - out[8] = val[2]; out[9] = val[6]; out[10] = val[10];out[11] = val[14]; - out[12] = val[3];out[13] = val[7];out[14] = val[11];out[15] = val[15]; - return out; -} - -function ubo_obj_to_array(pipeline, name, obj) { - var ubo; - for (var i = 0; i < pipeline.vertex.reflection.ubos.length; i++) { - ubo = pipeline.vertex.reflection.ubos[i]; - if (ubo.name.endsWith(name)) break; - } - var type = pipeline.vertex.reflection.types[ubo.type]; - var len = 0; - for (var mem of type.members) - len += type_to_byte_count(mem.type); - - var buf = new ArrayBuffer(len); - var view = new DataView(buf); - - for (var mem of type.members) { - var val = obj[mem.name]; - if (!val) throw new Error (`Could not find ${mem.name} on supplied object`); - - if (mem.name == 'model') - val = transpose4x4(val.array()); - - for (var i = 0; i < val.length; i++) - view.setFloat32(mem.offset + i*4, val[i],true); - } - return buf; -} - -function type_to_byte_count(type) { - switch (type) { - case 'float': return 4; - case 'vec2': return 8; - case 'vec3': return 12; - case 'vec4': return 16; - case 'mat4': return 64; - default: throw new Error("Unknown or unsupported float-based type: " + type); - } -} - -var sprite_model_ubo = { - model: unit_transform, - color: [1,1,1,1] -}; - -var shader_cache = {}; -var shader_times = {}; - -function make_pipeline(pipeline) { - if (pipeline.hasOwnProperty("gpu")) return; // this pipeline has already been made - - if (typeof pipeline.vertex == 'string') - pipeline.vertex = make_shader(pipeline.vertex); - if (typeof pipeline.fragment == 'string') - pipeline.fragment = make_shader(pipeline.fragment) - - // 1) Reflection data for vertex shader - var refl = pipeline.vertex.reflection - if (!refl || !refl.inputs || !Array.isArray(refl.inputs)) { - pipeline.gpu = context.make_pipeline(pipeline); - return; - } - - var inputs = refl.inputs - var buffer_descriptions = [] - var attributes = [] - - // 2) Build buffer + attribute for each reflection input - for (var i = 0; i < inputs.length; i++) { - var inp = inputs[i] - var typeStr = inp.type - var nameStr = (inp.name || "").toUpperCase() - var pitch = 4 - var fmt = "float1" - - if (typeStr == "vec2") { - pitch = 8 - fmt = "float2" - } else if (typeStr == "vec3") { - pitch = 12 - fmt = "float3" - } else if (typeStr == "vec4") { - if (nameStr.indexOf("COLOR") >= 0) { - pitch = 16 - fmt = "color" - } else { - pitch = 16 - fmt = "float4" - } - } - - buffer_descriptions.push({ - slot: i, - pitch: pitch, - input_rate: "vertex", - instance_step_rate: 0, - name:inp.name.split(".").pop() - }) - - attributes.push({ - location: inp.location, - buffer_slot: i, - format: fmt, - offset: 0 - }) - } - - pipeline.vertex_buffer_descriptions = buffer_descriptions - pipeline.vertex_attributes = attributes - - pipeline.gpu = context.make_pipeline(pipeline); -} - -var shader_type; - -function make_shader(sh_file) { - var file = `shaders/${shader_type}/${sh_file}.${shader_type}` - if (shader_cache[file]) return shader_cache[file] - var refl = json.decode(io.slurp(`shaders/reflection/${sh_file}.json`)) - - var shader = { - code: io.slurpbytes(file), - format: shader_type, - stage: sh_file.endsWith("vert") ? "vertex" : "fragment", - num_samplers: refl.separate_samplers ? refl.separate_samplers.length : 0, - num_textures: 0, - num_storage_buffers: refl.separate_storage_buffers ? refl.separate_storage_buffers.length : 0, - num_uniform_buffers: refl.ubos ? refl.ubos.length : 0, - entrypoint: shader_type == "msl" ? "main0" : "main" - } - - shader.gpu = context.make_shader(shader) - shader.reflection = refl; - shader_cache[file] = shader - shader.file = sh_file - return shader -} - -var render_queue = []; -var hud_queue = []; - -var current_queue = render_queue; - -var std_sampler = { - min_filter: "nearest", - mag_filter: "nearest", - mipmap: "linear", - u: "repeat", - v: "repeat", - w: "repeat", - mip_bias: 0, - max_anisotropy: 0, - compare_op: "none", - min_lod: 0, - max_lod: 0, - anisotropy: false, - compare: false -}; - -function upload_model(model) { - var bufs = []; - for (var i in model) { - if (typeof model[i] != 'object') continue; - bufs.push(model[i]); - } - context.upload(this, bufs); -} - -function bind_model(pass, pipeline, model) { - var buffers = pipeline.vertex_buffer_descriptions; - var bufs = []; - if (buffers) - for (var b of buffers) { - if (b.name in model) bufs.push(model[b.name]) - else throw Error (`could not find buffer ${b.name} on model`); - } - pass.bind_buffers(0,bufs); - pass.bind_index_buffer(model.indices); -} - -function bind_mat(pass, pipeline, mat) { - var imgs = []; - var refl = pipeline.fragment.reflection; - if (refl.separate_images) { - for (var i of refl.separate_images) { - if (i.name in mat) { - var tex = mat[i.name]; - imgs.push({texture:tex.texture, sampler:tex.sampler}); - } else - throw Error (`could not find all necessary images: ${i.name}`) - } - pass.bind_samplers(false, 0,imgs); - } -} - -function group_sprites_by_texture(sprites, mesh) { - if (sprites.length == 0) return; - for (var i = 0; i < sprites.length; i++) { - sprites[i].mesh = mesh; - sprites[i].first_index = i*6; - sprites[i].num_indices = 6; - } - return; - // The code below is an alternate approach to grouping by image. Currently not in use. - /* - var groups = []; - var group = {image:sprites[0].image, first_index:0}; - var count = 1; - for (var i = 1; i < sprites.length; i++) { - if (sprites[i].image == group.image) { - count++; - continue; - } - group.num_indices = count*6; - var newgroup = {image:sprites[i].image, first_index:group.first_index+group.num_indices}; - group = newgroup; - groups.push(group); - count=1; - } - group.num_indices = count*6; - return groups; - */ -} - -var main_color = { - type:"2d", - format: "rgba8", - layers: 1, - mip_levels: 1, - samples: 0, - sampler:true, - color_target:true -}; - -var main_depth = { - type: "2d", - format: "d32 float s8", - layers:1, - mip_levels:1, - samples:0, - sampler:true, - depth_target:true -}; - -function render_camera(cmds, camera) { - var pass; - delete camera.target // TODO: HORRIBLE - if (!camera.target) { - main_color.width = main_depth.width = camera.size.x; - main_color.height = main_depth.height = camera.size.y; - camera.target = { - color_targets: [{ - texture: context.texture(main_color), - mip_level:0, - layer: 0, - load:"clear", - store:"store", - clear: cornflower - }], - depth_stencil: { - texture: context.texture(main_depth), - clear:1, - load:"dont_care", - store:"dont_care", - stencil_load:"dont_care", - stencil_store:"dont_care", - stencil_clear:0 - } - }; - } - - var buffers = []; - buffers = buffers.concat(graphics.queue_sprite_mesh(render_queue)); - var unique_meshes = [...new Set(render_queue.map(x => x.mesh))]; - for (var q of unique_meshes) - buffers = buffers.concat([q.pos, q.color, q.uv, q.indices]); - - buffers = buffers.concat(graphics.queue_sprite_mesh(hud_queue)); - for (var q of hud_queue) - if (q.type == 'geometry') buffers = buffers.concat([q.mesh.pos, q.mesh.color, q.mesh.uv, q.mesh.indices]); - - full_upload(buffers) - - var pass = cmds.render_pass(camera.target); - - var pipeline = sprite_pipeline; - bind_pipeline(pass,pipeline); - - var camslot = get_pipeline_ubo_slot(pipeline, 'TransformBuffer'); - if (camslot != null) - cmds.camera(camera, camslot); - - modelslot = get_pipeline_ubo_slot(pipeline, "model"); - if (modelslot != null) { - var ubo = ubo_obj_to_array(pipeline, 'model', sprite_model_ubo); - cmds.push_vertex_uniform_data(modelslot, ubo); - } - - var mesh; - var img; - var modelslot; - - cmds.push_debug_group("draw") - for (var group of render_queue) { - if (mesh != group.mesh) { - mesh = group.mesh; - bind_model(pass,pipeline,mesh); - } - - if (group.image && img != group.image) { - img = group.image; - img.sampler = std_sampler; - bind_mat(pass,pipeline,{diffuse:img}); - } - - pass.draw_indexed(group.num_indices, 1, group.first_index, 0, 0); - } - cmds.pop_debug_group() - - cmds.push_debug_group("hud") - var camslot = get_pipeline_ubo_slot(pipeline, 'TransformBuffer'); - if (camslot != null) - cmds.hud(camera.size, camslot); - - for (var group of hud_queue) { - if (mesh != group.mesh) { - mesh = group.mesh; - bind_model(pass,pipeline,mesh); - } - - if (group.image && img != group.image) { - img = group.image; - img.sampler = std_sampler; - bind_mat(pass,pipeline,{diffuse:img}); - } - - pass.draw_indexed(group.num_indices, 1, group.first_index, 0, 0); - } - cmds.pop_debug_group(); - - pass?.end(); - - render_queue = []; - hud_queue = []; -} - -var swaps = []; -render.present = function() { - os.clean_transforms(); - var cmds = context.acquire_cmd_buffer(); - render_camera(cmds, prosperon.camera); - var swapchain_tex = cmds.acquire_swapchain(); - if (!swapchain_tex) - cmds.cancel(); - else { - var torect = prosperon.camera.draw_rect(prosperon.window.size); - torect.texture = swapchain_tex; - if (swapchain_tex) { - cmds.blit({ - src: prosperon.camera.target.color_targets[0].texture, - dst: torect, - filter:"nearest", - load: "clear" - }); - - if (imgui) { // draws any imgui commands present - cmds.push_debug_group("imgui") - imgui.prepend(cmds); - var pass = cmds.render_pass({ - color_targets:[{texture:swapchain_tex}]}); - imgui.endframe(cmds,pass); - pass.end(); - cmds.pop_debug_group() - } - } - cmds.submit() - } -} - -var stencil_write = { - compare: "always", - fail_op: "replace", - depth_fail_op: "replace", - pass_op: "replace" -}; - -function stencil_writer(ref) { - var pipe = Object.create(base_pipeline); - Object.assign(pipe, { - stencil: { - enabled: true, - front: stencil_write, - back: stencil_write, - write:true, - read:true, - ref:ref - }, - write_mask: colormask.none - }); - return pipe; -}.hashify(); - -// objects by default draw where the stencil buffer is 0 -function fillmask(ref) { - var pipe = stencil_writer(ref); - render.use_shader('screenfill.cg', pipe); - render.draw(shape.quad); -} - -var stencil_invert = { - compare: "always", - fail_op: "invert", - depth_fail_op: "invert", - pass_op: "invert" -}; - -function mask(image, pos, scale, rotation = 0, ref = 1) { - if (typeof image == 'string') - image = graphics.texture(image); - - var tex = image.texture; - if (scale) scale = scale.div([tex.width,tex.height]); - else scale = [1,1,1] - - var pipe = stencil_writer(ref); - render.use_shader('sprite.cg', pipe); - var t = new transform; - t.trs(pos, null, scale); - set_model(t); - render.use_mat({ - diffuse:image.texture, - rect: image.rect, - shade: color.white - }); - render.draw(shape.quad); -} - -render.viewport = function(rect) { - context.viewport(rect); -} - -render.scissor = function(rect) { - render.viewport(rect) -} - -var std_sampler - -if (tracy) tracy.gpu_init() - -render.queue = function(cmd) { - if (Array.isArray(cmd)) - for (var i of cmd) current_queue.push(i) - else - current_queue.push(cmd) -} - -render.setup_draw = function() { - current_queue = render_queue; - prosperon.draw(); -} - -render.setup_hud = function() { - current_queue = hud_queue; - prosperon.hud(); -} - -render.initialize = function(config) -{ - var default_conf = { - title:`Prosperon [${prosperon.version}-${prosperon.revision}]`, - width: 1280, - height: 720, - icon: graphics.make_texture(io.slurpbytes('icons/moon.gif')), - high_dpi:0, - alpha:1, - fullscreen:0, - sample_count:1, - enable_clipboard:true, - enable_dragndrop: true, - max_dropped_files: 1, - swap_interval: 1, - name: "Prosperon", - version:prosperon.version + "-" + prosperon.revision, - identifier: "world.pockle.prosperon", - creator: "Pockle World LLC", - copyright: "Copyright Pockle World 2025", - type: "game", - url: "https://prosperon.dev" - } - - config.__proto__ = default_conf - - prosperon.camera = use('ext/camera').make() - prosperon.camera.size = [config.width,config.height] - - prosperon.window = prosperon.engine_start(config) - - context = prosperon.window.make_gpu(false,driver) - context.window = prosperon.window - context.claim_window(prosperon.window) - context.set_swapchain('sdr', 'vsync') - - if (imgui) imgui.init(context, prosperon.window) - - shader_type = context.shader_format()[0]; - - std_sampler = context.make_sampler({ - min_filter: "nearest", - mag_filter: "nearest", - mipmap_mode: "nearest", - address_mode_u: "repeat", - address_mode_v: "repeat", - address_mode_w: "repeat" - }); -} - -return render - diff --git a/source/jsffi.c b/source/jsffi.c index 67517b13..55c591f1 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -390,11 +390,6 @@ char *js2strdup(JSContext *js, JSValue v) { #include "qjs_macros.h" -void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c) -{ - -} - QJSCLASS(font,) QJSCLASS(datastream,) @@ -1613,6 +1608,7 @@ JSC_CCALL(os_value_id, #include "qjs_socket.h" #include "qjs_nota.h" #include "qjs_layout.h" +#include "qjs_sdl_gpu.h" JSValue js_imgui_use(JSContext *js); #define MISTLINE(NAME) (ModuleEntry){#NAME, js_##NAME##_use} @@ -1650,6 +1646,7 @@ void ffi_load(JSContext *js) arrput(rt->module_registry, MISTLINE(text)); arrput(rt->module_registry, MISTLINE(wota)); arrput(rt->module_registry, MISTLINE(nota)); + arrput(rt->module_registry, MISTLINE(sdl_gpu)); // power user arrput(rt->module_registry, MISTLINE(js)); diff --git a/source/qjs_sdl_gpu.c b/source/qjs_sdl_gpu.c index d8eeca23..7ee86a39 100644 --- a/source/qjs_sdl_gpu.c +++ b/source/qjs_sdl_gpu.c @@ -1,17 +1,105 @@ #include "qjs_sdl_gpu.h" #include "jsffi.h" #include "qjs_macros.h" +#include "qjs_sdl_surface.h" +#include "qjs_blob.h" +#include "quickjs.h" #include #include "render.h" #include "cell.h" -#include "sprite.h" + +// Macro for GPU wrapper classes that need cleanup +#define QJSCLASSGPUWRAPPER(WRAPPERTYPE, SDLTYPE, MEMBER) \ +JSClassID js_##SDLTYPE##_id; \ +static void js_##SDLTYPE##_finalizer(JSRuntime *rt, JSValue val) { \ + WRAPPERTYPE *wrapper = JS_GetOpaque(val, js_##SDLTYPE##_id); \ + SDLTYPE##_free(rt, wrapper); \ +} \ +static JSClassDef js_##SDLTYPE##_class = { \ + .class_name = #SDLTYPE, \ + .finalizer = js_##SDLTYPE##_finalizer, \ +}; \ +SDLTYPE *js2##SDLTYPE(JSContext *js, JSValue val) { \ + if (JS_GetClassID(val) != js_##SDLTYPE##_id) return NULL; \ + WRAPPERTYPE *wrapper = JS_GetOpaque(val, js_##SDLTYPE##_id); \ + return wrapper ? wrapper->MEMBER : NULL; \ +} \ +JSValue SDLTYPE##2js(JSContext *js, WRAPPERTYPE *wrapper) { \ + JSValue j = JS_NewObjectClass(js, js_##SDLTYPE##_id); \ + JS_SetOpaque(j, wrapper); \ + return j; \ +} #include "stb_dxt.h" -// Global GPU device and window -static SDL_GPUDevice *global_gpu; -static SDL_Window *global_window; +// Include additional headers +#include "font.h" + +// Type array constants +#define JS_TYPED_ARRAY_FLOAT32 0 +#define JS_TYPED_ARRAY_UINT16 1 +#define JS_TYPED_ARRAY_UINT32 2 + +// External function declarations +extern SDL_Surface *js2SDL_Surface(JSContext *js, JSValue v); +extern double js2number(JSContext *js, JSValue v); +extern int JS_GETBOOL(JSContext *js, JSValue obj, const char *prop); +extern double js_getnum_str(JSContext *js, JSValue obj, const char *str); + +// Simple string conversion helper +const char *js2cstring(JSContext *js, JSValue v) +{ + return JS_ToCString(js, v); +} + +// Global GPU device and window removed - now passed as parameters + +// Wrapper structures that hold GPU device reference for proper cleanup +typedef struct { + SDL_GPUDevice *device; + SDL_GPUBuffer *buffer; +} gpu_buffer_wrapper; + +typedef struct { + SDL_GPUDevice *device; + SDL_GPUComputePipeline *pipeline; +} gpu_compute_pipeline_wrapper; + +typedef struct { + SDL_GPUDevice *device; + SDL_GPUGraphicsPipeline *pipeline; +} gpu_graphics_pipeline_wrapper; + +typedef struct { + SDL_GPUDevice *device; + SDL_GPUSampler *sampler; +} gpu_sampler_wrapper; + +typedef struct { + SDL_GPUDevice *device; + SDL_GPUShader *shader; +} gpu_shader_wrapper; + +typedef struct { + SDL_GPUDevice *device; + SDL_GPUTexture *texture; +} gpu_texture_wrapper; + +typedef struct { + SDL_GPUDevice *device; + SDL_GPUTransferBuffer *buffer; +} gpu_transfer_buffer_wrapper; + +typedef struct { + SDL_GPUDevice *device; + SDL_GPUFence *fence; +} gpu_fence_wrapper; + +typedef struct { + SDL_GPUDevice *device; + SDL_GPUCommandBuffer *command_buffer; +} gpu_command_buffer_wrapper; // GPU Free functions void SDL_GPUDevice_free(JSRuntime *rt, SDL_GPUDevice *d) @@ -19,37 +107,271 @@ void SDL_GPUDevice_free(JSRuntime *rt, SDL_GPUDevice *d) SDL_DestroyGPUDevice(d); } -void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c) +void SDL_GPUBuffer_free(JSRuntime *rt, gpu_buffer_wrapper *w) { + if (w && w->device && w->buffer) { + SDL_ReleaseGPUBuffer(w->device, w->buffer); + } + free(w); } +void SDL_GPUTexture_free(JSRuntime *rt, gpu_texture_wrapper *w) +{ + if (w && w->device && w->texture) { + SDL_ReleaseGPUTexture(w->device, w->texture); + } + free(w); +} + +void SDL_GPUTransferBuffer_free(JSRuntime *rt, gpu_transfer_buffer_wrapper *w) +{ + if (w && w->device && w->buffer) { + SDL_ReleaseGPUTransferBuffer(w->device, w->buffer); + } + free(w); +} + +void SDL_GPUSampler_free(JSRuntime *rt, gpu_sampler_wrapper *w) +{ + if (w && w->device && w->sampler) { + SDL_ReleaseGPUSampler(w->device, w->sampler); + } + free(w); +} + +void SDL_GPUShader_free(JSRuntime *rt, gpu_shader_wrapper *w) +{ + if (w && w->device && w->shader) { + SDL_ReleaseGPUShader(w->device, w->shader); + } + free(w); +} + +void SDL_GPUGraphicsPipeline_free(JSRuntime *rt, gpu_graphics_pipeline_wrapper *w) +{ + if (w && w->device && w->pipeline) { + SDL_ReleaseGPUGraphicsPipeline(w->device, w->pipeline); + } + free(w); +} + +void SDL_GPUComputePipeline_free(JSRuntime *rt, gpu_compute_pipeline_wrapper *w) +{ + if (w && w->device && w->pipeline) { + SDL_ReleaseGPUComputePipeline(w->device, w->pipeline); + } + free(w); +} + +void SDL_GPUFence_free(JSRuntime *rt, gpu_fence_wrapper *w) +{ + if (w && w->device && w->fence) { + SDL_ReleaseGPUFence(w->device, w->fence); + } + free(w); +} + +void SDL_GPUCommandBuffer_free(JSRuntime *rt, gpu_command_buffer_wrapper *w) +{ + // Command buffers are typically managed by SDL, no explicit release needed + free(w); +} + +// Wrapper free functions that use the device reference for proper cleanup +void gpu_buffer_wrapper_free(JSRuntime *rt, gpu_buffer_wrapper *w) +{ + if (w && w->device && w->buffer) { + SDL_ReleaseGPUBuffer(w->device, w->buffer); + } + free(w); +} + +void gpu_compute_pipeline_wrapper_free(JSRuntime *rt, gpu_compute_pipeline_wrapper *w) +{ + if (w && w->device && w->pipeline) { + SDL_ReleaseGPUComputePipeline(w->device, w->pipeline); + } + free(w); +} + +void gpu_graphics_pipeline_wrapper_free(JSRuntime *rt, gpu_graphics_pipeline_wrapper *w) +{ + if (w && w->device && w->pipeline) { + SDL_ReleaseGPUGraphicsPipeline(w->device, w->pipeline); + } + free(w); +} + +void gpu_sampler_wrapper_free(JSRuntime *rt, gpu_sampler_wrapper *w) +{ + if (w && w->device && w->sampler) { + SDL_ReleaseGPUSampler(w->device, w->sampler); + } + free(w); +} + +void gpu_shader_wrapper_free(JSRuntime *rt, gpu_shader_wrapper *w) +{ + if (w && w->device && w->shader) { + SDL_ReleaseGPUShader(w->device, w->shader); + } + free(w); +} + +void gpu_texture_wrapper_free(JSRuntime *rt, gpu_texture_wrapper *w) +{ + if (w && w->device && w->texture) { + SDL_ReleaseGPUTexture(w->device, w->texture); + } + free(w); +} + +void gpu_transfer_buffer_wrapper_free(JSRuntime *rt, gpu_transfer_buffer_wrapper *w) +{ + if (w && w->device && w->buffer) { + SDL_ReleaseGPUTransferBuffer(w->device, w->buffer); + } + free(w); +} + +void gpu_fence_wrapper_free(JSRuntime *rt, gpu_fence_wrapper *w) +{ + if (w && w->device && w->fence) { + SDL_ReleaseGPUFence(w->device, w->fence); + } + free(w); +} + +void gpu_command_buffer_wrapper_free(JSRuntime *rt, gpu_command_buffer_wrapper *w) +{ + free(w); +} + +// Moved to jsffi.c - extern declaration + void SDL_GPUComputePass_free(JSRuntime *rt, SDL_GPUComputePass *c) { } void SDL_GPUCopyPass_free(JSRuntime *rt, SDL_GPUCopyPass *c) { } void SDL_GPURenderPass_free(JSRuntime *rt, SDL_GPURenderPass *c) { } // GPU Class definitions -#define GPURELEASECLASS(NAME) \ -void SDL_GPU##NAME##_free(JSRuntime *rt, SDL_GPU##NAME *c) { \ - SDL_ReleaseGPU##NAME(global_gpu, c); } \ -QJSCLASS(SDL_GPU##NAME,) \ - QJSCLASS(SDL_GPUDevice,) - -GPURELEASECLASS(Buffer) -GPURELEASECLASS(ComputePipeline) -GPURELEASECLASS(GraphicsPipeline) -GPURELEASECLASS(Sampler) -GPURELEASECLASS(Shader) -GPURELEASECLASS(Texture) -GPURELEASECLASS(TransferBuffer) -GPURELEASECLASS(Fence) - -QJSCLASS(SDL_GPUCommandBuffer,) +QJSCLASSGPUWRAPPER(gpu_buffer_wrapper, SDL_GPUBuffer, buffer) +QJSCLASSGPUWRAPPER(gpu_compute_pipeline_wrapper, SDL_GPUComputePipeline, pipeline) +QJSCLASSGPUWRAPPER(gpu_graphics_pipeline_wrapper, SDL_GPUGraphicsPipeline, pipeline) +QJSCLASSGPUWRAPPER(gpu_sampler_wrapper, SDL_GPUSampler, sampler) +QJSCLASSGPUWRAPPER(gpu_shader_wrapper, SDL_GPUShader, shader) +QJSCLASSGPUWRAPPER(gpu_texture_wrapper, SDL_GPUTexture, texture) +QJSCLASSGPUWRAPPER(gpu_transfer_buffer_wrapper, SDL_GPUTransferBuffer, buffer) +QJSCLASS(SDL_GPUFence,) +QJSCLASSGPUWRAPPER(gpu_command_buffer_wrapper, SDL_GPUCommandBuffer, command_buffer) QJSCLASS(SDL_GPUComputePass,) QJSCLASS(SDL_GPUCopyPass,) QJSCLASS(SDL_GPURenderPass,) +// Helper functions to create wrapper objects and extract SDL objects +JSValue gpu_buffer_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUBuffer *buffer) +{ + gpu_buffer_wrapper *wrapper = malloc(sizeof(gpu_buffer_wrapper)); + wrapper->device = device; + wrapper->buffer = buffer; + return SDL_GPUBuffer2js(js, wrapper); +} + + +JSValue gpu_texture_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUTexture *texture) +{ + gpu_texture_wrapper *wrapper = malloc(sizeof(gpu_texture_wrapper)); + wrapper->device = device; + wrapper->texture = texture; + return SDL_GPUTexture2js(js, wrapper); +} + + +// Helper functions for sampler +JSValue gpu_sampler_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUSampler *sampler) +{ + gpu_sampler_wrapper *wrapper = malloc(sizeof(gpu_sampler_wrapper)); + wrapper->device = device; + wrapper->sampler = sampler; + return SDL_GPUSampler2js(js, wrapper); +} + + +// Helper functions for shader +JSValue gpu_shader_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUShader *shader) +{ + gpu_shader_wrapper *wrapper = malloc(sizeof(gpu_shader_wrapper)); + wrapper->device = device; + wrapper->shader = shader; + return SDL_GPUShader2js(js, wrapper); +} + + +// Helper functions for graphics pipeline +JSValue gpu_graphics_pipeline_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUGraphicsPipeline *pipeline) +{ + gpu_graphics_pipeline_wrapper *wrapper = malloc(sizeof(gpu_graphics_pipeline_wrapper)); + wrapper->device = device; + wrapper->pipeline = pipeline; + return SDL_GPUGraphicsPipeline2js(js, wrapper); +} + + +// Helper functions for compute pipeline +JSValue gpu_compute_pipeline_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUComputePipeline *pipeline) +{ + gpu_compute_pipeline_wrapper *wrapper = malloc(sizeof(gpu_compute_pipeline_wrapper)); + wrapper->device = device; + wrapper->pipeline = pipeline; + return SDL_GPUComputePipeline2js(js, wrapper); +} + + +// Helper functions for transfer buffer +JSValue gpu_transfer_buffer_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUTransferBuffer *buffer) +{ + gpu_transfer_buffer_wrapper *wrapper = malloc(sizeof(gpu_transfer_buffer_wrapper)); + wrapper->device = device; + wrapper->buffer = buffer; + return SDL_GPUTransferBuffer2js(js, wrapper); +} + + +// Helper functions for fence +JSValue gpu_fence_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUFence *fence) +{ + gpu_fence_wrapper *wrapper = malloc(sizeof(gpu_fence_wrapper)); + wrapper->device = device; + wrapper->fence = fence; + return SDL_GPUFence2js(js, wrapper); +} + + +// Helper functions for command buffer +JSValue gpu_command_buffer_wrapper2js_from_components(JSContext *js, SDL_GPUDevice *device, SDL_GPUCommandBuffer *command_buffer) +{ + gpu_command_buffer_wrapper *wrapper = malloc(sizeof(gpu_command_buffer_wrapper)); + wrapper->device = device; + wrapper->command_buffer = command_buffer; + return SDL_GPUCommandBuffer2js(js, wrapper); +} + +// Override the default js2SDL_GPUCommandBuffer to work with our wrapper + +// Compatibility functions for the old naming scheme +#define SDL_GPUBuffer2js(js, buffer) gpu_buffer_wrapper2js_from_components(js, NULL, buffer) +#define SDL_GPUTexture2js(js, texture) gpu_texture_wrapper2js_from_components(js, NULL, texture) +#define SDL_GPUSampler2js(js, sampler) gpu_sampler_wrapper2js_from_components(js, NULL, sampler) +#define SDL_GPUShader2js(js, shader) gpu_shader_wrapper2js_from_components(js, NULL, shader) +#define SDL_GPUGraphicsPipeline2js(js, pipeline) gpu_graphics_pipeline_wrapper2js_from_components(js, NULL, pipeline) +#define SDL_GPUComputePipeline2js(js, pipeline) gpu_compute_pipeline_wrapper2js_from_components(js, NULL, pipeline) +#define SDL_GPUTransferBuffer2js(js, buffer) gpu_transfer_buffer_wrapper2js_from_components(js, NULL, buffer) +#define SDL_GPUFence2js(js, fence) gpu_fence_wrapper2js_from_components(js, NULL, fence) +#define SDL_GPUCommandBuffer2js(js, command_buffer) gpu_command_buffer_wrapper2js_from_components(js, NULL, command_buffer) + + +// GPU type conversion functions SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v) { SDL_GPUGraphicsPipelineTargetInfo info = {0}; @@ -65,7 +387,7 @@ SDL_GPUSampleCount js2SDL_GPUSampleCount(JSContext *js, JSValue v) case 4: return SDL_GPU_SAMPLECOUNT_4; case 8: return SDL_GPU_SAMPLECOUNT_8; } - return -1; + return SDL_GPU_SAMPLECOUNT_1; } #define JS2ENUM(NAME, RETS, VALS) \ @@ -85,6 +407,7 @@ int js2##NAME(JSContext *js, JSValue v) { \ return 0; \ } +// Enum conversion arrays static int rets_SDL_GPUSwapchainComposition[] = { SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR, @@ -750,10 +1073,15 @@ SDL_GPUStencilOpState js2SDL_GPUStencilOpState(JSContext *js, JSValue v) // Unpacks a typed array javascript object. If it has a gpu property, returns it, too. Otherwise, if requested, makes one. void *gpu_buffer_unpack(JSContext *js, SDL_GPUDevice *device, JSValue buffer, size_t *size, void **send_data, SDL_GPUBuffer **send_gpu) { - size_t o, len, bytes, msize; - JSValue buf = JS_GetTypedArrayBuffer(js, buffer, &o, &len, &bytes); - void *data = js_get_blob_data(js, &msize, buf); - JS_FreeValue(js,buf); + size_t msize; + // Use blob system for array buffer data + void *data = js_get_blob_data(js, &msize, buffer); + if (!data) { + // If not a blob, try to get data from buffer property + JSValue buf_val = JS_GetPropertyStr(js, buffer, "buffer"); + data = js_get_blob_data(js, &msize, buf_val); + JS_FreeValue(js, buf_val); + } if (size) *size = msize; if (send_gpu) { JSValue gpu = JS_GetPropertyStr(js,buffer,"gpu"); @@ -774,30 +1102,31 @@ void *gpu_buffer_unpack(JSContext *js, SDL_GPUDevice *device, JSValue buffer, si return data; } - // GPU buffer management functions -void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr) -{ - TracyCFree(ptr); -} +// Moved to jsffi.c - extern declaration +extern void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr); // GPU API JSC_CCALL(gpu_claim_window, SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); SDL_Window *win = js2SDL_Window(js, argv[0]); - SDL_ClaimWindowForGPUDevice(gpu,win); + if (!SDL_ClaimWindowForGPUDevice(gpu,win)) + printf("couldn't claim: %s\n", SDL_GetError()); ) JSC_CCALL(gpu_set_swapchain, - if (!SDL_SetGPUSwapchainParameters(global_gpu,global_window, js2SDL_GPUSwapchainComposition(js,argv[0]), js2SDL_GPUPresentMode(js,argv[1]))) + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); + SDL_Window *window = js2SDL_Window(js, argv[0]); + if (!SDL_SetGPUSwapchainParameters(gpu, window, js2SDL_GPUSwapchainComposition(js,argv[1]), js2SDL_GPUPresentMode(js,argv[2]))) return JS_ThrowReferenceError(js, "Could not set: %s\n", SDL_GetError()); ) JSC_CCALL(cmd_acquire_swapchain, SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + SDL_Window *window = js2SDL_Window(js, argv[0]); Uint32 w,h; SDL_GPUTexture *texture; - SDL_AcquireGPUSwapchainTexture(cmds,global_window, &texture, &w, &h); + SDL_AcquireGPUSwapchainTexture(cmds, window, &texture, &w, &h); if (!texture) return JS_NULL; JSValue swap = JS_NULL; @@ -822,12 +1151,13 @@ JSC_CCALL(cmd_acquire_swapchain, JSC_CCALL(cmd_swapchain_pass, SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + SDL_Window *window = js2SDL_Window(js, argv[0]); SDL_GPUColorTargetInfo info = {0}; Uint32 w, h; - SDL_AcquireGPUSwapchainTexture(cmds, global_window, &info.texture, &w,&h); + SDL_AcquireGPUSwapchainTexture(cmds, window, &info.texture, &w,&h); info.load_op = SDL_GPU_LOADOP_CLEAR; info.store_op = SDL_GPU_STOREOP_STORE; - colorf c = js2color(js,argv[0]); + colorf c = js2color(js,argv[1]); info.clear_color = (SDL_FColor){c.r,c.g,c.b,c.a}; SDL_GPURenderPass *pass = SDL_BeginGPURenderPass( cmds, @@ -1057,12 +1387,15 @@ if (compress) { ) -static JSValue js_gpu_make_pipeline(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - if (argc < 1) - return JS_ThrowTypeError(js, "gpu_pipeline requires a pipeline object"); +// Standalone graphics pipeline constructor: new sdl_gpu.graphics_pipeline(device, config) +static JSValue js_gpu_graphics_pipeline_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 2) + return JS_ThrowTypeError(js, "graphics pipeline constructor requires device and config parameters"); - JSValue pipe = argv[0]; + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]); + if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device"); + + JSValue pipe = argv[1]; if (!JS_IsObject(pipe)) return JS_ThrowTypeError(js, "gpu_pipeline argument must be an object"); @@ -1177,14 +1510,9 @@ static JSValue js_gpu_make_pipeline(JSContext *js, JSValueConst self, int argc, }; // Vertex Shader - JSValue vertex_val = JS_GetPropertyStr(js, pipe, "vertex"); - JSValue fragment_val = JS_GetPropertyStr(js,pipe,"fragment"); - JS_GETPROP(js,info.vertex_shader, vertex_val, gpu, SDL_GPUShader) - JS_GETPROP(js, info.fragment_shader, fragment_val, gpu, SDL_GPUShader) + JS_GETPROP(js,info.vertex_shader, pipe, vertex,SDL_GPUShader) + JS_GETPROP(js, info.fragment_shader, pipe, fragment, SDL_GPUShader) - JS_FreeValue(js, vertex_val); - JS_FreeValue(js, fragment_val); - // Primitive Type JS_GETPROP(js, info.primitive_type, pipe, primitive, SDL_GPUPrimitiveType) @@ -1253,14 +1581,18 @@ static JSValue js_gpu_make_pipeline(JSContext *js, JSValueConst self, int argc, SDL_GPUGraphicsPipeline *pipeline = SDL_CreateGPUGraphicsPipeline(gpu, &info); if (!pipeline) return JS_ThrowInternalError(js, "Failed to create GPU pipeline"); - return SDL_GPUGraphicsPipeline2js(js, pipeline); + return gpu_graphics_pipeline_wrapper2js_from_components(js, gpu, pipeline); } -// Now the function to create the sampler -static JSValue js_gpu_make_sampler(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); +// Standalone sampler constructor: new sdl_gpu.sampler(device, config) +static JSValue js_gpu_sampler_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 2) return JS_ThrowTypeError(js, "sampler constructor requires device and config parameters"); + + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]); + if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device"); + SDL_GPUSamplerCreateInfo info = {0}; - JSValue sampler = argv[0]; + JSValue sampler = argv[1]; JS_GETPROP(js,info.min_filter,sampler,min_filter,SDL_GPUFilter) JS_GETPROP(js,info.mag_filter,sampler,mag_filter,SDL_GPUFilter) @@ -1278,9 +1610,9 @@ static JSValue js_gpu_make_sampler(JSContext *js, JSValueConst self, int argc, J // Create the sampler SDL_GPUSampler *sdl_sampler = SDL_CreateGPUSampler(gpu, &info); - if (!sdl_sampler) return JS_ThrowInternalError(js, "Failed to create GPU sampler"); + if (!sdl_sampler) return JS_ThrowInternalError(js, "Failed to create GPU sampler: %s", SDL_GetError()); - return SDL_GPUSampler2js(js, sdl_sampler); // You need to implement SDL_GPUSampler2js similar to pipeline + return gpu_sampler_wrapper2js_from_components(js, gpu, sdl_sampler); } @@ -1317,189 +1649,97 @@ JSC_CCALL(gpu_texture, return jstex; ) -typedef struct { - text_vert vert[4]; -} quad; - -// Comparator inline for potential compiler inlining -static inline int sprite_compare(const sprite *a, const sprite *b) { - if (a->layer != b->layer) return a->layer - b->layer; - - if (a->pos.Y != b->pos.Y) - return (b->pos.Y - a->pos.Y); - - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - - return 0; -} - -// Merge two sorted sub-ranges [left..mid] and [mid+1..right] -static void merge_sprites(sprite *arr, sprite *temp, size_t left, size_t mid, size_t right) { - size_t i = left, j = mid + 1, k = left; - - while (i <= mid && j <= right) - if (sprite_compare(&arr[i], &arr[j]) <= 0) temp[k++] = arr[i++]; - else temp[k++] = arr[j++]; - - while (i <= mid) temp[k++] = arr[i++]; - while (j <= right) temp[k++] = arr[j++]; - - for (size_t p = left; p <= right; p++) - arr[p] = temp[p]; -} - -// Recursive mergesort -static void merge_sort_recursive(sprite *arr, sprite *temp, size_t left, size_t right) { - if (left >= right) return; - size_t mid = (left + right) / 2; - merge_sort_recursive(arr, temp, left, mid); - merge_sort_recursive(arr, temp, mid + 1, right); - merge_sprites(arr, temp, left, mid, right); -} - -// Public function to sort the array of sprites with mergesort -void better_sort_sprites(sprite *arr, size_t length) { - if (length < 2) return; - sprite *temp = malloc(length * sizeof(sprite)); - if (!temp) return; // fallback or handle error - merge_sort_recursive(arr, temp, 0, length - 1); - free(temp); -} - -int sort_sprite_backtofront(const sprite *a, const sprite *b) -{ - if (a->layer != b->layer) return a->layer - b->layer; - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - return 0; -} - -int sort_sprite_fronttoback(const sprite *a, const sprite *b) -{ - if (a->layer != b->layer) return b->layer - a->layer; - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - return 0; -} - -int sort_sprite_texture(const sprite *a, const sprite *b) -{ - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - return 0; -} - -int sort_sprite(const sprite *a, const sprite *b) -{ - if (a->layer != b->layer) return a->layer - b->layer; +JSC_CCALL(gpu_buffer, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUBufferCreateInfo info = {0}; + JSValue config = argv[0]; - if (a->pos.Y != b->pos.Y) - return (b->pos.Y - a->pos.Y); - - if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image)) - return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1; - return 0; -} - -JSC_CCALL(gpu_sort_sprite, - JSValue a = argv[0]; - JSValue b = argv[1]; + // Get size - this is required + JS_GETPROP(js, info.size, config, size, number) + if (info.size == 0) return JS_ThrowTypeError(js, "Buffer size must be greater than 0"); - int alayer, blayer; - JS_GETATOM(js,alayer,a,layer,number) - JS_GETATOM(js,blayer,b,layer,number) - if (alayer != blayer) return number2js(js,alayer - blayer); - - rect ar, br; - JS_GETATOM(js,ar,a,rect,rect) - JS_GETATOM(js,br,b,rect,rect) - if (ar.y != br.y) return number2js(js,br.y-ar.y); - - JSValue aimg,bimg; - aimg = JS_GetPropertyStr(js,a,"image"); - bimg = JS_GetPropertyStr(js,b,"image"); - JS_FreeValue(js,aimg); - JS_FreeValue(js,bimg); - if (!JS_SameValue(js,aimg,bimg)) return number2js(js,JS_VALUE_GET_PTR(aimg) < JS_VALUE_GET_PTR(bimg) ? -1 : 1); - return number2js(js,0); + // Parse usage flags + if (JS_GETBOOL(js, config, "vertex")) + info.usage |= SDL_GPU_BUFFERUSAGE_VERTEX; + if (JS_GETBOOL(js, config, "index")) + info.usage |= SDL_GPU_BUFFERUSAGE_INDEX; + if (JS_GETBOOL(js, config, "indirect")) + info.usage |= SDL_GPU_BUFFERUSAGE_INDIRECT; + if (JS_GETBOOL(js, config, "graphics_storage_read")) + info.usage |= SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ; + if (JS_GETBOOL(js, config, "compute_storage_read")) + info.usage |= SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ; + if (JS_GETBOOL(js, config, "compute_storage_write")) + info.usage |= SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE; + + // If no usage flags were set, throw an error + if (info.usage == 0) return JS_ThrowTypeError(js, "Buffer must have at least one usage flag"); + + SDL_GPUBuffer *buffer = SDL_CreateGPUBuffer(gpu, &info); + if (!buffer) return JS_ThrowReferenceError(js, "Unable to create buffer: %s", SDL_GetError()); + + return gpu_buffer_wrapper2js_from_components(js, gpu, buffer); ) - - -JSC_CCALL(gpu_make_quad, - size_t quads = 1; - size_t verts = quads*4; - size_t count = quads*6; - - // Prepare arrays on CPU - HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts); - HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts); - HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts); - HMM_Vec4 usecolor = (HMM_Vec4){1,1,1,1}; - posdata[0] = (HMM_Vec2){0,1}; - posdata[1] = (HMM_Vec2){1,1}; - posdata[2] = (HMM_Vec2){0,0}; - posdata[3] = (HMM_Vec2){1,0}; +JSC_CCALL(gpu_transfer_buffer, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); + SDL_GPUTransferBufferCreateInfo info = {0}; + JSValue config = argv[0]; - uvdata[0] = (HMM_Vec2){0,0}; - uvdata[1] = (HMM_Vec2){1,0}; - uvdata[2] = (HMM_Vec2){0,1}; - uvdata[3] = (HMM_Vec2){1,1}; + // Get size - this is required + JS_GETPROP(js, info.size, config, size, number) + if (info.size == 0) return JS_ThrowTypeError(js, "Transfer buffer size must be greater than 0"); - colordata[0] = usecolor; - colordata[1] = usecolor; - colordata[2] = usecolor; - colordata[3] = usecolor; - - ret = JS_NewObject(js); - JSValue pos = make_gpu_buffer(js, posdata, sizeof(*posdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); - JSValue uv = make_gpu_buffer(js, uvdata, sizeof(*uvdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0); - JSValue color = make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, JS_TYPED_ARRAY_FLOAT32, 0, 1,0); - - JS_SetPropertyStr(js, ret, "pos", pos); - JS_SetPropertyStr(js, ret, "uv", uv); - JS_SetPropertyStr(js, ret, "color", color); - JSValue indices = make_quad_indices_buffer(js, quads); - JS_SetPropertyStr(js, ret, "indices", indices); - - JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts)); - JS_SetPropertyStr(js, ret, "count", number2js(js, count)); - JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count)); - - // Free temporary CPU arrays - free(posdata); - free(uvdata); - free(colordata); - - return ret; + // Get usage - this is required + JSValue usage_val = JS_GetPropertyStr(js, config, "usage"); + if (JS_IsString(usage_val)) { + const char *usage_str = JS_ToCString(js, usage_val); + if (strcmp(usage_str, "upload") == 0) { + info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + } else if (strcmp(usage_str, "download") == 0) { + info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD; + } else { + JS_FreeCString(js, usage_str); + JS_FreeValue(js, usage_val); + return JS_ThrowTypeError(js, "Invalid transfer buffer usage. Must be 'upload' or 'download'"); + } + JS_FreeCString(js, usage_str); + } else { + JS_FreeValue(js, usage_val); + return JS_ThrowTypeError(js, "Transfer buffer usage must be specified as 'upload' or 'download'"); + } + JS_FreeValue(js, usage_val); + + SDL_GPUTransferBuffer *buffer = SDL_CreateGPUTransferBuffer(gpu, &info); + if (!buffer) return JS_ThrowReferenceError(js, "Unable to create transfer buffer: %s", SDL_GetError()); + + return gpu_transfer_buffer_wrapper2js_from_components(js, gpu, buffer); ) - JSC_CCALL(gpu_driver, SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); ret = JS_NewString(js, SDL_GetGPUDeviceDriver(gpu)); ) -static JSValue js_gpu_make_shader(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { - if (argc < 1 || !JS_IsObject(argv[0])) - return JS_ThrowTypeError(js, "make_shader expects an object with code, stage, num_samplers, num_textures, num_storage_buffers, num_uniform_buffers"); +// Standalone shader constructor: new sdl_gpu.shader(device, config) +static JSValue js_gpu_shader_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 2 || !JS_IsObject(argv[1])) + return JS_ThrowTypeError(js, "shader constructor requires device and config parameters"); - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]); + if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device"); - JSValue obj = argv[0]; + JSValue obj = argv[1]; SDL_GPUShaderCreateInfo info = {0}; - // code JSValue code_val = JS_GetPropertyStr(js, obj, "code"); size_t code_size; void *code_data = js_get_blob_data(js, &code_size, code_val); JS_FreeValue(js, code_val); if (!code_data) - return JS_ThrowTypeError(js, "shader.code must be an ArrayBuffer"); + return JS_ThrowTypeError(js, "shader.code must be a blob"); - // stage JSValue stage_val = JS_GetPropertyStr(js, obj, "stage"); const char *stage_str = JS_ToCString(js, stage_val); if (stage_str) { @@ -1530,15 +1770,14 @@ static JSValue js_gpu_make_shader(JSContext *js, JSValueConst self, int argc, JS if (!shader) return JS_ThrowReferenceError(js, "Unable to create shader: %s", SDL_GetError()); - return SDL_GPUShader2js(js, shader); + return gpu_shader_wrapper2js_from_components(js, gpu, shader); } JSC_CCALL(gpu_acquire_cmd_buffer, SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); SDL_GPUCommandBuffer *cb = SDL_AcquireGPUCommandBuffer(gpu); if (!cb) return JS_ThrowReferenceError(js,"Unable to acquire command buffer: %s", SDL_GetError()); - // Wrap cb in a JS object - return SDL_GPUCommandBuffer2js(js, cb); + return gpu_command_buffer_wrapper2js_from_components(js, gpu, cb); ) /* takes argv @@ -1693,10 +1932,15 @@ JSC_CCALL(gpu_shader_format, return arr; ) -JSC_CCALL(gpu_compute_pipeline, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); +// Standalone compute pipeline constructor: new sdl_gpu.compute_pipeline(device, config) +static JSValue js_gpu_compute_pipeline_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 2) return JS_ThrowTypeError(js, "compute pipeline constructor requires device and config parameters"); + + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]); + if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device"); + SDL_GPUComputePipelineCreateInfo info = {0}; - JSValue pipe = argv[0]; + JSValue pipe = argv[1]; JS_GETPROP(js, info.num_samplers, pipe, num_samplers, number) JS_GETPROP(js,info.num_readonly_storage_textures,pipe,num_readonly_storage_textures,number) JS_GETPROP(js,info.num_readonly_storage_buffers,pipe,num_readonly_storage_buffers,number) @@ -1715,21 +1959,144 @@ JSC_CCALL(gpu_compute_pipeline, SDL_GPUComputePipeline *pipeline = SDL_CreateGPUComputePipeline(gpu, &info); JS_FreeCString(js,info.entrypoint); if (!pipeline) return JS_ThrowReferenceError(js,"Could not create compute pipeline: %s", SDL_GetError()); - return SDL_GPUComputePipeline2js(js,pipeline); + return gpu_compute_pipeline_wrapper2js_from_components(js, gpu, pipeline); +} + +// Standalone buffer constructor: new sdl_gpu.buffer(device, config) +static JSValue js_gpu_buffer_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 2) return JS_ThrowTypeError(js, "buffer constructor requires device and config parameters"); + + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]); + if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device"); + + SDL_GPUBufferCreateInfo info = {0}; + JSValue config = argv[1]; + + // Get size - this is required + JS_GETPROP(js, info.size, config, size, number) + if (info.size == 0) return JS_ThrowTypeError(js, "Buffer size must be greater than 0"); + + // Parse usage flags + if (JS_GETBOOL(js, config, "vertex")) + info.usage |= SDL_GPU_BUFFERUSAGE_VERTEX; + if (JS_GETBOOL(js, config, "index")) + info.usage |= SDL_GPU_BUFFERUSAGE_INDEX; + if (JS_GETBOOL(js, config, "indirect")) + info.usage |= SDL_GPU_BUFFERUSAGE_INDIRECT; + if (JS_GETBOOL(js, config, "graphics_storage_read")) + info.usage |= SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ; + if (JS_GETBOOL(js, config, "compute_storage_read")) + info.usage |= SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ; + if (JS_GETBOOL(js, config, "compute_storage_write")) + info.usage |= SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE; + + // If no usage flags were set, throw an error + if (info.usage == 0) return JS_ThrowTypeError(js, "Buffer must have at least one usage flag"); + + SDL_GPUBuffer *buffer = SDL_CreateGPUBuffer(gpu, &info); + if (!buffer) return JS_ThrowReferenceError(js, "Unable to create buffer: %s", SDL_GetError()); + + return gpu_buffer_wrapper2js_from_components(js, gpu, buffer); +} + +// Standalone transfer buffer constructor: new sdl_gpu.transfer_buffer(device, config) +static JSValue js_gpu_transfer_buffer_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 2) return JS_ThrowTypeError(js, "transfer_buffer constructor requires device and config parameters"); + + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]); + if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device"); + + SDL_GPUTransferBufferCreateInfo info = {0}; + JSValue config = argv[1]; + + // Get size - this is required + JS_GETPROP(js, info.size, config, size, number) + if (info.size == 0) return JS_ThrowTypeError(js, "Transfer buffer size must be greater than 0"); + + // Get usage - this is required + JSValue usage_val = JS_GetPropertyStr(js, config, "usage"); + if (JS_IsString(usage_val)) { + const char *usage_str = JS_ToCString(js, usage_val); + if (strcmp(usage_str, "upload") == 0) { + info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + } else if (strcmp(usage_str, "download") == 0) { + info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD; + } else { + JS_FreeCString(js, usage_str); + JS_FreeValue(js, usage_val); + return JS_ThrowTypeError(js, "Invalid transfer buffer usage. Must be 'upload' or 'download'"); + } + JS_FreeCString(js, usage_str); + } else { + JS_FreeValue(js, usage_val); + return JS_ThrowTypeError(js, "Transfer buffer usage must be specified as 'upload' or 'download'"); + } + JS_FreeValue(js, usage_val); + + SDL_GPUTransferBuffer *buffer = SDL_CreateGPUTransferBuffer(gpu, &info); + if (!buffer) return JS_ThrowReferenceError(js, "Unable to create transfer buffer: %s", SDL_GetError()); + + return gpu_transfer_buffer_wrapper2js_from_components(js, gpu, buffer); +} + +// Standalone texture constructor: new sdl_gpu.texture(device, config) +static JSValue js_gpu_texture_constructor(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) { + if (argc < 2) return JS_ThrowTypeError(js, "texture constructor requires device and config parameters"); + + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]); + if (!gpu) return JS_ThrowTypeError(js, "Invalid GPU device"); + + SDL_GPUTextureCreateInfo info = {0}; + JSValue fmt = argv[1]; + + JS_GETPROP(js, info.width, fmt, width, number) + JS_GETPROP(js, info.height, fmt, height, number) + JS_GETPROP(js, info.layer_count_or_depth, fmt, layers, number) + JS_GETPROP(js, info.num_levels, fmt, mip_levels, number) + JS_GETPROP(js, info.sample_count, fmt, samples, number) + JS_GETPROP(js, info.type, fmt, type, SDL_GPUTextureType) + JS_GETPROP(js, info.format, fmt, format, SDL_GPUTextureFormat) + + // Parse usage flags + if (JS_GETBOOL(js, fmt, "sampler")) + info.usage |= SDL_GPU_TEXTUREUSAGE_SAMPLER; + if (JS_GETBOOL(js, fmt, "color_target")) + info.usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; + if (JS_GETBOOL(js, fmt, "depth_target")) + info.usage |= SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; + if (JS_GETBOOL(js, fmt, "read")) + info.usage |= (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ); + if (JS_GETBOOL(js, fmt, "write")) + info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE; + if (JS_GETBOOL(js, fmt, "readwrite")) + info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE; + + SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu, &info); + if (!tex) return JS_ThrowReferenceError(js, "Unable to create texture: %s", SDL_GetError()); + + JSValue jstex = gpu_texture_wrapper2js_from_components(js, gpu, tex); + JS_SetPropertyStr(js, jstex, "width", number2js(js, info.width)); + JS_SetPropertyStr(js, jstex, "height", number2js(js, info.height)); + JS_SetPropertyStr(js, jstex, "dim", vec22js(js, (HMM_Vec2){info.width, info.height})); + return jstex; +} + +JSC_CCALL(gpu_get_swapchain_texture_format, + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self); + SDL_Window *window = js2SDL_Window(js, argv[0]); + SDL_GPUTextureFormat format = SDL_GetGPUSwapchainTextureFormat(gpu, window); + return JS_NewInt32(js, format); ) static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = { MIST_FUNC_DEF(gpu, claim_window, 1), - MIST_FUNC_DEF(gpu, make_pipeline, 1), // loads pipeline state into an object - MIST_FUNC_DEF(gpu,compute_pipeline,1), - MIST_FUNC_DEF(gpu, set_swapchain, 2), - MIST_FUNC_DEF(gpu,sort_sprite,2), - MIST_FUNC_DEF(gpu, make_sampler,1), + MIST_FUNC_DEF(gpu, set_swapchain, 3), MIST_FUNC_DEF(gpu, load_texture, 2), MIST_FUNC_DEF(gpu, texture, 1), - MIST_FUNC_DEF(gpu, make_quad, 0), + MIST_FUNC_DEF(gpu, buffer, 1), + MIST_FUNC_DEF(gpu, transfer_buffer, 1), + MIST_FUNC_DEF(gpu, get_swapchain_texture_format, 1), MIST_FUNC_DEF(gpu, driver, 0), - MIST_FUNC_DEF(gpu, make_shader, 1), MIST_FUNC_DEF(gpu, acquire_cmd_buffer, 0), MIST_FUNC_DEF(gpu, upload, 3), MIST_FUNC_DEF(gpu, wait_for_fences, 2), @@ -1745,7 +2112,9 @@ JSC_CCALL(renderpass_bind_pipeline, JSC_CCALL(renderpass_draw_indexed, SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + printf("DRAWING INDEX\n"); SDL_DrawGPUIndexedPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4])); + printf("DRAW!!!\n"); ) JSC_CCALL(renderpass_draw, @@ -1757,12 +2126,13 @@ JSC_CCALL(renderpass_bind_buffers, SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); int first = js2number(js,argv[0]); JSValue buffers = argv[1]; + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[2]); int len = JS_ArrayLength(js,buffers); SDL_GPUBufferBinding bindings[len]; for (int i = 0; i < len; i++) { JSValue buffer = JS_GetPropertyUint32(js,buffers,i); bindings[i].offset = 0; - gpu_buffer_unpack(js,global_gpu, buffer, NULL, NULL,&bindings[i].buffer); + gpu_buffer_unpack(js, gpu, buffer, NULL, NULL,&bindings[i].buffer); JS_FreeValue(js,buffer); } @@ -1775,9 +2145,10 @@ JSC_CCALL(renderpass_bind_buffers, JSC_CCALL(renderpass_bind_index_buffer, SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[1]); SDL_GPUBufferBinding bind; bind.offset = 0; - gpu_buffer_unpack(js,global_gpu,argv[0], NULL, NULL, &bind.buffer); + gpu_buffer_unpack(js, gpu, argv[0], NULL, NULL, &bind.buffer); int elen; JSValue b = argv[0]; JS_GETPROP(js, elen, b, elen, number) @@ -1845,6 +2216,59 @@ JSC_CCALL(renderpass_scissor, SDL_SetGPUScissor(pass,&rr); ) +JSC_CCALL(renderpass_bind_vertex_samplers, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + int first_slot = js2number(js,argv[0]); + JSValue arr = argv[1]; + int num = JS_ArrayLength(js,arr); + SDL_GPUTextureSamplerBinding binds[num]; + for (int i = 0; i < num; i++) { + JSValue val = JS_GetPropertyUint32(js,arr,i); + JSValue tex = JS_GetPropertyStr(js,val,"texture"); + JSValue smp = JS_GetPropertyStr(js,val,"sampler"); + binds[i].texture = js2SDL_GPUTexture(js,tex); + binds[i].sampler = js2SDL_GPUSampler(js,smp); + JS_FreeValue(js,tex); + JS_FreeValue(js,smp); + JS_FreeValue(js,val); + } + SDL_BindGPUVertexSamplers(pass, first_slot, binds, num); +) + +JSC_CCALL(renderpass_bind_fragment_samplers, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + int first_slot = js2number(js,argv[0]); + JSValue arr = argv[1]; + int num = JS_ArrayLength(js,arr); + SDL_GPUTextureSamplerBinding binds[num]; + for (int i = 0; i < num; i++) { + JSValue val = JS_GetPropertyUint32(js,arr,i); + JSValue tex = JS_GetPropertyStr(js,val,"texture"); + JSValue smp = JS_GetPropertyStr(js,val,"sampler"); + binds[i].texture = js2SDL_GPUTexture(js,tex); + binds[i].sampler = js2SDL_GPUSampler(js,smp); + JS_FreeValue(js,tex); + JS_FreeValue(js,smp); + JS_FreeValue(js,val); + } + SDL_BindGPUFragmentSamplers(pass, first_slot, binds, num); +) + +JSC_CCALL(renderpass_bind_vertex_buffers, + SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self); + int first_slot = js2number(js,argv[0]); + JSValue arr = argv[1]; + int num = JS_ArrayLength(js,arr); + SDL_GPUBufferBinding binds[num]; + for (int i = 0; i < num; i++) { + JSValue val = JS_GetPropertyUint32(js,arr,i); + JS_GETPROP(js, binds[i].buffer, val, buffer, SDL_GPUBuffer) + JS_GETPROP(js, binds[i].offset, val, offset, number) + JS_FreeValue(js,val); + } + SDL_BindGPUVertexBuffers(pass, first_slot, binds, num); +) + static const JSCFunctionListEntry js_SDL_GPURenderPass_funcs[] = { MIST_FUNC_DEF(renderpass, bind_pipeline, 1), MIST_FUNC_DEF(renderpass, viewport, 1), @@ -1855,6 +2279,9 @@ static const JSCFunctionListEntry js_SDL_GPURenderPass_funcs[] = { MIST_FUNC_DEF(renderpass, bind_index_buffer, 1), MIST_FUNC_DEF(renderpass, bind_buffers, 2), MIST_FUNC_DEF(renderpass, bind_samplers, 3), + MIST_FUNC_DEF(renderpass, bind_vertex_samplers, 2), + MIST_FUNC_DEF(renderpass, bind_fragment_samplers, 2), + MIST_FUNC_DEF(renderpass, bind_vertex_buffers, 2), MIST_FUNC_DEF(renderpass, bind_storage_buffers, 2), MIST_FUNC_DEF(renderpass, bind_storage_textures, 2), }; @@ -1904,11 +2331,18 @@ JSC_CCALL(cmd_render_pass, JS_FreeValue(js, colorTargetsVal); - if (!pass) return JS_ThrowInternalError(js, "render_pass: Failed to begin render pass"); + if (!pass) return JS_ThrowInternalError(js, "render_pass: Failed to begin render pass: %s", SDL_GetError()); return SDL_GPURenderPass2js(js, pass); ) +JSC_CCALL(cmd_copy_pass, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + SDL_GPUCopyPass *pass = SDL_BeginGPUCopyPass(cmds); + if (!pass) return JS_ThrowInternalError(js, "copy_pass: Failed to begin copy pass: %s", SDL_GetError()); + return SDL_GPUCopyPass2js(js, pass); +) + JSC_CCALL(cmd_bind_vertex_buffer, SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); int slot; @@ -2083,8 +2517,29 @@ JSC_CCALL(cmd_compute_pass, return SDL_GPUComputePass2js(js,pass); ) +JSC_CCALL(cmd_wait_and_acquire_swapchain_texture, + SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self); + SDL_Window *window = js2SDL_Window(js, argv[0]); + + SDL_GPUTexture *swapchain_texture; + Uint32 width, height; + + bool result = SDL_WaitAndAcquireGPUSwapchainTexture(cmds, window, &swapchain_texture, &width, &height); + if (!result) return JS_ThrowReferenceError(js, "Failed to acquire swapchain texture: %s", SDL_GetError()); + + if (swapchain_texture) { + JSValue texture_obj = SDL_GPUTexture2js(js, swapchain_texture); + JS_SetPropertyStr(js, texture_obj, "width", JS_NewUint32(js, width)); + JS_SetPropertyStr(js, texture_obj, "height", JS_NewUint32(js, height)); + return texture_obj; + } + + return JS_NULL; +) + static const JSCFunctionListEntry js_SDL_GPUCommandBuffer_funcs[] = { MIST_FUNC_DEF(cmd, render_pass, 1), + MIST_FUNC_DEF(cmd, copy_pass, 0), MIST_FUNC_DEF(cmd, compute_pass, 2), MIST_FUNC_DEF(cmd, swapchain_pass, 1), MIST_FUNC_DEF(cmd, acquire_swapchain,0), @@ -2104,6 +2559,63 @@ static const JSCFunctionListEntry js_SDL_GPUCommandBuffer_funcs[] = { MIST_FUNC_DEF(cmd, blit, 1), }; +JSC_CCALL(copypass_end, + SDL_GPUCopyPass *pass = js2SDL_GPUCopyPass(js,self); + SDL_EndGPUCopyPass(pass); +) + +JSC_CCALL(copypass_upload_to_buffer, + SDL_GPUCopyPass *pass = js2SDL_GPUCopyPass(js,self); + JSValue transfer_loc = argv[0]; + JSValue buffer_region = argv[1]; + + SDL_GPUTransferBufferLocation transfer_info = {0}; + JS_GETPROP(js, transfer_info.transfer_buffer, transfer_loc, transfer_buffer, SDL_GPUTransferBuffer) + JS_GETPROP(js, transfer_info.offset, transfer_loc, offset, number) + + SDL_GPUBufferRegion buffer_info = {0}; + JS_GETPROP(js, buffer_info.buffer, buffer_region, buffer, SDL_GPUBuffer) + JS_GETPROP(js, buffer_info.offset, buffer_region, offset, number) + JS_GETPROP(js, buffer_info.size, buffer_region, size, number) + + bool cycle = argc > 2 ? JS_ToBool(js, argv[2]) : false; + + SDL_UploadToGPUBuffer(pass, &transfer_info, &buffer_info, cycle); +) + +JSC_CCALL(copypass_upload_to_texture, + SDL_GPUCopyPass *pass = js2SDL_GPUCopyPass(js,self); + JSValue transfer_info_val = argv[0]; + JSValue texture_region_val = argv[1]; + + SDL_GPUTextureTransferInfo transfer_info = {0}; + JS_GETPROP(js, transfer_info.transfer_buffer, transfer_info_val, transfer_buffer, SDL_GPUTransferBuffer) + JS_GETPROP(js, transfer_info.offset, transfer_info_val, offset, number) + JS_GETPROP(js, transfer_info.pixels_per_row, transfer_info_val, pixels_per_row, number) + JS_GETPROP(js, transfer_info.rows_per_layer, transfer_info_val, rows_per_layer, number) + + SDL_GPUTextureRegion texture_region = {0}; + JS_GETPROP(js, texture_region.texture, texture_region_val, texture, SDL_GPUTexture) + JS_GETPROP(js, texture_region.mip_level, texture_region_val, mip_level, number) + JS_GETPROP(js, texture_region.layer, texture_region_val, layer, number) + JS_GETPROP(js, texture_region.x, texture_region_val, x, number) + JS_GETPROP(js, texture_region.y, texture_region_val, y, number) + JS_GETPROP(js, texture_region.z, texture_region_val, z, number) + JS_GETPROP(js, texture_region.w, texture_region_val, w, number) + JS_GETPROP(js, texture_region.h, texture_region_val, h, number) + JS_GETPROP(js, texture_region.d, texture_region_val, d, number) + + bool cycle = argc > 2 ? JS_ToBool(js, argv[2]) : false; + + SDL_UploadToGPUTexture(pass, &transfer_info, &texture_region, cycle); +) + +static const JSCFunctionListEntry js_SDL_GPUCopyPass_funcs[] = { + MIST_FUNC_DEF(copypass, end, 0), + MIST_FUNC_DEF(copypass, upload_to_buffer, 3), + MIST_FUNC_DEF(copypass, upload_to_texture, 3), +}; + JSC_SCALL(buffer_name, SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js,self); SDL_GPUDevice *gpu; @@ -2115,6 +2627,43 @@ static const JSCFunctionListEntry js_SDL_GPUBuffer_funcs[] = { MIST_FUNC_DEF(buffer, name, 1), }; +JSC_SCALL(texture_name, + SDL_GPUTexture *texture = js2SDL_GPUTexture(js,self); + SDL_GPUDevice *gpu; + JS_GETPROP(js, gpu, self, gpu, SDL_GPUDevice) + SDL_SetGPUTextureName(gpu,texture,str); +) + +static const JSCFunctionListEntry js_SDL_GPUTexture_funcs[] = { + MIST_FUNC_DEF(texture, name, 1), +}; + +JSC_CCALL(transferbuffer_copy_blob, + SDL_GPUTransferBuffer *buffer = js2SDL_GPUTransferBuffer(js, self); + SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, argv[0]); + JSValue blob = argv[1]; + + size_t blob_size; + void *blob_data = js_get_blob_data(js, &blob_size, blob); + if (!blob_data) return JS_ThrowTypeError(js, "copy_blob: Expected a blob"); + + // Map the transfer buffer + void *mapped_data = SDL_MapGPUTransferBuffer(gpu, buffer, false); + if (!mapped_data) return JS_ThrowReferenceError(js, "copy_blob: Failed to map transfer buffer: %s", SDL_GetError()); + + // Copy blob data to mapped buffer + SDL_memcpy(mapped_data, blob_data, blob_size); + + // Unmap the transfer buffer + SDL_UnmapGPUTransferBuffer(gpu, buffer); + + return JS_UNINITIALIZED; +) + +static const JSCFunctionListEntry js_SDL_GPUTransferBuffer_funcs[] = { + MIST_FUNC_DEF(transferbuffer, copy_blob, 2), +}; + JSC_CCALL(compute_dispatch, SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self); SDL_DispatchGPUCompute(pass,js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2])); @@ -2179,257 +2728,141 @@ static const JSCFunctionListEntry js_SDL_GPUComputePass_funcs[] = { MIST_FUNC_DEF(compute, storage_textures, 2), }; -JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index) +// Empty function arrays for classes with no methods yet +static const JSCFunctionListEntry js_SDL_GPUGraphicsPipeline_funcs[] = {}; +static const JSCFunctionListEntry js_SDL_GPUComputePipeline_funcs[] = {}; +static const JSCFunctionListEntry js_SDL_GPUSampler_funcs[] = {}; +static const JSCFunctionListEntry js_SDL_GPUShader_funcs[] = {}; + + +// GPU device constructor function +static JSValue js_gpu_constructor(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv) { - JSValue tstack[2]; - if (copy) { - tstack[0] = js_new_blob_stoned_copy(js,data,size);//, make_gpu_buffer, NULL, 1); - } else { - tstack[0] = JS_NewArrayBuffer(js,data,size,free_gpu_buffer, NULL, 0); - } - tstack[1] = JS_NewObject(js); - JS_SetPropertyStr(js,tstack[1],"type",number2js(js,type)); - JS_SetPropertyStr(js,tstack[1],"elements",number2js(js,elements)); - JS_SetPropertyStr(js,tstack[1],"buffer",tstack[0]); - JS_SetPropertyStr(js,tstack[1],"index", number2js(js,index)); - return tstack[1]; -} - -void *get_gpu_buffer(JSContext *js, JSValue argv, size_t *stride, size_t *size) -{ - if (JS_IsArray(js,argv)) { - if (stride) *stride = sizeof(float); - int arrlen = JS_ArrayLength(js,argv); - float *arr = malloc(sizeof(float)*arrlen); - for (int i = 0; i < arrlen; i++) { - JSValue v = JS_GetPropertyUint32(js,argv,i); - arr[i] = js2number(js,v); - JS_FreeValue(js,v); - } - if (size) *size = sizeof(float)*arrlen; - return arr; - } - - JSValue buffer = JS_GetPropertyStr(js,argv,"buffer"); - void *data = js_get_blob_data(js,NULL,buffer); - JS_FreeValue(js,buffer); - - JSValue typeval = JS_GetPropertyStr(js,argv,"type"); - int type = js2number(js,typeval); - JS_FreeValue(js,typeval); - - return data; -} - -// Conversion functions for SDL GPU types -SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v) -{ - SDL_GPUGraphicsPipelineTargetInfo info = {0}; - return info; -} - -SDL_GPUSampleCount js2SDL_GPUSampleCount(JSContext *js, JSValue v) -{ - int n = js2number(js,v); - switch(n) { - case 1: return SDL_GPU_SAMPLECOUNT_1; - case 2: return SDL_GPU_SAMPLECOUNT_2; - case 4: return SDL_GPU_SAMPLECOUNT_4; - case 8: return SDL_GPU_SAMPLECOUNT_8; - } - return -1; -} - -// Enum conversion macro and definitions -#define JS2ENUM(NAME, RETS, VALS) \ -int js2##NAME(JSContext *js, JSValue v) { \ - if (JS_IsNull(v)) return 0; \ - const char *str = JS_ToCString(js, v); \ - int *rets = (RETS); \ - const char **vals = (VALS); \ - /* Compute how many entries are in the arrays */ \ - int n = (int)(sizeof((RETS)) / sizeof((RETS)[0])); \ - for(int i = 0; i < n; i++) \ - if(!strcmp(vals[i], str)) { \ - JS_FreeCString(js, str); \ - return rets[i]; \ - } \ - JS_FreeCString(js, str); \ - return 0; \ -} - -// SDL GPU Swapchain Composition -static int rets_SDL_GPUSwapchainComposition[] = { - SDL_GPU_SWAPCHAINCOMPOSITION_SDR, - SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR, - SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR, -// SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084 -}; - -static const char *vals_SDL_GPUSwapchainComposition[] = { - "sdr", - "linear", - "hdr", -// "hdr10" -}; - -JS2ENUM(SDL_GPUSwapchainComposition, rets_SDL_GPUSwapchainComposition, vals_SDL_GPUSwapchainComposition) - -// SDL GPU Blend Factor -static int rets_SDL_GPUBlendFactor[] = { - SDL_GPU_BLENDFACTOR_INVALID, - SDL_GPU_BLENDFACTOR_ZERO, - SDL_GPU_BLENDFACTOR_ONE, - SDL_GPU_BLENDFACTOR_SRC_COLOR, - SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR, - SDL_GPU_BLENDFACTOR_DST_COLOR, - SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR, - SDL_GPU_BLENDFACTOR_SRC_ALPHA, - SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, - SDL_GPU_BLENDFACTOR_DST_ALPHA, - SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA, - SDL_GPU_BLENDFACTOR_CONSTANT_COLOR, - SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR, - SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE -}; - -static const char *vals_SDL_GPUBlendFactor[] = { - "invalid", - "zero", - "one", - "src_color", - "one_minus_src_color", - "dst_color", - "one_minus_dst_color", - "src_alpha", - "one_minus_src_alpha", - "dst_alpha", - "one_minus_dst_alpha", - "constant_color", - "one_minus_constant_color", - "src_alpha_saturate" -}; - -JS2ENUM(SDL_GPUBlendFactor, rets_SDL_GPUBlendFactor, vals_SDL_GPUBlendFactor) - -// GPU FUNCTIONS - -JSC_CCALL(SDL_Window_make_gpu, - if (global_gpu) - return JS_ThrowReferenceError(js, "A gpu has already been created somewhere."); - const char *name = JS_ToCString(js,argv[1]); SDL_PropertiesID props = SDL_CreateProperties(); - SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, name); - JS_FreeCString(js,name); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, JS_ToBool(js,argv[0])); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, 1); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN, 1); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, 1); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, 1); - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN, 1); - - SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, 1); - - SDL_GPUDevice *gpu = SDL_CreateGPUDeviceWithProperties(props); + + // Handle properties object if provided + if (argc > 0 && JS_IsObject(argv[0])) { + JSValue opts = argv[0]; + + // Helper function to check and set boolean properties + #define SET_BOOL_PROP(js_name, sdl_prop) do { \ + JSValue val = JS_GetPropertyStr(js, opts, js_name); \ + if (!JS_IsNull(val)) { \ + SDL_SetBooleanProperty(props, sdl_prop, JS_ToBool(js, val)); \ + } \ + JS_FreeValue(js, val); \ + } while(0) + + // Set boolean properties + SET_BOOL_PROP("debug", SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN); + SET_BOOL_PROP("lowpower", SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN); + // SET_BOOL_PROP("verbose", SDL_PROP_GPU_DEVICE_CREATE_VERBOSE_BOOLEAN); // Not available in current SDL + + // Shader format properties + SET_BOOL_PROP("shaders_private", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_PRIVATE_BOOLEAN); + SET_BOOL_PROP("shaders_spirv", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN); + SET_BOOL_PROP("shaders_dxbc", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN); + SET_BOOL_PROP("shaders_dxil", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN); + SET_BOOL_PROP("shaders_msl", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN); + SET_BOOL_PROP("shaders_metallib", SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN); + + // Vulkan-specific properties - uncomment when available in SDL + // SET_BOOL_PROP("vulkan_shaderclipdistance", SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SHADERCLIPDISTANCE_BOOLEAN); + // SET_BOOL_PROP("vulkan_depthclamp", SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DEPTHCLAMP_BOOLEAN); + // SET_BOOL_PROP("vulkan_drawindirectfirst", SDL_PROP_GPU_DEVICE_CREATE_VULKAN_DRAWINDIRECTFIRST_BOOLEAN); + // SET_BOOL_PROP("vulkan_sampleranisotropy", SDL_PROP_GPU_DEVICE_CREATE_VULKAN_SAMPLERANISOTROPY_BOOLEAN); + + #undef SET_BOOL_PROP + + // Handle string properties + JSValue name_val = JS_GetPropertyStr(js, opts, "name"); + if (!JS_IsNull(name_val)) { + const char *name = JS_ToCString(js, name_val); + if (name) { + SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, name); + JS_FreeCString(js, name); + } + } + JS_FreeValue(js, name_val); + + // D3D12 semantic name + JSValue semantic_val = JS_GetPropertyStr(js, opts, "d3d12_semantic_name"); + if (!JS_IsNull(semantic_val)) { + const char *semantic = JS_ToCString(js, semantic_val); + if (semantic) { + SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_D3D12_SEMANTIC_NAME_STRING, semantic); + JS_FreeCString(js, semantic); + } + } + JS_FreeValue(js, semantic_val); + } + + // Create GPU device with properties + SDL_GPUDevice *device = SDL_CreateGPUDeviceWithProperties(props); SDL_DestroyProperties(props); - - if (!gpu) return JS_ThrowReferenceError(js, "Could not create GPU device! %s", SDL_GetError()); - - global_gpu = gpu; - return SDL_GPUDevice2js(js,gpu); -) - -JSC_CCALL(gpu_claim_window, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_Window *w = js2SDL_Window(js,argv[0]); - if (!SDL_ClaimWindowForGPUDevice(gpu, w)) - return JS_ThrowReferenceError(js, "%s", SDL_GetError()); -) - -JSC_CCALL(gpu_set_swapchain, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_Window *w = js2SDL_Window(js,argv[0]); - SDL_GPUSwapchainComposition comp = js2SDL_GPUSwapchainComposition(js,argv[1]); - if (!SDL_SetGPUSwapchainParameters(gpu, w, comp, SDL_GPU_PRESENTMODE_VSYNC)) - return JS_ThrowReferenceError(js, "%s", SDL_GetError()); -) - -JSC_CCALL(gpu_driver, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - return JS_NewString(js, SDL_GetGPUDeviceDriver(gpu)); -) - -JSC_CCALL(gpu_shader_format, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_GPUShaderFormat format = SDL_GetGPUShaderFormats(gpu); - switch(format) { - case SDL_GPU_SHADERFORMAT_SPIRV: return JS_NewString(js, "spirv"); - case SDL_GPU_SHADERFORMAT_DXBC: return JS_NewString(js, "dxbc"); - case SDL_GPU_SHADERFORMAT_DXIL: return JS_NewString(js, "dxil"); - case SDL_GPU_SHADERFORMAT_MSL: return JS_NewString(js, "msl"); - case SDL_GPU_SHADERFORMAT_METALLIB: return JS_NewString(js, "metallib"); + + if (!device) { + return JS_ThrowReferenceError(js, "Failed to create GPU device: %s", SDL_GetError()); } - return JS_NewString(js, "unknown"); -) + + // Global device storage removed - device now passed as parameter + + // Create the GPU device JS object + JSValue device_obj = SDL_GPUDevice2js(js, device); + + return device_obj; +} -JSC_CCALL(gpu_acquire_cmd_buffer, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(gpu); - if (!cmd) return JS_ThrowReferenceError(js, "%s", SDL_GetError()); - return SDL_GPUCommandBuffer2js(js,cmd); -) - -JSC_CCALL(gpu_wait_for_fences, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - int fencecount = js2number(js,argv[1]); - SDL_GPUFence **fences = malloc(sizeof(SDL_GPUFence*)*fencecount); - for (int i = 0; i < fencecount; i++) { - JSValue fenceval = JS_GetPropertyUint32(js,argv[0],i); - fences[i] = js2SDL_GPUFence(js,fenceval); - JS_FreeValue(js,fenceval); - } - int ok = SDL_WaitForGPUFences(gpu,1,fences,fencecount); - free(fences); - if (!ok) return JS_ThrowReferenceError(js, "%s", SDL_GetError()); -) - -JSC_CCALL(gpu_query_fence, - SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self); - SDL_GPUFence *fence = js2SDL_GPUFence(js,argv[0]); - return JS_NewBool(js, SDL_QueryGPUFence(gpu,fence)); -) - -// Function array for SDL_GPUDevice -static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = { - MIST_FUNC_DEF(gpu, claim_window, 1), - MIST_FUNC_DEF(gpu, set_swapchain, 2), - MIST_FUNC_DEF(gpu, driver, 0), - MIST_FUNC_DEF(gpu, acquire_cmd_buffer, 0), - MIST_FUNC_DEF(gpu, wait_for_fences, 2), - MIST_FUNC_DEF(gpu, query_fence, 1), - MIST_FUNC_DEF(gpu, shader_format, 0), -}; - -// Function array for SDL_Window GPU functions -static const JSCFunctionListEntry js_SDL_Window_gpu_funcs[] = { - MIST_FUNC_DEF(SDL_Window, make_gpu, 2), -}; - -JSC_SCALL(texture_name, - SDL_GPUTexture *texture = js2SDL_GPUTexture(js,self); - SDL_GPUDevice *gpu; - JS_GETPROP(js,gpu,self,gpu,SDL_GPUDevice) - SDL_SetGPUTextureName(gpu,texture,str); -) - -static const JSCFunctionListEntry js_SDL_GPUTexture_funcs[] = { - MIST_FUNC_DEF(texture, name, 1), -}; - -// Function to register module - GPU classes are registered in main FFI loading +// Function to register module JSValue js_sdl_gpu_use(JSContext *js) { - // GPU classes are registered via the main FFI loading system - // This module doesn't export its own functions, it just provides class definitions - return JS_NULL; + JSValue ret = JS_NewObject(js); + + // Initialize classes + QJSCLASSPREP_FUNCS(SDL_GPUDevice) + QJSCLASSPREP_NO_FUNCS(SDL_GPUBuffer) + QJSCLASSPREP_NO_FUNCS(SDL_GPUComputePipeline) + QJSCLASSPREP_NO_FUNCS(SDL_GPUGraphicsPipeline) + QJSCLASSPREP_NO_FUNCS(SDL_GPUSampler) + QJSCLASSPREP_NO_FUNCS(SDL_GPUShader) + QJSCLASSPREP_FUNCS(SDL_GPUTexture) + QJSCLASSPREP_FUNCS(SDL_GPUTransferBuffer) + QJSCLASSPREP_NO_FUNCS(SDL_GPUFence) + QJSCLASSPREP_FUNCS(SDL_GPUCommandBuffer) + QJSCLASSPREP_FUNCS(SDL_GPUComputePass) + QJSCLASSPREP_FUNCS(SDL_GPUCopyPass) + QJSCLASSPREP_FUNCS(SDL_GPURenderPass) + + // Create GPU constructor + JSValue gpu_ctor = JS_NewCFunction2(js, js_gpu_constructor, "gpu", 1, JS_CFUNC_constructor, 0); + + // Set prototype on constructor + JS_SetConstructor(js, gpu_ctor, SDL_GPUDevice_proto); + + // Set constructor in exports + JS_SetPropertyStr(js, ret, "gpu", gpu_ctor); + + // Add GPU object constructors + JSValue sampler_ctor = JS_NewCFunction2(js, js_gpu_sampler_constructor, "sampler", 2, JS_CFUNC_constructor, 0); + JS_SetPropertyStr(js, ret, "sampler", sampler_ctor); + + JSValue shader_ctor = JS_NewCFunction2(js, js_gpu_shader_constructor, "shader", 2, JS_CFUNC_constructor, 0); + JS_SetPropertyStr(js, ret, "shader", shader_ctor); + + JSValue graphics_pipeline_ctor = JS_NewCFunction2(js, js_gpu_graphics_pipeline_constructor, "graphics_pipeline", 2, JS_CFUNC_constructor, 0); + JS_SetPropertyStr(js, ret, "graphics_pipeline", graphics_pipeline_ctor); + + JSValue compute_pipeline_ctor = JS_NewCFunction2(js, js_gpu_compute_pipeline_constructor, "compute_pipeline", 2, JS_CFUNC_constructor, 0); + JS_SetPropertyStr(js, ret, "compute_pipeline", compute_pipeline_ctor); + + JSValue buffer_ctor = JS_NewCFunction2(js, js_gpu_buffer_constructor, "buffer", 2, JS_CFUNC_constructor, 0); + JS_SetPropertyStr(js, ret, "buffer", buffer_ctor); + + JSValue transfer_buffer_ctor = JS_NewCFunction2(js, js_gpu_transfer_buffer_constructor, "transfer_buffer", 2, JS_CFUNC_constructor, 0); + JS_SetPropertyStr(js, ret, "transfer_buffer", transfer_buffer_ctor); + + JSValue texture_ctor = JS_NewCFunction2(js, js_gpu_texture_constructor, "texture", 2, JS_CFUNC_constructor, 0); + JS_SetPropertyStr(js, ret, "texture", texture_ctor); + + return ret; } diff --git a/source/qjs_sdl_input.c b/source/qjs_sdl_input.c index 5e9c6e61..812bb959 100644 --- a/source/qjs_sdl_input.c +++ b/source/qjs_sdl_input.c @@ -721,7 +721,7 @@ JSC_CCALL(input_get_events, while (SDL_PollEvent(&event)) { // Process event with ImGui first - gui_input(&event); +// gui_input(&event); WotaBuffer wb = event2wota(&event); JSValue event_obj = wota2value(js, wb.data); diff --git a/source/qjs_sdl_video.c b/source/qjs_sdl_video.c index 08fe8ff6..6ad4688e 100644 --- a/source/qjs_sdl_video.c +++ b/source/qjs_sdl_video.c @@ -80,6 +80,8 @@ static SDL_BlendMode js2blendmode(JSContext *js, JSValue v); // Window constructor function static JSValue js_window_constructor(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv) { + SDL_Window *www = SDL_CreateWindow("prosperon", 500, 500, 0); + return SDL_Window2js(js, www); if (argc < 1 || !JS_IsObject(argv[0])) return JS_ThrowTypeError(js, "Window constructor requires an object argument"); @@ -251,6 +253,8 @@ static JSValue js_window_constructor(JSContext *js, JSValueConst new_target, int } JS_FreeValue(js, text_input); + printf("created window %p\n", window); + return window_obj; }