sdl 3 gpu
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.')
|
||||
|
||||
|
||||
@@ -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) {
|
||||
function get_img_gpu(img)
|
||||
{
|
||||
if (img.gpu) return 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
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
1363
source/qjs_sdl_gpu.c
1363
source/qjs_sdl_gpu.c
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user