sdl renderer
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled

'
This commit is contained in:
2025-04-13 21:32:34 -05:00
parent 19a8bd41a9
commit 566baa250c
12 changed files with 196 additions and 694 deletions

View File

@@ -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())

35
scripts/modules/device.js Normal file
View File

@@ -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 }
};

View File

@@ -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]).

View File

@@ -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
}

View File

@@ -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',

View File

@@ -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] = `

View File

@@ -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

View File

@@ -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]
if (img.texture)
throw new Error('Image has already been uploaded to GPU.')
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: {}
img.texture = context.load_texture(img.surface)
}
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"
};
render.queue = function(cmd)
{
switch(cmd.type) {
case "geometry":
context.geometry(cmd.texture, cmd.mesh)
break;
sprite_pipeline.target = {
color_targets: [{
format:"rgba8",
blend:sprite_pipeline.blend
}],
depth: "d32 float s8"
};
case "hardware_line":
context.line(cmd.points, cmd.color)
break;
prosperon.window = prosperon.engine_start(config);
case "hardware_point":
context.points(cmd.points, cmd.color)
break;
var driver = "vulkan"
switch(os.platform()) {
case "Linux":
driver = "vulkan"
break
case "Windows":
driver = "direct3d12"
break
case "macOS":
driver = "metal"
break
}
case "scissor":
context.clip(cmd.rect)
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});
}
pass.draw_indexed(group.num_indices, 1, group.first_index, 0, 0);
}
cmds.pop_debug_group();
pass?.end();
render_queue = [];
hud_queue = [];
}
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()
case "viewport":
context.viewport(cmd.rect)
break;
}
}
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]
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);
}
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

View File

@@ -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,

View File

@@ -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;

17
tests/dull.js Normal file
View File

@@ -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)

View File

@@ -1,4 +1,4 @@
var draw = use('draw2d')
//var draw = use('draw2d')
prosperon.win = prosperon.engine_start({
title:`Prosperon [${prosperon.version}-${prosperon.revision}]`,