From 566baa250c3772db4232b63de71945818cbe4d42 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sun, 13 Apr 2025 21:32:34 -0500 Subject: [PATCH] sdl renderer ' --- scripts/core/engine.js | 2 + scripts/modules/device.js | 35 ++ scripts/modules/draw2d.js | 21 +- scripts/modules/dull.js | 10 +- scripts/modules/ext/emitter.js | 3 +- scripts/modules/graphics.js | 28 +- scripts/modules/render.js | 190 ++++------- scripts/modules/sdl_render.js | 568 ++------------------------------- source/jsffi.c | 5 +- source/prosperon.c | 9 - tests/dull.js | 17 + tests/window.js | 2 +- 12 files changed, 196 insertions(+), 694 deletions(-) create mode 100644 scripts/modules/device.js create mode 100644 tests/dull.js diff --git a/scripts/core/engine.js b/scripts/core/engine.js index a1b8c868..a4de2794 100644 --- a/scripts/core/engine.js +++ b/scripts/core/engine.js @@ -847,6 +847,8 @@ $_.send = function(actor, message, reply) { } $_.send[prosperon.DOC] = "sends a message to another actor..." +$_.blast = $_.send; + var cmd = use('cmd') cmd.process(prosperon.argv.slice()) diff --git a/scripts/modules/device.js b/scripts/modules/device.js new file mode 100644 index 00000000..48c12e35 --- /dev/null +++ b/scripts/modules/device.js @@ -0,0 +1,35 @@ +// helpful render devices. width and height in pixels; diagonal in inches. +return { + pc: { width: 1920, height: 1080 }, + macbook_m2: { width: 2560, height: 1664, diagonal: 13.6 }, + ds_top: { width: 400, height: 240, diagonal: 3.53 }, + ds_bottom: { width: 320, height: 240, diagonal: 3.02 }, + playdate: { width: 400, height: 240, diagonal: 2.7 }, + switch: { width: 1280, height: 720, diagonal: 6.2 }, + switch_lite: { width: 1280, height: 720, diagonal: 5.5 }, + switch_oled: { width: 1280, height: 720, diagonal: 7 }, + dsi: { width: 256, height: 192, diagonal: 3.268 }, + ds: { width: 256, height: 192, diagonal: 3 }, + dsixl: { width: 256, height: 192, diagonal: 4.2 }, + ipad_air_m2: { width: 2360, height: 1640, diagonal: 11.97 }, + iphone_se: { width: 1334, height: 750, diagonal: 4.7 }, + iphone_12_pro: { width: 2532, height: 1170, diagonal: 6.06 }, + iphone_15: { width: 2556, height: 1179, diagonal: 6.1 }, + gba: { width: 240, height: 160, diagonal: 2.9 }, + gameboy: { width: 160, height: 144, diagonal: 2.48 }, + gbc: { width: 160, height: 144, diagonal: 2.28 }, + steamdeck: { width: 1280, height: 800, diagonal: 7 }, + vita: { width: 960, height: 544, diagonal: 5 }, + psp: { width: 480, height: 272, diagonal: 4.3 }, + imac_m3: { width: 4480, height: 2520, diagonal: 23.5 }, + macbook_pro_m3: { width: 3024, height: 1964, diagonal: 14.2 }, + ps1: { width: 320, height: 240, diagonal: 5 }, + ps2: { width: 640, height: 480 }, + snes: { width: 256, height: 224 }, + gamecube: { width: 640, height: 480 }, + n64: { width: 320, height: 240 }, + c64: { width: 320, height: 200 }, + macintosh: { width: 512, height: 342 }, + gamegear: { width: 160, height: 144, diagonal: 3.2 } +}; + diff --git a/scripts/modules/draw2d.js b/scripts/modules/draw2d.js index d9862988..31c8e999 100644 --- a/scripts/modules/draw2d.js +++ b/scripts/modules/draw2d.js @@ -3,6 +3,7 @@ var graphics = use('graphics') var math = use('math') var util = use('util') var os = use('os') +var geometry = use('geomtry') var draw = {} draw[prosperon.DOC] = ` @@ -13,15 +14,15 @@ for lines, rectangles, text, sprite drawing, etc. var whiteimage = {} whiteimage.surface = graphics.make_surface([1,1]) whiteimage.surface.rect({x:0,y:0,width:1,height:1}, [1,1,1,1]) -whiteimage.texture = prosperon.gpu.load_texture(whiteimage.surface) +render.load_texture(whiteimage) draw.point = function (pos, size, color = Color.blue) { - render._main.point(pos, color) + draw.rectangle({x:pos.x,y:pos.y, width:size,height:size}) } draw.point[prosperon.DOC] = ` :param pos: A 2D position ([x, y]) where the point should be drawn. -:param size: The size of the point (not currently affecting rendering). -:param color: The color of the point, defaults to Color.blue. +:param size: The size of the point. +:param color: The color of the point, defaults to white. :return: None ` @@ -63,9 +64,9 @@ draw.arrow = function render_arrow(start, end, color = Color.red, wingspan = 4, var dir = math.norm(end.sub(start)) var wing1 = [math.rotate(dir, wingangle).scale(wingspan).add(end), end] var wing2 = [math.rotate(dir, -wingangle).scale(wingspan).add(end), end] - render.line([start, end], color) - render.line(wing1, color) - render.line(wing2, color) + draw.line([start, end], color) + draw.line(wing1, color) + draw.line(wing2, color) } draw.arrow[prosperon.DOC] = ` :param start: The start position of the arrow ([x, y]). @@ -101,7 +102,7 @@ draw.tile = function(image, rect, color = Color.white, tile = tile_def, pipeline if (!image) throw Error('Need an image to render.') if (typeof image === "string") image = graphics.texture(image) - var mesh = render._main.tile(image.texture, {x:0,y:0,width:image.texture.width,height:image.texture.height}, rect, tile) + var mesh = geometry.tile(image.texture, {x:0,y:0,width:image.texture.width,height:image.texture.height}, rect, tile) render.queue({ type:'geometry', mesh, @@ -134,7 +135,7 @@ draw.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.whit if (!image) throw Error('Need an image to render.') if (typeof image === "string") image = graphics.texture(image) - var mesh = render._main.slice9(image.texture, rect, util.normalizeSpacing(slice), info) + var mesh = geometry.slice9(image.texture, rect, util.normalizeSpacing(slice), info) render.queue({ type: 'geometry', mesh, @@ -224,7 +225,7 @@ draw.sprites[prosperon.DOC] = ` ` draw.circle = function render_circle(pos, radius, color, inner_radius = 1, pipeline) { - render.rectangle({x:pos.x, y:pos.y, width:radius*2,height:radius*2}, color, circle_pipeline) + draw.rectangle({x:pos.x, y:pos.y, width:radius*2,height:radius*2}, color, circle_pipeline) } draw.circle[prosperon.DOC] = ` :param pos: Center of the circle ([x, y]). diff --git a/scripts/modules/dull.js b/scripts/modules/dull.js index 3e84a840..cb2c6751 100644 --- a/scripts/modules/dull.js +++ b/scripts/modules/dull.js @@ -4,7 +4,7 @@ This sets up a lot of different modules to be used altogether */ -var render = use('render') // The refactored file above +var render = use('render') var layout = use('clay') var input = use('input') var emitter = use('emitter') @@ -36,12 +36,18 @@ function step() { if (imgui) imgui.prosperon_menu(); // Now do the GPU present (calls gpupresent in render.js) - render._main.present() + render.present() tracy.end_frame() } +function start() +{ + +} + // Return or export them so you can call from a main script return { + start, step } diff --git a/scripts/modules/ext/emitter.js b/scripts/modules/ext/emitter.js index c8eb9650..fce72009 100644 --- a/scripts/modules/ext/emitter.js +++ b/scripts/modules/ext/emitter.js @@ -1,7 +1,6 @@ var Color = use('color') var os = use('os') var graphics = use('graphics') -//var render = use('render') var ex = {} @@ -111,7 +110,7 @@ ex.draw = function() /* var diff = graphics.texture(this.diffuse) if (!diff) throw new Error("emitter does not have a proper diffuse texture") - var mesh = render._main.make_sprite_mesh(this.particles) + var mesh = graphics.make_sprite_mesh(this.particles) if (mesh.num_indices === 0) return render.queue({ type:'geometry', diff --git a/scripts/modules/graphics.js b/scripts/modules/graphics.js index b5db4151..b218e9df 100644 --- a/scripts/modules/graphics.js +++ b/scripts/modules/graphics.js @@ -1,4 +1,5 @@ var graphics = this + graphics[prosperon.DOC] = ` Provides functionality for loading and managing images, fonts, textures, and sprite meshes. Includes both JavaScript and C-implemented routines for creating geometry buffers, performing @@ -7,6 +8,7 @@ rectangle packing, etc. var io = use('io') var res = use('resources') +var render = use('render') function calc_image_size(img) { if (!img.texture || !img.rect) return @@ -25,26 +27,24 @@ function create_image(path) { switch (path.ext()) { case 'gif': newimg = graphics.make_gif(data); - if (newimg.surface) { - newimg.texture = prosperon.gpu.load_texture(newimg.surface); - } else { - for (var frame of newimg.frames) { - frame.texture = prosperon.gpu.load_texture(frame.surface); - } + if (newimg.surface) + render.load_texture(newimg) + else { + for (var frame of newimg.frames) + render.load_texture(frame) } break; case 'ase': case 'aseprite': newimg = graphics.make_aseprite(data); - if (newimg.surface) { - newimg.texture = prosperon.gpu.load_texture(newimg.surface); - } else { + if (newimg.surface) + render.load_texture(newimg) + else { for (var anim in newimg) { var a = newimg[anim]; - for (var frame of a.frames) { - frame.texture = prosperon.gpu.load_texture(frame.surface); - } + for (var frame of a.frames) + render.load_texture(frame) } } break; @@ -53,7 +53,7 @@ function create_image(path) { newimg = { surface: graphics.make_texture(data) }; - newimg.texture = prosperon.gpu.load_texture(newimg.surface); + render.load_texture(newimg) break; } @@ -191,7 +191,7 @@ graphics.get_font = function get_font(path, size) { var data = io.slurpbytes(fullpath) fontcache[fontstr] = graphics.make_font(data, size) - fontcache[fontstr].texture = prosperon.gpu.load_texture(fontcache[fontstr].surface) + render.load_texture(fontcache[fontstr]) return fontcache[fontstr] } graphics.get_font[prosperon.DOC] = ` diff --git a/scripts/modules/render.js b/scripts/modules/render.js index 0d5b73b3..ac2e2ba8 100644 --- a/scripts/modules/render.js +++ b/scripts/modules/render.js @@ -5,34 +5,7 @@ var os = use('os') var controller = use('controller') var tracy = use('tracy') var graphics = use('graphics') - -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" -} - -var config = use('config.js') -config.__proto__ = default_conf - -prosperon.camera = use('ext/camera').make() -prosperon.camera.size = [config.width,config.height] +var imgui = use('imgui') var base_pipeline = { vertex: "sprite.vert", @@ -97,6 +70,8 @@ sprite_pipeline.blend = { op_alpha: "add" }; +var context; + sprite_pipeline.target = { color_targets: [{ format:"rgba8", @@ -105,18 +80,6 @@ sprite_pipeline.target = { depth: "d32 float s8" }; -var appy = {}; -appy.inputs = {}; -if (os.platform() === "macos") { - appy.inputs["S-q"] = os.exit; -} - -appy.inputs["M-f4"] = os.exit; - -controller.player[0].control(appy); - -prosperon.window = prosperon.engine_start(config); - var driver = "vulkan" switch(os.platform()) { case "Linux": @@ -131,20 +94,6 @@ switch(os.platform()) { break } -render._main = prosperon.window.make_gpu(false,driver) -prosperon.gpu = render._main -render._main.window = prosperon.window -render._main.claim_window(prosperon.window) -render._main.set_swapchain('sdr', 'vsync') - -var whiteimage = {} -whiteimage.surface = graphics.make_surface([1,1]) -whiteimage.surface.rect({x:0,y:0,width:1,height:1}, [1,1,1,1]) -whiteimage.texture = render._main.load_texture(whiteimage.surface) - -var imgui = use('imgui') -if (imgui) imgui.init(render._main, prosperon.window) - var unit_transform = os.make_transform(); var cur = {}; @@ -153,8 +102,8 @@ cur.samplers = []; var tbuffer; function full_upload(buffers) { - var cmds = render._main.acquire_cmd_buffer(); - tbuffer = render._main.upload(cmds, buffers, tbuffer); + var cmds = context.acquire_cmd_buffer(); + tbuffer = context.upload(cmds, buffers, tbuffer); cmds.submit(); } @@ -284,7 +233,7 @@ function make_pipeline(pipeline) { // 1) Reflection data for vertex shader var refl = pipeline.vertex.reflection if (!refl || !refl.inputs || !Array.isArray(refl.inputs)) { - pipeline.gpu = render._main.make_pipeline(pipeline); + pipeline.gpu = context.make_pipeline(pipeline); return; } @@ -335,7 +284,7 @@ function make_pipeline(pipeline) { pipeline.vertex_buffer_descriptions = buffer_descriptions pipeline.vertex_attributes = attributes - pipeline.gpu = render._main.make_pipeline(pipeline); + pipeline.gpu = context.make_pipeline(pipeline); } make_pipeline[prosperon.DOC] = `Create and store a GPU pipeline object if it has not already been created. @@ -362,7 +311,7 @@ function make_shader(sh_file) { entrypoint: shader_type === "msl" ? "main0" : "main" } - shader.gpu = render._main.make_shader(shader) + shader.gpu = context.make_shader(shader) shader.reflection = refl; shader_cache[file] = shader shader.file = sh_file @@ -375,43 +324,6 @@ make_shader[prosperon.DOC] = `Load and compile a shader from disk, caching the r :return: A shader object with GPU and reflection data attached. ` -// helpful render devices. width and height in pixels; diagonal in inches. -render.device = { - pc: { width: 1920, height: 1080 }, - macbook_m2: { width: 2560, height: 1664, diagonal: 13.6 }, - ds_top: { width: 400, height: 240, diagonal: 3.53 }, - ds_bottom: { width: 320, height: 240, diagonal: 3.02 }, - playdate: { width: 400, height: 240, diagonal: 2.7 }, - switch: { width: 1280, height: 720, diagonal: 6.2 }, - switch_lite: { width: 1280, height: 720, diagonal: 5.5 }, - switch_oled: { width: 1280, height: 720, diagonal: 7 }, - dsi: { width: 256, height: 192, diagonal: 3.268 }, - ds: { width: 256, height: 192, diagonal: 3 }, - dsixl: { width: 256, height: 192, diagonal: 4.2 }, - ipad_air_m2: { width: 2360, height: 1640, diagonal: 11.97 }, - iphone_se: { width: 1334, height: 750, diagonal: 4.7 }, - iphone_12_pro: { width: 2532, height: 1170, diagonal: 6.06 }, - iphone_15: { width: 2556, height: 1179, diagonal: 6.1 }, - gba: { width: 240, height: 160, diagonal: 2.9 }, - gameboy: { width: 160, height: 144, diagonal: 2.48 }, - gbc: { width: 160, height: 144, diagonal: 2.28 }, - steamdeck: { width: 1280, height: 800, diagonal: 7 }, - vita: { width: 960, height: 544, diagonal: 5 }, - psp: { width: 480, height: 272, diagonal: 4.3 }, - imac_m3: { width: 4480, height: 2520, diagonal: 23.5 }, - macbook_pro_m3: { width: 3024, height: 1964, diagonal: 14.2 }, - ps1: { width: 320, height: 240, diagonal: 5 }, - ps2: { width: 640, height: 480 }, - snes: { width: 256, height: 224 }, - gamecube: { width: 640, height: 480 }, - n64: { width: 320, height: 240 }, - c64: { width: 320, height: 200 }, - macintosh: { width: 512, height: 342 }, - gamegear: { width: 160, height: 144, diagonal: 3.2 } -}; - -render.device.doc = `Device resolutions given as [x,y,inches diagonal].`; - var render_queue = []; var hud_queue = []; @@ -439,7 +351,7 @@ function upload_model(model) { if (typeof model[i] !== 'object') continue; bufs.push(model[i]); } - render._main.upload(this, bufs); + context.upload(this, bufs); } upload_model[prosperon.DOC] = `Upload all buffer-like properties of the given model to the GPU. @@ -555,7 +467,7 @@ function render_camera(cmds, camera) { main_color.height = main_depth.height = camera.size.y; camera.target = { color_targets: [{ - texture: render._main.texture(main_color), + texture: context.texture(main_color), mip_level:0, layer: 0, load:"clear", @@ -563,7 +475,7 @@ function render_camera(cmds, camera) { clear: cornflower }], depth_stencil: { - texture: render._main.texture(main_depth), + texture: context.texture(main_depth), clear:1, load:"dont_care", store:"dont_care", @@ -657,9 +569,9 @@ render_camera[prosperon.DOC] = `Render a scene using the provided camera, drawin ` var swaps = []; -function gpupresent() { +render.present = function() { os.clean_transforms(); - var cmds = render._main.acquire_cmd_buffer(); + var cmds = context.acquire_cmd_buffer(); render_camera(cmds, prosperon.camera); var swapchain_tex = cmds.acquire_swapchain(); if (!swapchain_tex) @@ -689,7 +601,7 @@ function gpupresent() { } } -gpupresent[prosperon.DOC] = `Perform the per-frame rendering and present the final swapchain image, including imgui pass if available. +render.present[prosperon.DOC] = `Perform the per-frame rendering and present the final swapchain image, including imgui pass if available. :return: None ` @@ -701,7 +613,7 @@ var stencil_write = { pass_op: "replace" }; -var stencil_writer = function stencil_writer(ref) { +function stencil_writer(ref) { var pipe = Object.create(base_pipeline); Object.assign(pipe, { stencil: { @@ -717,10 +629,8 @@ var stencil_writer = function stencil_writer(ref) { return pipe; }.hashify(); -render.stencil_writer = stencil_writer; - // objects by default draw where the stencil buffer is 0 -render.fillmask = function fillmask(ref) { +function fillmask(ref) { var pipe = stencil_writer(ref); render.use_shader('screenfill.cg', pipe); render.draw(shape.quad); @@ -739,7 +649,7 @@ var stencil_invert = { pass_op: "invert" }; -render.mask = function mask(image, pos, scale, rotation = 0, ref = 1) { +function mask(image, pos, scale, rotation = 0, ref = 1) { if (typeof image === 'string') image = graphics.texture(image); @@ -771,7 +681,7 @@ render.mask[prosperon.DOC] = `Draw an image to the stencil buffer, marking its a ` render.viewport = function(rect) { - render._main.viewport(rect); + context.viewport(rect); } render.viewport[prosperon.DOC] = `Set the GPU viewport to the specified rectangle. @@ -790,19 +700,7 @@ render.scissor[prosperon.DOC] = `Set the GPU scissor region to the specified rec :return: None ` -// Some initialization -shader_type = render._main.shader_format()[0]; - -std_sampler = render._main.make_sampler({ - min_filter: "nearest", - mag_filter: "nearest", - mipmap_mode: "nearest", - address_mode_u: "repeat", - address_mode_v: "repeat", - address_mode_w: "repeat" -}); - -render._main.present = gpupresent; +var std_sampler if (tracy) tracy.gpu_init() @@ -839,4 +737,54 @@ render.setup_hud[prosperon.DOC] = `Switch the current queue to the HUD render qu :return: None ` +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/scripts/modules/sdl_render.js b/scripts/modules/sdl_render.js index 850e4af2..817af4c9 100644 --- a/scripts/modules/sdl_render.js +++ b/scripts/modules/sdl_render.js @@ -1,546 +1,48 @@ var render = {} -var io = use('io') -var os = use('os') -var controller = use('controller') -var tracy = use('tracy') -var graphics = use('graphics') +var context -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" +render.initialize = function(config) +{ + prosperon.window = prosperon.engine_start(config) + context = prosperon.window.make_renderer() } -var config = use('config.js') -config.__proto__ = default_conf +// img here is the engine surface +render.load_texture(img) +{ + if (!img.surface) + throw new Error('Image must have a surface.') -prosperon.camera = use('ext/camera').make() -prosperon.camera.size = [config.width,config.height] - -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" -}; - -sprite_pipeline.target = { - color_targets: [{ - format:"rgba8", - blend:sprite_pipeline.blend - }], - depth: "d32 float s8" -}; - -prosperon.window = prosperon.engine_start(config); - -var driver = "vulkan" -switch(os.platform()) { - case "Linux": - driver = "vulkan" - break - case "Windows": - driver = "direct3d12" - break - case "macOS": - driver = "metal" - break -} - -render._main = prosperon.window.make_renderer(driver) -prosperon.gpu = render._main -render._main.window = prosperon.window - -//var imgui = use('imgui') -//if (imgui) imgui.init(render._main, prosperon.window) - -var imgui - -var unit_transform = os.make_transform(); - -var cornflower = [62/255,96/255,113/255,1]; - -var sprite_model_ubo = { - model: unit_transform, - color: [1,1,1,1] -}; - -// helpful render devices. width and height in pixels; diagonal in inches. -render.device = { - pc: { width: 1920, height: 1080 }, - macbook_m2: { width: 2560, height: 1664, diagonal: 13.6 }, - ds_top: { width: 400, height: 240, diagonal: 3.53 }, - ds_bottom: { width: 320, height: 240, diagonal: 3.02 }, - playdate: { width: 400, height: 240, diagonal: 2.7 }, - switch: { width: 1280, height: 720, diagonal: 6.2 }, - switch_lite: { width: 1280, height: 720, diagonal: 5.5 }, - switch_oled: { width: 1280, height: 720, diagonal: 7 }, - dsi: { width: 256, height: 192, diagonal: 3.268 }, - ds: { width: 256, height: 192, diagonal: 3 }, - dsixl: { width: 256, height: 192, diagonal: 4.2 }, - ipad_air_m2: { width: 2360, height: 1640, diagonal: 11.97 }, - iphone_se: { width: 1334, height: 750, diagonal: 4.7 }, - iphone_12_pro: { width: 2532, height: 1170, diagonal: 6.06 }, - iphone_15: { width: 2556, height: 1179, diagonal: 6.1 }, - gba: { width: 240, height: 160, diagonal: 2.9 }, - gameboy: { width: 160, height: 144, diagonal: 2.48 }, - gbc: { width: 160, height: 144, diagonal: 2.28 }, - steamdeck: { width: 1280, height: 800, diagonal: 7 }, - vita: { width: 960, height: 544, diagonal: 5 }, - psp: { width: 480, height: 272, diagonal: 4.3 }, - imac_m3: { width: 4480, height: 2520, diagonal: 23.5 }, - macbook_pro_m3: { width: 3024, height: 1964, diagonal: 14.2 }, - ps1: { width: 320, height: 240, diagonal: 5 }, - ps2: { width: 640, height: 480 }, - snes: { width: 256, height: 224 }, - gamecube: { width: 640, height: 480 }, - n64: { width: 320, height: 240 }, - c64: { width: 320, height: 200 }, - macintosh: { width: 512, height: 342 }, - gamegear: { width: 160, height: 144, diagonal: 3.2 } -}; - -render.device.doc = `Device resolutions given as [x,y,inches diagonal].`; - -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 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; - */ -} - -group_sprites_by_texture[prosperon.DOC] = `Assign each sprite to the provided mesh, generating index data as needed. - -:param sprites: An array of sprite objects. -:param mesh: A mesh object (pos, color, uv, indices, etc.) to link to each sprite. -:return: None -` - -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: render._main.texture(main_color), - mip_level:0, - layer: 0, - load:"clear", - store:"store", - clear: cornflower - }], - depth_stencil: { - texture: render._main.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 (typeof camslot !== 'undefined') - cmds.camera(camera, camslot); - - modelslot = get_pipeline_ubo_slot(pipeline, "model"); - if (typeof modelslot !== 'undefined') { - 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 (typeof camslot !== 'undefined') - 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}); - } + if (img.texture) + throw new Error('Image has already been uploaded to GPU.') - pass.draw_indexed(group.num_indices, 1, group.first_index, 0, 0); - } - cmds.pop_debug_group(); - - pass?.end(); - - render_queue = []; - hud_queue = []; + img.texture = context.load_texture(img.surface) } -render_camera[prosperon.DOC] = `Render a scene using the provided camera, drawing both render queue and HUD queue items. - -:param cmds: A command buffer obtained from the GPU context. -:param camera: The camera object (with size, optional target, etc.). -:return: None -` - -var swaps = []; -function gpupresent() { - os.clean_transforms(); - var cmds = render._main.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() - } -} - -gpupresent[prosperon.DOC] = `Perform the per-frame rendering and present the final swapchain image, including imgui pass if available. - -:return: None -` - -var stencil_write = { - compare: "always", - fail_op: "replace", - depth_fail_op: "replace", - pass_op: "replace" -}; - -var stencil_writer = 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(); - -render.stencil_writer = stencil_writer; - -// objects by default draw where the stencil buffer is 0 -render.fillmask = function fillmask(ref) { - var pipe = stencil_writer(ref); - render.use_shader('screenfill.cg', pipe); - render.draw(shape.quad); -} - -render.fillmask[prosperon.DOC] = `Draw a fullscreen shape using a 'screenfill' shader to populate the stencil buffer with a given reference. - -:param ref: The stencil reference value to write. -:return: None -` - -var stencil_invert = { - compare: "always", - fail_op: "invert", - depth_fail_op: "invert", - pass_op: "invert" -}; - -render.mask = 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] +render.queue = function(cmd) +{ + switch(cmd.type) { + case "geometry": + context.geometry(cmd.texture, cmd.mesh) + break; - var pipe = stencil_writer(ref); - render.use_shader('sprite.cg', pipe); - var t = os.make_transform(); - t.trs(pos, undefined, scale); - set_model(t); - render.use_mat({ - diffuse:image.texture, - rect: image.rect, - shade: Color.white - }); - render.draw(shape.quad); + case "hardware_line": + context.line(cmd.points, cmd.color) + break; + + case "hardware_point": + context.points(cmd.points, cmd.color) + break; + + case "scissor": + context.clip(cmd.rect) + break; + + case "viewport": + context.viewport(cmd.rect) + break; + } } -render.mask[prosperon.DOC] = `Draw an image to the stencil buffer, marking its area with a specified reference value. - -:param image: A texture or string path (which is converted to a texture). -:param pos: The translation (x, y) for the image placement. -:param scale: Optional scaling applied to the texture. -:param rotation: Optional rotation in radians (unused by default). -:param ref: The stencil reference value to write. -:return: None -` - -render.viewport = function(rect) { - render._main.viewport(rect); -} - -render.viewport[prosperon.DOC] = `Set the GPU viewport to the specified rectangle. - -:param rect: A rectangle [x, y, width, height]. -:return: None -` - -render.scissor = function(rect) { - render.viewport(rect) -} - -render.scissor[prosperon.DOC] = `Set the GPU scissor region to the specified rectangle (alias of render.viewport). - -:param rect: A rectangle [x, y, width, height]. -:return: None -` - -// Some initialization -shader_type = render._main.shader_format()[0]; - -std_sampler = render._main.make_sampler({ - min_filter: "nearest", - mag_filter: "nearest", - mipmap_mode: "nearest", - address_mode_u: "repeat", - address_mode_v: "repeat", - address_mode_w: "repeat" -}); - -render._main.present = gpupresent; - -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.queue[prosperon.DOC] = `Enqueue one or more draw commands. These commands are batched until render_camera is called. - -:param cmd: Either a single command object or an array of command objects. -:return: None -` - -render.setup_draw = function() { - current_queue = render_queue; - prosperon.draw(); -} - -render.setup_draw[prosperon.DOC] = `Switch the current queue to the primary scene render queue, then invoke 'prosperon.draw' if defined. - -:return: None -` - -render.setup_hud = function() { - current_queue = hud_queue; - prosperon.hud(); -} - -render.setup_hud[prosperon.DOC] = `Switch the current queue to the HUD render queue, then invoke 'prosperon.hud' if defined. - -:return: None -` - return render diff --git a/source/jsffi.c b/source/jsffi.c index 1cdcc2e7..a96911b7 100644 --- a/source/jsffi.c +++ b/source/jsffi.c @@ -3128,6 +3128,7 @@ JSC_CCALL(renderer_make_sprite_mesh, ) + static const JSCFunctionListEntry js_SDL_Renderer_funcs[] = { MIST_FUNC_DEF(SDL_Renderer, draw_color, 1), MIST_FUNC_DEF(SDL_Renderer, present, 0), @@ -4664,8 +4665,6 @@ static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = { MIST_FUNC_DEF(gpu, wait_for_fences, 2), MIST_FUNC_DEF(gpu, query_fence, 1), MIST_FUNC_DEF(gpu, shader_format, 0), - MIST_FUNC_DEF(gpu, slice9, 3), - MIST_FUNC_DEF(gpu, tile, 4), }; JSC_CCALL(renderpass_bind_pipeline, @@ -5994,6 +5993,8 @@ static const JSCFunctionListEntry js_geometry_funcs[] = { MIST_FUNC_DEF(geometry, rect_point_inside, 2), MIST_FUNC_DEF(geometry, rect_pos, 1), MIST_FUNC_DEF(geometry, rect_move, 2), + MIST_FUNC_DEF(gpu, tile, 4), + MIST_FUNC_DEF(gpu, slice9, 3), }; JSC_SCALL(os_env, diff --git a/source/prosperon.c b/source/prosperon.c index 7945a25d..a1917292 100644 --- a/source/prosperon.c +++ b/source/prosperon.c @@ -901,15 +901,6 @@ static int event2wota_count_props(const SDL_Event *event) break; case SDL_EVENT_MOUSE_MOTION: - count += 4; // window, which, state, pos, d_pos => actually 5 keys - // "pos" is 1 key, "d_pos" is another => total is 6 keys - // Let's break that down carefully: - // "window" - // "which" - // "state" - // "pos" - // "d_pos" - // That is 5 additional keys, so overall = 2 (base) + 5 = 7 count += 5; break; diff --git a/tests/dull.js b/tests/dull.js new file mode 100644 index 00000000..c6bc99ef --- /dev/null +++ b/tests/dull.js @@ -0,0 +1,17 @@ +var render = use('render') + +for (var i in render) console.log(i) + +function loop() +{ + prosperon.gpu.draw_color([1,1,1,1]) + prosperon.gpu.clear() + ren.draw_color([0,0,0,1]) + draw.rectangle({x:50,y:50,width:50,height:50}) + prosperon.gpu.present() + $_.delay(loop, 1/60) +} + +loop() + +$_.delay($_.stop, 3) diff --git a/tests/window.js b/tests/window.js index fc1b9634..e5a2631d 100644 --- a/tests/window.js +++ b/tests/window.js @@ -1,4 +1,4 @@ -var draw = use('draw2d') +//var draw = use('draw2d') prosperon.win = prosperon.engine_start({ title:`Prosperon [${prosperon.version}-${prosperon.revision}]`,