2436 lines
81 KiB
C
2436 lines
81 KiB
C
#include "qjs_sdl_gpu.h"
|
|
#include "jsffi.h"
|
|
#include "qjs_macros.h"
|
|
|
|
#include <SDL3/SDL_gpu.h>
|
|
#include "render.h"
|
|
#include "cell.h"
|
|
#include "sprite.h"
|
|
|
|
#include "stb_dxt.h"
|
|
|
|
// Global GPU device and window
|
|
static SDL_GPUDevice *global_gpu;
|
|
static SDL_Window *global_window;
|
|
|
|
// GPU Free functions
|
|
void SDL_GPUDevice_free(JSRuntime *rt, SDL_GPUDevice *d)
|
|
{
|
|
SDL_DestroyGPUDevice(d);
|
|
}
|
|
|
|
void SDL_GPUCommandBuffer_free(JSRuntime *rt, SDL_GPUCommandBuffer *c)
|
|
{
|
|
}
|
|
|
|
void SDL_GPUComputePass_free(JSRuntime *rt, SDL_GPUComputePass *c) { }
|
|
void SDL_GPUCopyPass_free(JSRuntime *rt, SDL_GPUCopyPass *c) { }
|
|
void SDL_GPURenderPass_free(JSRuntime *rt, SDL_GPURenderPass *c) { }
|
|
|
|
|
|
// GPU Class definitions
|
|
#define GPURELEASECLASS(NAME) \
|
|
void SDL_GPU##NAME##_free(JSRuntime *rt, SDL_GPU##NAME *c) { \
|
|
SDL_ReleaseGPU##NAME(global_gpu, c); } \
|
|
QJSCLASS(SDL_GPU##NAME,) \
|
|
|
|
QJSCLASS(SDL_GPUDevice,)
|
|
|
|
GPURELEASECLASS(Buffer)
|
|
GPURELEASECLASS(ComputePipeline)
|
|
GPURELEASECLASS(GraphicsPipeline)
|
|
GPURELEASECLASS(Sampler)
|
|
GPURELEASECLASS(Shader)
|
|
GPURELEASECLASS(Texture)
|
|
GPURELEASECLASS(TransferBuffer)
|
|
GPURELEASECLASS(Fence)
|
|
|
|
QJSCLASS(SDL_GPUCommandBuffer,)
|
|
QJSCLASS(SDL_GPUComputePass,)
|
|
QJSCLASS(SDL_GPUCopyPass,)
|
|
QJSCLASS(SDL_GPURenderPass,)
|
|
|
|
SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v)
|
|
{
|
|
SDL_GPUGraphicsPipelineTargetInfo info = {0};
|
|
return info;
|
|
}
|
|
|
|
SDL_GPUSampleCount js2SDL_GPUSampleCount(JSContext *js, JSValue v)
|
|
{
|
|
int n = js2number(js,v);
|
|
switch(n) {
|
|
case 1: return SDL_GPU_SAMPLECOUNT_1;
|
|
case 2: return SDL_GPU_SAMPLECOUNT_2;
|
|
case 4: return SDL_GPU_SAMPLECOUNT_4;
|
|
case 8: return SDL_GPU_SAMPLECOUNT_8;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#define JS2ENUM(NAME, RETS, VALS) \
|
|
int js2##NAME(JSContext *js, JSValue v) { \
|
|
if (JS_IsNull(v)) return 0; \
|
|
const char *str = JS_ToCString(js, v); \
|
|
int *rets = (RETS); \
|
|
const char **vals = (VALS); \
|
|
/* Compute how many entries are in the arrays */ \
|
|
int n = (int)(sizeof((RETS)) / sizeof((RETS)[0])); \
|
|
for(int i = 0; i < n; i++) \
|
|
if(!strcmp(vals[i], str)) { \
|
|
JS_FreeCString(js, str); \
|
|
return rets[i]; \
|
|
} \
|
|
JS_FreeCString(js, str); \
|
|
return 0; \
|
|
}
|
|
|
|
static int rets_SDL_GPUSwapchainComposition[] = {
|
|
SDL_GPU_SWAPCHAINCOMPOSITION_SDR,
|
|
SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR,
|
|
SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR,
|
|
// SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084
|
|
};
|
|
|
|
static const char *vals_SDL_GPUSwapchainComposition[] = {
|
|
"sdr",
|
|
"linear",
|
|
"hdr",
|
|
// "hdr10"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUSwapchainComposition, rets_SDL_GPUSwapchainComposition, vals_SDL_GPUSwapchainComposition)
|
|
|
|
static int rets_SDL_FlipMode[] = {
|
|
SDL_FLIP_NONE,
|
|
SDL_FLIP_HORIZONTAL,
|
|
SDL_FLIP_VERTICAL
|
|
};
|
|
|
|
static const char *vals_SDL_FlipMode[] = {
|
|
"none",
|
|
"horizontal",
|
|
"vertical"
|
|
};
|
|
|
|
SDL_FColor js2SDL_FColor(JSContext *js, JSValue v)
|
|
{
|
|
colorf color = js2color(js,v);
|
|
return (SDL_FColor){color.r,color.g,color.b,color.a};
|
|
}
|
|
|
|
JS2ENUM(SDL_FlipMode, rets_SDL_FlipMode, vals_SDL_FlipMode)
|
|
|
|
/* -------------------------------------------------------
|
|
1) SDL_GPUBlendFactor
|
|
------------------------------------------------------- */
|
|
|
|
static int rets_SDL_GPUBlendFactor[] = {
|
|
SDL_GPU_BLENDFACTOR_INVALID,
|
|
SDL_GPU_BLENDFACTOR_ZERO,
|
|
SDL_GPU_BLENDFACTOR_ONE,
|
|
SDL_GPU_BLENDFACTOR_SRC_COLOR,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
|
|
SDL_GPU_BLENDFACTOR_DST_COLOR,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR,
|
|
SDL_GPU_BLENDFACTOR_SRC_ALPHA,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
|
|
SDL_GPU_BLENDFACTOR_DST_ALPHA,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA,
|
|
SDL_GPU_BLENDFACTOR_CONSTANT_COLOR,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR,
|
|
SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE
|
|
};
|
|
static const char *vals_SDL_GPUBlendFactor[] = {
|
|
"invalid",
|
|
"zero",
|
|
"one",
|
|
"src_color",
|
|
"one_minus_src_color",
|
|
"dst_color",
|
|
"one_minus_dst_color",
|
|
"src_alpha",
|
|
"one_minus_src_alpha",
|
|
"dst_alpha",
|
|
"one_minus_dst_alpha",
|
|
"constant_color",
|
|
"one_minus_constant_color",
|
|
"src_alpha_saturate"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUBlendFactor, rets_SDL_GPUBlendFactor, vals_SDL_GPUBlendFactor)
|
|
|
|
/* -------------------------------------------------------
|
|
2) SDL_GPUBlendOp
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUBlendOp[] = {
|
|
SDL_GPU_BLENDOP_INVALID,
|
|
SDL_GPU_BLENDOP_ADD,
|
|
SDL_GPU_BLENDOP_SUBTRACT,
|
|
SDL_GPU_BLENDOP_REVERSE_SUBTRACT,
|
|
SDL_GPU_BLENDOP_MIN,
|
|
SDL_GPU_BLENDOP_MAX
|
|
};
|
|
static const char *vals_SDL_GPUBlendOp[] = {
|
|
"invalid",
|
|
"add",
|
|
"subtract",
|
|
"reverse_subtract",
|
|
"min",
|
|
"max"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUBlendOp, rets_SDL_GPUBlendOp, vals_SDL_GPUBlendOp)
|
|
|
|
/* -------------------------------------------------------
|
|
3) SDL_GPUCompareOp
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUCompareOp[] = {
|
|
SDL_GPU_COMPAREOP_INVALID,
|
|
SDL_GPU_COMPAREOP_NEVER,
|
|
SDL_GPU_COMPAREOP_LESS,
|
|
SDL_GPU_COMPAREOP_EQUAL,
|
|
SDL_GPU_COMPAREOP_LESS_OR_EQUAL,
|
|
SDL_GPU_COMPAREOP_GREATER,
|
|
SDL_GPU_COMPAREOP_NOT_EQUAL,
|
|
SDL_GPU_COMPAREOP_GREATER_OR_EQUAL,
|
|
SDL_GPU_COMPAREOP_ALWAYS
|
|
};
|
|
static const char *vals_SDL_GPUCompareOp[] = {
|
|
"invalid",
|
|
"never",
|
|
"less",
|
|
"equal",
|
|
"less_or_equal",
|
|
"greater",
|
|
"not_equal",
|
|
"greater_or_equal",
|
|
"always"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUCompareOp, rets_SDL_GPUCompareOp, vals_SDL_GPUCompareOp)
|
|
|
|
/* -------------------------------------------------------
|
|
4) SDL_GPUCullMode
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUCullMode[] = {
|
|
SDL_GPU_CULLMODE_NONE,
|
|
SDL_GPU_CULLMODE_FRONT,
|
|
SDL_GPU_CULLMODE_BACK
|
|
};
|
|
static const char *vals_SDL_GPUCullMode[] = {
|
|
"none",
|
|
"front",
|
|
"back"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUCullMode, rets_SDL_GPUCullMode, vals_SDL_GPUCullMode)
|
|
|
|
/* -------------------------------------------------------
|
|
5) SDL_GPUFillMode
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUFillMode[] = {
|
|
SDL_GPU_FILLMODE_FILL,
|
|
SDL_GPU_FILLMODE_LINE
|
|
};
|
|
static const char *vals_SDL_GPUFillMode[] = {
|
|
"fill",
|
|
"line"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUFillMode, rets_SDL_GPUFillMode, vals_SDL_GPUFillMode)
|
|
|
|
/* -------------------------------------------------------
|
|
6) SDL_GPUFilter
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUFilter[] = {
|
|
SDL_GPU_FILTER_NEAREST,
|
|
SDL_GPU_FILTER_LINEAR
|
|
};
|
|
static const char *vals_SDL_GPUFilter[] = {
|
|
"nearest",
|
|
"linear"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUFilter, rets_SDL_GPUFilter, vals_SDL_GPUFilter)
|
|
|
|
/* -------------------------------------------------------
|
|
7) SDL_GPUFrontFace
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUFrontFace[] = {
|
|
SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE,
|
|
SDL_GPU_FRONTFACE_CLOCKWISE
|
|
};
|
|
static const char *vals_SDL_GPUFrontFace[] = {
|
|
"counter_clockwise",
|
|
"clockwise"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUFrontFace, rets_SDL_GPUFrontFace, vals_SDL_GPUFrontFace)
|
|
|
|
/* -------------------------------------------------------
|
|
8) SDL_GPULoadOp
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPULoadOp[] = {
|
|
SDL_GPU_LOADOP_LOAD,
|
|
SDL_GPU_LOADOP_CLEAR,
|
|
SDL_GPU_LOADOP_DONT_CARE
|
|
};
|
|
static const char *vals_SDL_GPULoadOp[] = {
|
|
"load",
|
|
"clear",
|
|
"dont_care"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPULoadOp, rets_SDL_GPULoadOp, vals_SDL_GPULoadOp)
|
|
|
|
/* -------------------------------------------------------
|
|
9) SDL_GPUPresentMode
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUPresentMode[] = {
|
|
SDL_GPU_PRESENTMODE_VSYNC,
|
|
SDL_GPU_PRESENTMODE_IMMEDIATE,
|
|
SDL_GPU_PRESENTMODE_MAILBOX
|
|
};
|
|
static const char *vals_SDL_GPUPresentMode[] = {
|
|
"vsync",
|
|
"immediate",
|
|
"mailbox"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUPresentMode, rets_SDL_GPUPresentMode, vals_SDL_GPUPresentMode)
|
|
|
|
/* -------------------------------------------------------
|
|
10) SDL_GPUPrimitiveType
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUPrimitiveType[] = {
|
|
SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
|
|
SDL_GPU_PRIMITIVETYPE_TRIANGLESTRIP,
|
|
SDL_GPU_PRIMITIVETYPE_LINELIST,
|
|
SDL_GPU_PRIMITIVETYPE_LINESTRIP,
|
|
SDL_GPU_PRIMITIVETYPE_POINTLIST
|
|
};
|
|
static const char *vals_SDL_GPUPrimitiveType[] = {
|
|
"triangle",
|
|
"trianglestrip",
|
|
"line",
|
|
"linestrip",
|
|
"point"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUPrimitiveType, rets_SDL_GPUPrimitiveType, vals_SDL_GPUPrimitiveType)
|
|
|
|
/* -------------------------------------------------------
|
|
11) SDL_GPUSamplerAddressMode
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUSamplerAddressMode[] = {
|
|
SDL_GPU_SAMPLERADDRESSMODE_REPEAT,
|
|
SDL_GPU_SAMPLERADDRESSMODE_MIRRORED_REPEAT,
|
|
SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE
|
|
};
|
|
static const char *vals_SDL_GPUSamplerAddressMode[] = {
|
|
"repeat",
|
|
"mirrored_repeat",
|
|
"clamp"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUSamplerAddressMode, rets_SDL_GPUSamplerAddressMode, vals_SDL_GPUSamplerAddressMode)
|
|
|
|
/* -------------------------------------------------------
|
|
12) SDL_GPUSamplerMipmapMode
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUSamplerMipmapMode[] = {
|
|
SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,
|
|
SDL_GPU_SAMPLERMIPMAPMODE_LINEAR
|
|
};
|
|
static const char *vals_SDL_GPUSamplerMipmapMode[] = {
|
|
"nearest",
|
|
"linear"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUSamplerMipmapMode, rets_SDL_GPUSamplerMipmapMode, vals_SDL_GPUSamplerMipmapMode)
|
|
|
|
/* -------------------------------------------------------
|
|
13) SDL_GPUStencilOp
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUStencilOp[] = {
|
|
SDL_GPU_STENCILOP_INVALID,
|
|
SDL_GPU_STENCILOP_KEEP,
|
|
SDL_GPU_STENCILOP_ZERO,
|
|
SDL_GPU_STENCILOP_REPLACE,
|
|
SDL_GPU_STENCILOP_INCREMENT_AND_CLAMP,
|
|
SDL_GPU_STENCILOP_DECREMENT_AND_CLAMP,
|
|
SDL_GPU_STENCILOP_INVERT,
|
|
SDL_GPU_STENCILOP_INCREMENT_AND_WRAP,
|
|
SDL_GPU_STENCILOP_DECREMENT_AND_WRAP
|
|
};
|
|
static const char *vals_SDL_GPUStencilOp[] = {
|
|
"invalid",
|
|
"keep",
|
|
"zero",
|
|
"replace",
|
|
"increment_and_clamp",
|
|
"decrement_and_clamp",
|
|
"invert",
|
|
"increment_and_wrap",
|
|
"decrement_and_wrap"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUStencilOp, rets_SDL_GPUStencilOp, vals_SDL_GPUStencilOp)
|
|
|
|
static int rets_SDL_GPUTextureType[] = {
|
|
SDL_GPU_TEXTURETYPE_2D, /**< The texture is a 2-dimensional image. */
|
|
SDL_GPU_TEXTURETYPE_2D_ARRAY, /**< The texture is a 2-dimensional array image. */
|
|
SDL_GPU_TEXTURETYPE_3D, /**< The texture is a 3-dimensional image. */
|
|
SDL_GPU_TEXTURETYPE_CUBE, /**< The texture is a cube image. */
|
|
SDL_GPU_TEXTURETYPE_CUBE_ARRAY /**< The texture is a cube array image. */
|
|
};
|
|
|
|
static const char *vals_SDL_GPUTextureType[] = {
|
|
"2d",
|
|
"2d array",
|
|
"3d",
|
|
"cube",
|
|
"cube array"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUTextureType, rets_SDL_GPUTextureType, vals_SDL_GPUTextureType)
|
|
|
|
/* -------------------------------------------------------
|
|
14) SDL_GPUStoreOp
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUStoreOp[] = {
|
|
SDL_GPU_STOREOP_STORE,
|
|
SDL_GPU_STOREOP_DONT_CARE,
|
|
SDL_GPU_STOREOP_RESOLVE,
|
|
SDL_GPU_STOREOP_RESOLVE_AND_STORE
|
|
};
|
|
|
|
static const char *vals_SDL_GPUStoreOp[] = {
|
|
"store",
|
|
"dont_care",
|
|
"resolve",
|
|
"resolve_and_store"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUStoreOp, rets_SDL_GPUStoreOp, vals_SDL_GPUStoreOp)
|
|
|
|
/* -------------------------------------------------------
|
|
X) SDL_GPUTextureType
|
|
------------------------------------------------------- */
|
|
static int rets_SDL_GPUTextureFormat[] = {
|
|
SDL_GPU_TEXTUREFORMAT_INVALID,
|
|
|
|
/* Unsigned Normalized Float Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_A8_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R8_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R8G8_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R16_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R10G10B10A2_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM,
|
|
/* Compressed Unsigned Normalized Float Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_BC4_R_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_BC5_RG_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM,
|
|
/* Compressed Signed Float Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_BC6H_RGB_FLOAT,
|
|
/* Compressed Unsigned Float Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_BC6H_RGB_UFLOAT,
|
|
/* Signed Normalized Float Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_R8_SNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R8G8_SNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_SNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R16_SNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16_SNORM,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_SNORM,
|
|
/* Signed Float Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_R16_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_R32_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_R32G32_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_R32G32B32A32_FLOAT,
|
|
/* Unsigned Float Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_R11G11B10_UFLOAT,
|
|
/* Unsigned Integer Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_R8_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_R8G8_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_R16_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_R32_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_R32G32_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_R32G32B32A32_UINT,
|
|
/* Signed Integer Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_R8_INT,
|
|
SDL_GPU_TEXTUREFORMAT_R8G8_INT,
|
|
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_INT,
|
|
SDL_GPU_TEXTUREFORMAT_R16_INT,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16_INT,
|
|
SDL_GPU_TEXTUREFORMAT_R16G16B16A16_INT,
|
|
SDL_GPU_TEXTUREFORMAT_R32_INT,
|
|
SDL_GPU_TEXTUREFORMAT_R32G32_INT,
|
|
SDL_GPU_TEXTUREFORMAT_R32G32B32A32_INT,
|
|
/* SRGB Unsigned Normalized Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM_SRGB,
|
|
/* Compressed SRGB Unsigned Normalized Color Formats */
|
|
SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_BC2_RGBA_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_BC7_RGBA_UNORM_SRGB,
|
|
/* Depth Formats */
|
|
SDL_GPU_TEXTUREFORMAT_D16_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_D24_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_D32_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT,
|
|
SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT,
|
|
/* Compressed ASTC Normalized Float Color Formats*/
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM,
|
|
/* Compressed SRGB ASTC Normalized Float Color Formats*/
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_4x4_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_5x4_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_5x5_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_6x5_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_6x6_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x5_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x6_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x8_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x5_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x6_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x8_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x10_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_12x10_UNORM_SRGB,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_12x12_UNORM_SRGB,
|
|
/* Compressed ASTC Signed Float Color Formats*/
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_4x4_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_5x4_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_5x5_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_6x5_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_6x6_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x5_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x6_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_8x8_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x5_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x6_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x8_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_10x10_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_12x10_FLOAT,
|
|
SDL_GPU_TEXTUREFORMAT_ASTC_12x12_FLOAT
|
|
};
|
|
|
|
static const char *vals_SDL_GPUTextureFormat[] = {
|
|
"invalid",
|
|
"a8",
|
|
"r8",
|
|
"rg8",
|
|
"rgba8",
|
|
"r16",
|
|
"rg16",
|
|
"rgba16",
|
|
"r10g10b10a2",
|
|
"b5g6r5",
|
|
"b5g5r5a1",
|
|
"b4g4r4a4",
|
|
"b8g8r8a8",
|
|
|
|
"bc1",
|
|
"bc2",
|
|
"bc3",
|
|
"bc4",
|
|
"bc5",
|
|
"bc7",
|
|
"bc6h float",
|
|
"bc6h ufloat",
|
|
|
|
"r8 snorm",
|
|
"rg8 snorm",
|
|
"rgba8 snorm",
|
|
"r16 snorm",
|
|
"rg16 snorm",
|
|
"rgba16 snorm",
|
|
|
|
"r16 float",
|
|
"rg16 float",
|
|
"rgba16 float",
|
|
"r32 float",
|
|
"rg32 float",
|
|
"rgba32 float",
|
|
|
|
"r11g11b10",
|
|
|
|
"r8 uint",
|
|
"rg8 uint",
|
|
"rgba8 uint",
|
|
"r16 uint",
|
|
"rg16 uint",
|
|
"rgba16 uint",
|
|
"r32 uint",
|
|
"rg32 uint",
|
|
"rgba32 uint",
|
|
|
|
"r8 int",
|
|
"rg8 int",
|
|
"rgba8 int",
|
|
"r16 int",
|
|
"rg16 int",
|
|
"rgba16 int",
|
|
"r32 int",
|
|
"rg32 int",
|
|
"rgba32 int",
|
|
|
|
"rgba8 srgb",
|
|
"b8g8r8a8 srgb",
|
|
"bc1 srgb",
|
|
"bc2 srgb",
|
|
"bc3 srgb",
|
|
"bc7 srgb",
|
|
|
|
"d16",
|
|
"d24",
|
|
"d32 float",
|
|
"d24 s8",
|
|
"d32 float s8",
|
|
|
|
"astc 4x4",
|
|
"astc 5x4",
|
|
"astc 5x5",
|
|
"astc 6x5",
|
|
"astc 6x6",
|
|
"astc 8x5",
|
|
"astc 8x6",
|
|
"astc 8x8",
|
|
"astc 10x5",
|
|
"astc 10x6",
|
|
"astc 10x8",
|
|
"astc 10x10",
|
|
"astc 12x10",
|
|
"astc 12x12",
|
|
|
|
"astc 4x4 srgb",
|
|
"astc 5x4 srgb",
|
|
"astc 5x5 srgb",
|
|
"astc 6x5 srgb",
|
|
"astc 6x6 srgb",
|
|
"astc 8x5 srgb",
|
|
"astc 8x6 srgb",
|
|
"astc 8x8 srgb",
|
|
"astc 10x5 srgb",
|
|
"astc 10x6 srgb",
|
|
"astc 10x8 srgb",
|
|
"astc 10x10 srgb",
|
|
"astc 12x10 srgb",
|
|
"astc 12x12 srgb",
|
|
|
|
"astc 4x4 float",
|
|
"astc 5x4 float",
|
|
"astc 5x5 float",
|
|
"astc 6x5 float",
|
|
"astc 6x6 float",
|
|
"astc 8x5 float",
|
|
"astc 8x6 float",
|
|
"astc 8x8 float",
|
|
"astc 10x5 float",
|
|
"astc 10x6 float",
|
|
"astc 10x8 float",
|
|
"astc 10x10 float",
|
|
"astc 12x10 float",
|
|
"astc 12x12 float"
|
|
};
|
|
|
|
SDL_GPUColorTargetInfo js2SDL_GPUColorTargetInfo(JSContext *js, JSValue v)
|
|
{
|
|
SDL_GPUColorTargetInfo info = {0};
|
|
JS_GETPROP(js, info.texture, v, texture, SDL_GPUTexture)
|
|
JS_GETPROP(js,info.mip_level,v,mip_level,number)
|
|
JS_GETPROP(js,info.layer_or_depth_plane, v, layer, number)
|
|
JS_GETPROP(js,info.load_op,v,load,SDL_GPULoadOp)
|
|
JS_GETPROP(js,info.store_op,v,store,SDL_GPUStoreOp)
|
|
JS_GETPROP(js,info.resolve_mip_level,v,resolve_mip_level,number)
|
|
JS_GETPROP(js,info.resolve_layer,v,resolve_layer,number)
|
|
JS_GETPROP(js,info.clear_color,v,clear,SDL_FColor)
|
|
return info;
|
|
}
|
|
|
|
SDL_GPUTextureSamplerBinding js2SDL_GPUTextureSamplerBinding(JSContext *js, JSValue v)
|
|
{
|
|
SDL_GPUTextureSamplerBinding b;
|
|
JS_GETPROP(js, b.texture, v, texture, SDL_GPUTexture)
|
|
JS_GETPROP(js, b.sampler, v, sampler, SDL_GPUSampler)
|
|
return b;
|
|
}
|
|
|
|
JS2ENUM(SDL_GPUTextureFormat, rets_SDL_GPUTextureFormat, vals_SDL_GPUTextureFormat)
|
|
|
|
SDL_GPUColorTargetBlendState js2SDL_GPUColorTargetBlendState(JSContext *js, JSValue v)
|
|
{
|
|
SDL_GPUColorTargetBlendState state = {0};
|
|
JS_GETPROP(js,state.src_color_blendfactor,v,src_rgb,SDL_GPUBlendFactor);
|
|
JS_GETPROP(js,state.dst_color_blendfactor,v,dst_rgb,SDL_GPUBlendFactor);
|
|
JS_GETPROP(js,state.src_alpha_blendfactor,v,src_alpha,SDL_GPUBlendFactor);
|
|
JS_GETPROP(js,state.dst_alpha_blendfactor,v,src_alpha,SDL_GPUBlendFactor);
|
|
JS_GETPROP(js,state.color_blend_op,v,op_rgb,SDL_GPUBlendOp)
|
|
JS_GETPROP(js,state.alpha_blend_op,v,op_alpha,SDL_GPUBlendOp)
|
|
JS_GETPROP(js,state.enable_blend,v,enabled,bool)
|
|
return state;
|
|
}
|
|
|
|
SDL_GPUShaderFormat js2SDL_GPUShaderFormat(JSContext *js, JSValue v)
|
|
{
|
|
const char *format_str = JS_ToCString(js, v);
|
|
SDL_GPUShaderFormat format = SDL_GPU_SHADERFORMAT_SPIRV; // default to SPIR-V
|
|
if (format_str) {
|
|
if (!strcmp(format_str, "private")) format = SDL_GPU_SHADERFORMAT_PRIVATE;
|
|
else if (!strcmp(format_str, "spv")) format = SDL_GPU_SHADERFORMAT_SPIRV;
|
|
else if (!strcmp(format_str, "dxbc")) format = SDL_GPU_SHADERFORMAT_DXBC;
|
|
else if (!strcmp(format_str, "dxil")) format = SDL_GPU_SHADERFORMAT_DXIL;
|
|
else if (!strcmp(format_str, "msl")) format = SDL_GPU_SHADERFORMAT_MSL;
|
|
else if (!strcmp(format_str, "metallib")) format = SDL_GPU_SHADERFORMAT_METALLIB;
|
|
JS_FreeCString(js, format_str);
|
|
}
|
|
return format;
|
|
}
|
|
|
|
SDL_GPUColorTargetDescription js2SDL_GPUColorTargetDescription(JSContext *js, JSValue v)
|
|
{
|
|
SDL_GPUColorTargetDescription dsc = {0};
|
|
JS_GETPROP(js,dsc.format,v,format,SDL_GPUTextureFormat)
|
|
JS_GETPROP(js,dsc.blend_state,v,blend,SDL_GPUColorTargetBlendState)
|
|
return dsc;
|
|
}
|
|
|
|
SDL_GPUStencilOpState js2SDL_GPUStencilOpState(JSContext *js, JSValue v)
|
|
{
|
|
SDL_GPUStencilOpState state;
|
|
memset(&state, 0, sizeof(state));
|
|
|
|
JSValue compare_val = JS_GetPropertyStr(js, v, "compare");
|
|
if(!JS_IsNull(compare_val)) state.compare_op = js2SDL_GPUCompareOp(js, compare_val);
|
|
JS_FreeValue(js, compare_val);
|
|
|
|
JSValue fail_val = JS_GetPropertyStr(js, v, "fail");
|
|
if(!JS_IsNull(fail_val)) state.fail_op = js2SDL_GPUStencilOp(js, fail_val);
|
|
JS_FreeValue(js, fail_val);
|
|
|
|
JSValue depth_fail_val = JS_GetPropertyStr(js, v, "depth_fail");
|
|
if(!JS_IsNull(depth_fail_val)) state.depth_fail_op = js2SDL_GPUStencilOp(js, depth_fail_val);
|
|
JS_FreeValue(js, depth_fail_val);
|
|
|
|
JSValue pass_val = JS_GetPropertyStr(js, v, "pass");
|
|
if(!JS_IsNull(pass_val)) state.pass_op = js2SDL_GPUStencilOp(js, pass_val);
|
|
JS_FreeValue(js, pass_val);
|
|
|
|
return state;
|
|
}
|
|
|
|
|
|
// Unpacks a typed array javascript object. If it has a gpu property, returns it, too. Otherwise, if requested, makes one.
|
|
void *gpu_buffer_unpack(JSContext *js, SDL_GPUDevice *device, JSValue buffer, size_t *size, void **send_data, SDL_GPUBuffer **send_gpu)
|
|
{
|
|
size_t o, len, bytes, msize;
|
|
JSValue buf = JS_GetTypedArrayBuffer(js, buffer, &o, &len, &bytes);
|
|
void *data = js_get_blob_data(js, &msize, buf);
|
|
JS_FreeValue(js,buf);
|
|
if (size) *size = msize;
|
|
if (send_gpu) {
|
|
JSValue gpu = JS_GetPropertyStr(js,buffer,"gpu");
|
|
*send_gpu = js2SDL_GPUBuffer(js,gpu);
|
|
if (!*send_gpu) {
|
|
JSValue idx = JS_GetPropertyStr(js,buffer, "index");
|
|
Uint32 usage = JS_ToBool(js,idx) ? SDL_GPU_BUFFERUSAGE_INDEX : SDL_GPU_BUFFERUSAGE_VERTEX;
|
|
JS_FreeValue(js,idx);
|
|
*send_gpu = SDL_CreateGPUBuffer(device, &(SDL_GPUBufferCreateInfo) { .usage=usage,.size=msize});
|
|
if (!*send_gpu) printf("COULDN'T MAKE GPU BUFFER: %s\n", SDL_GetError());
|
|
JS_SetPropertyStr(js, buffer, "gpu", SDL_GPUBuffer2js(js,*send_gpu));
|
|
}
|
|
JS_FreeValue(js,gpu);
|
|
}
|
|
|
|
if (send_data)
|
|
*send_data = data;
|
|
return data;
|
|
}
|
|
|
|
|
|
// GPU buffer management functions
|
|
void free_gpu_buffer(JSRuntime *rt, void *opaque, void *ptr)
|
|
{
|
|
TracyCFree(ptr);
|
|
}
|
|
|
|
// GPU API
|
|
JSC_CCALL(gpu_claim_window,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_Window *win = js2SDL_Window(js, argv[0]);
|
|
SDL_ClaimWindowForGPUDevice(gpu,win);
|
|
)
|
|
|
|
JSC_CCALL(gpu_set_swapchain,
|
|
if (!SDL_SetGPUSwapchainParameters(global_gpu,global_window, js2SDL_GPUSwapchainComposition(js,argv[0]), js2SDL_GPUPresentMode(js,argv[1])))
|
|
return JS_ThrowReferenceError(js, "Could not set: %s\n", SDL_GetError());
|
|
)
|
|
|
|
JSC_CCALL(cmd_acquire_swapchain,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
Uint32 w,h;
|
|
SDL_GPUTexture *texture;
|
|
SDL_AcquireGPUSwapchainTexture(cmds,global_window, &texture, &w, &h);
|
|
if (!texture) return JS_NULL;
|
|
JSValue swap = JS_NULL;
|
|
|
|
JSValue *js_swapchains = ((cell_rt*)JS_GetContextOpaque(js))->js_swapchains;
|
|
|
|
for (int i = 0; i < arrlen(js_swapchains); i++) {
|
|
if (js2SDL_GPUTexture(js,js_swapchains[i]) == texture) {
|
|
swap = js_swapchains[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (JS_IsNull(swap)) {
|
|
swap = SDL_GPUTexture2js(js,texture);
|
|
arrput(js_swapchains,swap);
|
|
}
|
|
|
|
JS_SetPropertyStr(js,swap,"width", number2js(js,w));
|
|
JS_SetPropertyStr(js,swap,"height", number2js(js,h));
|
|
return JS_DupValue(js,swap);
|
|
)
|
|
|
|
JSC_CCALL(cmd_swapchain_pass,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
SDL_GPUColorTargetInfo info = {0};
|
|
Uint32 w, h;
|
|
SDL_AcquireGPUSwapchainTexture(cmds, global_window, &info.texture, &w,&h);
|
|
info.load_op = SDL_GPU_LOADOP_CLEAR;
|
|
info.store_op = SDL_GPU_STOREOP_STORE;
|
|
colorf c = js2color(js,argv[0]);
|
|
info.clear_color = (SDL_FColor){c.r,c.g,c.b,c.a};
|
|
SDL_GPURenderPass *pass = SDL_BeginGPURenderPass(
|
|
cmds,
|
|
&info,
|
|
1,
|
|
NULL
|
|
);
|
|
if (!pass) return JS_ThrowReferenceError(js, "Unable to create swapchain pass: %s", SDL_GetError());
|
|
JSValue jspass = SDL_GPURenderPass2js(js,pass);
|
|
JS_SetPropertyStr(js,jspass,"size", vec22js(js,(HMM_Vec2){w,h}));
|
|
return jspass;
|
|
)
|
|
|
|
JSC_CCALL(gpu_load_texture,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
|
|
SDL_Surface *surf = js2SDL_Surface(js, argv[0]);
|
|
if (!surf) return JS_ThrowReferenceError(js, "Surface was not a surface.");
|
|
|
|
int compression_level = js2number(js,argv[1]);
|
|
if (compression_level < 0) compression_level = 0;
|
|
if (compression_level > 2) compression_level = 2;
|
|
|
|
int dofree = 0;
|
|
|
|
SDL_PixelFormat sfmt = surf->format;
|
|
const SDL_PixelFormatDetails *pdetails = SDL_GetPixelFormatDetails(sfmt);
|
|
|
|
if (!pdetails) {
|
|
// If we can't get pixel format details, fall back to converting to RGBA8888
|
|
surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA8888);
|
|
dofree = 1;
|
|
sfmt = SDL_PIXELFORMAT_RGBA8888;
|
|
pdetails = SDL_GetPixelFormatDetails(sfmt);
|
|
if (!pdetails) {
|
|
// Should never happen with RGBA8888, but just in case
|
|
return JS_ThrowReferenceError(js, "Unable to get pixel format details.");
|
|
}
|
|
}
|
|
|
|
// Check if format has alpha
|
|
bool has_alpha = (pdetails->Amask != 0);
|
|
|
|
// Choose a GPU format that closely matches the surface's format
|
|
SDL_GPUTextureFormat chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
|
|
|
switch (sfmt) {
|
|
case SDL_PIXELFORMAT_RGBA32:
|
|
case SDL_PIXELFORMAT_RGBX8888:
|
|
case SDL_PIXELFORMAT_XRGB8888:
|
|
case SDL_PIXELFORMAT_XBGR8888:
|
|
chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
|
break;
|
|
|
|
case SDL_PIXELFORMAT_RGBA4444:
|
|
case SDL_PIXELFORMAT_ABGR4444:
|
|
case SDL_PIXELFORMAT_ARGB4444:
|
|
case SDL_PIXELFORMAT_BGRA4444:
|
|
// 4-4-4-4 format
|
|
chosen_format = SDL_GPU_TEXTUREFORMAT_B4G4R4A4_UNORM;
|
|
break;
|
|
|
|
case SDL_PIXELFORMAT_RGB565:
|
|
case SDL_PIXELFORMAT_BGR565:
|
|
// 5-6-5 format
|
|
chosen_format = SDL_GPU_TEXTUREFORMAT_B5G6R5_UNORM;
|
|
break;
|
|
|
|
case SDL_PIXELFORMAT_RGBA5551:
|
|
case SDL_PIXELFORMAT_ARGB1555:
|
|
case SDL_PIXELFORMAT_BGRA5551:
|
|
case SDL_PIXELFORMAT_ABGR1555:
|
|
// 5-5-5-1 format
|
|
chosen_format = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM;
|
|
break;
|
|
|
|
default:
|
|
surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32);
|
|
dofree = 1;
|
|
sfmt = SDL_PIXELFORMAT_RGBA32;
|
|
pdetails = SDL_GetPixelFormatDetails(sfmt);
|
|
chosen_format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
|
has_alpha = true;
|
|
break;
|
|
}
|
|
|
|
// If compression_level > 0, we override format with BC1/BC3
|
|
bool compress = (compression_level > 0);
|
|
int stb_mode = STB_DXT_NORMAL;
|
|
SDL_GPUTextureFormat compressed_format = chosen_format;
|
|
|
|
if (compress) {
|
|
if (has_alpha)
|
|
compressed_format = SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM; // DXT5
|
|
else
|
|
compressed_format = SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM; // DXT1
|
|
|
|
if (compression_level > 1) stb_mode = STB_DXT_HIGHQUAL;
|
|
}
|
|
|
|
SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu, &(SDL_GPUTextureCreateInfo) {
|
|
.type = SDL_GPU_TEXTURETYPE_2D,
|
|
.format = compress ? compressed_format : chosen_format,
|
|
.width = surf->w,
|
|
.height = surf->h,
|
|
.layer_count_or_depth = 1,
|
|
.num_levels = 1,
|
|
.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER
|
|
});
|
|
|
|
const void *pixel_data = surf->pixels;
|
|
size_t pixel_data_size = surf->pitch * surf->h;
|
|
unsigned char *upload_data = NULL;
|
|
size_t upload_size = pixel_data_size;
|
|
|
|
if (compress) {
|
|
// Compress with stb_dxt
|
|
int block_width = (surf->w + 3) / 4;
|
|
int block_height = (surf->h + 3) / 4;
|
|
int block_size = (compressed_format == SDL_GPU_TEXTUREFORMAT_BC1_RGBA_UNORM) ? 8 : 16;
|
|
int blocks_count = block_width * block_height;
|
|
|
|
unsigned char *compressed_data = (unsigned char*)malloc(blocks_count * block_size);
|
|
if (!compressed_data) {
|
|
if (dofree) SDL_DestroySurface(surf);
|
|
return JS_ThrowOutOfMemory(js);
|
|
}
|
|
|
|
const unsigned char *src_pixels = (const unsigned char *)pixel_data;
|
|
for (int by = 0; by < block_height; ++by) {
|
|
for (int bx = 0; bx < block_width; ++bx) {
|
|
unsigned char block_rgba[4*4*4];
|
|
memset(block_rgba, 0, sizeof(block_rgba));
|
|
|
|
for (int y = 0; y < 4; ++y) {
|
|
for (int x = 0; x < 4; ++x) {
|
|
int sx = bx*4 + x;
|
|
int sy = by*4 + y;
|
|
if (sx < surf->w && sy < surf->h) {
|
|
const unsigned char *pixel = src_pixels + sy * surf->pitch + sx * pdetails->bytes_per_pixel;
|
|
Uint32 pixelValue = 0;
|
|
// Copy pixel data into a Uint32 for SDL_GetRGBA-like extraction
|
|
// We'll use masks/shifts from pdetails
|
|
memcpy(&pixelValue, pixel, pdetails->bytes_per_pixel);
|
|
|
|
// Extract RGBA
|
|
unsigned char r = (pixelValue & pdetails->Rmask) >> pdetails->Rshift;
|
|
unsigned char g = (pixelValue & pdetails->Gmask) >> pdetails->Gshift;
|
|
unsigned char b = (pixelValue & pdetails->Bmask) >> pdetails->Bshift;
|
|
unsigned char a = pdetails->Amask ? ((pixelValue & pdetails->Amask) >> pdetails->Ashift) : 255;
|
|
|
|
// If bits are not fully 8-bit, scale them:
|
|
// pdetails->Rbits (etc.) give how many bits each channel uses
|
|
int Rmax = (1 << pdetails->Rbits) - 1;
|
|
int Gmax = (1 << pdetails->Gbits) - 1;
|
|
int Bmax = (1 << pdetails->Bbits) - 1;
|
|
int Amax = pdetails->Abits ? ((1 << pdetails->Abits) - 1) : 255;
|
|
|
|
if (pdetails->Rbits < 8) r = (r * 255) / Rmax;
|
|
if (pdetails->Gbits < 8) g = (g * 255) / Gmax;
|
|
if (pdetails->Bbits < 8) b = (b * 255) / Bmax;
|
|
if (pdetails->Amask && pdetails->Abits < 8) a = (a * 255) / Amax;
|
|
|
|
// Set block pixel
|
|
block_rgba[(y*4+x)*4+0] = r;
|
|
block_rgba[(y*4+x)*4+1] = g;
|
|
block_rgba[(y*4+x)*4+2] = b;
|
|
block_rgba[(y*4+x)*4+3] = a;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned char *dest_block = compressed_data + (by * block_width + bx)*block_size;
|
|
int alpha = (compressed_format == SDL_GPU_TEXTUREFORMAT_BC3_RGBA_UNORM) ? 1 : 0;
|
|
stb_compress_dxt_block(dest_block, block_rgba, alpha, stb_mode);
|
|
}
|
|
}
|
|
|
|
upload_data = compressed_data;
|
|
upload_size = blocks_count * block_size;
|
|
} else {
|
|
// No compression, upload directly
|
|
upload_data = (unsigned char*)pixel_data;
|
|
upload_size = pixel_data_size;
|
|
}
|
|
|
|
SDL_GPUTransferBuffer *tex_buffer = SDL_CreateGPUTransferBuffer(
|
|
gpu,
|
|
&(SDL_GPUTransferBufferCreateInfo) {
|
|
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
|
|
.size = upload_size
|
|
});
|
|
void *tex_ptr = SDL_MapGPUTransferBuffer(gpu, tex_buffer, false);
|
|
memcpy(tex_ptr, upload_data, upload_size);
|
|
SDL_UnmapGPUTransferBuffer(gpu, tex_buffer);
|
|
|
|
SDL_GPUCommandBuffer *uploadcmd = SDL_AcquireGPUCommandBuffer(gpu);
|
|
SDL_GPUCopyPass *copypass = SDL_BeginGPUCopyPass(uploadcmd);
|
|
SDL_UploadToGPUTexture(
|
|
copypass,
|
|
&(SDL_GPUTextureTransferInfo) {
|
|
.transfer_buffer = tex_buffer,
|
|
.offset = 0
|
|
},
|
|
&(SDL_GPUTextureRegion) {
|
|
.texture = tex,
|
|
.w = surf->w,
|
|
.h = surf->h,
|
|
.d = 1
|
|
},
|
|
false
|
|
);
|
|
|
|
SDL_EndGPUCopyPass(copypass);
|
|
SDL_SubmitGPUCommandBuffer(uploadcmd);
|
|
SDL_ReleaseGPUTransferBuffer(gpu, tex_buffer);
|
|
|
|
if (compress) {
|
|
free(upload_data);
|
|
}
|
|
|
|
ret = SDL_GPUTexture2js(js, tex);
|
|
JS_SetPropertyStr(js, ret, "width", number2js(js, surf->w));
|
|
JS_SetPropertyStr(js, ret, "height", number2js(js, surf->h));
|
|
JS_SetPropertyStr(js,ret,"dim", vec22js(js,(HMM_Vec2){surf->w,surf->h}));
|
|
|
|
if (dofree) SDL_DestroySurface(surf);
|
|
|
|
)
|
|
|
|
static JSValue js_gpu_make_pipeline(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
if (argc < 1)
|
|
return JS_ThrowTypeError(js, "gpu_pipeline requires a pipeline object");
|
|
|
|
JSValue pipe = argv[0];
|
|
if (!JS_IsObject(pipe))
|
|
return JS_ThrowTypeError(js, "gpu_pipeline argument must be an object");
|
|
|
|
SDL_GPUGraphicsPipelineCreateInfo info = {0};
|
|
|
|
// ---------------------------------------------------
|
|
// 2. Retrieve vertex buffer descriptions array
|
|
// ---------------------------------------------------
|
|
JSValue vbd_val = JS_GetPropertyStr(js, pipe, "vertex_buffer_descriptions");
|
|
Uint32 vbd_len = JS_ArrayLength(js,vbd_val);
|
|
SDL_GPUVertexBufferDescription vbd[vbd_len];
|
|
for (Uint32 i = 0; i < vbd_len; i++) {
|
|
JSValue elem = JS_GetPropertyUint32(js, vbd_val, i);
|
|
if (JS_IsObject(elem)) {
|
|
JSValue slot_val = JS_GetPropertyStr(js, elem, "slot");
|
|
JSValue pitch_val = JS_GetPropertyStr(js, elem, "pitch");
|
|
JSValue rate_val = JS_GetPropertyStr(js, elem, "input_rate");
|
|
JSValue step_val = JS_GetPropertyStr(js, elem, "instance_step_rate");
|
|
|
|
// Slot
|
|
Uint32 slot = 0;
|
|
JS_ToUint32(js, &slot, slot_val);
|
|
JS_FreeValue(js, slot_val);
|
|
|
|
// Pitch
|
|
Uint32 pitch = 0;
|
|
JS_ToUint32(js, &pitch, pitch_val);
|
|
JS_FreeValue(js, pitch_val);
|
|
|
|
// Input Rate
|
|
const char *rate_str = JS_ToCString(js, rate_val);
|
|
SDL_GPUVertexInputRate input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
|
|
if (rate_str) {
|
|
if (!strcmp(rate_str, "vertex")) input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX;
|
|
else if (!strcmp(rate_str, "instance")) input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE;
|
|
JS_FreeCString(js, rate_str);
|
|
}
|
|
JS_FreeValue(js, rate_val);
|
|
|
|
// Instance Step Rate
|
|
Uint32 step_rate = 0;
|
|
JS_ToUint32(js, &step_rate, step_val);
|
|
JS_FreeValue(js, step_val);
|
|
|
|
vbd[i].slot = slot;
|
|
vbd[i].pitch = pitch;
|
|
vbd[i].input_rate = input_rate;
|
|
vbd[i].instance_step_rate = step_rate;
|
|
}
|
|
JS_FreeValue(js, elem);
|
|
}
|
|
JS_FreeValue(js, vbd_val);
|
|
|
|
// ---------------------------------------------------
|
|
// 3. Retrieve vertex attributes array
|
|
// ---------------------------------------------------
|
|
JSValue vat_val = JS_GetPropertyStr(js, pipe, "vertex_attributes");
|
|
Uint32 vat_len = JS_ArrayLength(js,vat_val);
|
|
|
|
SDL_GPUVertexAttribute vat[vat_len];
|
|
for (Uint32 i = 0; i < vat_len; i++) {
|
|
JSValue elem = JS_GetPropertyUint32(js, vat_val, i);
|
|
if (JS_IsObject(elem)) {
|
|
JSValue loc_val = JS_GetPropertyStr(js, elem, "location");
|
|
JSValue slot_val = JS_GetPropertyStr(js, elem, "buffer_slot");
|
|
JSValue fmt_val = JS_GetPropertyStr(js, elem, "format");
|
|
JSValue off_val = JS_GetPropertyStr(js, elem, "offset");
|
|
|
|
// location
|
|
Uint32 location = 0;
|
|
JS_ToUint32(js, &location, loc_val);
|
|
JS_FreeValue(js, loc_val);
|
|
|
|
// buffer_slot
|
|
Uint32 buffer_slot = 0;
|
|
JS_ToUint32(js, &buffer_slot, slot_val);
|
|
JS_FreeValue(js, slot_val);
|
|
|
|
// format
|
|
const char *fmt_str = JS_ToCString(js, fmt_val);
|
|
SDL_GPUVertexElementFormat format = -1; // TODO: Check for error (if this is still -1)
|
|
if (fmt_str) {
|
|
// Map from string to SDL_GPUVertexElementFormat
|
|
if (!strcmp(fmt_str, "float2")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
|
|
else if (!strcmp(fmt_str, "float3")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3;
|
|
else if (!strcmp(fmt_str, "float4")) format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4;
|
|
else if (!strcmp(fmt_str, "color"))
|
|
format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4;
|
|
JS_FreeCString(js, fmt_str);
|
|
}
|
|
JS_FreeValue(js, fmt_val);
|
|
|
|
// offset
|
|
Uint32 offset = 0;
|
|
JS_ToUint32(js, &offset, off_val);
|
|
JS_FreeValue(js, off_val);
|
|
|
|
vat[i].location = location;
|
|
vat[i].buffer_slot = buffer_slot;
|
|
vat[i].format = format;
|
|
vat[i].offset = offset;
|
|
}
|
|
JS_FreeValue(js, elem);
|
|
}
|
|
JS_FreeValue(js, vat_val);
|
|
|
|
info.vertex_input_state = (SDL_GPUVertexInputState){
|
|
.vertex_buffer_descriptions = vbd,
|
|
.num_vertex_buffers = vbd_len,
|
|
.vertex_attributes = vat,
|
|
.num_vertex_attributes = vat_len
|
|
};
|
|
|
|
// Vertex Shader
|
|
JSValue vertex_val = JS_GetPropertyStr(js, pipe, "vertex");
|
|
JSValue fragment_val = JS_GetPropertyStr(js,pipe,"fragment");
|
|
JS_GETPROP(js,info.vertex_shader, vertex_val, gpu, SDL_GPUShader)
|
|
JS_GETPROP(js, info.fragment_shader, fragment_val, gpu, SDL_GPUShader)
|
|
|
|
JS_FreeValue(js, vertex_val);
|
|
JS_FreeValue(js, fragment_val);
|
|
|
|
// Primitive Type
|
|
JS_GETPROP(js, info.primitive_type, pipe, primitive, SDL_GPUPrimitiveType)
|
|
|
|
// Rasterizer State
|
|
JS_GETPROP(js, info.rasterizer_state.fill_mode, pipe, fill, SDL_GPUFillMode)
|
|
JS_GETPROP(js, info.rasterizer_state.cull_mode, pipe, cull, SDL_GPUCullMode)
|
|
JS_GETPROP(js, info.rasterizer_state.front_face, pipe, face, SDL_GPUFrontFace)
|
|
|
|
// Depth Stencil State
|
|
JSValue depth_val = JS_GetPropertyStr(js, pipe, "depth");
|
|
if (JS_IsObject(depth_val)) {
|
|
JS_GETPROP(js,info.depth_stencil_state.compare_op, depth_val, compare, SDL_GPUCompareOp)
|
|
JS_GETPROP(js,info.depth_stencil_state.enable_depth_test, depth_val, test, bool)
|
|
JS_GETPROP(js,info.depth_stencil_state.enable_depth_write, depth_val, write, bool)
|
|
JS_GETPROP(js,info.rasterizer_state.depth_bias_constant_factor, depth_val, bias, number)
|
|
JS_GETPROP(js,info.rasterizer_state.depth_bias_slope_factor, depth_val,bias_slope_scale, number)
|
|
JS_GETPROP(js,info.rasterizer_state.depth_bias_clamp, depth_val,bias_clamp_val, number)
|
|
}
|
|
JS_FreeValue(js, depth_val);
|
|
|
|
// Stencil State
|
|
JSValue stencil_val = JS_GetPropertyStr(js, pipe, "stencil");
|
|
if (JS_IsObject(stencil_val)) {
|
|
JS_GETPROP(js,info.depth_stencil_state.enable_stencil_test, stencil_val, enabled, bool)
|
|
if (info.depth_stencil_state.enable_stencil_test) {
|
|
JS_GETPROP(js, info.depth_stencil_state.front_stencil_state, stencil_val, front, SDL_GPUStencilOpState)
|
|
JS_GETPROP(js, info.depth_stencil_state.back_stencil_state, stencil_val, back, SDL_GPUStencilOpState)
|
|
|
|
JSValue compare_mask_val = JS_GetPropertyStr(js, stencil_val, "compare_mask");
|
|
uint32_t tmp;
|
|
JS_ToUint32(js, &tmp, compare_mask_val);
|
|
info.depth_stencil_state.compare_mask = tmp;
|
|
JS_FreeValue(js, compare_mask_val);
|
|
|
|
// Write Mask
|
|
JSValue write_mask_val = JS_GetPropertyStr(js, stencil_val, "write_mask");
|
|
JS_ToUint32(js, &tmp, write_mask_val);
|
|
info.depth_stencil_state.write_mask = tmp;
|
|
}
|
|
}
|
|
JS_FreeValue(js, stencil_val);
|
|
|
|
JSValue js_tar = JS_GetPropertyStr(js,pipe,"target");
|
|
|
|
SDL_GPUGraphicsPipelineTargetInfo target_info = {0};
|
|
JSValue color_tars = JS_GetPropertyStr(js,js_tar,"color_targets");
|
|
target_info.num_color_targets = JS_ArrayLength(js,color_tars);
|
|
SDL_GPUColorTargetDescription dsc[target_info.num_color_targets];
|
|
target_info.color_target_descriptions = dsc;
|
|
|
|
for (int i = 0; i < target_info.num_color_targets; i++) {
|
|
JSValue c = JS_GetPropertyUint32(js,color_tars,i);
|
|
dsc[i] = js2SDL_GPUColorTargetDescription(js,c);
|
|
JS_FreeValue(js,c);
|
|
}
|
|
JS_FreeValue(js,color_tars);
|
|
|
|
JS_GETPROP(js,target_info.depth_stencil_format,js_tar,depth,SDL_GPUTextureFormat);
|
|
if (target_info.depth_stencil_format) target_info.has_depth_stencil_target = 1;
|
|
|
|
info.target_info = target_info;
|
|
|
|
JS_FreeValue(js, js_tar);
|
|
|
|
// Create the pipeline
|
|
SDL_GPUGraphicsPipeline *pipeline = SDL_CreateGPUGraphicsPipeline(gpu, &info);
|
|
if (!pipeline) return JS_ThrowInternalError(js, "Failed to create GPU pipeline");
|
|
|
|
return SDL_GPUGraphicsPipeline2js(js, pipeline);
|
|
}
|
|
|
|
// Now the function to create the sampler
|
|
static JSValue js_gpu_make_sampler(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_GPUSamplerCreateInfo info = {0};
|
|
JSValue sampler = argv[0];
|
|
|
|
JS_GETPROP(js,info.min_filter,sampler,min_filter,SDL_GPUFilter)
|
|
JS_GETPROP(js,info.mag_filter,sampler,mag_filter,SDL_GPUFilter)
|
|
JS_GETPROP(js,info.mipmap_mode, sampler, mipmap, SDL_GPUSamplerMipmapMode)
|
|
JS_GETPROP(js,info.address_mode_u, sampler, u, SDL_GPUSamplerAddressMode)
|
|
JS_GETPROP(js,info.address_mode_v, sampler, v, SDL_GPUSamplerAddressMode)
|
|
JS_GETPROP(js,info.address_mode_w, sampler, w, SDL_GPUSamplerAddressMode)
|
|
JS_GETPROP(js,info.mip_lod_bias, sampler, mip_bias, number)
|
|
JS_GETPROP(js,info.max_anisotropy, sampler, max_anisotropy, number)
|
|
JS_GETPROP(js,info.compare_op,sampler,compare_op,SDL_GPUCompareOp)
|
|
JS_GETPROP(js,info.min_lod,sampler,min_lod,number)
|
|
JS_GETPROP(js,info.max_lod,sampler,max_lod,number)
|
|
JS_GETPROP(js, info.enable_anisotropy, sampler, anistropy, bool)
|
|
JS_GETPROP(js, info.enable_compare, sampler, compare, bool)
|
|
|
|
// Create the sampler
|
|
SDL_GPUSampler *sdl_sampler = SDL_CreateGPUSampler(gpu, &info);
|
|
if (!sdl_sampler) return JS_ThrowInternalError(js, "Failed to create GPU sampler");
|
|
|
|
return SDL_GPUSampler2js(js, sdl_sampler); // You need to implement SDL_GPUSampler2js similar to pipeline
|
|
}
|
|
|
|
|
|
JSC_CCALL(gpu_texture,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_GPUTextureCreateInfo info = {0};
|
|
JSValue fmt = argv[0];
|
|
JS_GETPROP(js, info.width, fmt, width, number)
|
|
JS_GETPROP(js, info.height, fmt, height, number)
|
|
JS_GETPROP(js, info.layer_count_or_depth, fmt, layers, number)
|
|
JS_GETPROP(js, info.num_levels, fmt, mip_levels, number)
|
|
JS_GETPROP(js, info.sample_count, fmt, samples, number)
|
|
JS_GETPROP(js, info.type, fmt, type, SDL_GPUTextureType)
|
|
JS_GETPROP(js, info.format, fmt, format, SDL_GPUTextureFormat)
|
|
if (JS_GETBOOL(js,fmt,"sampler"))
|
|
info.usage |= SDL_GPU_TEXTUREUSAGE_SAMPLER;
|
|
if (JS_GETBOOL(js,fmt,"color_target"))
|
|
info.usage |= SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
|
|
if (JS_GETBOOL(js,fmt,"depth_target"))
|
|
info.usage |= SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
|
|
if (JS_GETBOOL(js,fmt,"read"))
|
|
info.usage |= (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ);
|
|
if (JS_GETBOOL(js,fmt,"write"))
|
|
info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE;
|
|
if (JS_GETBOOL(js,fmt,"readwrite"))
|
|
info.usage |= SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_SIMULTANEOUS_READ_WRITE;
|
|
|
|
SDL_GPUTexture *tex = SDL_CreateGPUTexture(gpu,&info);
|
|
if (!tex) return JS_ThrowReferenceError(js, "Unable to create texture: %s", SDL_GetError());
|
|
JSValue jstex = SDL_GPUTexture2js(js,tex);
|
|
JS_SetPropertyStr(js,jstex,"width", number2js(js,info.width));
|
|
JS_SetPropertyStr(js,jstex,"height", number2js(js,info.height));
|
|
JS_SetPropertyStr(js,jstex,"dim", vec22js(js,(HMM_Vec2){info.width, info.height}));
|
|
return jstex;
|
|
)
|
|
|
|
typedef struct {
|
|
text_vert vert[4];
|
|
} quad;
|
|
|
|
// Comparator inline for potential compiler inlining
|
|
static inline int sprite_compare(const sprite *a, const sprite *b) {
|
|
if (a->layer != b->layer) return a->layer - b->layer;
|
|
|
|
if (a->pos.Y != b->pos.Y)
|
|
return (b->pos.Y - a->pos.Y);
|
|
|
|
if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image))
|
|
return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Merge two sorted sub-ranges [left..mid] and [mid+1..right]
|
|
static void merge_sprites(sprite *arr, sprite *temp, size_t left, size_t mid, size_t right) {
|
|
size_t i = left, j = mid + 1, k = left;
|
|
|
|
while (i <= mid && j <= right)
|
|
if (sprite_compare(&arr[i], &arr[j]) <= 0) temp[k++] = arr[i++];
|
|
else temp[k++] = arr[j++];
|
|
|
|
while (i <= mid) temp[k++] = arr[i++];
|
|
while (j <= right) temp[k++] = arr[j++];
|
|
|
|
for (size_t p = left; p <= right; p++)
|
|
arr[p] = temp[p];
|
|
}
|
|
|
|
// Recursive mergesort
|
|
static void merge_sort_recursive(sprite *arr, sprite *temp, size_t left, size_t right) {
|
|
if (left >= right) return;
|
|
size_t mid = (left + right) / 2;
|
|
merge_sort_recursive(arr, temp, left, mid);
|
|
merge_sort_recursive(arr, temp, mid + 1, right);
|
|
merge_sprites(arr, temp, left, mid, right);
|
|
}
|
|
|
|
// Public function to sort the array of sprites with mergesort
|
|
void better_sort_sprites(sprite *arr, size_t length) {
|
|
if (length < 2) return;
|
|
sprite *temp = malloc(length * sizeof(sprite));
|
|
if (!temp) return; // fallback or handle error
|
|
merge_sort_recursive(arr, temp, 0, length - 1);
|
|
free(temp);
|
|
}
|
|
|
|
int sort_sprite_backtofront(const sprite *a, const sprite *b)
|
|
{
|
|
if (a->layer != b->layer) return a->layer - b->layer;
|
|
if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image))
|
|
return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1;
|
|
return 0;
|
|
}
|
|
|
|
int sort_sprite_fronttoback(const sprite *a, const sprite *b)
|
|
{
|
|
if (a->layer != b->layer) return b->layer - a->layer;
|
|
if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image))
|
|
return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1;
|
|
return 0;
|
|
}
|
|
|
|
int sort_sprite_texture(const sprite *a, const sprite *b)
|
|
{
|
|
if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image))
|
|
return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1;
|
|
return 0;
|
|
}
|
|
|
|
int sort_sprite(const sprite *a, const sprite *b)
|
|
{
|
|
if (a->layer != b->layer) return a->layer - b->layer;
|
|
|
|
if (a->pos.Y != b->pos.Y)
|
|
return (b->pos.Y - a->pos.Y);
|
|
|
|
if (JS_VALUE_GET_PTR(a->image) != JS_VALUE_GET_PTR(b->image))
|
|
return JS_VALUE_GET_PTR(a->image) < JS_VALUE_GET_PTR(b->image) ? -1 : 1;
|
|
return 0;
|
|
}
|
|
|
|
JSC_CCALL(gpu_sort_sprite,
|
|
JSValue a = argv[0];
|
|
JSValue b = argv[1];
|
|
|
|
int alayer, blayer;
|
|
JS_GETATOM(js,alayer,a,layer,number)
|
|
JS_GETATOM(js,blayer,b,layer,number)
|
|
if (alayer != blayer) return number2js(js,alayer - blayer);
|
|
|
|
rect ar, br;
|
|
JS_GETATOM(js,ar,a,rect,rect)
|
|
JS_GETATOM(js,br,b,rect,rect)
|
|
if (ar.y != br.y) return number2js(js,br.y-ar.y);
|
|
|
|
JSValue aimg,bimg;
|
|
aimg = JS_GetPropertyStr(js,a,"image");
|
|
bimg = JS_GetPropertyStr(js,b,"image");
|
|
JS_FreeValue(js,aimg);
|
|
JS_FreeValue(js,bimg);
|
|
if (!JS_SameValue(js,aimg,bimg)) return number2js(js,JS_VALUE_GET_PTR(aimg) < JS_VALUE_GET_PTR(bimg) ? -1 : 1);
|
|
return number2js(js,0);
|
|
)
|
|
|
|
|
|
|
|
JSC_CCALL(gpu_make_quad,
|
|
size_t quads = 1;
|
|
size_t verts = quads*4;
|
|
size_t count = quads*6;
|
|
|
|
// Prepare arrays on CPU
|
|
HMM_Vec2 *posdata = malloc(sizeof(*posdata)*verts);
|
|
HMM_Vec2 *uvdata = malloc(sizeof(*uvdata)*verts);
|
|
HMM_Vec4 *colordata = malloc(sizeof(*colordata)*verts);
|
|
HMM_Vec4 usecolor = (HMM_Vec4){1,1,1,1};
|
|
posdata[0] = (HMM_Vec2){0,1};
|
|
posdata[1] = (HMM_Vec2){1,1};
|
|
posdata[2] = (HMM_Vec2){0,0};
|
|
posdata[3] = (HMM_Vec2){1,0};
|
|
|
|
uvdata[0] = (HMM_Vec2){0,0};
|
|
uvdata[1] = (HMM_Vec2){1,0};
|
|
uvdata[2] = (HMM_Vec2){0,1};
|
|
uvdata[3] = (HMM_Vec2){1,1};
|
|
|
|
colordata[0] = usecolor;
|
|
colordata[1] = usecolor;
|
|
colordata[2] = usecolor;
|
|
colordata[3] = usecolor;
|
|
|
|
ret = JS_NewObject(js);
|
|
JSValue pos = make_gpu_buffer(js, posdata, sizeof(*posdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0);
|
|
JSValue uv = make_gpu_buffer(js, uvdata, sizeof(*uvdata)*verts, JS_TYPED_ARRAY_FLOAT32, 2, 1,0);
|
|
JSValue color = make_gpu_buffer(js, colordata, sizeof(*colordata)*verts, JS_TYPED_ARRAY_FLOAT32, 0, 1,0);
|
|
|
|
JS_SetPropertyStr(js, ret, "pos", pos);
|
|
JS_SetPropertyStr(js, ret, "uv", uv);
|
|
JS_SetPropertyStr(js, ret, "color", color);
|
|
JSValue indices = make_quad_indices_buffer(js, quads);
|
|
JS_SetPropertyStr(js, ret, "indices", indices);
|
|
|
|
JS_SetPropertyStr(js, ret, "vertices", number2js(js, verts));
|
|
JS_SetPropertyStr(js, ret, "count", number2js(js, count));
|
|
JS_SetPropertyStr(js,ret,"num_indices", number2js(js,count));
|
|
|
|
// Free temporary CPU arrays
|
|
free(posdata);
|
|
free(uvdata);
|
|
free(colordata);
|
|
|
|
return ret;
|
|
)
|
|
|
|
|
|
JSC_CCALL(gpu_driver,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
ret = JS_NewString(js, SDL_GetGPUDeviceDriver(gpu));
|
|
)
|
|
|
|
static JSValue js_gpu_make_shader(JSContext *js, JSValueConst self, int argc, JSValueConst *argv) {
|
|
if (argc < 1 || !JS_IsObject(argv[0]))
|
|
return JS_ThrowTypeError(js, "make_shader expects an object with code, stage, num_samplers, num_textures, num_storage_buffers, num_uniform_buffers");
|
|
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
|
|
|
|
JSValue obj = argv[0];
|
|
|
|
SDL_GPUShaderCreateInfo info = {0};
|
|
|
|
// code
|
|
JSValue code_val = JS_GetPropertyStr(js, obj, "code");
|
|
size_t code_size;
|
|
void *code_data = js_get_blob_data(js, &code_size, code_val);
|
|
JS_FreeValue(js, code_val);
|
|
if (!code_data)
|
|
return JS_ThrowTypeError(js, "shader.code must be an ArrayBuffer");
|
|
|
|
// stage
|
|
JSValue stage_val = JS_GetPropertyStr(js, obj, "stage");
|
|
const char *stage_str = JS_ToCString(js, stage_val);
|
|
if (stage_str) {
|
|
if (!strcmp(stage_str, "vertex")) info.stage = SDL_GPU_SHADERSTAGE_VERTEX;
|
|
else if (!strcmp(stage_str, "fragment")) info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
|
|
JS_FreeCString(js, stage_str);
|
|
} // TODO: Make this its own function
|
|
JS_FreeValue(js, stage_val);
|
|
|
|
// num_samplers
|
|
JS_GETPROP(js, info.num_samplers, obj, num_samplers, number)
|
|
JS_GETPROP(js, info.num_storage_textures, obj, num_textures, number)
|
|
JS_GETPROP(js, info.num_storage_buffers, obj, num_storage_buffers, number)
|
|
JS_GETPROP(js, info.num_uniform_buffers, obj, num_uniform_buffers, number)
|
|
JS_GETPROP(js, info.format, obj, format, SDL_GPUShaderFormat)
|
|
|
|
JSValue entry_val = JS_GetPropertyStr(js,obj,"entrypoint");
|
|
info.entrypoint = JS_ToCString(js,entry_val);
|
|
JS_FreeValue(js,entry_val);
|
|
|
|
info.code_size = code_size;
|
|
info.code = code_data;
|
|
info.props = 0; // No extension properties by default
|
|
|
|
JS_FreeCString(js,info.entrypoint);
|
|
|
|
SDL_GPUShader *shader = SDL_CreateGPUShader(gpu, &info);
|
|
if (!shader)
|
|
return JS_ThrowReferenceError(js, "Unable to create shader: %s", SDL_GetError());
|
|
|
|
return SDL_GPUShader2js(js, shader);
|
|
}
|
|
|
|
JSC_CCALL(gpu_acquire_cmd_buffer,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
|
|
SDL_GPUCommandBuffer *cb = SDL_AcquireGPUCommandBuffer(gpu);
|
|
if (!cb) return JS_ThrowReferenceError(js,"Unable to acquire command buffer: %s", SDL_GetError());
|
|
// Wrap cb in a JS object
|
|
return SDL_GPUCommandBuffer2js(js, cb);
|
|
)
|
|
|
|
/* takes argv
|
|
0: a command buffer to write to
|
|
1: a buffer or array of buffers to upload
|
|
2: an optional transfer buffer to use; if undefined a temporary one is used
|
|
*/
|
|
JSC_CCALL(gpu_upload,
|
|
JSValue js_cmd = argv[0];
|
|
JSValue js_buffers = argv[1];
|
|
JSValue js_transfer = argv[2];
|
|
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js, self);
|
|
if (!gpu)
|
|
return JS_ThrowTypeError(js, "Invalid GPU device");
|
|
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, js_cmd);
|
|
if (!cmds)
|
|
return JS_ThrowTypeError(js, "Invalid command buffer");
|
|
|
|
if (!JS_IsArray(js, js_buffers))
|
|
return JS_ThrowTypeError(js, "buffers must be an array");
|
|
|
|
size_t len = JS_ArrayLength(js, js_buffers);
|
|
if (len == 0)
|
|
return JS_DupValue(js, js_transfer); // No data to upload, just return existing transfer buffer
|
|
|
|
struct {
|
|
SDL_GPUBuffer *gpu_buffer;
|
|
void *data;
|
|
size_t size;
|
|
} *items = malloc(sizeof(*items) * len);
|
|
|
|
if (!items)
|
|
return JS_ThrowOutOfMemory(js);
|
|
|
|
size_t total_size_needed = 0;
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
JSValue js_buf = JS_GetPropertyUint32(js, js_buffers, i);
|
|
|
|
if (JS_IsNull(js_buf))
|
|
continue;
|
|
|
|
gpu_buffer_unpack(js, gpu, js_buf, &items[i].size, &items[i].data, &items[i].gpu_buffer);
|
|
total_size_needed += items[i].size;
|
|
JS_FreeValue(js, js_buf);
|
|
}
|
|
|
|
SDL_GPUTransferBuffer *transfer = js2SDL_GPUTransferBuffer(js,js_transfer);
|
|
if (transfer) {
|
|
// ensure it's large enough
|
|
size_t transfer_size = js_getnum_str(js,js_transfer, "size");
|
|
if (transfer_size < total_size_needed) {
|
|
total_size_needed *= 1.5;
|
|
transfer = SDL_CreateGPUTransferBuffer( gpu, &(SDL_GPUTransferBufferCreateInfo){
|
|
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
|
|
.size = total_size_needed
|
|
}
|
|
);
|
|
ret = SDL_GPUTransferBuffer2js(js,transfer);
|
|
JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed));
|
|
} else
|
|
ret = JS_DupValue(js,js_transfer); // supplied transfer buffer is fine so we use it
|
|
} else {
|
|
transfer = SDL_CreateGPUTransferBuffer( gpu, &(SDL_GPUTransferBufferCreateInfo){
|
|
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
|
|
.size = total_size_needed
|
|
}
|
|
);
|
|
ret = SDL_GPUTransferBuffer2js(js,transfer);
|
|
JS_SetPropertyStr(js,ret,"size", number2js(js,total_size_needed));
|
|
}
|
|
|
|
SDL_GPUCopyPass *copy_pass = SDL_BeginGPUCopyPass(cmds);
|
|
if (!copy_pass) {
|
|
free(items);
|
|
return JS_ThrowReferenceError(js, "Failed to begin copy pass");
|
|
}
|
|
|
|
void *mapped_data = SDL_MapGPUTransferBuffer(gpu, transfer, false);
|
|
if (!mapped_data) {
|
|
SDL_EndGPUCopyPass(copy_pass);
|
|
free(items);
|
|
return JS_ThrowReferenceError(js, "Failed to map transfer buffer: %s", SDL_GetError());
|
|
}
|
|
|
|
// Copy all data into the mapped transfer buffer
|
|
size_t current_offset = 0;
|
|
for (size_t i = 0; i < len; i++) {
|
|
memcpy((char*)mapped_data + current_offset, items[i].data, items[i].size);
|
|
current_offset += items[i].size;
|
|
}
|
|
SDL_UnmapGPUTransferBuffer(gpu, transfer);
|
|
|
|
// Issue uploads for each item
|
|
current_offset = 0;
|
|
for (size_t i = 0; i < len; i++) {
|
|
SDL_UploadToGPUBuffer(
|
|
copy_pass,
|
|
&(SDL_GPUTransferBufferLocation){
|
|
.transfer_buffer = transfer,
|
|
.offset = current_offset
|
|
},
|
|
&(SDL_GPUBufferRegion){
|
|
.buffer = items[i].gpu_buffer,
|
|
.offset = 0,
|
|
.size = items[i].size
|
|
},
|
|
false
|
|
);
|
|
current_offset += items[i].size;
|
|
}
|
|
|
|
SDL_EndGPUCopyPass(copy_pass);
|
|
free(items);
|
|
)
|
|
|
|
JSC_CCALL(gpu_wait_for_fences,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
int n = JS_ArrayLength(js,argv[0]);
|
|
SDL_GPUFence *fences[n];
|
|
for (int i = 0; i < n; i++) {
|
|
JSValue a = JS_GetPropertyUint32(js,argv[0],i);
|
|
fences[i] = js2SDL_GPUFence(js,a);
|
|
JS_FreeValue(js,a);
|
|
}
|
|
|
|
int wait_all = JS_ToBool(js,argv[1]);
|
|
return JS_NewBool(js,SDL_WaitForGPUFences(gpu,wait_all,fences,n));
|
|
)
|
|
|
|
JSC_CCALL(gpu_query_fence,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_GPUFence *fence = js2SDL_GPUFence(js,argv[0]);
|
|
return JS_NewBool(js,SDL_QueryGPUFence(gpu,fence));
|
|
)
|
|
|
|
JSC_CCALL(gpu_shader_format,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_GPUShaderFormat fmt = SDL_GetGPUShaderFormats(gpu);
|
|
if (!fmt) return JS_ThrowReferenceError(js, "Shader format available invalid.");
|
|
|
|
JSValue arr = JS_NewArray(js);
|
|
int i = 0;
|
|
if (fmt & SDL_GPU_SHADERFORMAT_PRIVATE) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, ".private"));
|
|
if (fmt & SDL_GPU_SHADERFORMAT_SPIRV) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "spv"));
|
|
if (fmt & SDL_GPU_SHADERFORMAT_DXBC) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "dxbc"));
|
|
if (fmt & SDL_GPU_SHADERFORMAT_DXIL) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "dxil"));
|
|
if (fmt & SDL_GPU_SHADERFORMAT_MSL) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "msl"));
|
|
if (fmt & SDL_GPU_SHADERFORMAT_METALLIB) JS_SetPropertyUint32(js, arr, i++, JS_NewString(js, "metallib"));
|
|
return arr;
|
|
)
|
|
|
|
JSC_CCALL(gpu_compute_pipeline,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_GPUComputePipelineCreateInfo info = {0};
|
|
JSValue pipe = argv[0];
|
|
JS_GETPROP(js, info.num_samplers, pipe, num_samplers, number)
|
|
JS_GETPROP(js,info.num_readonly_storage_textures,pipe,num_readonly_storage_textures,number)
|
|
JS_GETPROP(js,info.num_readonly_storage_buffers,pipe,num_readonly_storage_buffers,number)
|
|
JS_GETPROP(js,info.num_readwrite_storage_textures,pipe,num_readwrite_storage_textures,number)
|
|
JS_GETPROP(js,info.num_readwrite_storage_buffers,pipe,num_readwrite_storage_buffers,number)
|
|
JS_GETPROP(js,info.threadcount_x,pipe,threadcount_x,number)
|
|
JS_GETPROP(js,info.threadcount_y,pipe,threadcount_y,number)
|
|
JS_GETPROP(js,info.threadcount_z,pipe,threadcount_z,number)
|
|
JS_GETPROP(js,info.entrypoint,pipe,entrypoint,cstring)
|
|
|
|
|
|
JSValue shader = JS_GetPropertyStr(js,pipe,"shader");
|
|
info.code = js_get_blob_data(js,&info.code_size, shader);
|
|
JS_FreeValue(js,shader);
|
|
|
|
SDL_GPUComputePipeline *pipeline = SDL_CreateGPUComputePipeline(gpu, &info);
|
|
JS_FreeCString(js,info.entrypoint);
|
|
if (!pipeline) return JS_ThrowReferenceError(js,"Could not create compute pipeline: %s", SDL_GetError());
|
|
return SDL_GPUComputePipeline2js(js,pipeline);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = {
|
|
MIST_FUNC_DEF(gpu, claim_window, 1),
|
|
MIST_FUNC_DEF(gpu, make_pipeline, 1), // loads pipeline state into an object
|
|
MIST_FUNC_DEF(gpu,compute_pipeline,1),
|
|
MIST_FUNC_DEF(gpu, set_swapchain, 2),
|
|
MIST_FUNC_DEF(gpu,sort_sprite,2),
|
|
MIST_FUNC_DEF(gpu, make_sampler,1),
|
|
MIST_FUNC_DEF(gpu, load_texture, 2),
|
|
MIST_FUNC_DEF(gpu, texture, 1),
|
|
MIST_FUNC_DEF(gpu, make_quad, 0),
|
|
MIST_FUNC_DEF(gpu, driver, 0),
|
|
MIST_FUNC_DEF(gpu, make_shader, 1),
|
|
MIST_FUNC_DEF(gpu, acquire_cmd_buffer, 0),
|
|
MIST_FUNC_DEF(gpu, upload, 3),
|
|
MIST_FUNC_DEF(gpu, wait_for_fences, 2),
|
|
MIST_FUNC_DEF(gpu, query_fence, 1),
|
|
MIST_FUNC_DEF(gpu, shader_format, 0),
|
|
};
|
|
|
|
JSC_CCALL(renderpass_bind_pipeline,
|
|
SDL_GPURenderPass *r = js2SDL_GPURenderPass(js,self);
|
|
SDL_GPUGraphicsPipeline *pipe = js2SDL_GPUGraphicsPipeline(js,argv[0]);
|
|
SDL_BindGPUGraphicsPipeline(r,pipe);
|
|
)
|
|
|
|
JSC_CCALL(renderpass_draw_indexed,
|
|
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
|
|
SDL_DrawGPUIndexedPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]), js2number(js,argv[4]));
|
|
)
|
|
|
|
JSC_CCALL(renderpass_draw,
|
|
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
|
|
SDL_DrawGPUPrimitives(pass, js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]), js2number(js,argv[3]));
|
|
)
|
|
|
|
JSC_CCALL(renderpass_bind_buffers,
|
|
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
|
|
int first = js2number(js,argv[0]);
|
|
JSValue buffers = argv[1];
|
|
int len = JS_ArrayLength(js,buffers);
|
|
SDL_GPUBufferBinding bindings[len];
|
|
for (int i = 0; i < len; i++) {
|
|
JSValue buffer = JS_GetPropertyUint32(js,buffers,i);
|
|
bindings[i].offset = 0;
|
|
gpu_buffer_unpack(js,global_gpu, buffer, NULL, NULL,&bindings[i].buffer);
|
|
JS_FreeValue(js,buffer);
|
|
}
|
|
|
|
SDL_BindGPUVertexBuffers(
|
|
pass,
|
|
first,
|
|
bindings,
|
|
len);
|
|
)
|
|
|
|
JSC_CCALL(renderpass_bind_index_buffer,
|
|
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
|
|
SDL_GPUBufferBinding bind;
|
|
bind.offset = 0;
|
|
gpu_buffer_unpack(js,global_gpu,argv[0], NULL, NULL, &bind.buffer);
|
|
int elen;
|
|
JSValue b = argv[0];
|
|
JS_GETPROP(js, elen, b, elen, number)
|
|
|
|
SDL_BindGPUIndexBuffer(pass,&bind,elen == 2 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT);
|
|
)
|
|
|
|
JSC_CCALL(renderpass_end,
|
|
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
|
|
SDL_EndGPURenderPass(pass);
|
|
)
|
|
|
|
JSC_CCALL(renderpass_bind_samplers,
|
|
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
|
|
int first_slot = js2number(js,argv[1]);
|
|
JSValue arr = argv[2];
|
|
int num = JS_ArrayLength(js,arr);
|
|
SDL_GPUTextureSamplerBinding binds[num];
|
|
for (int i = 0; i < num; i++) {
|
|
JSValue val = JS_GetPropertyUint32(js,arr,i);
|
|
JSValue tex = JS_GetPropertyStr(js,val,"texture");
|
|
JSValue smp = JS_GetPropertyStr(js,val,"sampler");
|
|
binds[i].texture = js2SDL_GPUTexture(js,tex);
|
|
binds[i].sampler = js2SDL_GPUSampler(js,smp);
|
|
JS_FreeValue(js,tex);
|
|
JS_FreeValue(js,smp);
|
|
JS_FreeValue(js,val);
|
|
}
|
|
int vertex = JS_ToBool(js,argv[0]);
|
|
if (vertex)
|
|
SDL_BindGPUVertexSamplers(pass, first_slot, binds, num);
|
|
else
|
|
SDL_BindGPUFragmentSamplers(pass, first_slot, binds, num);
|
|
)
|
|
|
|
JSC_CCALL(renderpass_bind_storage_buffers,
|
|
|
|
)
|
|
|
|
JSC_CCALL(renderpass_bind_storage_textures,
|
|
|
|
)
|
|
|
|
JSC_CCALL(renderpass_viewport,
|
|
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
|
|
rect r = js2rect(js,argv[0]);
|
|
SDL_GPUViewport vp ={0};
|
|
vp.x = r.x;
|
|
vp.y = r.y;
|
|
vp.w = r.w;
|
|
vp.h = r.h;
|
|
vp.min_depth = 0;
|
|
vp.max_depth = 1;
|
|
SDL_SetGPUViewport(pass,&vp);
|
|
)
|
|
|
|
JSC_CCALL(renderpass_scissor,
|
|
SDL_GPURenderPass *pass = js2SDL_GPURenderPass(js,self);
|
|
rect r = js2rect(js,argv[0]);
|
|
SDL_Rect rr;
|
|
rr.x = r.x;
|
|
rr.y = r.y;
|
|
rr.w = r.w;
|
|
rr.h = r.h;
|
|
SDL_SetGPUScissor(pass,&rr);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_GPURenderPass_funcs[] = {
|
|
MIST_FUNC_DEF(renderpass, bind_pipeline, 1),
|
|
MIST_FUNC_DEF(renderpass, viewport, 1),
|
|
MIST_FUNC_DEF(renderpass, scissor, 1),
|
|
MIST_FUNC_DEF(renderpass, draw, 4),
|
|
MIST_FUNC_DEF(renderpass, draw_indexed, 5),
|
|
MIST_FUNC_DEF(renderpass, end, 0),
|
|
MIST_FUNC_DEF(renderpass, bind_index_buffer, 1),
|
|
MIST_FUNC_DEF(renderpass, bind_buffers, 2),
|
|
MIST_FUNC_DEF(renderpass, bind_samplers, 3),
|
|
MIST_FUNC_DEF(renderpass, bind_storage_buffers, 2),
|
|
MIST_FUNC_DEF(renderpass, bind_storage_textures, 2),
|
|
};
|
|
|
|
JSC_CCALL(cmd_render_pass,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
|
|
if (!JS_IsObject(argv[0])) return JS_ThrowTypeError(js, "render_pass: Expected a render pass descriptor object");
|
|
JSValue passObj = argv[0];
|
|
|
|
JSValue colorTargetsVal = JS_GetPropertyStr(js, passObj, "color_targets");
|
|
if (!JS_IsArray(js, colorTargetsVal))
|
|
return JS_ThrowTypeError(js, "render_pass: colorTargets must be an array");
|
|
|
|
uint32_t colorCount = JS_ArrayLength(js, colorTargetsVal);
|
|
SDL_GPUColorTargetInfo colortars[colorCount];
|
|
SDL_GPUDepthStencilTargetInfo depthtar;
|
|
int has_depth = 0;
|
|
|
|
// Fill colorInfos from JS array
|
|
for (uint32_t i = 0; i < colorCount; i++) {
|
|
JSValue ctargetVal = JS_GetPropertyUint32(js, colorTargetsVal, i);
|
|
colortars[i] = js2SDL_GPUColorTargetInfo(js,ctargetVal);
|
|
JS_FreeValue(js, ctargetVal);
|
|
}
|
|
|
|
// Optional depth_stencil
|
|
JSValue depthval = JS_GetPropertyStr(js, passObj, "depth_stencil");
|
|
if (!JS_IsNull(depthval)) {
|
|
has_depth = 1;
|
|
JS_GETPROP(js, depthtar.texture, depthval, texture, SDL_GPUTexture)
|
|
JS_GETPROP(js, depthtar.load_op, depthval, load, SDL_GPULoadOp)
|
|
JS_GETPROP(js, depthtar.store_op, depthval, store, SDL_GPUStoreOp)
|
|
JS_GETPROP(js, depthtar.stencil_load_op, depthval, stencil_load, SDL_GPULoadOp)
|
|
JS_GETPROP(js, depthtar.stencil_store_op, depthval, stencil_store, SDL_GPUStoreOp)
|
|
JS_GETPROP(js,depthtar.clear_depth, depthval, clear, number)
|
|
JS_GETPROP(js,depthtar.clear_stencil,depthval,clear_stencil,number)
|
|
}
|
|
JS_FreeValue(js, depthval);
|
|
|
|
SDL_GPURenderPass *pass = SDL_BeginGPURenderPass(
|
|
cmds,
|
|
colortars,
|
|
colorCount,
|
|
has_depth ? &depthtar : NULL
|
|
);
|
|
|
|
JS_FreeValue(js, colorTargetsVal);
|
|
|
|
if (!pass) return JS_ThrowInternalError(js, "render_pass: Failed to begin render pass");
|
|
|
|
return SDL_GPURenderPass2js(js, pass);
|
|
)
|
|
|
|
JSC_CCALL(cmd_bind_vertex_buffer,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
int slot;
|
|
JS_ToInt32(js, &slot, argv[0]);
|
|
SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js, argv[1]);
|
|
size_t offset = 0;
|
|
if (argc > 2) JS_ToIndex(js, &offset, argv[2]);
|
|
SDL_BindGPUVertexBuffers(cmds, slot, &(SDL_GPUBufferBinding){.buffer = buffer, .offset = offset}, 1);
|
|
)
|
|
|
|
JSC_CCALL(cmd_bind_index_buffer,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js, argv[0]);
|
|
size_t offset = 0;
|
|
if (argc > 1) JS_ToIndex(js, &offset, argv[1]);
|
|
SDL_BindGPUIndexBuffer(cmds, &(SDL_GPUBufferBinding){.buffer = buffer, .offset = offset}, SDL_GPU_INDEXELEMENTSIZE_16BIT);
|
|
)
|
|
|
|
JSC_CCALL(cmd_bind_fragment_sampler,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
int slot;
|
|
JS_ToInt32(js, &slot, argv[0]);
|
|
SDL_GPUTexture *tex = js2SDL_GPUTexture(js, argv[1]);
|
|
SDL_GPUSampler *sampler = js2SDL_GPUSampler(js, argv[2]);
|
|
SDL_BindGPUFragmentSamplers(cmds, slot, &(SDL_GPUTextureSamplerBinding){.texture = tex, .sampler = sampler}, 1);
|
|
)
|
|
|
|
JSC_CCALL(cmd_push_vertex_uniform_data,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
int slot;
|
|
JS_ToInt32(js, &slot, argv[0]);
|
|
size_t buf_size;
|
|
void *data = js_get_blob_data(js, &buf_size, argv[1]);
|
|
SDL_PushGPUVertexUniformData(cmds, slot, data, buf_size);
|
|
)
|
|
|
|
JSC_CCALL(cmd_push_fragment_uniform_data,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
int slot;
|
|
JS_ToInt32(js, &slot, argv[0]);
|
|
size_t buf_size;
|
|
void *data = js_get_blob_data(js, &buf_size, argv[1]);
|
|
SDL_PushGPUFragmentUniformData(cmds, slot, data, buf_size);
|
|
)
|
|
|
|
JSC_CCALL(cmd_push_compute_uniform_data,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
int slot;
|
|
JS_ToInt32(js, &slot, argv[0]);
|
|
size_t buf_size;
|
|
void *data = js_get_blob_data(js, &buf_size, argv[1]);
|
|
SDL_PushGPUComputeUniformData(cmds, slot, data, buf_size);
|
|
)
|
|
|
|
JSC_CCALL(cmd_submit,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self);
|
|
SDL_GPUFence *fence = SDL_SubmitGPUCommandBufferAndAcquireFence(cmds);
|
|
return SDL_GPUFence2js(js,fence);
|
|
)
|
|
|
|
JSC_CCALL(cmd_hud,
|
|
SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js,self);
|
|
HMM_Vec2 size = js2vec2(js,argv[0]);
|
|
HMM_Mat4 proj = HMM_Orthographic_RH_NO(0,size.x,0,size.y,-1,1);
|
|
shader_globals data = {0};
|
|
data.world_to_projection = proj;
|
|
data.projection_to_world = HMM_InvGeneralM4(proj);
|
|
data.viewport_min_z = -1,
|
|
data.viewport_max_z = 1;
|
|
data.render_size = size;
|
|
data.view_to_projection = proj;
|
|
data.viewport_size = (HMM_Vec2){1,1};
|
|
data.viewport_offset = (HMM_Vec2){0,0};
|
|
data.time = SDL_GetTicksNS() / 1000000000.0f;
|
|
SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data));
|
|
)
|
|
|
|
JSC_CCALL(cmd_camera,
|
|
// SDL_GPUCommandBuffer *cmds = js2SDL_GPUCommandBuffer(js, self);
|
|
// shader_globals data = camera_globals(js, argv[0]);
|
|
// SDL_PushGPUVertexUniformData(cmds, js2number(js,argv[1]), &data, sizeof(data));
|
|
)
|
|
|
|
JSC_SCALL(cmd_push_debug_group,
|
|
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
|
|
SDL_PushGPUDebugGroup(cmd,str);
|
|
)
|
|
|
|
JSC_CCALL(cmd_pop_debug_group,
|
|
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
|
|
SDL_PopGPUDebugGroup(cmd);
|
|
)
|
|
|
|
JSC_SCALL(cmd_debug_label,
|
|
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
|
|
SDL_InsertGPUDebugLabel(cmd, str);
|
|
)
|
|
|
|
SDL_GPUBlitRegion js2SDL_GPUBlitRegion(JSContext *js, JSValue v)
|
|
{
|
|
SDL_GPUBlitRegion info = {0};
|
|
if (JS_GetClassID(v) == js_SDL_GPUTexture_id) {
|
|
// texture path
|
|
JSValue tex = v;
|
|
info.texture = js2SDL_GPUTexture(js,tex);
|
|
info.mip_level = 0,
|
|
info.layer_or_depth_plane = 0;
|
|
info.x = 0;
|
|
info.y = 0;
|
|
JS_GETPROP(js,info.w,tex,width,number)
|
|
JS_GETPROP(js,info.h,tex,height,number)
|
|
return info;
|
|
}
|
|
JSValue tex = JS_GetPropertyStr(js, v, "texture");
|
|
info.texture = js2SDL_GPUTexture(js,tex);
|
|
JS_GETPROP(js,info.mip_level,v,mip_level, number)
|
|
JS_GETPROP(js,info.mip_level,v,layer,number)
|
|
JS_GETPROP(js,info.x,v,x,number)
|
|
JS_GETPROP(js,info.y,v,y,number)
|
|
JS_GETPROP(js,info.w,v,width,number)
|
|
JS_GETPROP(js,info.h,v,height,number)
|
|
|
|
if (!info.w) JS_GETPROP(js,info.w,tex,width,number)
|
|
if (!info.h) JS_GETPROP(js,info.h,tex,height,number)
|
|
|
|
JS_FreeValue(js,tex);
|
|
return info;
|
|
}
|
|
|
|
JSC_CCALL(cmd_blit,
|
|
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
|
|
SDL_GPUBlitInfo info = {0};
|
|
JSValue v = argv[0];
|
|
JS_GETPROP(js,info.source,v,src,SDL_GPUBlitRegion)
|
|
JS_GETPROP(js,info.destination,v,dst,SDL_GPUBlitRegion)
|
|
JS_GETPROP(js,info.load_op,v,load,SDL_GPULoadOp)
|
|
JS_GETPROP(js,info.flip_mode,v,flip,SDL_FlipMode)
|
|
JS_GETPROP(js,info.filter,v,filter,SDL_GPUFilter)
|
|
JS_GETPROP(js,info.clear_color,v,color,SDL_FColor)
|
|
SDL_BlitGPUTexture(cmd,&info);
|
|
)
|
|
|
|
JSC_CCALL(cmd_cancel,
|
|
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
|
|
SDL_CancelGPUCommandBuffer(cmd);
|
|
)
|
|
|
|
JSC_CCALL(cmd_compute_pass,
|
|
SDL_GPUCommandBuffer *cmd = js2SDL_GPUCommandBuffer(js,self);
|
|
JSValue textures = argv[0];
|
|
JSValue buffers = argv[1];
|
|
|
|
int t_n = JS_ArrayLength(js,textures);
|
|
SDL_GPUStorageTextureReadWriteBinding t_bind[t_n];
|
|
for (int i = 0; i < t_n; i++) {
|
|
JSValue T = JS_GetPropertyUint32(js,textures,i);
|
|
JS_GETPROP(js, t_bind[i].texture, T, texture, SDL_GPUTexture)
|
|
JS_GETPROP(js,t_bind[i].mip_level,T,mip, number)
|
|
JS_GETPROP(js,t_bind[i].layer,T,layer, number)
|
|
JS_FreeValue(js,T);
|
|
}
|
|
|
|
int b_n = JS_ArrayLength(js,buffers);
|
|
SDL_GPUStorageBufferReadWriteBinding b_bind[b_n];
|
|
for (int i = 0; i < b_n; i++) {
|
|
JSValue T = JS_GetPropertyUint32(js,buffers,i);
|
|
JS_GETPROP(js,b_bind[i].buffer, T,buffer,SDL_GPUBuffer)
|
|
}
|
|
|
|
SDL_GPUComputePass *pass = SDL_BeginGPUComputePass(cmd,t_bind,t_n,b_bind,b_n);
|
|
if (!pass) return JS_ThrowReferenceError(js, "Unable to begin compute pass: %s", SDL_GetError());
|
|
return SDL_GPUComputePass2js(js,pass);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_GPUCommandBuffer_funcs[] = {
|
|
MIST_FUNC_DEF(cmd, render_pass, 1),
|
|
MIST_FUNC_DEF(cmd, compute_pass, 2),
|
|
MIST_FUNC_DEF(cmd, swapchain_pass, 1),
|
|
MIST_FUNC_DEF(cmd, acquire_swapchain,0),
|
|
MIST_FUNC_DEF(cmd, bind_vertex_buffer, 2),
|
|
MIST_FUNC_DEF(cmd, bind_index_buffer, 1),
|
|
MIST_FUNC_DEF(cmd, bind_fragment_sampler, 3),
|
|
MIST_FUNC_DEF(cmd, push_vertex_uniform_data, 2),
|
|
MIST_FUNC_DEF(cmd, push_fragment_uniform_data, 2),
|
|
MIST_FUNC_DEF(cmd, push_compute_uniform_data, 2),
|
|
MIST_FUNC_DEF(cmd, submit, 0),
|
|
MIST_FUNC_DEF(cmd, cancel, 0),
|
|
MIST_FUNC_DEF(cmd, camera, 2),
|
|
MIST_FUNC_DEF(cmd, hud, 2),
|
|
MIST_FUNC_DEF(cmd, push_debug_group, 1),
|
|
MIST_FUNC_DEF(cmd, pop_debug_group, 0),
|
|
MIST_FUNC_DEF(cmd, debug_label, 1),
|
|
MIST_FUNC_DEF(cmd, blit, 1),
|
|
};
|
|
|
|
JSC_SCALL(buffer_name,
|
|
SDL_GPUBuffer *buffer = js2SDL_GPUBuffer(js,self);
|
|
SDL_GPUDevice *gpu;
|
|
JS_GETPROP(js, gpu, self, gpu, SDL_GPUDevice)
|
|
SDL_SetGPUBufferName(gpu,buffer,str);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_GPUBuffer_funcs[] = {
|
|
MIST_FUNC_DEF(buffer, name, 1),
|
|
};
|
|
|
|
JSC_CCALL(compute_dispatch,
|
|
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
|
|
SDL_DispatchGPUCompute(pass,js2number(js,argv[0]), js2number(js,argv[1]), js2number(js,argv[2]));
|
|
)
|
|
|
|
JSC_CCALL(compute_end,
|
|
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
|
|
SDL_EndGPUComputePass(pass);
|
|
)
|
|
|
|
JSC_CCALL(compute_pipeline,
|
|
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
|
|
SDL_GPUComputePipeline *pipeline = js2SDL_GPUComputePipeline(js,argv[0]);
|
|
SDL_BindGPUComputePipeline(pass,pipeline);
|
|
)
|
|
|
|
JSC_CCALL(compute_samplers,
|
|
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
|
|
JSValue samplers = argv[0];
|
|
int n = JS_ArrayLength(js,samplers);
|
|
SDL_GPUTextureSamplerBinding b[n];
|
|
for (int i = 0; i < n; i++) {
|
|
JSValue s = JS_GetPropertyUint32(js,samplers,i);
|
|
b[i] = js2SDL_GPUTextureSamplerBinding(js,s);
|
|
JS_FreeValue(js,s);
|
|
}
|
|
SDL_BindGPUComputeSamplers(pass,js2number(js,argv[1]),b,n);
|
|
)
|
|
|
|
JSC_CCALL(compute_storage_textures,
|
|
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
|
|
JSValue textures = argv[0];
|
|
int n = JS_ArrayLength(js,textures);
|
|
SDL_GPUTexture *b[n];
|
|
for (int i = 0; i < n; i++) {
|
|
JSValue s = JS_GetPropertyUint32(js,textures,i);
|
|
b[i] = js2SDL_GPUTexture(js,s);
|
|
JS_FreeValue(js,s);
|
|
}
|
|
SDL_BindGPUComputeStorageTextures(pass,js2number(js,argv[1]),b,n);
|
|
)
|
|
|
|
JSC_CCALL(compute_storage_buffers,
|
|
SDL_GPUComputePass *pass = js2SDL_GPUComputePass(js,self);
|
|
JSValue buffers = argv[0];
|
|
int n = JS_ArrayLength(js,buffers);
|
|
SDL_GPUBuffer *b[n];
|
|
for (int i = 0; i < n; i++) {
|
|
JSValue s = JS_GetPropertyUint32(js,buffers,i);
|
|
b[i] = js2SDL_GPUBuffer(js,s);
|
|
JS_FreeValue(js,s);
|
|
}
|
|
SDL_BindGPUComputeStorageTextures(pass,js2number(js,argv[1]),b,n);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_GPUComputePass_funcs[] = {
|
|
MIST_FUNC_DEF(compute, dispatch, 3),
|
|
MIST_FUNC_DEF(compute, end, 0),
|
|
MIST_FUNC_DEF(compute, pipeline, 1),
|
|
MIST_FUNC_DEF(compute, samplers, 2),
|
|
MIST_FUNC_DEF(compute, storage_buffers, 2),
|
|
MIST_FUNC_DEF(compute, storage_textures, 2),
|
|
};
|
|
|
|
JSValue make_gpu_buffer(JSContext *js, void *data, size_t size, int type, int elements, int copy, int index)
|
|
{
|
|
JSValue tstack[2];
|
|
if (copy) {
|
|
tstack[0] = js_new_blob_stoned_copy(js,data,size);//, make_gpu_buffer, NULL, 1);
|
|
} else {
|
|
tstack[0] = JS_NewArrayBuffer(js,data,size,free_gpu_buffer, NULL, 0);
|
|
}
|
|
tstack[1] = JS_NewObject(js);
|
|
JS_SetPropertyStr(js,tstack[1],"type",number2js(js,type));
|
|
JS_SetPropertyStr(js,tstack[1],"elements",number2js(js,elements));
|
|
JS_SetPropertyStr(js,tstack[1],"buffer",tstack[0]);
|
|
JS_SetPropertyStr(js,tstack[1],"index", number2js(js,index));
|
|
return tstack[1];
|
|
}
|
|
|
|
void *get_gpu_buffer(JSContext *js, JSValue argv, size_t *stride, size_t *size)
|
|
{
|
|
if (JS_IsArray(js,argv)) {
|
|
if (stride) *stride = sizeof(float);
|
|
int arrlen = JS_ArrayLength(js,argv);
|
|
float *arr = malloc(sizeof(float)*arrlen);
|
|
for (int i = 0; i < arrlen; i++) {
|
|
JSValue v = JS_GetPropertyUint32(js,argv,i);
|
|
arr[i] = js2number(js,v);
|
|
JS_FreeValue(js,v);
|
|
}
|
|
if (size) *size = sizeof(float)*arrlen;
|
|
return arr;
|
|
}
|
|
|
|
JSValue buffer = JS_GetPropertyStr(js,argv,"buffer");
|
|
void *data = js_get_blob_data(js,NULL,buffer);
|
|
JS_FreeValue(js,buffer);
|
|
|
|
JSValue typeval = JS_GetPropertyStr(js,argv,"type");
|
|
int type = js2number(js,typeval);
|
|
JS_FreeValue(js,typeval);
|
|
|
|
return data;
|
|
}
|
|
|
|
// Conversion functions for SDL GPU types
|
|
SDL_GPUGraphicsPipelineTargetInfo js2SDL_GPUGraphicsPipelineTargetInfo(JSContext *js, JSValue v)
|
|
{
|
|
SDL_GPUGraphicsPipelineTargetInfo info = {0};
|
|
return info;
|
|
}
|
|
|
|
SDL_GPUSampleCount js2SDL_GPUSampleCount(JSContext *js, JSValue v)
|
|
{
|
|
int n = js2number(js,v);
|
|
switch(n) {
|
|
case 1: return SDL_GPU_SAMPLECOUNT_1;
|
|
case 2: return SDL_GPU_SAMPLECOUNT_2;
|
|
case 4: return SDL_GPU_SAMPLECOUNT_4;
|
|
case 8: return SDL_GPU_SAMPLECOUNT_8;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Enum conversion macro and definitions
|
|
#define JS2ENUM(NAME, RETS, VALS) \
|
|
int js2##NAME(JSContext *js, JSValue v) { \
|
|
if (JS_IsNull(v)) return 0; \
|
|
const char *str = JS_ToCString(js, v); \
|
|
int *rets = (RETS); \
|
|
const char **vals = (VALS); \
|
|
/* Compute how many entries are in the arrays */ \
|
|
int n = (int)(sizeof((RETS)) / sizeof((RETS)[0])); \
|
|
for(int i = 0; i < n; i++) \
|
|
if(!strcmp(vals[i], str)) { \
|
|
JS_FreeCString(js, str); \
|
|
return rets[i]; \
|
|
} \
|
|
JS_FreeCString(js, str); \
|
|
return 0; \
|
|
}
|
|
|
|
// SDL GPU Swapchain Composition
|
|
static int rets_SDL_GPUSwapchainComposition[] = {
|
|
SDL_GPU_SWAPCHAINCOMPOSITION_SDR,
|
|
SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR,
|
|
SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR,
|
|
// SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2084
|
|
};
|
|
|
|
static const char *vals_SDL_GPUSwapchainComposition[] = {
|
|
"sdr",
|
|
"linear",
|
|
"hdr",
|
|
// "hdr10"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUSwapchainComposition, rets_SDL_GPUSwapchainComposition, vals_SDL_GPUSwapchainComposition)
|
|
|
|
// SDL GPU Blend Factor
|
|
static int rets_SDL_GPUBlendFactor[] = {
|
|
SDL_GPU_BLENDFACTOR_INVALID,
|
|
SDL_GPU_BLENDFACTOR_ZERO,
|
|
SDL_GPU_BLENDFACTOR_ONE,
|
|
SDL_GPU_BLENDFACTOR_SRC_COLOR,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR,
|
|
SDL_GPU_BLENDFACTOR_DST_COLOR,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR,
|
|
SDL_GPU_BLENDFACTOR_SRC_ALPHA,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
|
|
SDL_GPU_BLENDFACTOR_DST_ALPHA,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA,
|
|
SDL_GPU_BLENDFACTOR_CONSTANT_COLOR,
|
|
SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR,
|
|
SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE
|
|
};
|
|
|
|
static const char *vals_SDL_GPUBlendFactor[] = {
|
|
"invalid",
|
|
"zero",
|
|
"one",
|
|
"src_color",
|
|
"one_minus_src_color",
|
|
"dst_color",
|
|
"one_minus_dst_color",
|
|
"src_alpha",
|
|
"one_minus_src_alpha",
|
|
"dst_alpha",
|
|
"one_minus_dst_alpha",
|
|
"constant_color",
|
|
"one_minus_constant_color",
|
|
"src_alpha_saturate"
|
|
};
|
|
|
|
JS2ENUM(SDL_GPUBlendFactor, rets_SDL_GPUBlendFactor, vals_SDL_GPUBlendFactor)
|
|
|
|
// GPU FUNCTIONS
|
|
|
|
JSC_CCALL(SDL_Window_make_gpu,
|
|
if (global_gpu)
|
|
return JS_ThrowReferenceError(js, "A gpu has already been created somewhere.");
|
|
const char *name = JS_ToCString(js,argv[1]);
|
|
SDL_PropertiesID props = SDL_CreateProperties();
|
|
SDL_SetStringProperty(props, SDL_PROP_GPU_DEVICE_CREATE_NAME_STRING, name);
|
|
JS_FreeCString(js,name);
|
|
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, JS_ToBool(js,argv[0]));
|
|
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, 1);
|
|
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXBC_BOOLEAN, 1);
|
|
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_DXIL_BOOLEAN, 1);
|
|
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_MSL_BOOLEAN, 1);
|
|
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_METALLIB_BOOLEAN, 1);
|
|
|
|
SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_PREFERLOWPOWER_BOOLEAN, 1);
|
|
|
|
SDL_GPUDevice *gpu = SDL_CreateGPUDeviceWithProperties(props);
|
|
SDL_DestroyProperties(props);
|
|
|
|
if (!gpu) return JS_ThrowReferenceError(js, "Could not create GPU device! %s", SDL_GetError());
|
|
|
|
global_gpu = gpu;
|
|
return SDL_GPUDevice2js(js,gpu);
|
|
)
|
|
|
|
JSC_CCALL(gpu_claim_window,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_Window *w = js2SDL_Window(js,argv[0]);
|
|
if (!SDL_ClaimWindowForGPUDevice(gpu, w))
|
|
return JS_ThrowReferenceError(js, "%s", SDL_GetError());
|
|
)
|
|
|
|
JSC_CCALL(gpu_set_swapchain,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_Window *w = js2SDL_Window(js,argv[0]);
|
|
SDL_GPUSwapchainComposition comp = js2SDL_GPUSwapchainComposition(js,argv[1]);
|
|
if (!SDL_SetGPUSwapchainParameters(gpu, w, comp, SDL_GPU_PRESENTMODE_VSYNC))
|
|
return JS_ThrowReferenceError(js, "%s", SDL_GetError());
|
|
)
|
|
|
|
JSC_CCALL(gpu_driver,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
return JS_NewString(js, SDL_GetGPUDeviceDriver(gpu));
|
|
)
|
|
|
|
JSC_CCALL(gpu_shader_format,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_GPUShaderFormat format = SDL_GetGPUShaderFormats(gpu);
|
|
switch(format) {
|
|
case SDL_GPU_SHADERFORMAT_SPIRV: return JS_NewString(js, "spirv");
|
|
case SDL_GPU_SHADERFORMAT_DXBC: return JS_NewString(js, "dxbc");
|
|
case SDL_GPU_SHADERFORMAT_DXIL: return JS_NewString(js, "dxil");
|
|
case SDL_GPU_SHADERFORMAT_MSL: return JS_NewString(js, "msl");
|
|
case SDL_GPU_SHADERFORMAT_METALLIB: return JS_NewString(js, "metallib");
|
|
}
|
|
return JS_NewString(js, "unknown");
|
|
)
|
|
|
|
JSC_CCALL(gpu_acquire_cmd_buffer,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(gpu);
|
|
if (!cmd) return JS_ThrowReferenceError(js, "%s", SDL_GetError());
|
|
return SDL_GPUCommandBuffer2js(js,cmd);
|
|
)
|
|
|
|
JSC_CCALL(gpu_wait_for_fences,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
int fencecount = js2number(js,argv[1]);
|
|
SDL_GPUFence **fences = malloc(sizeof(SDL_GPUFence*)*fencecount);
|
|
for (int i = 0; i < fencecount; i++) {
|
|
JSValue fenceval = JS_GetPropertyUint32(js,argv[0],i);
|
|
fences[i] = js2SDL_GPUFence(js,fenceval);
|
|
JS_FreeValue(js,fenceval);
|
|
}
|
|
int ok = SDL_WaitForGPUFences(gpu,1,fences,fencecount);
|
|
free(fences);
|
|
if (!ok) return JS_ThrowReferenceError(js, "%s", SDL_GetError());
|
|
)
|
|
|
|
JSC_CCALL(gpu_query_fence,
|
|
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
|
|
SDL_GPUFence *fence = js2SDL_GPUFence(js,argv[0]);
|
|
return JS_NewBool(js, SDL_QueryGPUFence(gpu,fence));
|
|
)
|
|
|
|
// Function array for SDL_GPUDevice
|
|
static const JSCFunctionListEntry js_SDL_GPUDevice_funcs[] = {
|
|
MIST_FUNC_DEF(gpu, claim_window, 1),
|
|
MIST_FUNC_DEF(gpu, set_swapchain, 2),
|
|
MIST_FUNC_DEF(gpu, driver, 0),
|
|
MIST_FUNC_DEF(gpu, acquire_cmd_buffer, 0),
|
|
MIST_FUNC_DEF(gpu, wait_for_fences, 2),
|
|
MIST_FUNC_DEF(gpu, query_fence, 1),
|
|
MIST_FUNC_DEF(gpu, shader_format, 0),
|
|
};
|
|
|
|
// Function array for SDL_Window GPU functions
|
|
static const JSCFunctionListEntry js_SDL_Window_gpu_funcs[] = {
|
|
MIST_FUNC_DEF(SDL_Window, make_gpu, 2),
|
|
};
|
|
|
|
JSC_SCALL(texture_name,
|
|
SDL_GPUTexture *texture = js2SDL_GPUTexture(js,self);
|
|
SDL_GPUDevice *gpu;
|
|
JS_GETPROP(js,gpu,self,gpu,SDL_GPUDevice)
|
|
SDL_SetGPUTextureName(gpu,texture,str);
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_SDL_GPUTexture_funcs[] = {
|
|
MIST_FUNC_DEF(texture, name, 1),
|
|
};
|
|
|
|
// Function to register module - GPU classes are registered in main FFI loading
|
|
JSValue js_sdl_gpu_use(JSContext *js)
|
|
{
|
|
// GPU classes are registered via the main FFI loading system
|
|
// This module doesn't export its own functions, it just provides class definitions
|
|
return JS_NULL;
|
|
}
|