OpenGL ES added for handhelds.

This commit is contained in:
Scott Duensing 2026-09-12 19:24:35 -05:00
parent d73966c1e6
commit ffc1d54a89
17 changed files with 3446 additions and 310 deletions

View file

@ -1245,6 +1245,27 @@ SINGE 2.10
New Features New Features
------------ ------------
- Texture samplers now walk the whole mipmap chain. SDL hands max_lod
straight to Vulkan's maxLod and Direct3D 12's MaxLOD, and the scene
and GUI left it at the zero a memset gives, which clamps every lookup
to mip level 0. Mipmapping was therefore off on every backend:
distant surfaces aliased, the 8x anisotropy did far less than it
looked, and the sky cube could not pick a level by roughness.
- An OpenGL ES 3.1 backend for the 3D scene and the GUI. SDL_GPU has
Vulkan, Direct3D 12 and Metal and no OpenGL of any kind, which left the
cheap Linux handhelds and the Raspberry Pi with 2D only: their Mali and
VideoCore parts have a mature GLES driver and either no Vulkan or an
immature one. The subset of SDL_GPU the scene and the GUI use is now
behind one table of function pointers with two implementations, so both
files stay the single description of what Singe draws, and the backend is
chosen at run time -- one aarch64 binary serves a board with Vulkan and one
without. The ES shaders come from the same HLSL through SPIRV-Cross, and
the GL entry points are loaded through SDL rather than linked, so a machine
with no GLES driver simply keeps its 2D renderer. The Raspberry Pi 4
remains the minimum for 3D: the Pi 3 has only OpenGL ES 2.0, which cannot
express the scene's skinning or morph targets.
- spriteDraw() now has two more forms. In addition to being able to draw - spriteDraw() now has two more forms. In addition to being able to draw
regular sprites and stretched sprites it can now draw both using the regular sprites and stretched sprites it can now draw both using the
sprite's center as the anchor instead of the upper left. This is highly sprite's center as the anchor instead of the upper left. This is highly

View file

@ -327,6 +327,12 @@ set(SINGE_SOURCE
src/midiIo.h src/midiIo.h
thirdparty/tinysoundfont/tsf.h thirdparty/tinysoundfont/tsf.h
thirdparty/tinysoundfont/tml.h thirdparty/tinysoundfont/tml.h
src/render.c
src/render.h
src/renderGles.c
src/renderGlesApi.h
src/renderGlesLoad.c
src/renderGpu.c
src/scene.c src/scene.c
src/scene.h src/scene.h
src/singe.c src/singe.c
@ -755,10 +761,19 @@ if(NOT SINGE_SHADERCROSS)
message(FATAL_ERROR "SDL_shadercross is needed to compile the scene shaders; build through the superbuild (cmake --preset ...) or set SINGE_SHADERCROSS.") message(FATAL_ERROR "SDL_shadercross is needed to compile the scene shaders; build through the superbuild (cmake --preset ...) or set SINGE_SHADERCROSS.")
endif() endif()
endif() endif()
# SPIRV-Cross turns the same SPIR-V into GLSL ES for the GLES backend (PLAN.md section 56). Unlike
# shadercross this one is not fatal when missing: the header then carries null ES strings, the GLES
# backend declines to start, and every other platform builds exactly as before.
if(NOT SINGE_SPIRV_CROSS)
find_program(SINGE_SPIRV_CROSS spirv-cross)
endif()
if(NOT SINGE_SPIRV_CROSS)
message(WARNING "spirv-cross was not found; this build will have no OpenGL ES shaders and so no GLES backend.")
endif()
set(shaderHeader ${CMAKE_BINARY_DIR}/generated/shaders/sceneShaders.h) set(shaderHeader ${CMAKE_BINARY_DIR}/generated/shaders/sceneShaders.h)
add_custom_command( add_custom_command(
OUTPUT ${shaderHeader} OUTPUT ${shaderHeader}
COMMAND ${CMAKE_COMMAND} -DSHADERCROSS=${SINGE_SHADERCROSS} -DSOURCE=${CMAKE_SOURCE_DIR}/src/shaders/scene.hlsl -DOUTPUT=${shaderHeader} -P ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake COMMAND ${CMAKE_COMMAND} -DSHADERCROSS=${SINGE_SHADERCROSS} -DSPIRVCROSS=${SINGE_SPIRV_CROSS} -DSOURCE=${CMAKE_SOURCE_DIR}/src/shaders/scene.hlsl -DOUTPUT=${shaderHeader} -P ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake
DEPENDS ${CMAKE_SOURCE_DIR}/src/shaders/scene.hlsl ${CMAKE_SOURCE_DIR}/src/sceneShared.h ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake DEPENDS ${CMAKE_SOURCE_DIR}/src/shaders/scene.hlsl ${CMAKE_SOURCE_DIR}/src/sceneShared.h ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake
COMMENT "Compiling the scene shaders" COMMENT "Compiling the scene shaders"
) )
@ -769,7 +784,7 @@ set(guiShaderHeader ${CMAKE_BINARY_DIR}/generated/shaders/guiShaders.h)
set(guiShaderEntries "guiVertex:vertex;guiFragmentColor:fragment;guiFragmentTexture:fragment;guiFragmentFilter:fragment;guiFragmentBlur:fragment;guiFragmentColorMatrix:fragment;guiFragmentGradient:fragment") set(guiShaderEntries "guiVertex:vertex;guiFragmentColor:fragment;guiFragmentTexture:fragment;guiFragmentFilter:fragment;guiFragmentBlur:fragment;guiFragmentColorMatrix:fragment;guiFragmentGradient:fragment")
add_custom_command( add_custom_command(
OUTPUT ${guiShaderHeader} OUTPUT ${guiShaderHeader}
COMMAND ${CMAKE_COMMAND} -DSHADERCROSS=${SINGE_SHADERCROSS} -DSOURCE=${CMAKE_SOURCE_DIR}/src/shaders/gui.hlsl -DOUTPUT=${guiShaderHeader} "-DENTRIES=${guiShaderEntries}" -DPREFIX=guiShader -DTYPE=GuiShaderT -P ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake COMMAND ${CMAKE_COMMAND} -DSHADERCROSS=${SINGE_SHADERCROSS} -DSPIRVCROSS=${SINGE_SPIRV_CROSS} -DSOURCE=${CMAKE_SOURCE_DIR}/src/shaders/gui.hlsl -DOUTPUT=${guiShaderHeader} "-DENTRIES=${guiShaderEntries}" -DPREFIX=guiShader -DTYPE=GuiShaderT -P ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake
DEPENDS ${CMAKE_SOURCE_DIR}/src/shaders/gui.hlsl ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake DEPENDS ${CMAKE_SOURCE_DIR}/src/shaders/gui.hlsl ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake
COMMENT "Compiling the GUI shaders" COMMENT "Compiling the GUI shaders"
VERBATIM VERBATIM

View file

@ -122,7 +122,8 @@ newer, the 64-bit Raspberry Pi OS among them. It is not Pi specific: the
decoder it builds talks to any V4L2 memory-to-memory device, so one binary decoder it builds talks to any V4L2 memory-to-memory device, so one binary
also serves Amlogic, Exynos, Qualcomm and other boards whose kernel offers also serves Amlogic, Exynos, Qualcomm and other boards whose kernel offers
one, and it carries Rockchip's own decoders besides. 3D games need a Pi 4 one, and it carries Rockchip's own decoders besides. 3D games need a Pi 4
or later; the Pi 3 has no Vulkan driver and plays 2D games only. The build or later, which has OpenGL ES 3.1 (and Vulkan); the Pi 3 has only OpenGL
ES 2.0 and plays 2D games only. The build
uses zig. The platform headers and libraries it links against are Debian uses zig. The platform headers and libraries it links against are Debian
bookworm arm64 packages listed in cmake/zig/arm64Packages.cmake, fetched bookworm arm64 packages listed in cmake/zig/arm64Packages.cmake, fetched
from snapshot.debian.org and unpacked with dpkg-deb into from snapshot.debian.org and unpacked with dpkg-deb into

View file

@ -58,9 +58,13 @@ endfunction()
# SDL3 for the host, shared, with nothing SDL_shadercross's tool does not use. # SDL3 for the host, shared, with nothing SDL_shadercross's tool does not use.
singeHostProject(hostSDL3 ${SB_THIRDPARTY}/SDL3 "" "-DSDL_SHARED=ON;-DSDL_STATIC=OFF;-DSDL_TESTS=OFF;-DSDL_EXAMPLES=OFF;-DSDL_AUDIO=OFF;-DSDL_VIDEO=OFF;-DSDL_GPU=OFF;-DSDL_RENDER=OFF;-DSDL_CAMERA=OFF;-DSDL_JOYSTICK=OFF;-DSDL_HAPTIC=OFF;-DSDL_HIDAPI=OFF;-DSDL_POWER=OFF;-DSDL_SENSOR=OFF;-DSDL_DIALOG=OFF;-DSDL_UNIX_CONSOLE_BUILD=ON") singeHostProject(hostSDL3 ${SB_THIRDPARTY}/SDL3 "" "-DSDL_SHARED=ON;-DSDL_STATIC=OFF;-DSDL_TESTS=OFF;-DSDL_EXAMPLES=OFF;-DSDL_AUDIO=OFF;-DSDL_VIDEO=OFF;-DSDL_GPU=OFF;-DSDL_RENDER=OFF;-DSDL_CAMERA=OFF;-DSDL_JOYSTICK=OFF;-DSDL_HAPTIC=OFF;-DSDL_HIDAPI=OFF;-DSDL_POWER=OFF;-DSDL_SENSOR=OFF;-DSDL_DIALOG=OFF;-DSDL_UNIX_CONSOLE_BUILD=ON")
# SPIRV-Cross as the shared C library SDL_shadercross looks for. # SPIRV-Cross as the shared C library SDL_shadercross looks for, and as the command line tool that
singeHostProject(spirvCross ${SB_THIRDPARTY}/SPIRV-Cross "" "-DSPIRV_CROSS_SHARED=ON;-DSPIRV_CROSS_STATIC=OFF;-DSPIRV_CROSS_CLI=OFF;-DSPIRV_CROSS_ENABLE_TESTS=OFF") # turns SPIR-V into GLSL ES for the GLES backend -- SDL_shadercross emits DXBC, DXIL, MSL, SPIRV,
# HLSL and JSON and no GLSL of any kind, so the ES shaders come from this one (PLAN.md section 56).
# The CLI needs the static library as well as the shared one.
singeHostProject(spirvCross ${SB_THIRDPARTY}/SPIRV-Cross "" "-DSPIRV_CROSS_SHARED=ON;-DSPIRV_CROSS_STATIC=ON;-DSPIRV_CROSS_CLI=ON;-DSPIRV_CROSS_ENABLE_TESTS=OFF")
# SDL_shadercross, unvendored: SPIRV-Cross and SDL3 from the host prefix, DXC from its download. # SDL_shadercross, unvendored: SPIRV-Cross and SDL3 from the host prefix, DXC from its download.
singeHostProject(shadercross ${SB_THIRDPARTY}/SDL_shadercross "hostSDL3;spirvCross" "-DSDLSHADERCROSS_VENDORED=OFF;-DSDLSHADERCROSS_DXC=ON;-DSDLSHADERCROSS_SHARED=OFF;-DSDLSHADERCROSS_STATIC=ON;-DSDLSHADERCROSS_CLI=ON;-DSDLSHADERCROSS_INSTALL=ON;-DSDLSHADERCROSS_INSTALL_CPACK=OFF;-DCMAKE_PREFIX_PATH=${hostPrefix}|${hostDxc};-DSDL3_DIR=${hostPrefix}/lib/cmake/SDL3") singeHostProject(shadercross ${SB_THIRDPARTY}/SDL_shadercross "hostSDL3;spirvCross" "-DSDLSHADERCROSS_VENDORED=OFF;-DSDLSHADERCROSS_DXC=ON;-DSDLSHADERCROSS_SHARED=OFF;-DSDLSHADERCROSS_STATIC=ON;-DSDLSHADERCROSS_CLI=ON;-DSDLSHADERCROSS_INSTALL=ON;-DSDLSHADERCROSS_INSTALL_CPACK=OFF;-DCMAKE_PREFIX_PATH=${hostPrefix}|${hostDxc};-DSDL3_DIR=${hostPrefix}/lib/cmake/SDL3")
set(SINGE_SHADERCROSS ${hostPrefix}/bin/shadercross) set(SINGE_SHADERCROSS ${hostPrefix}/bin/shadercross)
set(SINGE_SPIRV_CROSS ${hostPrefix}/bin/spirv-cross)

View file

@ -1,7 +1,11 @@
# Compiles src/shaders/scene.hlsl (which includes src/sceneShared.h) into a C header of SPIR-V, DXIL and MSL blobs with # Compiles src/shaders/scene.hlsl (which includes src/sceneShared.h) into a C header of SPIR-V, DXIL, MSL and GLSL ES
# SDL_shadercross. Run by the build as # with SDL_shadercross and SPIRV-Cross. Run by the build as
# cmake -DSHADERCROSS=<tool> -DSOURCE=<scene.hlsl> -DOUTPUT=<sceneShaders.h> -P shaderHeader.cmake # cmake -DSHADERCROSS=<tool> -DSPIRVCROSS=<tool> -DSOURCE=<scene.hlsl> -DOUTPUT=<sceneShaders.h> -P shaderHeader.cmake
# so the header is generated into the build tree like the icon and the other embedded files. # so the header is generated into the build tree like the icon and the other embedded files.
#
# SDL_shadercross has no GLSL destination (DXBC, DXIL, MSL, SPIRV, HLSL, JSON), so the ES form is a second step over the
# SPIR-V it already produced: one pipeline, four outputs. SPIRVCROSS may be empty, and then the ES strings are null and
# the GLES backend refuses to start -- which is what a build without the host tool should do rather than fail.
# Invoked in script mode: -DSHADERCROSS -DSOURCE=<file.hlsl> -DOUTPUT=<header> -DENTRIES=<name:stage;...> # Invoked in script mode: -DSHADERCROSS -DSOURCE=<file.hlsl> -DOUTPUT=<header> -DENTRIES=<name:stage;...>
# -DPREFIX=<lowerCamel prefix> -DTYPE=<struct name>. ENTRIES, PREFIX and TYPE default to the scene's. # -DPREFIX=<lowerCamel prefix> -DTYPE=<struct name>. ENTRIES, PREFIX and TYPE default to the scene's.
@ -19,6 +23,28 @@ string(TOUPPER "${PREFIX}" guard)
string(REPLACE "SHADER" "_SHADERS_H" guard "${guard}") string(REPLACE "SHADER" "_SHADERS_H" guard "${guard}")
set(entries ${ENTRIES}) set(entries ${ENTRIES})
set(formats SPIRV DXIL MSL) set(formats SPIRV DXIL MSL)
# GLSL ES 3.10 for the GLES backend. Three switches matter and each is load bearing:
# --es --version 310 ES 3.1, the floor the scene needs; its storage buffers are ES 3.1 only.
# --combined-samplers-inherit-bindings
# HLSL's separate Texture2D and SamplerState have no ES equivalent, so SPIRV-Cross folds
# each pair into one sampler and keeps the HLSL t-register as the binding, which is the
# slot the engine already binds by.
# --remove-unused-variables Without it every entry point declares every cbuffer and both storage buffers in the file:
# fragmentMain came out with twelve blocks instead of two, and a fragment shader that
# merely declares an unused SSBO can fail to link where MAX_FRAGMENT_SHADER_STORAGE_BLOCKS
# is zero, which ES 3.1 permits.
# --flip-vert-y SDL_GPU puts the framebuffer origin at the top left, as Vulkan, D3D12 and Metal do, and
# GL puts it at the bottom left. Without this every render to a texture arrives upside
# down -- the GUI came out mirrored the first time this ran. Inverting gl_Position.y is
# the same fix as a negative viewport height, and it also reverses triangle winding, which
# renderGles.c undoes by flipping the front face it sets.
# --fixup-clipspace HLSL puts clip-space Z in [0, w] and GL in [-w, w], and GL then stores (z/w + 1) / 2.
# Without the rewrite a depth written as d lands in the buffer as (d + 1) / 2, while the
# shader's own shadow comparison still computes d -- so everything in the near half of a
# light's range read as lit. Sponza showed it as a 2.6x too bright mid-shadow band with
# correct highlights and correct deep shadow, which is what a half-range comparison does.
set(esslFlags --es --version 310 --combined-samplers-inherit-bindings --remove-unused-variables --flip-vert-y --fixup-clipspace)
get_filename_component(sourceDir ${SOURCE} DIRECTORY) get_filename_component(sourceDir ${SOURCE} DIRECTORY)
get_filename_component(includeDir ${sourceDir} DIRECTORY) get_filename_component(includeDir ${sourceDir} DIRECTORY)
get_filename_component(outputDir ${OUTPUT} DIRECTORY) get_filename_component(outputDir ${OUTPUT} DIRECTORY)
@ -26,10 +52,10 @@ set(work ${outputDir}/work)
file(MAKE_DIRECTORY ${work}) file(MAKE_DIRECTORY ${work})
set(header "// Generated from ${sourceName} by cmake/shaderHeader.cmake with SDL_shadercross; do not edit.\n") set(header "// Generated from ${sourceName} by cmake/shaderHeader.cmake with SDL_shadercross; do not edit.\n")
string(APPEND header "// SPIR-V for Vulkan, DXIL for Direct3D 12, MSL for Metal, one set per entry point.\n\n") string(APPEND header "// SPIR-V for Vulkan, DXIL for Direct3D 12, MSL for Metal, GLSL ES for OpenGL ES, one set per entry point.\n\n")
string(APPEND header "#ifndef ${guard}\n#define ${guard}\n\n#include <stddef.h>\n\n") string(APPEND header "#ifndef ${guard}\n#define ${guard}\n\n#include <stddef.h>\n#include <stdlib.h>\n\n")
string(REGEX REPLACE "T$" "S" structName "${TYPE}") string(REGEX REPLACE "T$" "S" structName "${TYPE}")
string(APPEND header "typedef struct ${structName} {\n\tconst char *entryPoint;\n\tconst unsigned char *spirv;\n\tsize_t spirvSize;\n\tconst unsigned char *dxil;\n\tsize_t dxilSize;\n\tconst unsigned char *msl;\n\tsize_t mslSize;\n} ${TYPE};\n\n") string(APPEND header "typedef struct ${structName} {\n\tconst char *entryPoint;\n\tconst unsigned char *spirv;\n\tsize_t spirvSize;\n\tconst unsigned char *dxil;\n\tsize_t dxilSize;\n\tconst unsigned char *msl;\n\tsize_t mslSize;\n\tconst char *essl; // GLSL ES 3.10 source, or NULL when the host tool was absent\n} ${TYPE};\n\n")
foreach(entry IN LISTS entries) foreach(entry IN LISTS entries)
string(REPLACE ":" ";" parts ${entry}) string(REPLACE ":" ";" parts ${entry})
list(GET parts 0 name) list(GET parts 0 name)
@ -45,10 +71,31 @@ foreach(entry IN LISTS entries)
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," bytes "${hex}") string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," bytes "${hex}")
string(APPEND header "static const unsigned char _${name}${format}[] = {\n\t${bytes}\n};\n\n") string(APPEND header "static const unsigned char _${name}${format}[] = {\n\t${bytes}\n};\n\n")
endforeach() endforeach()
# The ES form, cross compiled from the SPIR-V above. Emitted as a C string rather than bytes because that is what
# glShaderSource takes; the escaping is only quotes and backslashes, neither of which GLSL ES has a use for.
set(esslSymbol "NULL")
if(SPIRVCROSS)
set(essl ${work}/${name}.essl)
execute_process(COMMAND ${SPIRVCROSS} ${esslFlags} ${work}/${name}.spirv --output ${essl} RESULT_VARIABLE code OUTPUT_VARIABLE out ERROR_VARIABLE err)
if(NOT code EQUAL 0)
message(FATAL_ERROR "Shader ${name} (GLSL ES) failed to cross compile:\n${out}\n${err}")
endif()
file(READ ${essl} esslText)
# SPIRV-Cross defaults an ES fragment shader to "precision mediump float", and a struct
# declared inside a uniform block carries no qualifier of its own, so Light's members inherit
# mediump -- fp16 on a real tiler. The scene's own values do not fit that: a camera or a
# light tens of units from the origin loses whole units of position. Ask for highp instead.
string(REPLACE "precision mediump float;" "precision highp float;" esslText "${esslText}")
string(REPLACE "\\" "\\\\" esslText "${esslText}")
string(REPLACE "\"" "\\\"" esslText "${esslText}")
string(REPLACE "\n" "\\n\"\n\t\"" esslText "${esslText}")
string(APPEND header "static const char _${name}ESSL[] =\n\t\"${esslText}\";\n\n")
set(esslSymbol "_${name}ESSL")
endif()
string(SUBSTRING ${name} 0 1 initial) string(SUBSTRING ${name} 0 1 initial)
string(SUBSTRING ${name} 1 -1 rest) string(SUBSTRING ${name} 1 -1 rest)
string(TOUPPER ${initial} initial) string(TOUPPER ${initial} initial)
string(APPEND header "static const ${TYPE} ${PREFIX}${initial}${rest} = { \"${name}\", _${name}SPIRV, sizeof(_${name}SPIRV), _${name}DXIL, sizeof(_${name}DXIL), _${name}MSL, sizeof(_${name}MSL) };\n\n") string(APPEND header "static const ${TYPE} ${PREFIX}${initial}${rest} = { \"${name}\", _${name}SPIRV, sizeof(_${name}SPIRV), _${name}DXIL, sizeof(_${name}DXIL), _${name}MSL, sizeof(_${name}MSL), ${esslSymbol} };\n\n")
endforeach() endforeach()
string(APPEND header "#endif\n") string(APPEND header "#endif\n")
file(WRITE ${OUTPUT} "${header}") file(WRITE ${OUTPUT} "${header}")

View file

@ -840,10 +840,13 @@ do not allow you to use stereo, you can use Virtual Audio Cable to fix this:
*Does 3D work on a Raspberry Pi?* *Does 3D work on a Raspberry Pi?*
On a Raspberry Pi 4 or later, yes: they have the Vulkan driver the 3D scene On a Raspberry Pi 4 or later, yes. They have both a Vulkan driver and OpenGL
needs. The Pi 3 and earlier do not, so they run 2D games (2D physics included) ES 3.1, and Singe uses whichever it finds. The Pi 3 has only OpenGL ES 2.0,
exactly as before and refuse the first 3D call. The Pi 4 is the minimum for which cannot express the scene's skinning or morph targets, so it runs 2D games
any game that uses the 3D Scenes chapter. (2D physics included) exactly as before and refuses the first 3D call. The Pi 4
is the minimum for any game that uses the 3D Scenes chapter. The same applies to
the cheap handhelds: a Mali-G31 or G610 has the ES 3.1 the scene needs, whether
or not it has Vulkan.
*Why does my audio stutter on the Raspberry Pi?* *Why does my audio stutter on the Raspberry Pi?*
@ -2557,11 +2560,15 @@ end
==== Performance and Requirements ==== Performance and Requirements
The scene needs a GPU that speaks Vulkan (Linux), Direct3D 12 (Windows 10 The scene needs a GPU that speaks Vulkan (Linux), Direct3D 12 (Windows 10
and later) or Metal (macOS). The Raspberry Pi 4 is the minimum Pi for 3D: and later), Metal (macOS) or OpenGL ES 3.1. The ES backend is what brings in
it and every later model have a Vulkan driver, the Pi 3 and earlier do not. the cheap Linux handhelds and the Raspberry Pi, whose Mali and VideoCore parts
On a machine without a suitable GPU, 2D games run exactly as before (2D have a mature GLES driver and either no Vulkan at all or an immature one; it is
physics included) and the first 3D call ends the game with an error naming chosen automatically when none of the other three is available, and it draws
the problem. the same picture from the same shaders. The Raspberry Pi 4 remains the minimum
Pi for 3D: the Pi 3 has only OpenGL ES 2.0, which cannot express the scene's
skinning or morph targets. On a machine without any of them, 2D games run
exactly as before (2D physics included) and the first 3D call ends the game
with an error naming the problem.
Meshes outside the camera's view are skipped, and copies of the same mesh Meshes outside the camera's view are skipped, and copies of the same mesh
with the same material draw as one instanced call, so a forest of one tree with the same material draw as one instanced call, so a forest of one tree

View file

@ -39,6 +39,7 @@
#include <RmlUi/Lua.h> #include <RmlUi/Lua.h>
#include "gui.h" #include "gui.h"
#include "guiRender.h" #include "guiRender.h"
#include "render.h"
extern "C" { extern "C" {
#include "util.h" #include "util.h"
#include "vfs.h" #include "vfs.h"
@ -459,10 +460,10 @@ static void _releaseContext(GuiContextT *context) {
SDL_DestroyTexture(context->wrap); SDL_DestroyTexture(context->wrap);
} }
if (context->target != nullptr) { if (context->target != nullptr) {
SDL_ReleaseGPUTexture(_device, context->target); rgpuReleaseTexture(_device, context->target);
} }
if (context->stencil != nullptr) { if (context->stencil != nullptr) {
SDL_ReleaseGPUTexture(_device, context->stencil); rgpuReleaseTexture(_device, context->stencil);
} }
_render->ReleaseLayers((uint32_t)context->width, (uint32_t)context->height); _render->ReleaseLayers((uint32_t)context->width, (uint32_t)context->height);
memset(context, 0, sizeof(GuiContextT)); memset(context, 0, sizeof(GuiContextT));
@ -478,12 +479,12 @@ static void _renderContext(GuiContextT *context, SDL_GPUCommandBuffer *commands)
target.load_op = SDL_GPU_LOADOP_CLEAR; target.load_op = SDL_GPU_LOADOP_CLEAR;
target.store_op = SDL_GPU_STOREOP_STORE; target.store_op = SDL_GPU_STOREOP_STORE;
target.clear_color = { 0.0f, 0.0f, 0.0f, 0.0f }; target.clear_color = { 0.0f, 0.0f, 0.0f, 0.0f };
pass = SDL_BeginGPURenderPass(commands, &target, 1, nullptr); pass = rgpuBeginRenderPass(commands, &target, 1, nullptr);
if (pass == nullptr) { if (pass == nullptr) {
utilTrace("Gui: %s", SDL_GetError()); utilTrace("Gui: %s", SDL_GetError());
return; return;
} }
SDL_EndGPURenderPass(pass); rgpuEndRenderPass(pass);
_render->BeginFrame(commands, context->target, context->stencil, (uint32_t)context->width, (uint32_t)context->height); _render->BeginFrame(commands, context->target, context->stencil, (uint32_t)context->width, (uint32_t)context->height);
context->context->Render(); context->context->Render();
_render->EndFrame(); _render->EndFrame();
@ -666,7 +667,7 @@ bool guiInit(SDL_GPUDevice *device, SDL_Renderer *renderer, SDL_Window *window)
utilTrace("Gui: no GPU device; the GUI is unavailable"); utilTrace("Gui: no GPU device; the GUI is unavailable");
return false; return false;
} }
_targetFormat = SDL_GetGPUSwapchainTextureFormat(device, window); _targetFormat = rgpuGetSwapchainTextureFormat(device, window);
_wrapFormat = _pixelFormatOf(_targetFormat); _wrapFormat = _pixelFormatOf(_targetFormat);
if (_wrapFormat == SDL_PIXELFORMAT_UNKNOWN) { if (_wrapFormat == SDL_PIXELFORMAT_UNKNOWN) {
utilTrace("Gui: unsupported swapchain format %d; the GUI is unavailable", (int32_t)_targetFormat); utilTrace("Gui: unsupported swapchain format %d; the GUI is unavailable", (int32_t)_targetFormat);
@ -838,7 +839,6 @@ bool guiMouseWheel(int32_t gui, float delta) {
int32_t guiNew(int32_t width, int32_t height) { int32_t guiNew(int32_t width, int32_t height) {
GuiContextT *context = nullptr; GuiContextT *context = nullptr;
SDL_GPUTextureCreateInfo info = {}; SDL_GPUTextureCreateInfo info = {};
SDL_PropertiesID props = 0;
int32_t slot = GUI_NO_HANDLE; int32_t slot = GUI_NO_HANDLE;
int32_t i = 0; int32_t i = 0;
@ -870,7 +870,7 @@ int32_t guiNew(int32_t width, int32_t height) {
info.layer_count_or_depth = 1; info.layer_count_or_depth = 1;
info.num_levels = 1; info.num_levels = 1;
info.sample_count = SDL_GPU_SAMPLECOUNT_1; info.sample_count = SDL_GPU_SAMPLECOUNT_1;
context->target = SDL_CreateGPUTexture(_device, &info); context->target = rgpuCreateTexture(_device, &info);
if (context->target == nullptr) { if (context->target == nullptr) {
_setError("%s", SDL_GetError()); _setError("%s", SDL_GetError());
_releaseContext(context); _releaseContext(context);
@ -881,21 +881,14 @@ int32_t guiNew(int32_t width, int32_t height) {
if (_render->StencilFormat() != SDL_GPU_TEXTUREFORMAT_INVALID) { if (_render->StencilFormat() != SDL_GPU_TEXTUREFORMAT_INVALID) {
info.format = _render->StencilFormat(); info.format = _render->StencilFormat();
info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET; info.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
context->stencil = SDL_CreateGPUTexture(_device, &info); context->stencil = rgpuCreateTexture(_device, &info);
if (context->stencil == nullptr) { if (context->stencil == nullptr) {
_setError("%s", SDL_GetError()); _setError("%s", SDL_GetError());
_releaseContext(context); _releaseContext(context);
return GUI_NO_HANDLE; return GUI_NO_HANDLE;
} }
} }
props = SDL_CreateProperties(); context->wrap = renderWrapTexture(_renderer, context->target, (SDL_PixelFormat)_wrapFormat, width, height);
SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER, context->target);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, _wrapFormat);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, width);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, height);
context->wrap = SDL_CreateTextureWithProperties(_renderer, props);
SDL_DestroyProperties(props);
if (context->wrap == nullptr) { if (context->wrap == nullptr) {
_setError("%s", SDL_GetError()); _setError("%s", SDL_GetError());
_releaseContext(context); _releaseContext(context);
@ -1024,7 +1017,7 @@ void guiUpdate(double seconds) {
} }
} }
_freeDeadListeners(); _freeDeadListeners();
commands = SDL_AcquireGPUCommandBuffer(_device); commands = rgpuAcquireCommandBuffer(_device);
if (commands == nullptr) { if (commands == nullptr) {
utilTrace("Gui: %s", SDL_GetError()); utilTrace("Gui: %s", SDL_GetError());
return; return;
@ -1036,7 +1029,7 @@ void guiUpdate(double seconds) {
} }
} }
_render->GetStats(&_stats); _render->GetStats(&_stats);
SDL_SubmitGPUCommandBuffer(commands); rgpuSubmitCommandBuffer(commands);
} }

View file

@ -69,6 +69,7 @@ extern "C" {
#include "decode.h" #include "decode.h"
} }
#include "guiRender.h" #include "guiRender.h"
#include "render.h"
#include "shaders/guiShaders.h" #include "shaders/guiShaders.h"
#define BYTES_PER_PIXEL 4 // RGBA8, as RmlUi hands textures over and as the layers are #define BYTES_PER_PIXEL 4 // RGBA8, as RmlUi hands textures over and as the layers are
@ -82,6 +83,7 @@ extern "C" {
#define UNIFORM_TRANSFORM 0 #define UNIFORM_TRANSFORM 0
#define UNIFORM_TRANSLATE 1 #define UNIFORM_TRANSLATE 1
#define PASS_UNIFORMS 1 // The filter, blur and colour matrix shaders' one block each #define PASS_UNIFORMS 1 // The filter, blur and colour matrix shaders' one block each
#define GUI_SAMPLER_MAX_LOD 1000.0f // "No clamp": walk the whole mipmap chain
#define FILTER_SAMPLERS 2 // guiFragmentFilter: the source and the mask image #define FILTER_SAMPLERS 2 // guiFragmentFilter: the source and the mask image
#define SCREEN_QUAD_VERTICES 4 #define SCREEN_QUAD_VERTICES 4
#define SCREEN_QUAD_INDICES 6 #define SCREEN_QUAD_INDICES 6
@ -185,7 +187,7 @@ static void _blurWeights(float sigma, float *weights) {
// A shader from the generated blobs, in whichever format the device takes (as the scene does). // A shader from the generated blobs, in whichever format the device takes (as the scene does).
static SDL_GPUShader *_createShader(SDL_GPUDevice *device, const GuiShaderT *shader, SDL_GPUShaderStage stage, uint32_t samplers, uint32_t uniforms) { static SDL_GPUShader *_createShader(SDL_GPUDevice *device, const GuiShaderT *shader, SDL_GPUShaderStage stage, uint32_t samplers, uint32_t uniforms) {
SDL_GPUShaderCreateInfo info; SDL_GPUShaderCreateInfo info;
SDL_GPUShaderFormat formats = SDL_GetGPUShaderFormats(device); SDL_GPUShaderFormat formats = rgpuGetShaderFormats(device);
SDL_GPUShader *result = nullptr; SDL_GPUShader *result = nullptr;
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
@ -201,15 +203,19 @@ static SDL_GPUShader *_createShader(SDL_GPUDevice *device, const GuiShaderT *sha
info.code = shader->msl; info.code = shader->msl;
info.code_size = shader->mslSize; info.code_size = shader->mslSize;
info.format = SDL_GPU_SHADERFORMAT_MSL; info.format = SDL_GPU_SHADERFORMAT_MSL;
} else if (formats & RGPU_SHADERFORMAT_ESSL) {
info.code = (const Uint8 *)shader->essl;
info.code_size = shader->essl != nullptr ? SDL_strlen(shader->essl) : 0;
info.format = RGPU_SHADERFORMAT_ESSL;
} else { } else {
Rml::Log::Message(Rml::Log::LT_ERROR, "the GPU device accepts none of SPIR-V, DXIL or MSL"); Rml::Log::Message(Rml::Log::LT_ERROR, "the device accepts none of SPIR-V, DXIL, MSL or GLSL ES");
return nullptr; return nullptr;
} }
info.entrypoint = shader->entryPoint; info.entrypoint = shader->entryPoint;
info.stage = stage; info.stage = stage;
info.num_samplers = samplers; info.num_samplers = samplers;
info.num_uniform_buffers = uniforms; info.num_uniform_buffers = uniforms;
result = SDL_CreateGPUShader(device, &info); result = rgpuCreateShader(device, &info);
if (result == nullptr) { if (result == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "shader %s: %s", shader->entryPoint, SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "shader %s: %s", shader->entryPoint, SDL_GetError());
} }
@ -341,7 +347,7 @@ void GuiRenderT::ReleaseShaderCommandT::Run(GuiRenderT &render) {
void GuiRenderT::ReleaseTextureCommandT::Run(GuiRenderT &render) { void GuiRenderT::ReleaseTextureCommandT::Run(GuiRenderT &render) {
SDL_ReleaseGPUTexture(render.device, reinterpret_cast<SDL_GPUTexture *>(handle)); rgpuReleaseTexture(render.device, reinterpret_cast<SDL_GPUTexture *>(handle));
} }
@ -644,11 +650,11 @@ void GuiRenderT::EndFrame() {
} }
commands.clear(); commands.clear();
if (copyPass != nullptr) { if (copyPass != nullptr) {
SDL_EndGPUCopyPass(copyPass); rgpuEndCopyPass(copyPass);
copyPass = nullptr; copyPass = nullptr;
} }
if (renderPass != nullptr) { if (renderPass != nullptr) {
SDL_EndGPURenderPass(renderPass); rgpuEndRenderPass(renderPass);
renderPass = nullptr; renderPass = nullptr;
} }
// The stack is balanced by contract; anything left is returned to the pool. // The stack is balanced by contract; anything left is returned to the pool.
@ -674,19 +680,19 @@ Rml::TextureHandle GuiRenderT::GenerateTexture(Rml::Span<const Rml::byte> source
transferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; transferInfo.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
transferInfo.size = size; transferInfo.size = size;
transfer = SDL_CreateGPUTransferBuffer(device, &transferInfo); transfer = rgpuCreateTransferBuffer(device, &transferInfo);
if (transfer == nullptr) { if (transfer == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create transfer buffer: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create transfer buffer: %s", SDL_GetError());
return 0; return 0;
} }
destination = SDL_MapGPUTransferBuffer(device, transfer, false); destination = rgpuMapTransferBuffer(device, transfer, false);
if (destination == nullptr) { if (destination == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to map transfer buffer: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to map transfer buffer: %s", SDL_GetError());
SDL_ReleaseGPUTransferBuffer(device, transfer); rgpuReleaseTransferBuffer(device, transfer);
return 0; return 0;
} }
memcpy(destination, source.data(), size); memcpy(destination, source.data(), size);
SDL_UnmapGPUTransferBuffer(device, transfer); rgpuUnmapTransferBuffer(device, transfer);
textureInfo.type = SDL_GPU_TEXTURETYPE_2D; textureInfo.type = SDL_GPU_TEXTURETYPE_2D;
textureInfo.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; textureInfo.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
textureInfo.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; textureInfo.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
@ -694,10 +700,10 @@ Rml::TextureHandle GuiRenderT::GenerateTexture(Rml::Span<const Rml::byte> source
textureInfo.height = (Uint32)sourceDimensions.y; textureInfo.height = (Uint32)sourceDimensions.y;
textureInfo.layer_count_or_depth = 1; textureInfo.layer_count_or_depth = 1;
textureInfo.num_levels = 1; textureInfo.num_levels = 1;
texture = SDL_CreateGPUTexture(device, &textureInfo); texture = rgpuCreateTexture(device, &textureInfo);
if (texture == nullptr) { if (texture == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create texture: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create texture: %s", SDL_GetError());
SDL_ReleaseGPUTransferBuffer(device, transfer); rgpuReleaseTransferBuffer(device, transfer);
return 0; return 0;
} }
upload.transfer_buffer = transfer; upload.transfer_buffer = transfer;
@ -705,25 +711,25 @@ Rml::TextureHandle GuiRenderT::GenerateTexture(Rml::Span<const Rml::byte> source
region.w = (Uint32)sourceDimensions.x; region.w = (Uint32)sourceDimensions.x;
region.h = (Uint32)sourceDimensions.y; region.h = (Uint32)sourceDimensions.y;
region.d = 1; region.d = 1;
uploadBuffer = SDL_AcquireGPUCommandBuffer(device); uploadBuffer = rgpuAcquireCommandBuffer(device);
if (uploadBuffer == nullptr) { if (uploadBuffer == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to acquire command buffer: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to acquire command buffer: %s", SDL_GetError());
SDL_ReleaseGPUTransferBuffer(device, transfer); rgpuReleaseTransferBuffer(device, transfer);
SDL_ReleaseGPUTexture(device, texture); rgpuReleaseTexture(device, texture);
return 0; return 0;
} }
pass = SDL_BeginGPUCopyPass(uploadBuffer); pass = rgpuBeginCopyPass(uploadBuffer);
if (pass == nullptr) { if (pass == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to begin copy pass: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to begin copy pass: %s", SDL_GetError());
SDL_ReleaseGPUTransferBuffer(device, transfer); rgpuReleaseTransferBuffer(device, transfer);
SDL_ReleaseGPUTexture(device, texture); rgpuReleaseTexture(device, texture);
SDL_CancelGPUCommandBuffer(uploadBuffer); rgpuCancelCommandBuffer(uploadBuffer);
return 0; return 0;
} }
SDL_UploadToGPUTexture(pass, &upload, &region, false); rgpuUploadToTexture(pass, &upload, &region, false);
SDL_ReleaseGPUTransferBuffer(device, transfer); rgpuReleaseTransferBuffer(device, transfer);
SDL_EndGPUCopyPass(pass); rgpuEndCopyPass(pass);
SDL_SubmitGPUCommandBuffer(uploadBuffer); rgpuSubmitCommandBuffer(uploadBuffer);
return reinterpret_cast<Rml::TextureHandle>(texture); return reinterpret_cast<Rml::TextureHandle>(texture);
} }
@ -747,7 +753,7 @@ GuiRenderT::GuiRenderT(SDL_GPUDevice *gpuDevice, SDL_Window *gpuWindow) {
linearSampler = nullptr; linearSampler = nullptr;
pointSampler = nullptr; pointSampler = nullptr;
linearClampSampler = nullptr; linearClampSampler = nullptr;
targetFormat = SDL_GetGPUSwapchainTextureFormat(device, window); targetFormat = rgpuGetSwapchainTextureFormat(device, window);
stencilFormat = SDL_GPU_TEXTUREFORMAT_INVALID; stencilFormat = SDL_GPU_TEXTUREFORMAT_INVALID;
screenQuad = nullptr; screenQuad = nullptr;
recordDepth = 0; recordDepth = 0;
@ -771,27 +777,30 @@ GuiRenderT::GuiRenderT(SDL_GPUDevice *gpuDevice, SDL_Window *gpuWindow) {
memset(&stats, 0, sizeof(stats)); memset(&stats, 0, sizeof(stats));
memset(pipelines, 0, sizeof(pipelines)); memset(pipelines, 0, sizeof(pipelines));
for (i = 0; i < SDL_arraysize(_stencilFormats); i++) { for (i = 0; i < SDL_arraysize(_stencilFormats); i++) {
if (SDL_GPUTextureSupportsFormat(device, _stencilFormats[i], SDL_GPU_TEXTURETYPE_2D, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) { if (rgpuTextureSupportsFormat(device, _stencilFormats[i], SDL_GPU_TEXTURETYPE_2D, SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
stencilFormat = _stencilFormats[i]; stencilFormat = _stencilFormats[i];
break; break;
} }
} }
createPipelines(); createPipelines();
// See scene.c: a zero max_lod clamps every lookup to mip level 0, because SDL passes it through
// to Vulkan and D3D12 unchanged.
info.max_lod = GUI_SAMPLER_MAX_LOD;
info.min_filter = SDL_GPU_FILTER_LINEAR; info.min_filter = SDL_GPU_FILTER_LINEAR;
info.mag_filter = SDL_GPU_FILTER_LINEAR; info.mag_filter = SDL_GPU_FILTER_LINEAR;
info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR;
info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_REPEAT;
info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_REPEAT;
info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_REPEAT;
linearSampler = SDL_CreateGPUSampler(device, &info); linearSampler = rgpuCreateSampler(device, &info);
info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE; info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
linearClampSampler = SDL_CreateGPUSampler(device, &info); linearClampSampler = rgpuCreateSampler(device, &info);
info.min_filter = SDL_GPU_FILTER_NEAREST; info.min_filter = SDL_GPU_FILTER_NEAREST;
info.mag_filter = SDL_GPU_FILTER_NEAREST; info.mag_filter = SDL_GPU_FILTER_NEAREST;
info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
pointSampler = SDL_CreateGPUSampler(device, &info); pointSampler = rgpuCreateSampler(device, &info);
if ((linearSampler == nullptr) || (pointSampler == nullptr) || (linearClampSampler == nullptr)) { if ((linearSampler == nullptr) || (pointSampler == nullptr) || (linearClampSampler == nullptr)) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create sampler: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create sampler: %s", SDL_GetError());
} }
@ -904,7 +913,7 @@ void GuiRenderT::ReleaseLayers(uint32_t width, uint32_t height) {
TargetT *pooled = targets[i].get(); TargetT *pooled = targets[i].get();
if (!pooled->inUse && (pooled->width == width) && (pooled->height == height)) { if (!pooled->inUse && (pooled->width == width) && (pooled->height == height)) {
SDL_ReleaseGPUTexture(device, pooled->texture); rgpuReleaseTexture(device, pooled->texture);
} else { } else {
targets[kept] = std::move(targets[i]); targets[kept] = std::move(targets[i]);
kept++; kept++;
@ -983,7 +992,7 @@ Rml::TextureHandle GuiRenderT::SaveLayerAsTexture() {
info.height = (Uint32)region.h; info.height = (Uint32)region.h;
info.layer_count_or_depth = 1; info.layer_count_or_depth = 1;
info.num_levels = 1; info.num_levels = 1;
texture = SDL_CreateGPUTexture(device, &info); texture = rgpuCreateTexture(device, &info);
if (texture == nullptr) { if (texture == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create layer texture: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create layer texture: %s", SDL_GetError());
return 0; return 0;
@ -1019,22 +1028,22 @@ void GuiRenderT::Shutdown() {
releaseGeometry(screenQuad); releaseGeometry(screenQuad);
screenQuad = nullptr; screenQuad = nullptr;
for (Rml::UniquePtr<BufferT> &buffer : buffers) { for (Rml::UniquePtr<BufferT> &buffer : buffers) {
SDL_ReleaseGPUTransferBuffer(device, buffer->transfer); rgpuReleaseTransferBuffer(device, buffer->transfer);
SDL_ReleaseGPUBuffer(device, buffer->buffer); rgpuReleaseBuffer(device, buffer->buffer);
} }
buffers.clear(); buffers.clear();
for (Rml::UniquePtr<TargetT> &pooled : targets) { for (Rml::UniquePtr<TargetT> &pooled : targets) {
SDL_ReleaseGPUTexture(device, pooled->texture); rgpuReleaseTexture(device, pooled->texture);
} }
targets.clear(); targets.clear();
SDL_ReleaseGPUSampler(device, linearSampler); rgpuReleaseSampler(device, linearSampler);
SDL_ReleaseGPUSampler(device, pointSampler); rgpuReleaseSampler(device, pointSampler);
SDL_ReleaseGPUSampler(device, linearClampSampler); rgpuReleaseSampler(device, linearClampSampler);
linearSampler = nullptr; linearSampler = nullptr;
pointSampler = nullptr; pointSampler = nullptr;
linearClampSampler = nullptr; linearClampSampler = nullptr;
for (i = 0; i < GUI_PIPELINE_COUNT; i++) { for (i = 0; i < GUI_PIPELINE_COUNT; i++) {
SDL_ReleaseGPUGraphicsPipeline(device, pipelines[i]); rgpuReleaseGraphicsPipeline(device, pipelines[i]);
pipelines[i] = nullptr; pipelines[i] = nullptr;
} }
} }
@ -1066,7 +1075,7 @@ GuiRenderT::TargetT *GuiRenderT::acquireTarget() {
info.layer_count_or_depth = 1; info.layer_count_or_depth = 1;
info.num_levels = 1; info.num_levels = 1;
made = Rml::MakeUnique<TargetT>(); made = Rml::MakeUnique<TargetT>();
made->texture = SDL_CreateGPUTexture(device, &info); made->texture = rgpuCreateTexture(device, &info);
made->width = targetWidth; made->width = targetWidth;
made->height = targetHeight; made->height = targetHeight;
made->inUse = true; made->inUse = true;
@ -1084,10 +1093,10 @@ bool GuiRenderT::beginCopyPass() {
return true; return true;
} }
if (renderPass != nullptr) { if (renderPass != nullptr) {
SDL_EndGPURenderPass(renderPass); rgpuEndRenderPass(renderPass);
renderPass = nullptr; renderPass = nullptr;
} }
copyPass = SDL_BeginGPUCopyPass(commandBuffer); copyPass = rgpuBeginCopyPass(commandBuffer);
if (copyPass == nullptr) { if (copyPass == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to begin copy pass: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to begin copy pass: %s", SDL_GetError());
return false; return false;
@ -1107,7 +1116,7 @@ bool GuiRenderT::beginRenderPass() {
return true; return true;
} }
if (copyPass != nullptr) { if (copyPass != nullptr) {
SDL_EndGPUCopyPass(copyPass); rgpuEndCopyPass(copyPass);
copyPass = nullptr; copyPass = nullptr;
} }
colorInfo.texture = target; colorInfo.texture = target;
@ -1122,7 +1131,7 @@ bool GuiRenderT::beginRenderPass() {
stencilInfo.stencil_store_op = SDL_GPU_STOREOP_STORE; stencilInfo.stencil_store_op = SDL_GPU_STOREOP_STORE;
stencilInfo.clear_stencil = MASK_OUTSIDE; stencilInfo.clear_stencil = MASK_OUTSIDE;
} }
renderPass = SDL_BeginGPURenderPass(commandBuffer, &colorInfo, 1, (stencil != nullptr) ? &stencilInfo : nullptr); renderPass = rgpuBeginRenderPass(commandBuffer, &colorInfo, 1, (stencil != nullptr) ? &stencilInfo : nullptr);
if (renderPass == nullptr) { if (renderPass == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to begin render pass: %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to begin render pass: %s", SDL_GetError());
return false; return false;
@ -1144,11 +1153,11 @@ void GuiRenderT::blitTarget(SDL_GPUTexture *source, SDL_Rect from, SDL_GPUTextur
return; return;
} }
if (copyPass != nullptr) { if (copyPass != nullptr) {
SDL_EndGPUCopyPass(copyPass); rgpuEndCopyPass(copyPass);
copyPass = nullptr; copyPass = nullptr;
} }
if (renderPass != nullptr) { if (renderPass != nullptr) {
SDL_EndGPURenderPass(renderPass); rgpuEndRenderPass(renderPass);
renderPass = nullptr; renderPass = nullptr;
} }
info.source.texture = source; info.source.texture = source;
@ -1163,7 +1172,7 @@ void GuiRenderT::blitTarget(SDL_GPUTexture *source, SDL_Rect from, SDL_GPUTextur
info.destination.h = (Uint32)to.h; info.destination.h = (Uint32)to.h;
info.load_op = SDL_GPU_LOADOP_LOAD; info.load_op = SDL_GPU_LOADOP_LOAD;
info.filter = SDL_GPU_FILTER_LINEAR; info.filter = SDL_GPU_FILTER_LINEAR;
SDL_BlitGPUTexture(commandBuffer, &info); rgpuBlitTexture(commandBuffer, &info);
stats.filterPasses++; stats.filterPasses++;
} }
@ -1240,7 +1249,7 @@ void GuiRenderT::copyTarget(SDL_GPUTexture *source, SDL_Rect region, SDL_GPUText
from.x = (Uint32)x; from.x = (Uint32)x;
from.y = (Uint32)y; from.y = (Uint32)y;
to.texture = destination; to.texture = destination;
SDL_CopyGPUTextureToTexture(copyPass, &from, &to, (Uint32)w, (Uint32)h, 1, false); rgpuCopyTextureToTexture(copyPass, &from, &to, (Uint32)w, (Uint32)h, 1, false);
} }
@ -1269,9 +1278,9 @@ void GuiRenderT::createPipelines() {
} }
if (!complete) { if (!complete) {
for (i = 0; i < GUI_FRAGMENT_COUNT; i++) { for (i = 0; i < GUI_FRAGMENT_COUNT; i++) {
SDL_ReleaseGPUShader(device, fragments[i]); rgpuReleaseShader(device, fragments[i]);
} }
SDL_ReleaseGPUShader(device, vertexShader); rgpuReleaseShader(device, vertexShader);
return; return;
} }
attributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2; attributes[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
@ -1320,15 +1329,15 @@ void GuiRenderT::createPipelines() {
info.depth_stencil_state.front_stencil_state = stencilOp; info.depth_stencil_state.front_stencil_state = stencilOp;
info.depth_stencil_state.back_stencil_state = stencilOp; info.depth_stencil_state.back_stencil_state = stencilOp;
info.fragment_shader = fragments[desc->fragment]; info.fragment_shader = fragments[desc->fragment];
pipelines[i] = SDL_CreateGPUGraphicsPipeline(device, &info); pipelines[i] = rgpuCreateGraphicsPipeline(device, &info);
if (pipelines[i] == nullptr) { if (pipelines[i] == nullptr) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create pipeline %d: %s", i, SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create pipeline %d: %s", i, SDL_GetError());
} }
} }
for (i = 0; i < GUI_FRAGMENT_COUNT; i++) { for (i = 0; i < GUI_FRAGMENT_COUNT; i++) {
SDL_ReleaseGPUShader(device, fragments[i]); rgpuReleaseShader(device, fragments[i]);
} }
SDL_ReleaseGPUShader(device, vertexShader); rgpuReleaseShader(device, vertexShader);
} }
@ -1344,28 +1353,28 @@ void GuiRenderT::drawGeometry(GeometryT *geometry, GuiPipelineE pipeline, SDL_GP
if ((geometry == nullptr) || (pipelines[pipeline] == nullptr) || (textureCount > FILTER_SAMPLERS) || !beginRenderPass()) { if ((geometry == nullptr) || (pipelines[pipeline] == nullptr) || (textureCount > FILTER_SAMPLERS) || !beginRenderPass()) {
return; return;
} }
SDL_BindGPUGraphicsPipeline(renderPass, pipelines[pipeline]); rgpuBindGraphicsPipeline(renderPass, pipelines[pipeline]);
if (textureCount > 0) { if (textureCount > 0) {
for (i = 0; i < textureCount; i++) { for (i = 0; i < textureCount; i++) {
bindings[i].texture = textures[i]; bindings[i].texture = textures[i];
bindings[i].sampler = sampler; bindings[i].sampler = sampler;
} }
SDL_BindGPUFragmentSamplers(renderPass, 0, bindings, textureCount); rgpuBindFragmentSamplers(renderPass, 0, bindings, textureCount);
} }
vertices.buffer = geometry->vertices->buffer; vertices.buffer = geometry->vertices->buffer;
indices.buffer = geometry->indices->buffer; indices.buffer = geometry->indices->buffer;
SDL_BindGPUVertexBuffers(renderPass, 0, &vertices, 1); rgpuBindVertexBuffers(renderPass, 0, &vertices, 1);
SDL_BindGPUIndexBuffer(renderPass, &indices, SDL_GPU_INDEXELEMENTSIZE_32BIT); rgpuBindIndexBuffer(renderPass, &indices, SDL_GPU_INDEXELEMENTSIZE_32BIT);
SDL_SetGPUScissor(renderPass, &scissor); rgpuSetScissor(renderPass, &scissor);
if (stencil != nullptr) { if (stencil != nullptr) {
SDL_SetGPUStencilReference(renderPass, reference); rgpuSetStencilReference(renderPass, reference);
} }
SDL_PushGPUVertexUniformData(commandBuffer, UNIFORM_TRANSFORM, &matrix, sizeof(matrix)); rgpuPushVertexUniformData(commandBuffer, UNIFORM_TRANSFORM, &matrix, sizeof(matrix));
SDL_PushGPUVertexUniformData(commandBuffer, UNIFORM_TRANSLATE, &translation, sizeof(translation)); rgpuPushVertexUniformData(commandBuffer, UNIFORM_TRANSLATE, &translation, sizeof(translation));
if (uniforms != nullptr) { if (uniforms != nullptr) {
SDL_PushGPUFragmentUniformData(commandBuffer, 0, uniforms, uniformSize); rgpuPushFragmentUniformData(commandBuffer, 0, uniforms, uniformSize);
} }
SDL_DrawGPUIndexedPrimitives(renderPass, (Uint32)geometry->indexCount, 1, 0, 0, 0); rgpuDrawIndexedPrimitives(renderPass, (Uint32)geometry->indexCount, 1, 0, 0, 0);
stats.drawCalls++; stats.drawCalls++;
if (_pipelineDescs[pipeline].mask) { if (_pipelineDescs[pipeline].mask) {
stats.maskWrites++; stats.maskWrites++;
@ -1492,15 +1501,15 @@ GuiRenderT::BufferT *GuiRenderT::requestBuffer(int32_t capacity, SDL_GPUBufferUs
transferInfo.size = (Uint32)capacity; transferInfo.size = (Uint32)capacity;
bufferInfo.usage = usage; bufferInfo.usage = usage;
bufferInfo.size = (Uint32)capacity; bufferInfo.size = (Uint32)capacity;
made->transfer = SDL_CreateGPUTransferBuffer(device, &transferInfo); made->transfer = rgpuCreateTransferBuffer(device, &transferInfo);
made->buffer = SDL_CreateGPUBuffer(device, &bufferInfo); made->buffer = rgpuCreateBuffer(device, &bufferInfo);
made->usage = usage; made->usage = usage;
made->capacity = capacity; made->capacity = capacity;
made->inUse = false; made->inUse = false;
if ((made->transfer == nullptr) || (made->buffer == nullptr)) { if ((made->transfer == nullptr) || (made->buffer == nullptr)) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create buffer(s): %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to create buffer(s): %s", SDL_GetError());
SDL_ReleaseGPUTransferBuffer(device, made->transfer); rgpuReleaseTransferBuffer(device, made->transfer);
SDL_ReleaseGPUBuffer(device, made->buffer); rgpuReleaseBuffer(device, made->buffer);
return nullptr; return nullptr;
} }
return buffers.insert(first, std::move(made))->get(); return buffers.insert(first, std::move(made))->get();
@ -1511,7 +1520,7 @@ GuiRenderT::BufferT *GuiRenderT::requestBuffer(int32_t capacity, SDL_GPUBufferUs
// draw begins one on the new target. // draw begins one on the new target.
void GuiRenderT::setTarget(SDL_GPUTexture *texture, bool clear) { void GuiRenderT::setTarget(SDL_GPUTexture *texture, bool clear) {
if ((renderPass != nullptr) && ((texture != target) || clear)) { if ((renderPass != nullptr) && ((texture != target) || clear)) {
SDL_EndGPURenderPass(renderPass); rgpuEndRenderPass(renderPass);
renderPass = nullptr; renderPass = nullptr;
} }
target = texture; target = texture;
@ -1538,31 +1547,31 @@ GuiRenderT::GeometryT *GuiRenderT::uploadGeometry(const void *vertexData, uint32
delete geometry; delete geometry;
return nullptr; return nullptr;
} }
vertexStage = SDL_MapGPUTransferBuffer(device, geometry->vertices->transfer, true); vertexStage = rgpuMapTransferBuffer(device, geometry->vertices->transfer, true);
indexStage = SDL_MapGPUTransferBuffer(device, geometry->indices->transfer, true); indexStage = rgpuMapTransferBuffer(device, geometry->indices->transfer, true);
if ((vertexStage == nullptr) || (indexStage == nullptr)) { if ((vertexStage == nullptr) || (indexStage == nullptr)) {
Rml::Log::Message(Rml::Log::LT_ERROR, "failed to map transfer buffer(s): %s", SDL_GetError()); Rml::Log::Message(Rml::Log::LT_ERROR, "failed to map transfer buffer(s): %s", SDL_GetError());
if (vertexStage != nullptr) { if (vertexStage != nullptr) {
SDL_UnmapGPUTransferBuffer(device, geometry->vertices->transfer); rgpuUnmapTransferBuffer(device, geometry->vertices->transfer);
} }
if (indexStage != nullptr) { if (indexStage != nullptr) {
SDL_UnmapGPUTransferBuffer(device, geometry->indices->transfer); rgpuUnmapTransferBuffer(device, geometry->indices->transfer);
} }
delete geometry; delete geometry;
return nullptr; return nullptr;
} }
memcpy(vertexStage, vertexData, vertexSize); memcpy(vertexStage, vertexData, vertexSize);
memcpy(indexStage, indexData, indexSize); memcpy(indexStage, indexData, indexSize);
SDL_UnmapGPUTransferBuffer(device, geometry->vertices->transfer); rgpuUnmapTransferBuffer(device, geometry->vertices->transfer);
SDL_UnmapGPUTransferBuffer(device, geometry->indices->transfer); rgpuUnmapTransferBuffer(device, geometry->indices->transfer);
location.transfer_buffer = geometry->vertices->transfer; location.transfer_buffer = geometry->vertices->transfer;
region.buffer = geometry->vertices->buffer; region.buffer = geometry->vertices->buffer;
region.size = vertexSize; region.size = vertexSize;
SDL_UploadToGPUBuffer(copyPass, &location, &region, false); rgpuUploadToBuffer(copyPass, &location, &region, false);
location.transfer_buffer = geometry->indices->transfer; location.transfer_buffer = geometry->indices->transfer;
region.buffer = geometry->indices->buffer; region.buffer = geometry->indices->buffer;
region.size = indexSize; region.size = indexSize;
SDL_UploadToGPUBuffer(copyPass, &location, &region, false); rgpuUploadToBuffer(copyPass, &location, &region, false);
geometry->indexCount = (int32_t)(indexSize / sizeof(int32_t)); geometry->indexCount = (int32_t)(indexSize / sizeof(int32_t));
geometry->vertices->inUse = true; geometry->vertices->inUse = true;
geometry->indices->inUse = true; geometry->indices->inUse = true;

View file

@ -64,6 +64,7 @@
#include "singe.h" #include "singe.h"
#include "pack.h" #include "pack.h"
#include "vfs.h" #include "vfs.h"
#include "render.h"
#include "../thirdparty/ffmpeg/libavformat/avformat.h" #include "../thirdparty/ffmpeg/libavformat/avformat.h"
#include "embedded.h" #include "embedded.h"
@ -1249,11 +1250,11 @@ static void _launcher(const char *exeName, ConfigT *conf) {
_mainTrace(conf, "Creating renderer"); _mainTrace(conf, "Creating renderer");
device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL, false, NULL); device = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL, false, NULL);
if (device == NULL) { if (device == NULL) {
_mainTrace(conf, "No GPU device (%s); 3D is unavailable", SDL_GetError()); _mainTrace(conf, "No SDL_GPU device (%s); trying OpenGL ES", SDL_GetError());
} else { } else {
renderer = SDL_CreateGPURenderer(device, window); renderer = SDL_CreateGPURenderer(device, window);
if (renderer == NULL) { if (renderer == NULL) {
_mainTrace(conf, "GPU renderer failed (%s); 3D is unavailable", SDL_GetError()); _mainTrace(conf, "GPU renderer failed (%s); trying OpenGL ES", SDL_GetError());
SDL_DestroyGPUDevice(device); SDL_DestroyGPUDevice(device);
device = NULL; device = NULL;
} }
@ -1265,6 +1266,22 @@ static void _launcher(const char *exeName, ConfigT *conf) {
} }
} }
_mainTrace(conf, "Renderer: %s", SDL_GetRendererName(renderer)); _mainTrace(conf, "Renderer: %s", SDL_GetRendererName(renderer));
// Tell the render layer which backend the scene and the GUI will be calling through. SDL_GPU
// when the platform has it; otherwise OpenGL ES through the context SDL_Renderer just made,
// which is the only one a Pi or a Mali handheld offers. Neither leaves 2D untouched and the
// scene and the GUI reporting themselves unavailable, as they already did on such a machine.
if (device != NULL) {
renderSelect(RENDER_GPU, &renderGpuBackend, renderer);
} else if (renderGlesStart()) {
renderSelect(RENDER_GLES, &renderGlesBackend, renderer);
device = rgpuCreateDevice(RGPU_SHADERFORMAT_ESSL, false, NULL);
if (device == NULL) {
renderSelect(RENDER_NONE, NULL, renderer);
}
} else {
renderSelect(RENDER_NONE, NULL, renderer);
}
_mainTrace(conf, "Render backend: %s", renderApiName());
// Clear screen with black // Clear screen with black
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
@ -1308,7 +1325,7 @@ static void _launcher(const char *exeName, ConfigT *conf) {
_mainTrace(conf, "Destroying renderer"); _mainTrace(conf, "Destroying renderer");
SDL_DestroyRenderer(renderer); SDL_DestroyRenderer(renderer);
if (device != NULL) { if (device != NULL) {
SDL_DestroyGPUDevice(device); rgpuDestroyDevice(device);
} }
_mainTrace(conf, "Destroying window"); _mainTrace(conf, "Destroying window");
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
@ -1761,7 +1778,7 @@ static void _traceHeader(const ConfigT *conf, SDL_Renderer *renderer, SDL_GPUDev
utilTrace("Command: %s", (_commandLine != NULL) ? _commandLine : ""); utilTrace("Command: %s", (_commandLine != NULL) ? _commandLine : "");
utilTrace("OS: %s", os); utilTrace("OS: %s", os);
utilTrace("CPU: %s", cpu); utilTrace("CPU: %s", cpu);
utilTrace("Renderer: %s%s%s", SDL_GetRendererName(renderer), (device != NULL) ? ", GPU driver " : " (no GPU device; 3D is unavailable)", (device != NULL) ? SDL_GetGPUDeviceDriver(device) : ""); utilTrace("Renderer: %s%s%s", SDL_GetRendererName(renderer), (device != NULL) ? ", 3D through " : " (no 3D and no GUI on this machine)", (device != NULL) ? rgpuGetDeviceDriver(device) : "");
utilTrace("Decoder: %s", videoGetDecoderDescription()); utilTrace("Decoder: %s", videoGetDecoderDescription());
utilTrace("Audio: %s", audio); utilTrace("Audio: %s", audio);
utilTrace("SoundFont: %s", midiSoundfont()); utilTrace("SoundFont: %s", midiSoundfont());

356
src/render.c Normal file
View file

@ -0,0 +1,356 @@
/*
*
* Singe 3
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
// Backend selection and dispatch.
//
// One indirect call per entry point. That is nothing beside the work each one goes on to do, and it
// buys runtime selection: the same binary runs on a machine with Vulkan and on one with GLES only.
#include "render.h"
#include "util.h"
static const RenderBackendT *_backend = NULL;
static SDL_Renderer *_renderer = NULL;
static RenderApiT _api = RENDER_NONE;
RenderApiT renderApi(void) {
return _api;
}
const char *renderApiName(void) {
switch (_api) {
case RENDER_GPU: return "SDL_GPU";
case RENDER_GLES: return "OpenGL ES";
default: return "none";
}
}
// GLES shares one context with SDL_Renderer, which caches its own drawing state. SDL_FlushRenderer
// both flushes the queue and invalidates that cache (SDL_render.c), which is what makes handing the
// context over safe rather than merely lucky -- see PLAN.md section 56, phase 0. Under SDL_GPU
// there is no shared state and this costs a comparison.
void renderBegin(void) {
if ((_api == RENDER_GLES) && (_renderer != NULL)) {
SDL_FlushRenderer(_renderer);
}
}
// Nothing to undo under SDL_GPU; it owns no state SDL_Renderer can see. GLES shares one context,
// so it puts back the bindings SDL does not track for itself.
void renderEnd(void) {
if (_api == RENDER_GLES) {
renderGlesRestore();
}
}
SDL_GPUTexture *renderTextureFor(SDL_Texture *texture) {
if (texture == NULL) {
return NULL;
}
if (_api == RENDER_GLES) {
float width = 0.0f;
float height = 0.0f;
SDL_GetTextureSize(texture, &width, &height);
return renderGlesTextureWrap((Uint32)SDL_GetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_NUMBER, 0), (int32_t)width, (int32_t)height);
}
return (SDL_GPUTexture *)SDL_GetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_GPU_TEXTURE_POINTER, NULL);
}
SDL_Texture *renderWrapTexture(SDL_Renderer *renderer, SDL_GPUTexture *texture, SDL_PixelFormat format, int32_t width, int32_t height) {
SDL_PropertiesID props;
SDL_Texture *result;
if ((renderer == NULL) || (texture == NULL)) {
return NULL;
}
props = SDL_CreateProperties();
if (_api == RENDER_GLES) {
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_NUMBER, (Sint64)renderGlesTextureName(texture));
// Core GLES has no BGRA storage, so a caller asking for BGRA32 -- which the scene does, as
// its way of naming the swapchain's layout -- really has an RGBA8 texture. Saying otherwise
// leaves SDL drawing nothing at all, which is how this was found.
if ((format == SDL_PIXELFORMAT_BGRA32) || (format == SDL_PIXELFORMAT_BGRX32)) {
format = SDL_PIXELFORMAT_RGBA32;
}
} else {
SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER, texture);
}
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, width);
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, height);
result = SDL_CreateTextureWithProperties(renderer, props);
SDL_DestroyProperties(props);
return result;
}
void renderSelect(RenderApiT api, const RenderBackendT *backend, SDL_Renderer *renderer) {
_api = backend != NULL ? api : RENDER_NONE;
_backend = backend;
_renderer = renderer;
utilTrace("Render: %s", renderApiName());
}
// Every run of rgpu* drawing sits between acquiring a command buffer and submitting it, so that is
// where the context is taken and given back. Bracketing here rather than at the callers means
// scene.c and guiRender.cpp need to know nothing about it.
SDL_GPUCommandBuffer *rgpuAcquireCommandBuffer(SDL_GPUDevice *device) {
renderBegin();
return _backend->acquireCommandBuffer(device);
}
SDL_GPUCopyPass *rgpuBeginCopyPass(SDL_GPUCommandBuffer *commandBuffer) {
return _backend->beginCopyPass(commandBuffer);
}
SDL_GPURenderPass *rgpuBeginRenderPass(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUColorTargetInfo *colorTargetInfos, Uint32 numColorTargets, const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo) {
return _backend->beginRenderPass(commandBuffer, colorTargetInfos, numColorTargets, depthStencilTargetInfo);
}
void rgpuBindFragmentSamplers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, const SDL_GPUTextureSamplerBinding *textureSamplerBindings, Uint32 numBindings) {
_backend->bindFragmentSamplers(renderPass, firstSlot, textureSamplerBindings, numBindings);
}
void rgpuBindGraphicsPipeline(SDL_GPURenderPass *renderPass, SDL_GPUGraphicsPipeline *graphicsPipeline) {
_backend->bindGraphicsPipeline(renderPass, graphicsPipeline);
}
void rgpuBindIndexBuffer(SDL_GPURenderPass *renderPass, const SDL_GPUBufferBinding *binding, SDL_GPUIndexElementSize indexElementSize) {
_backend->bindIndexBuffer(renderPass, binding, indexElementSize);
}
void rgpuBindVertexBuffers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, const SDL_GPUBufferBinding *bindings, Uint32 numBindings) {
_backend->bindVertexBuffers(renderPass, firstSlot, bindings, numBindings);
}
void rgpuBindVertexStorageBuffers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, SDL_GPUBuffer *const *storageBuffers, Uint32 numBindings) {
_backend->bindVertexStorageBuffers(renderPass, firstSlot, storageBuffers, numBindings);
}
void rgpuBlitTexture(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUBlitInfo *info) {
_backend->blitTexture(commandBuffer, info);
}
bool rgpuCancelCommandBuffer(SDL_GPUCommandBuffer *commandBuffer) {
bool ok = _backend->cancelCommandBuffer(commandBuffer);
renderEnd();
return ok;
}
void rgpuCopyTextureToTexture(SDL_GPUCopyPass *copyPass, const SDL_GPUTextureLocation *source, const SDL_GPUTextureLocation *destination, Uint32 w, Uint32 h, Uint32 d, bool cycle) {
_backend->copyTextureToTexture(copyPass, source, destination, w, h, d, cycle);
}
SDL_GPUBuffer *rgpuCreateBuffer(SDL_GPUDevice *device, const SDL_GPUBufferCreateInfo *createinfo) {
return _backend->createBuffer(device, createinfo);
}
SDL_GPUDevice *rgpuCreateDevice(SDL_GPUShaderFormat formatFlags, bool debugMode, const char *name) {
return _backend->createDevice(formatFlags, debugMode, name);
}
SDL_GPUGraphicsPipeline *rgpuCreateGraphicsPipeline(SDL_GPUDevice *device, const SDL_GPUGraphicsPipelineCreateInfo *createinfo) {
return _backend->createGraphicsPipeline(device, createinfo);
}
SDL_GPUSampler *rgpuCreateSampler(SDL_GPUDevice *device, const SDL_GPUSamplerCreateInfo *createinfo) {
return _backend->createSampler(device, createinfo);
}
SDL_GPUShader *rgpuCreateShader(SDL_GPUDevice *device, const SDL_GPUShaderCreateInfo *createinfo) {
return _backend->createShader(device, createinfo);
}
SDL_GPUTexture *rgpuCreateTexture(SDL_GPUDevice *device, const SDL_GPUTextureCreateInfo *createinfo) {
return _backend->createTexture(device, createinfo);
}
SDL_GPUTransferBuffer *rgpuCreateTransferBuffer(SDL_GPUDevice *device, const SDL_GPUTransferBufferCreateInfo *createinfo) {
return _backend->createTransferBuffer(device, createinfo);
}
void rgpuDestroyDevice(SDL_GPUDevice *device) {
_backend->destroyDevice(device);
}
void rgpuDrawIndexedPrimitives(SDL_GPURenderPass *renderPass, Uint32 numIndices, Uint32 numInstances, Uint32 firstIndex, Sint32 vertexOffset, Uint32 firstInstance) {
_backend->drawIndexedPrimitives(renderPass, numIndices, numInstances, firstIndex, vertexOffset, firstInstance);
}
void rgpuDrawPrimitives(SDL_GPURenderPass *renderPass, Uint32 numVertices, Uint32 numInstances, Uint32 firstVertex, Uint32 firstInstance) {
_backend->drawPrimitives(renderPass, numVertices, numInstances, firstVertex, firstInstance);
}
void rgpuEndCopyPass(SDL_GPUCopyPass *copyPass) {
_backend->endCopyPass(copyPass);
}
void rgpuEndRenderPass(SDL_GPURenderPass *renderPass) {
_backend->endRenderPass(renderPass);
}
void rgpuGenerateMipmapsForTexture(SDL_GPUCommandBuffer *commandBuffer, SDL_GPUTexture *texture) {
_backend->generateMipmapsForTexture(commandBuffer, texture);
}
const char *rgpuGetDeviceDriver(SDL_GPUDevice *device) {
return _backend->getDeviceDriver(device);
}
SDL_GPUShaderFormat rgpuGetShaderFormats(SDL_GPUDevice *device) {
return _backend->getShaderFormats(device);
}
SDL_GPUTextureFormat rgpuGetSwapchainTextureFormat(SDL_GPUDevice *device, SDL_Window *window) {
return _backend->getSwapchainTextureFormat(device, window);
}
SDL_GPUTextureFormat rgpuGetTextureFormatFromPixelFormat(SDL_PixelFormat format) {
return _backend->getTextureFormatFromPixelFormat(format);
}
void *rgpuMapTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer, bool cycle) {
return _backend->mapTransferBuffer(device, transferBuffer, cycle);
}
void rgpuPushFragmentUniformData(SDL_GPUCommandBuffer *commandBuffer, Uint32 slotIndex, const void *data, Uint32 length) {
_backend->pushFragmentUniformData(commandBuffer, slotIndex, data, length);
}
void rgpuPushVertexUniformData(SDL_GPUCommandBuffer *commandBuffer, Uint32 slotIndex, const void *data, Uint32 length) {
_backend->pushVertexUniformData(commandBuffer, slotIndex, data, length);
}
void rgpuReleaseBuffer(SDL_GPUDevice *device, SDL_GPUBuffer *buffer) {
_backend->releaseBuffer(device, buffer);
}
void rgpuReleaseGraphicsPipeline(SDL_GPUDevice *device, SDL_GPUGraphicsPipeline *graphicsPipeline) {
_backend->releaseGraphicsPipeline(device, graphicsPipeline);
}
void rgpuReleaseSampler(SDL_GPUDevice *device, SDL_GPUSampler *sampler) {
_backend->releaseSampler(device, sampler);
}
void rgpuReleaseShader(SDL_GPUDevice *device, SDL_GPUShader *shader) {
_backend->releaseShader(device, shader);
}
void rgpuReleaseTexture(SDL_GPUDevice *device, SDL_GPUTexture *texture) {
_backend->releaseTexture(device, texture);
}
void rgpuReleaseTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer) {
_backend->releaseTransferBuffer(device, transferBuffer);
}
void rgpuSetScissor(SDL_GPURenderPass *renderPass, const SDL_Rect *scissor) {
_backend->setScissor(renderPass, scissor);
}
void rgpuSetStencilReference(SDL_GPURenderPass *renderPass, Uint8 reference) {
_backend->setStencilReference(renderPass, reference);
}
bool rgpuSubmitCommandBuffer(SDL_GPUCommandBuffer *commandBuffer) {
bool ok = _backend->submitCommandBuffer(commandBuffer);
renderEnd();
return ok;
}
bool rgpuTextureSupportsFormat(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUTextureType type, SDL_GPUTextureUsageFlags usage) {
return _backend->textureSupportsFormat(device, format, type, usage);
}
bool rgpuTextureSupportsSampleCount(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUSampleCount sampleCount) {
return _backend->textureSupportsSampleCount(device, format, sampleCount);
}
void rgpuUnmapTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer) {
_backend->unmapTransferBuffer(device, transferBuffer);
}
void rgpuUploadToBuffer(SDL_GPUCopyPass *copyPass, const SDL_GPUTransferBufferLocation *source, const SDL_GPUBufferRegion *destination, bool cycle) {
_backend->uploadToBuffer(copyPass, source, destination, cycle);
}
void rgpuUploadToTexture(SDL_GPUCopyPass *copyPass, const SDL_GPUTextureTransferInfo *source, const SDL_GPUTextureRegion *destination, bool cycle) {
_backend->uploadToTexture(copyPass, source, destination, cycle);
}

193
src/render.h Normal file
View file

@ -0,0 +1,193 @@
/*
*
* Singe 3
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
#ifndef RENDER_H
#define RENDER_H
#include <stdbool.h>
#include <stdint.h>
#include <SDL3/SDL.h>
#ifdef __cplusplus
extern "C" {
#endif
// The rendering backend, behind one table of function pointers.
//
// SDL_GPU has Vulkan, Direct3D 12 and Metal and no OpenGL of any kind, which leaves the cheap
// handhelds and the Pi with 2D only: they have GLES and nothing else. Rather than grow a second
// renderer beside scene.c and guiRender.cpp -- two descriptions of the same picture, drifting apart
// -- the subset of SDL_GPU those files actually use is virtualised here and implemented twice. It
// is 45 entry points, and nothing exotic is in it: no compute, no indirect draws, no manual
// barriers. See PLAN.md section 56.
//
// The types are NOT reinvented. SDL3 declares SDL_GPUTextureCreateInfo and the rest whether or not
// a backend exists on this platform, so both backends share SDL's structures and enumerations and
// only the functions are dispatched. That holds this layer to names alone.
//
// Selection is at run time, not build time: one linux-aarch64 binary ships to an RK3588 that may
// have Vulkan and to a Mali-G31 that certainly does not.
// GLSL ES source, as a shader "format". SDL_GPUShaderFormat is a bit field and SDL uses bits 0 to 5
// (PRIVATE, SPIRV, DXBC, DXIL, MSL, METALLIB), so this takes one well clear of them. A shader's
// code pointer is then the ES source string rather than a blob, and its size is ignored.
#define RGPU_SHADERFORMAT_ESSL (1u << 16)
typedef struct RenderBackendS {
SDL_GPUCommandBuffer * (*acquireCommandBuffer)(SDL_GPUDevice *device);
SDL_GPUCopyPass * (*beginCopyPass)(SDL_GPUCommandBuffer *commandBuffer);
SDL_GPURenderPass * (*beginRenderPass)(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUColorTargetInfo *colorTargetInfos, Uint32 numColorTargets, const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo);
void (*bindFragmentSamplers)(SDL_GPURenderPass *renderPass, Uint32 firstSlot, const SDL_GPUTextureSamplerBinding *textureSamplerBindings, Uint32 numBindings);
void (*bindGraphicsPipeline)(SDL_GPURenderPass *renderPass, SDL_GPUGraphicsPipeline *graphicsPipeline);
void (*bindIndexBuffer)(SDL_GPURenderPass *renderPass, const SDL_GPUBufferBinding *binding, SDL_GPUIndexElementSize indexElementSize);
void (*bindVertexBuffers)(SDL_GPURenderPass *renderPass, Uint32 firstSlot, const SDL_GPUBufferBinding *bindings, Uint32 numBindings);
void (*bindVertexStorageBuffers)(SDL_GPURenderPass *renderPass, Uint32 firstSlot, SDL_GPUBuffer *const *storageBuffers, Uint32 numBindings);
void (*blitTexture)(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUBlitInfo *info);
bool (*cancelCommandBuffer)(SDL_GPUCommandBuffer *commandBuffer);
void (*copyTextureToTexture)(SDL_GPUCopyPass *copyPass, const SDL_GPUTextureLocation *source, const SDL_GPUTextureLocation *destination, Uint32 w, Uint32 h, Uint32 d, bool cycle);
SDL_GPUBuffer * (*createBuffer)(SDL_GPUDevice *device, const SDL_GPUBufferCreateInfo *createinfo);
SDL_GPUDevice * (*createDevice)(SDL_GPUShaderFormat formatFlags, bool debugMode, const char *name);
SDL_GPUGraphicsPipeline *(*createGraphicsPipeline)(SDL_GPUDevice *device, const SDL_GPUGraphicsPipelineCreateInfo *createinfo);
SDL_GPUSampler * (*createSampler)(SDL_GPUDevice *device, const SDL_GPUSamplerCreateInfo *createinfo);
SDL_GPUShader * (*createShader)(SDL_GPUDevice *device, const SDL_GPUShaderCreateInfo *createinfo);
SDL_GPUTexture * (*createTexture)(SDL_GPUDevice *device, const SDL_GPUTextureCreateInfo *createinfo);
SDL_GPUTransferBuffer * (*createTransferBuffer)(SDL_GPUDevice *device, const SDL_GPUTransferBufferCreateInfo *createinfo);
void (*destroyDevice)(SDL_GPUDevice *device);
void (*drawIndexedPrimitives)(SDL_GPURenderPass *renderPass, Uint32 numIndices, Uint32 numInstances, Uint32 firstIndex, Sint32 vertexOffset, Uint32 firstInstance);
void (*drawPrimitives)(SDL_GPURenderPass *renderPass, Uint32 numVertices, Uint32 numInstances, Uint32 firstVertex, Uint32 firstInstance);
void (*endCopyPass)(SDL_GPUCopyPass *copyPass);
void (*endRenderPass)(SDL_GPURenderPass *renderPass);
void (*generateMipmapsForTexture)(SDL_GPUCommandBuffer *commandBuffer, SDL_GPUTexture *texture);
const char * (*getDeviceDriver)(SDL_GPUDevice *device);
SDL_GPUShaderFormat (*getShaderFormats)(SDL_GPUDevice *device);
SDL_GPUTextureFormat (*getSwapchainTextureFormat)(SDL_GPUDevice *device, SDL_Window *window);
SDL_GPUTextureFormat (*getTextureFormatFromPixelFormat)(SDL_PixelFormat format);
void * (*mapTransferBuffer)(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer, bool cycle);
void (*pushFragmentUniformData)(SDL_GPUCommandBuffer *commandBuffer, Uint32 slotIndex, const void *data, Uint32 length);
void (*pushVertexUniformData)(SDL_GPUCommandBuffer *commandBuffer, Uint32 slotIndex, const void *data, Uint32 length);
void (*releaseBuffer)(SDL_GPUDevice *device, SDL_GPUBuffer *buffer);
void (*releaseGraphicsPipeline)(SDL_GPUDevice *device, SDL_GPUGraphicsPipeline *graphicsPipeline);
void (*releaseSampler)(SDL_GPUDevice *device, SDL_GPUSampler *sampler);
void (*releaseShader)(SDL_GPUDevice *device, SDL_GPUShader *shader);
void (*releaseTexture)(SDL_GPUDevice *device, SDL_GPUTexture *texture);
void (*releaseTransferBuffer)(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer);
void (*setScissor)(SDL_GPURenderPass *renderPass, const SDL_Rect *scissor);
void (*setStencilReference)(SDL_GPURenderPass *renderPass, Uint8 reference);
bool (*submitCommandBuffer)(SDL_GPUCommandBuffer *commandBuffer);
bool (*textureSupportsFormat)(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUTextureType type, SDL_GPUTextureUsageFlags usage);
bool (*textureSupportsSampleCount)(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUSampleCount sampleCount);
void (*unmapTransferBuffer)(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer);
void (*uploadToBuffer)(SDL_GPUCopyPass *copyPass, const SDL_GPUTransferBufferLocation *source, const SDL_GPUBufferRegion *destination, bool cycle);
void (*uploadToTexture)(SDL_GPUCopyPass *copyPass, const SDL_GPUTextureTransferInfo *source, const SDL_GPUTextureRegion *destination, bool cycle);
} RenderBackendT;
// Which backend is in use. RENDER_NONE means 3D and the GUI are unavailable and the engine draws
// 2D through SDL_Renderer alone, which is what a machine with no usable driver gets.
typedef enum RenderApiE {
RENDER_NONE = 0,
RENDER_GPU, // SDL_GPU: Vulkan, Direct3D 12 or Metal
RENDER_GLES // OpenGL ES 3.1, or 2.0 with the GUI only
} RenderApiT;
extern const RenderBackendT renderGlesBackend;
extern const RenderBackendT renderGpuBackend;
// The engine side. renderBegin and renderEnd bracket every frame's rgpu* calls: under SDL_GPU
// they do nothing, and under GLES they hand SDL_Renderer's context over and give it back.
RenderApiT renderApi(void);
const char *renderApiName(void);
void renderBegin(void);
void renderEnd(void);
void renderSelect(RenderApiT api, const RenderBackendT *backend, SDL_Renderer *renderer);
// The composite seam. The scene and the GUI do not draw to the swapchain; each renders into its
// own texture and hands it to SDL_Renderer to draw as an ordinary layer. Under SDL_GPU that is
// SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER and under GLES it is the twin taking a GLuint,
// SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_NUMBER, so the choice lives here rather than at each of
// the three call sites. renderTextureFor goes the other way, for a texture SDL made.
SDL_Texture *renderWrapTexture(SDL_Renderer *renderer, SDL_GPUTexture *texture, SDL_PixelFormat format, int32_t width, int32_t height);
SDL_GPUTexture *renderTextureFor(SDL_Texture *texture);
// The GLES backend's own two. renderGlesStart looks at the context SDL_Renderer already made and
// says whether it is one this backend can use; renderGlesRestore puts that context back as found.
void renderGlesRestore(void);
bool renderGlesStart(void);
Uint32 renderGlesTextureName(SDL_GPUTexture *texture);
SDL_GPUTexture *renderGlesTextureWrap(Uint32 name, int32_t width, int32_t height);
SDL_GPUCommandBuffer * rgpuAcquireCommandBuffer(SDL_GPUDevice *device);
SDL_GPUCopyPass * rgpuBeginCopyPass(SDL_GPUCommandBuffer *commandBuffer);
SDL_GPURenderPass * rgpuBeginRenderPass(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUColorTargetInfo *colorTargetInfos, Uint32 numColorTargets, const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo);
void rgpuBindFragmentSamplers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, const SDL_GPUTextureSamplerBinding *textureSamplerBindings, Uint32 numBindings);
void rgpuBindGraphicsPipeline(SDL_GPURenderPass *renderPass, SDL_GPUGraphicsPipeline *graphicsPipeline);
void rgpuBindIndexBuffer(SDL_GPURenderPass *renderPass, const SDL_GPUBufferBinding *binding, SDL_GPUIndexElementSize indexElementSize);
void rgpuBindVertexBuffers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, const SDL_GPUBufferBinding *bindings, Uint32 numBindings);
void rgpuBindVertexStorageBuffers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, SDL_GPUBuffer *const *storageBuffers, Uint32 numBindings);
void rgpuBlitTexture(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUBlitInfo *info);
bool rgpuCancelCommandBuffer(SDL_GPUCommandBuffer *commandBuffer);
void rgpuCopyTextureToTexture(SDL_GPUCopyPass *copyPass, const SDL_GPUTextureLocation *source, const SDL_GPUTextureLocation *destination, Uint32 w, Uint32 h, Uint32 d, bool cycle);
SDL_GPUBuffer * rgpuCreateBuffer(SDL_GPUDevice *device, const SDL_GPUBufferCreateInfo *createinfo);
SDL_GPUDevice * rgpuCreateDevice(SDL_GPUShaderFormat formatFlags, bool debugMode, const char *name);
SDL_GPUGraphicsPipeline *rgpuCreateGraphicsPipeline(SDL_GPUDevice *device, const SDL_GPUGraphicsPipelineCreateInfo *createinfo);
SDL_GPUSampler * rgpuCreateSampler(SDL_GPUDevice *device, const SDL_GPUSamplerCreateInfo *createinfo);
SDL_GPUShader * rgpuCreateShader(SDL_GPUDevice *device, const SDL_GPUShaderCreateInfo *createinfo);
SDL_GPUTexture * rgpuCreateTexture(SDL_GPUDevice *device, const SDL_GPUTextureCreateInfo *createinfo);
SDL_GPUTransferBuffer * rgpuCreateTransferBuffer(SDL_GPUDevice *device, const SDL_GPUTransferBufferCreateInfo *createinfo);
void rgpuDestroyDevice(SDL_GPUDevice *device);
void rgpuDrawIndexedPrimitives(SDL_GPURenderPass *renderPass, Uint32 numIndices, Uint32 numInstances, Uint32 firstIndex, Sint32 vertexOffset, Uint32 firstInstance);
void rgpuDrawPrimitives(SDL_GPURenderPass *renderPass, Uint32 numVertices, Uint32 numInstances, Uint32 firstVertex, Uint32 firstInstance);
void rgpuEndCopyPass(SDL_GPUCopyPass *copyPass);
void rgpuEndRenderPass(SDL_GPURenderPass *renderPass);
void rgpuGenerateMipmapsForTexture(SDL_GPUCommandBuffer *commandBuffer, SDL_GPUTexture *texture);
const char * rgpuGetDeviceDriver(SDL_GPUDevice *device);
SDL_GPUShaderFormat rgpuGetShaderFormats(SDL_GPUDevice *device);
SDL_GPUTextureFormat rgpuGetSwapchainTextureFormat(SDL_GPUDevice *device, SDL_Window *window);
SDL_GPUTextureFormat rgpuGetTextureFormatFromPixelFormat(SDL_PixelFormat format);
void * rgpuMapTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer, bool cycle);
void rgpuPushFragmentUniformData(SDL_GPUCommandBuffer *commandBuffer, Uint32 slotIndex, const void *data, Uint32 length);
void rgpuPushVertexUniformData(SDL_GPUCommandBuffer *commandBuffer, Uint32 slotIndex, const void *data, Uint32 length);
void rgpuReleaseBuffer(SDL_GPUDevice *device, SDL_GPUBuffer *buffer);
void rgpuReleaseGraphicsPipeline(SDL_GPUDevice *device, SDL_GPUGraphicsPipeline *graphicsPipeline);
void rgpuReleaseSampler(SDL_GPUDevice *device, SDL_GPUSampler *sampler);
void rgpuReleaseShader(SDL_GPUDevice *device, SDL_GPUShader *shader);
void rgpuReleaseTexture(SDL_GPUDevice *device, SDL_GPUTexture *texture);
void rgpuReleaseTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer);
void rgpuSetScissor(SDL_GPURenderPass *renderPass, const SDL_Rect *scissor);
void rgpuSetStencilReference(SDL_GPURenderPass *renderPass, Uint8 reference);
bool rgpuSubmitCommandBuffer(SDL_GPUCommandBuffer *commandBuffer);
bool rgpuTextureSupportsFormat(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUTextureType type, SDL_GPUTextureUsageFlags usage);
bool rgpuTextureSupportsSampleCount(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUSampleCount sampleCount);
void rgpuUnmapTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer);
void rgpuUploadToBuffer(SDL_GPUCopyPass *copyPass, const SDL_GPUTransferBufferLocation *source, const SDL_GPUBufferRegion *destination, bool cycle);
void rgpuUploadToTexture(SDL_GPUCopyPass *copyPass, const SDL_GPUTextureTransferInfo *source, const SDL_GPUTextureRegion *destination, bool cycle);
#ifdef __cplusplus
}
#endif
#endif // RENDER_H

1675
src/renderGles.c Normal file

File diff suppressed because it is too large Load diff

486
src/renderGlesApi.h Normal file
View file

@ -0,0 +1,486 @@
/*
*
* Singe 3
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
#ifndef RENDER_GLES_API_H
#define RENDER_GLES_API_H
// The OpenGL ES entry points renderGles.c uses: declared here, loaded through SDL, linked never.
//
// Nothing in this file includes a GL header. Linking -lGLESv2 would break the Windows and macOS
// builds, and merely *including* <GLES3/gl31.h> breaks them too -- neither platform ships it, and
// the first cross build after this backend landed failed on all three non-Linux targets for exactly
// that reason. Since every function is fetched at run time through SDL_GL_GetProcAddress anyway,
// the types and enumerants below are all the declaration that is needed, and a machine with no GLES
// driver simply fails renderGlesStart and keeps its 2D renderer.
//
// The lists are exactly what renderGles.c uses, so anything it gains that is not here fails to
// compile or link rather than crashing on a null pointer.
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <SDL3/SDL.h>
// The Khronos types, in the one spelling every ES implementation agrees on.
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef signed char GLbyte;
typedef short GLshort;
typedef int GLint;
typedef int GLsizei;
typedef unsigned char GLubyte;
typedef unsigned short GLushort;
typedef unsigned int GLuint;
typedef float GLfloat;
typedef char GLchar;
typedef void GLvoid;
typedef intptr_t GLintptr;
typedef intptr_t GLsizeiptr;
#define GL_APIENTRYP *
// The enumerants used, and only those.
#define GL_ALWAYS 0x0207
#define GL_ARRAY_BUFFER 0x8892
#define GL_BACK 0x0405
#define GL_BLEND 0x0BE2
#define GL_CCW 0x0901
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_COLOR 0x1800
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_COMPARE_REF_TO_TEXTURE 0x884E
#define GL_COMPILE_STATUS 0x8B81
#define GL_CONSTANT_COLOR 0x8001
#define GL_CULL_FACE 0x0B44
#define GL_CW 0x0900
#define GL_DECR 0x1E03
#define GL_DECR_WRAP 0x8508
#define GL_DEPTH24_STENCIL8 0x88F0
#define GL_DEPTH32F_STENCIL8 0x8CAD
#define GL_DEPTH_ATTACHMENT 0x8D00
#define GL_DEPTH_BUFFER_BIT 0x00000100
#define GL_DEPTH_COMPONENT 0x1902
#define GL_DEPTH_COMPONENT16 0x81A5
#define GL_DEPTH_COMPONENT24 0x81A6
#define GL_DEPTH_COMPONENT32F 0x8CAC
#define GL_DEPTH_STENCIL 0x84F9
#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
#define GL_DEPTH_TEST 0x0B71
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#define GL_DST_ALPHA 0x0304
#define GL_DST_COLOR 0x0306
#define GL_DYNAMIC_DRAW 0x88E8
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
#define GL_EQUAL 0x0202
#define GL_FALSE 0
#define GL_FLOAT 0x1406
#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_FRAMEBUFFER 0x8D40
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#define GL_FRONT 0x0404
#define GL_FRONT_AND_BACK 0x0408
#define GL_FUNC_ADD 0x8006
#define GL_FUNC_REVERSE_SUBTRACT 0x800B
#define GL_FUNC_SUBTRACT 0x800A
#define GL_GEQUAL 0x0206
#define GL_GREATER 0x0204
#define GL_HALF_FLOAT 0x140B
#define GL_INCR 0x1E02
#define GL_INCR_WRAP 0x8507
#define GL_INT 0x1404
#define GL_INVALID_INDEX 0xFFFFFFFFu
#define GL_INVERT 0x150A
#define GL_KEEP 0x1E00
#define GL_LEQUAL 0x0203
#define GL_LESS 0x0201
#define GL_LINEAR 0x2601
#define GL_LINEAR_MIPMAP_LINEAR 0x2703
#define GL_LINEAR_MIPMAP_NEAREST 0x2701
#define GL_LINES 0x0001
#define GL_LINE_STRIP 0x0003
#define GL_LINK_STATUS 0x8B82
#define GL_MAJOR_VERSION 0x821B
#define GL_MAX 0x8008
#define GL_MIN 0x8007
#define GL_MINOR_VERSION 0x821C
#define GL_MIRRORED_REPEAT 0x8370
#define GL_NEAREST 0x2600
#define GL_NEAREST_MIPMAP_LINEAR 0x2702
#define GL_NEAREST_MIPMAP_NEAREST 0x2700
#define GL_NEVER 0x0200
#define GL_NONE 0
#define GL_NOTEQUAL 0x0205
#define GL_NO_ERROR 0
#define GL_ONE 1
#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
#define GL_ONE_MINUS_DST_ALPHA 0x0305
#define GL_ONE_MINUS_DST_COLOR 0x0307
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_ONE_MINUS_SRC_COLOR 0x0301
#define GL_POINTS 0x0000
#define GL_POLYGON_OFFSET_FILL 0x8037
#define GL_R8 0x8229
#define GL_READ_FRAMEBUFFER 0x8CA8
#define GL_RED 0x1903
#define GL_RENDERBUFFER 0x8D41
#define GL_REPEAT 0x2901
#define GL_REPLACE 0x1E01
#define GL_RGBA 0x1908
#define GL_RGBA16F 0x881A
#define GL_RGBA32F 0x8814
#define GL_RGBA8 0x8058
#define GL_SAMPLES 0x80A9
#define GL_SCISSOR_TEST 0x0C11
#define GL_SHADER_STORAGE_BUFFER 0x90D2
#define GL_SRC_ALPHA 0x0302
#define GL_SRC_ALPHA_SATURATE 0x0308
#define GL_SRC_COLOR 0x0300
#define GL_SRGB8_ALPHA8 0x8C43
#define GL_STENCIL_BUFFER_BIT 0x00000400
#define GL_STENCIL_TEST 0x0B90
#define GL_STREAM_DRAW 0x88E0
#define GL_TEXTURE0 0x84C0
#define GL_TEXTURE_2D 0x0DE1
#define GL_TEXTURE_2D_ARRAY 0x8C1A
#define GL_TEXTURE_3D 0x806F
#define GL_EXTENSIONS 0x1F03
#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
#define GL_TEXTURE_BASE_LEVEL 0x813C
#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
#define GL_TEXTURE_MAX_LOD 0x813B
#define GL_TEXTURE_MIN_LOD 0x813A
#define GL_TEXTURE_MAX_LEVEL 0x813D
#define GL_TEXTURE_COMPARE_FUNC 0x884D
#define GL_TEXTURE_COMPARE_MODE 0x884C
#define GL_TEXTURE_CUBE_MAP 0x8513
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_TEXTURE_WRAP_R 0x8072
#define GL_TEXTURE_WRAP_S 0x2802
#define GL_TEXTURE_WRAP_T 0x2803
#define GL_TRIANGLES 0x0004
#define GL_TRIANGLE_STRIP 0x0005
#define GL_TRUE 1
#define GL_UNIFORM_BUFFER 0x8A11
#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34
#define GL_UNPACK_ALIGNMENT 0x0CF5
#define GL_UNSIGNED_BYTE 0x1401
#define GL_UNSIGNED_INT 0x1405
#define GL_UNSIGNED_INT_24_8 0x84FA
#define GL_UNSIGNED_SHORT 0x1403
#define GL_VERSION 0x1F02
#define GL_VERTEX_SHADER 0x8B31
#define GL_ZERO 0
typedef void (GL_APIENTRYP GlesActiveTextureFn)(GLenum texture);
typedef void (GL_APIENTRYP GlesAttachShaderFn)(GLuint program, GLuint shader);
typedef void (GL_APIENTRYP GlesBindBufferFn)(GLenum target, GLuint buffer);
typedef void (GL_APIENTRYP GlesBindBufferBaseFn)(GLenum target, GLuint index, GLuint buffer);
typedef void (GL_APIENTRYP GlesBindBufferRangeFn)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
typedef void (GL_APIENTRYP GlesBindFramebufferFn)(GLenum target, GLuint framebuffer);
typedef void (GL_APIENTRYP GlesBindRenderbufferFn)(GLenum target, GLuint renderbuffer);
typedef void (GL_APIENTRYP GlesBindSamplerFn)(GLuint unit, GLuint sampler);
typedef void (GL_APIENTRYP GlesBindTextureFn)(GLenum target, GLuint texture);
typedef void (GL_APIENTRYP GlesBindVertexArrayFn)(GLuint array);
typedef void (GL_APIENTRYP GlesBlendEquationSeparateFn)(GLenum modeRGB, GLenum modeAlpha);
typedef void (GL_APIENTRYP GlesBlendFuncSeparateFn)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
typedef void (GL_APIENTRYP GlesBlitFramebufferFn)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
typedef void (GL_APIENTRYP GlesBufferDataFn)(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
typedef void (GL_APIENTRYP GlesBufferSubDataFn)(GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
typedef GLenum (GL_APIENTRYP GlesCheckFramebufferStatusFn)(GLenum target);
typedef void (GL_APIENTRYP GlesClearFn)(GLbitfield mask);
typedef void (GL_APIENTRYP GlesClearBufferfvFn)(GLenum buffer, GLint drawbuffer, const GLfloat *value);
typedef void (GL_APIENTRYP GlesClearDepthfFn)(GLfloat d);
typedef void (GL_APIENTRYP GlesClearStencilFn)(GLint s);
typedef void (GL_APIENTRYP GlesColorMaskFn)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
typedef void (GL_APIENTRYP GlesCompileShaderFn)(GLuint shader);
typedef void (GL_APIENTRYP GlesCopyTexSubImage2DFn)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
typedef GLuint (GL_APIENTRYP GlesCreateProgramFn)(void);
typedef GLuint (GL_APIENTRYP GlesCreateShaderFn)(GLenum type);
typedef void (GL_APIENTRYP GlesCullFaceFn)(GLenum mode);
typedef void (GL_APIENTRYP GlesDeleteBuffersFn)(GLsizei n, const GLuint *buffers);
typedef void (GL_APIENTRYP GlesDeleteFramebuffersFn)(GLsizei n, const GLuint *framebuffers);
typedef void (GL_APIENTRYP GlesDeleteProgramFn)(GLuint program);
typedef void (GL_APIENTRYP GlesDeleteRenderbuffersFn)(GLsizei n, const GLuint *renderbuffers);
typedef void (GL_APIENTRYP GlesDeleteSamplersFn)(GLsizei count, const GLuint *samplers);
typedef void (GL_APIENTRYP GlesDeleteShaderFn)(GLuint shader);
typedef void (GL_APIENTRYP GlesDeleteTexturesFn)(GLsizei n, const GLuint *textures);
typedef void (GL_APIENTRYP GlesDeleteVertexArraysFn)(GLsizei n, const GLuint *arrays);
typedef void (GL_APIENTRYP GlesDepthFuncFn)(GLenum func);
typedef void (GL_APIENTRYP GlesDepthMaskFn)(GLboolean flag);
typedef void (GL_APIENTRYP GlesDisableFn)(GLenum cap);
typedef void (GL_APIENTRYP GlesDrawArraysInstancedFn)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
typedef void (GL_APIENTRYP GlesDrawBuffersFn)(GLsizei n, const GLenum *bufs);
typedef void (GL_APIENTRYP GlesDrawElementsInstancedFn)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);
typedef void (GL_APIENTRYP GlesEnableFn)(GLenum cap);
typedef void (GL_APIENTRYP GlesEnableVertexAttribArrayFn)(GLuint index);
typedef void (GL_APIENTRYP GlesFlushFn)(void);
typedef void (GL_APIENTRYP GlesFramebufferRenderbufferFn)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
typedef void (GL_APIENTRYP GlesFramebufferTexture2DFn)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
typedef void (GL_APIENTRYP GlesFramebufferTextureLayerFn)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
typedef void (GL_APIENTRYP GlesFrontFaceFn)(GLenum mode);
typedef void (GL_APIENTRYP GlesGenBuffersFn)(GLsizei n, GLuint *buffers);
typedef void (GL_APIENTRYP GlesGenFramebuffersFn)(GLsizei n, GLuint *framebuffers);
typedef void (GL_APIENTRYP GlesGenRenderbuffersFn)(GLsizei n, GLuint *renderbuffers);
typedef void (GL_APIENTRYP GlesGenSamplersFn)(GLsizei count, GLuint *samplers);
typedef void (GL_APIENTRYP GlesGenTexturesFn)(GLsizei n, GLuint *textures);
typedef void (GL_APIENTRYP GlesGenVertexArraysFn)(GLsizei n, GLuint *arrays);
typedef void (GL_APIENTRYP GlesGenerateMipmapFn)(GLenum target);
typedef GLenum (GL_APIENTRYP GlesGetErrorFn)(void);
typedef void (GL_APIENTRYP GlesGetIntegervFn)(GLenum pname, GLint *data);
typedef void (GL_APIENTRYP GlesGetInternalformativFn)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint *params);
typedef void (GL_APIENTRYP GlesGetProgramInfoLogFn)(GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef void (GL_APIENTRYP GlesGetProgramivFn)(GLuint program, GLenum pname, GLint *params);
typedef void (GL_APIENTRYP GlesGetShaderInfoLogFn)(GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef void (GL_APIENTRYP GlesGetShaderivFn)(GLuint shader, GLenum pname, GLint *params);
typedef const GLubyte * (GL_APIENTRYP GlesGetStringFn)(GLenum name);
typedef GLuint (GL_APIENTRYP GlesGetUniformBlockIndexFn)(GLuint program, const GLchar *uniformBlockName);
typedef void (GL_APIENTRYP GlesLinkProgramFn)(GLuint program);
typedef void (GL_APIENTRYP GlesPixelStoreiFn)(GLenum pname, GLint param);
typedef void (GL_APIENTRYP GlesPolygonOffsetFn)(GLfloat factor, GLfloat units);
typedef void (GL_APIENTRYP GlesReadBufferFn)(GLenum src);
typedef void (GL_APIENTRYP GlesRenderbufferStorageMultisampleFn)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
typedef void (GL_APIENTRYP GlesGetFloatvFn)(GLenum pname, GLfloat *data);
typedef GLint (GL_APIENTRYP GlesGetUniformLocationFn)(GLuint program, const GLchar *name);
typedef void (GL_APIENTRYP GlesUniform1iFn)(GLint location, GLint v0);
typedef void (GL_APIENTRYP GlesSamplerParameterfFn)(GLuint sampler, GLenum pname, GLfloat param);
typedef void (GL_APIENTRYP GlesSamplerParameteriFn)(GLuint sampler, GLenum pname, GLint param);
typedef void (GL_APIENTRYP GlesScissorFn)(GLint x, GLint y, GLsizei width, GLsizei height);
typedef void (GL_APIENTRYP GlesShaderSourceFn)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
typedef void (GL_APIENTRYP GlesStencilFuncSeparateFn)(GLenum face, GLenum func, GLint ref, GLuint mask);
typedef void (GL_APIENTRYP GlesStencilMaskFn)(GLuint mask);
typedef void (GL_APIENTRYP GlesStencilMaskSeparateFn)(GLenum face, GLuint mask);
typedef void (GL_APIENTRYP GlesStencilOpSeparateFn)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
typedef void (GL_APIENTRYP GlesTexParameteriFn)(GLenum target, GLenum pname, GLint param);
typedef void (GL_APIENTRYP GlesTexStorage2DFn)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
typedef void (GL_APIENTRYP GlesTexStorage3DFn)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
typedef void (GL_APIENTRYP GlesTexSubImage2DFn)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels);
typedef void (GL_APIENTRYP GlesTexSubImage3DFn)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);
typedef void (GL_APIENTRYP GlesUniformBlockBindingFn)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding);
typedef void (GL_APIENTRYP GlesUseProgramFn)(GLuint program);
typedef void (GL_APIENTRYP GlesValidateProgramFn)(GLuint program);
typedef void (GL_APIENTRYP GlesVertexAttribIPointerFn)(GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer);
typedef void (GL_APIENTRYP GlesVertexAttribPointerFn)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
typedef void (GL_APIENTRYP GlesViewportFn)(GLint x, GLint y, GLsizei width, GLsizei height);
typedef struct GlesApiS {
GlesActiveTextureFn activeTexture;
GlesAttachShaderFn attachShader;
GlesBindBufferFn bindBuffer;
GlesBindBufferBaseFn bindBufferBase;
GlesBindBufferRangeFn bindBufferRange;
GlesBindFramebufferFn bindFramebuffer;
GlesBindRenderbufferFn bindRenderbuffer;
GlesBindSamplerFn bindSampler;
GlesBindTextureFn bindTexture;
GlesBindVertexArrayFn bindVertexArray;
GlesBlendEquationSeparateFn blendEquationSeparate;
GlesBlendFuncSeparateFn blendFuncSeparate;
GlesBlitFramebufferFn blitFramebuffer;
GlesBufferDataFn bufferData;
GlesBufferSubDataFn bufferSubData;
GlesCheckFramebufferStatusFn checkFramebufferStatus;
GlesClearFn clear;
GlesClearBufferfvFn clearBufferfv;
GlesClearDepthfFn clearDepthf;
GlesClearStencilFn clearStencil;
GlesColorMaskFn colorMask;
GlesCompileShaderFn compileShader;
GlesCopyTexSubImage2DFn copyTexSubImage2D;
GlesCreateProgramFn createProgram;
GlesCreateShaderFn createShader;
GlesCullFaceFn cullFace;
GlesDeleteBuffersFn deleteBuffers;
GlesDeleteFramebuffersFn deleteFramebuffers;
GlesDeleteProgramFn deleteProgram;
GlesDeleteRenderbuffersFn deleteRenderbuffers;
GlesDeleteSamplersFn deleteSamplers;
GlesDeleteShaderFn deleteShader;
GlesDeleteTexturesFn deleteTextures;
GlesDeleteVertexArraysFn deleteVertexArrays;
GlesDepthFuncFn depthFunc;
GlesDepthMaskFn depthMask;
GlesDisableFn disable;
GlesDrawArraysInstancedFn drawArraysInstanced;
GlesDrawBuffersFn drawBuffers;
GlesDrawElementsInstancedFn drawElementsInstanced;
GlesEnableFn enable;
GlesEnableVertexAttribArrayFn enableVertexAttribArray;
GlesFlushFn flush;
GlesFramebufferRenderbufferFn framebufferRenderbuffer;
GlesFramebufferTexture2DFn framebufferTexture2D;
GlesFramebufferTextureLayerFn framebufferTextureLayer;
GlesFrontFaceFn frontFace;
GlesGenBuffersFn genBuffers;
GlesGenFramebuffersFn genFramebuffers;
GlesGenRenderbuffersFn genRenderbuffers;
GlesGenSamplersFn genSamplers;
GlesGenTexturesFn genTextures;
GlesGenVertexArraysFn genVertexArrays;
GlesGenerateMipmapFn generateMipmap;
GlesGetErrorFn getError;
GlesGetIntegervFn getIntegerv;
GlesGetInternalformativFn getInternalformativ;
GlesGetProgramInfoLogFn getProgramInfoLog;
GlesGetProgramivFn getProgramiv;
GlesGetShaderInfoLogFn getShaderInfoLog;
GlesGetShaderivFn getShaderiv;
GlesGetStringFn getString;
GlesGetUniformBlockIndexFn getUniformBlockIndex;
GlesLinkProgramFn linkProgram;
GlesPixelStoreiFn pixelStorei;
GlesPolygonOffsetFn polygonOffset;
GlesReadBufferFn readBuffer;
GlesRenderbufferStorageMultisampleFn renderbufferStorageMultisample;
GlesGetFloatvFn getFloatv;
GlesGetUniformLocationFn getUniformLocation;
GlesUniform1iFn uniform1i;
GlesSamplerParameterfFn samplerParameterf;
GlesSamplerParameteriFn samplerParameteri;
GlesScissorFn scissor;
GlesShaderSourceFn shaderSource;
GlesStencilFuncSeparateFn stencilFuncSeparate;
GlesStencilMaskFn stencilMask;
GlesStencilMaskSeparateFn stencilMaskSeparate;
GlesStencilOpSeparateFn stencilOpSeparate;
GlesTexParameteriFn texParameteri;
GlesTexStorage2DFn texStorage2D;
GlesTexStorage3DFn texStorage3D;
GlesTexSubImage2DFn texSubImage2D;
GlesTexSubImage3DFn texSubImage3D;
GlesUniformBlockBindingFn uniformBlockBinding;
GlesUseProgramFn useProgram;
GlesValidateProgramFn validateProgram;
GlesVertexAttribIPointerFn vertexAttribIPointer;
GlesVertexAttribPointerFn vertexAttribPointer;
GlesViewportFn viewport;
} GlesApiT;
extern GlesApiT _glesApi;
bool renderGlesLoad(void);
// Every call in renderGles.c goes through the table.
#define glActiveTexture _glesApi.activeTexture
#define glAttachShader _glesApi.attachShader
#define glBindBuffer _glesApi.bindBuffer
#define glBindBufferBase _glesApi.bindBufferBase
#define glBindBufferRange _glesApi.bindBufferRange
#define glBindFramebuffer _glesApi.bindFramebuffer
#define glBindRenderbuffer _glesApi.bindRenderbuffer
#define glBindSampler _glesApi.bindSampler
#define glBindTexture _glesApi.bindTexture
#define glBindVertexArray _glesApi.bindVertexArray
#define glBlendEquationSeparate _glesApi.blendEquationSeparate
#define glBlendFuncSeparate _glesApi.blendFuncSeparate
#define glBlitFramebuffer _glesApi.blitFramebuffer
#define glBufferData _glesApi.bufferData
#define glBufferSubData _glesApi.bufferSubData
#define glCheckFramebufferStatus _glesApi.checkFramebufferStatus
#define glClear _glesApi.clear
#define glClearBufferfv _glesApi.clearBufferfv
#define glClearDepthf _glesApi.clearDepthf
#define glClearStencil _glesApi.clearStencil
#define glColorMask _glesApi.colorMask
#define glCompileShader _glesApi.compileShader
#define glCopyTexSubImage2D _glesApi.copyTexSubImage2D
#define glCreateProgram _glesApi.createProgram
#define glCreateShader _glesApi.createShader
#define glCullFace _glesApi.cullFace
#define glDeleteBuffers _glesApi.deleteBuffers
#define glDeleteFramebuffers _glesApi.deleteFramebuffers
#define glDeleteProgram _glesApi.deleteProgram
#define glDeleteRenderbuffers _glesApi.deleteRenderbuffers
#define glDeleteSamplers _glesApi.deleteSamplers
#define glDeleteShader _glesApi.deleteShader
#define glDeleteTextures _glesApi.deleteTextures
#define glDeleteVertexArrays _glesApi.deleteVertexArrays
#define glDepthFunc _glesApi.depthFunc
#define glDepthMask _glesApi.depthMask
#define glDisable _glesApi.disable
#define glDrawArraysInstanced _glesApi.drawArraysInstanced
#define glDrawBuffers _glesApi.drawBuffers
#define glDrawElementsInstanced _glesApi.drawElementsInstanced
#define glEnable _glesApi.enable
#define glEnableVertexAttribArray _glesApi.enableVertexAttribArray
#define glFlush _glesApi.flush
#define glFramebufferRenderbuffer _glesApi.framebufferRenderbuffer
#define glFramebufferTexture2D _glesApi.framebufferTexture2D
#define glFramebufferTextureLayer _glesApi.framebufferTextureLayer
#define glFrontFace _glesApi.frontFace
#define glGenBuffers _glesApi.genBuffers
#define glGenFramebuffers _glesApi.genFramebuffers
#define glGenRenderbuffers _glesApi.genRenderbuffers
#define glGenSamplers _glesApi.genSamplers
#define glGenTextures _glesApi.genTextures
#define glGenVertexArrays _glesApi.genVertexArrays
#define glGenerateMipmap _glesApi.generateMipmap
#define glGetError _glesApi.getError
#define glGetIntegerv _glesApi.getIntegerv
#define glGetInternalformativ _glesApi.getInternalformativ
#define glGetProgramInfoLog _glesApi.getProgramInfoLog
#define glGetProgramiv _glesApi.getProgramiv
#define glGetShaderInfoLog _glesApi.getShaderInfoLog
#define glGetShaderiv _glesApi.getShaderiv
#define glGetString _glesApi.getString
#define glGetUniformBlockIndex _glesApi.getUniformBlockIndex
#define glLinkProgram _glesApi.linkProgram
#define glPixelStorei _glesApi.pixelStorei
#define glPolygonOffset _glesApi.polygonOffset
#define glReadBuffer _glesApi.readBuffer
#define glRenderbufferStorageMultisample _glesApi.renderbufferStorageMultisample
#define glGetFloatv _glesApi.getFloatv
#define glGetUniformLocation _glesApi.getUniformLocation
#define glUniform1i _glesApi.uniform1i
#define glSamplerParameterf _glesApi.samplerParameterf
#define glSamplerParameteri _glesApi.samplerParameteri
#define glScissor _glesApi.scissor
#define glShaderSource _glesApi.shaderSource
#define glStencilFuncSeparate _glesApi.stencilFuncSeparate
#define glStencilMask _glesApi.stencilMask
#define glStencilMaskSeparate _glesApi.stencilMaskSeparate
#define glStencilOpSeparate _glesApi.stencilOpSeparate
#define glTexParameteri _glesApi.texParameteri
#define glTexStorage2D _glesApi.texStorage2D
#define glTexStorage3D _glesApi.texStorage3D
#define glTexSubImage2D _glesApi.texSubImage2D
#define glTexSubImage3D _glesApi.texSubImage3D
#define glUniformBlockBinding _glesApi.uniformBlockBinding
#define glUseProgram _glesApi.useProgram
#define glValidateProgram _glesApi.validateProgram
#define glVertexAttribIPointer _glesApi.vertexAttribIPointer
#define glVertexAttribPointer _glesApi.vertexAttribPointer
#define glViewport _glesApi.viewport
#endif // RENDER_GLES_API_H

230
src/renderGlesLoad.c Normal file
View file

@ -0,0 +1,230 @@
/*
*
* Singe 3
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
// Loading the GLES entry points. See renderGlesApi.h for why they are fetched rather than linked.
#include "renderGlesApi.h"
#include "util.h"
GlesApiT _glesApi;
// Fails on the first missing entry point rather than half loading: a null here would be a crash
// later, inside a draw call, with nothing to say which function was absent.
bool renderGlesLoad(void) {
_glesApi.activeTexture = (GlesActiveTextureFn)SDL_GL_GetProcAddress("glActiveTexture");
_glesApi.attachShader = (GlesAttachShaderFn)SDL_GL_GetProcAddress("glAttachShader");
_glesApi.bindBuffer = (GlesBindBufferFn)SDL_GL_GetProcAddress("glBindBuffer");
_glesApi.bindBufferBase = (GlesBindBufferBaseFn)SDL_GL_GetProcAddress("glBindBufferBase");
_glesApi.bindBufferRange = (GlesBindBufferRangeFn)SDL_GL_GetProcAddress("glBindBufferRange");
_glesApi.bindFramebuffer = (GlesBindFramebufferFn)SDL_GL_GetProcAddress("glBindFramebuffer");
_glesApi.bindRenderbuffer = (GlesBindRenderbufferFn)SDL_GL_GetProcAddress("glBindRenderbuffer");
_glesApi.bindSampler = (GlesBindSamplerFn)SDL_GL_GetProcAddress("glBindSampler");
_glesApi.bindTexture = (GlesBindTextureFn)SDL_GL_GetProcAddress("glBindTexture");
_glesApi.bindVertexArray = (GlesBindVertexArrayFn)SDL_GL_GetProcAddress("glBindVertexArray");
_glesApi.blendEquationSeparate = (GlesBlendEquationSeparateFn)SDL_GL_GetProcAddress("glBlendEquationSeparate");
_glesApi.blendFuncSeparate = (GlesBlendFuncSeparateFn)SDL_GL_GetProcAddress("glBlendFuncSeparate");
_glesApi.blitFramebuffer = (GlesBlitFramebufferFn)SDL_GL_GetProcAddress("glBlitFramebuffer");
_glesApi.bufferData = (GlesBufferDataFn)SDL_GL_GetProcAddress("glBufferData");
_glesApi.bufferSubData = (GlesBufferSubDataFn)SDL_GL_GetProcAddress("glBufferSubData");
_glesApi.checkFramebufferStatus = (GlesCheckFramebufferStatusFn)SDL_GL_GetProcAddress("glCheckFramebufferStatus");
_glesApi.clear = (GlesClearFn)SDL_GL_GetProcAddress("glClear");
_glesApi.clearBufferfv = (GlesClearBufferfvFn)SDL_GL_GetProcAddress("glClearBufferfv");
_glesApi.clearDepthf = (GlesClearDepthfFn)SDL_GL_GetProcAddress("glClearDepthf");
_glesApi.clearStencil = (GlesClearStencilFn)SDL_GL_GetProcAddress("glClearStencil");
_glesApi.colorMask = (GlesColorMaskFn)SDL_GL_GetProcAddress("glColorMask");
_glesApi.compileShader = (GlesCompileShaderFn)SDL_GL_GetProcAddress("glCompileShader");
_glesApi.copyTexSubImage2D = (GlesCopyTexSubImage2DFn)SDL_GL_GetProcAddress("glCopyTexSubImage2D");
_glesApi.createProgram = (GlesCreateProgramFn)SDL_GL_GetProcAddress("glCreateProgram");
_glesApi.createShader = (GlesCreateShaderFn)SDL_GL_GetProcAddress("glCreateShader");
_glesApi.cullFace = (GlesCullFaceFn)SDL_GL_GetProcAddress("glCullFace");
_glesApi.deleteBuffers = (GlesDeleteBuffersFn)SDL_GL_GetProcAddress("glDeleteBuffers");
_glesApi.deleteFramebuffers = (GlesDeleteFramebuffersFn)SDL_GL_GetProcAddress("glDeleteFramebuffers");
_glesApi.deleteProgram = (GlesDeleteProgramFn)SDL_GL_GetProcAddress("glDeleteProgram");
_glesApi.deleteRenderbuffers = (GlesDeleteRenderbuffersFn)SDL_GL_GetProcAddress("glDeleteRenderbuffers");
_glesApi.deleteSamplers = (GlesDeleteSamplersFn)SDL_GL_GetProcAddress("glDeleteSamplers");
_glesApi.deleteShader = (GlesDeleteShaderFn)SDL_GL_GetProcAddress("glDeleteShader");
_glesApi.deleteTextures = (GlesDeleteTexturesFn)SDL_GL_GetProcAddress("glDeleteTextures");
_glesApi.deleteVertexArrays = (GlesDeleteVertexArraysFn)SDL_GL_GetProcAddress("glDeleteVertexArrays");
_glesApi.depthFunc = (GlesDepthFuncFn)SDL_GL_GetProcAddress("glDepthFunc");
_glesApi.depthMask = (GlesDepthMaskFn)SDL_GL_GetProcAddress("glDepthMask");
_glesApi.disable = (GlesDisableFn)SDL_GL_GetProcAddress("glDisable");
_glesApi.drawArraysInstanced = (GlesDrawArraysInstancedFn)SDL_GL_GetProcAddress("glDrawArraysInstanced");
_glesApi.drawBuffers = (GlesDrawBuffersFn)SDL_GL_GetProcAddress("glDrawBuffers");
_glesApi.drawElementsInstanced = (GlesDrawElementsInstancedFn)SDL_GL_GetProcAddress("glDrawElementsInstanced");
_glesApi.enable = (GlesEnableFn)SDL_GL_GetProcAddress("glEnable");
_glesApi.enableVertexAttribArray = (GlesEnableVertexAttribArrayFn)SDL_GL_GetProcAddress("glEnableVertexAttribArray");
_glesApi.flush = (GlesFlushFn)SDL_GL_GetProcAddress("glFlush");
_glesApi.framebufferRenderbuffer = (GlesFramebufferRenderbufferFn)SDL_GL_GetProcAddress("glFramebufferRenderbuffer");
_glesApi.framebufferTexture2D = (GlesFramebufferTexture2DFn)SDL_GL_GetProcAddress("glFramebufferTexture2D");
_glesApi.framebufferTextureLayer = (GlesFramebufferTextureLayerFn)SDL_GL_GetProcAddress("glFramebufferTextureLayer");
_glesApi.frontFace = (GlesFrontFaceFn)SDL_GL_GetProcAddress("glFrontFace");
_glesApi.genBuffers = (GlesGenBuffersFn)SDL_GL_GetProcAddress("glGenBuffers");
_glesApi.genFramebuffers = (GlesGenFramebuffersFn)SDL_GL_GetProcAddress("glGenFramebuffers");
_glesApi.genRenderbuffers = (GlesGenRenderbuffersFn)SDL_GL_GetProcAddress("glGenRenderbuffers");
_glesApi.genSamplers = (GlesGenSamplersFn)SDL_GL_GetProcAddress("glGenSamplers");
_glesApi.genTextures = (GlesGenTexturesFn)SDL_GL_GetProcAddress("glGenTextures");
_glesApi.genVertexArrays = (GlesGenVertexArraysFn)SDL_GL_GetProcAddress("glGenVertexArrays");
_glesApi.generateMipmap = (GlesGenerateMipmapFn)SDL_GL_GetProcAddress("glGenerateMipmap");
_glesApi.getError = (GlesGetErrorFn)SDL_GL_GetProcAddress("glGetError");
_glesApi.getIntegerv = (GlesGetIntegervFn)SDL_GL_GetProcAddress("glGetIntegerv");
_glesApi.getInternalformativ = (GlesGetInternalformativFn)SDL_GL_GetProcAddress("glGetInternalformativ");
_glesApi.getProgramInfoLog = (GlesGetProgramInfoLogFn)SDL_GL_GetProcAddress("glGetProgramInfoLog");
_glesApi.getProgramiv = (GlesGetProgramivFn)SDL_GL_GetProcAddress("glGetProgramiv");
_glesApi.getShaderInfoLog = (GlesGetShaderInfoLogFn)SDL_GL_GetProcAddress("glGetShaderInfoLog");
_glesApi.getShaderiv = (GlesGetShaderivFn)SDL_GL_GetProcAddress("glGetShaderiv");
_glesApi.getString = (GlesGetStringFn)SDL_GL_GetProcAddress("glGetString");
_glesApi.getUniformBlockIndex = (GlesGetUniformBlockIndexFn)SDL_GL_GetProcAddress("glGetUniformBlockIndex");
_glesApi.linkProgram = (GlesLinkProgramFn)SDL_GL_GetProcAddress("glLinkProgram");
_glesApi.pixelStorei = (GlesPixelStoreiFn)SDL_GL_GetProcAddress("glPixelStorei");
_glesApi.polygonOffset = (GlesPolygonOffsetFn)SDL_GL_GetProcAddress("glPolygonOffset");
_glesApi.readBuffer = (GlesReadBufferFn)SDL_GL_GetProcAddress("glReadBuffer");
_glesApi.renderbufferStorageMultisample = (GlesRenderbufferStorageMultisampleFn)SDL_GL_GetProcAddress("glRenderbufferStorageMultisample");
_glesApi.getFloatv = (GlesGetFloatvFn)SDL_GL_GetProcAddress("glGetFloatv");
_glesApi.getUniformLocation = (GlesGetUniformLocationFn)SDL_GL_GetProcAddress("glGetUniformLocation");
_glesApi.uniform1i = (GlesUniform1iFn)SDL_GL_GetProcAddress("glUniform1i");
_glesApi.samplerParameterf = (GlesSamplerParameterfFn)SDL_GL_GetProcAddress("glSamplerParameterf");
_glesApi.samplerParameteri = (GlesSamplerParameteriFn)SDL_GL_GetProcAddress("glSamplerParameteri");
_glesApi.scissor = (GlesScissorFn)SDL_GL_GetProcAddress("glScissor");
_glesApi.shaderSource = (GlesShaderSourceFn)SDL_GL_GetProcAddress("glShaderSource");
_glesApi.stencilFuncSeparate = (GlesStencilFuncSeparateFn)SDL_GL_GetProcAddress("glStencilFuncSeparate");
_glesApi.stencilMask = (GlesStencilMaskFn)SDL_GL_GetProcAddress("glStencilMask");
_glesApi.stencilMaskSeparate = (GlesStencilMaskSeparateFn)SDL_GL_GetProcAddress("glStencilMaskSeparate");
_glesApi.stencilOpSeparate = (GlesStencilOpSeparateFn)SDL_GL_GetProcAddress("glStencilOpSeparate");
_glesApi.texParameteri = (GlesTexParameteriFn)SDL_GL_GetProcAddress("glTexParameteri");
_glesApi.texStorage2D = (GlesTexStorage2DFn)SDL_GL_GetProcAddress("glTexStorage2D");
_glesApi.texStorage3D = (GlesTexStorage3DFn)SDL_GL_GetProcAddress("glTexStorage3D");
_glesApi.texSubImage2D = (GlesTexSubImage2DFn)SDL_GL_GetProcAddress("glTexSubImage2D");
_glesApi.texSubImage3D = (GlesTexSubImage3DFn)SDL_GL_GetProcAddress("glTexSubImage3D");
_glesApi.uniformBlockBinding = (GlesUniformBlockBindingFn)SDL_GL_GetProcAddress("glUniformBlockBinding");
_glesApi.useProgram = (GlesUseProgramFn)SDL_GL_GetProcAddress("glUseProgram");
_glesApi.validateProgram = (GlesValidateProgramFn)SDL_GL_GetProcAddress("glValidateProgram");
_glesApi.vertexAttribIPointer = (GlesVertexAttribIPointerFn)SDL_GL_GetProcAddress("glVertexAttribIPointer");
_glesApi.vertexAttribPointer = (GlesVertexAttribPointerFn)SDL_GL_GetProcAddress("glVertexAttribPointer");
_glesApi.viewport = (GlesViewportFn)SDL_GL_GetProcAddress("glViewport");
{
const void *const *slot = (const void *const *)&_glesApi;
const char *const names[] = {
"glActiveTexture",
"glAttachShader",
"glBindBuffer",
"glBindBufferBase",
"glBindBufferRange",
"glBindFramebuffer",
"glBindRenderbuffer",
"glBindSampler",
"glBindTexture",
"glBindVertexArray",
"glBlendEquationSeparate",
"glBlendFuncSeparate",
"glBlitFramebuffer",
"glBufferData",
"glBufferSubData",
"glCheckFramebufferStatus",
"glClear",
"glClearBufferfv",
"glClearDepthf",
"glClearStencil",
"glColorMask",
"glCompileShader",
"glCopyTexSubImage2D",
"glCreateProgram",
"glCreateShader",
"glCullFace",
"glDeleteBuffers",
"glDeleteFramebuffers",
"glDeleteProgram",
"glDeleteRenderbuffers",
"glDeleteSamplers",
"glDeleteShader",
"glDeleteTextures",
"glDeleteVertexArrays",
"glDepthFunc",
"glDepthMask",
"glDisable",
"glDrawArraysInstanced",
"glDrawBuffers",
"glDrawElementsInstanced",
"glEnable",
"glEnableVertexAttribArray",
"glFlush",
"glFramebufferRenderbuffer",
"glFramebufferTexture2D",
"glFramebufferTextureLayer",
"glFrontFace",
"glGenBuffers",
"glGenFramebuffers",
"glGenRenderbuffers",
"glGenSamplers",
"glGenTextures",
"glGenVertexArrays",
"glGenerateMipmap",
"glGetError",
"glGetIntegerv",
"glGetInternalformativ",
"glGetProgramInfoLog",
"glGetProgramiv",
"glGetShaderInfoLog",
"glGetShaderiv",
"glGetString",
"glGetUniformBlockIndex",
"glLinkProgram",
"glPixelStorei",
"glPolygonOffset",
"glReadBuffer",
"glRenderbufferStorageMultisample",
"glGetFloatv",
"glGetUniformLocation",
"glUniform1i",
"glSamplerParameterf",
"glSamplerParameteri",
"glScissor",
"glShaderSource",
"glStencilFuncSeparate",
"glStencilMask",
"glStencilMaskSeparate",
"glStencilOpSeparate",
"glTexParameteri",
"glTexStorage2D",
"glTexStorage3D",
"glTexSubImage2D",
"glTexSubImage3D",
"glUniformBlockBinding",
"glUseProgram",
"glValidateProgram",
"glVertexAttribIPointer",
"glVertexAttribPointer",
"glViewport",
};
size_t i;
for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
if (slot[i] == NULL) {
utilTrace("Gles: %s is missing from this driver", names[i]);
return false;
}
}
}
return true;
}

78
src/renderGpu.c Normal file
View file

@ -0,0 +1,78 @@
/*
*
* Singe 3
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*/
// The SDL_GPU backend: a table, not an implementation.
//
// Every entry in RenderBackendT carries SDL_GPU's own signature, so this backend is SDL's functions
// taken by address. Nothing is forwarded by hand, so there is no wrapper here to fall out of step
// with the header and nothing to get wrong. renderGles.c is where the work is.
#include "render.h"
const RenderBackendT renderGpuBackend = {
.acquireCommandBuffer = SDL_AcquireGPUCommandBuffer,
.beginCopyPass = SDL_BeginGPUCopyPass,
.beginRenderPass = SDL_BeginGPURenderPass,
.bindFragmentSamplers = SDL_BindGPUFragmentSamplers,
.bindGraphicsPipeline = SDL_BindGPUGraphicsPipeline,
.bindIndexBuffer = SDL_BindGPUIndexBuffer,
.bindVertexBuffers = SDL_BindGPUVertexBuffers,
.bindVertexStorageBuffers = SDL_BindGPUVertexStorageBuffers,
.blitTexture = SDL_BlitGPUTexture,
.cancelCommandBuffer = SDL_CancelGPUCommandBuffer,
.copyTextureToTexture = SDL_CopyGPUTextureToTexture,
.createBuffer = SDL_CreateGPUBuffer,
.createDevice = SDL_CreateGPUDevice,
.createGraphicsPipeline = SDL_CreateGPUGraphicsPipeline,
.createSampler = SDL_CreateGPUSampler,
.createShader = SDL_CreateGPUShader,
.createTexture = SDL_CreateGPUTexture,
.createTransferBuffer = SDL_CreateGPUTransferBuffer,
.destroyDevice = SDL_DestroyGPUDevice,
.drawIndexedPrimitives = SDL_DrawGPUIndexedPrimitives,
.drawPrimitives = SDL_DrawGPUPrimitives,
.endCopyPass = SDL_EndGPUCopyPass,
.endRenderPass = SDL_EndGPURenderPass,
.generateMipmapsForTexture = SDL_GenerateMipmapsForGPUTexture,
.getDeviceDriver = SDL_GetGPUDeviceDriver,
.getShaderFormats = SDL_GetGPUShaderFormats,
.getSwapchainTextureFormat = SDL_GetGPUSwapchainTextureFormat,
.getTextureFormatFromPixelFormat = SDL_GetGPUTextureFormatFromPixelFormat,
.mapTransferBuffer = SDL_MapGPUTransferBuffer,
.pushFragmentUniformData = SDL_PushGPUFragmentUniformData,
.pushVertexUniformData = SDL_PushGPUVertexUniformData,
.releaseBuffer = SDL_ReleaseGPUBuffer,
.releaseGraphicsPipeline = SDL_ReleaseGPUGraphicsPipeline,
.releaseSampler = SDL_ReleaseGPUSampler,
.releaseShader = SDL_ReleaseGPUShader,
.releaseTexture = SDL_ReleaseGPUTexture,
.releaseTransferBuffer = SDL_ReleaseGPUTransferBuffer,
.setScissor = SDL_SetGPUScissor,
.setStencilReference = SDL_SetGPUStencilReference,
.submitCommandBuffer = SDL_SubmitGPUCommandBuffer,
.textureSupportsFormat = SDL_GPUTextureSupportsFormat,
.textureSupportsSampleCount = SDL_GPUTextureSupportsSampleCount,
.unmapTransferBuffer = SDL_UnmapGPUTransferBuffer,
.uploadToBuffer = SDL_UploadToGPUBuffer,
.uploadToTexture = SDL_UploadToGPUTexture,
};

File diff suppressed because it is too large Load diff

View file

@ -71,6 +71,7 @@ LSEC_API int luaopen_ssl_config(lua_State *L);
#include "vfs.h" #include "vfs.h"
#include "persist.h" #include "persist.h"
#include "scene.h" #include "scene.h"
#include "render.h"
#include "scheduler.h" #include "scheduler.h"
#include "stats.h" #include "stats.h"
#include "hdr.h" #include "hdr.h"
@ -12646,7 +12647,7 @@ static int32_t apiSingeGetSystemInfo(lua_State *L) {
lua_pushstring(L, (os != NULL) ? os : ""); lua_setfield(L, -2, "os"); lua_pushstring(L, (os != NULL) ? os : ""); lua_setfield(L, -2, "os");
lua_pushstring(L, (cpu != NULL) ? cpu : ""); lua_setfield(L, -2, "cpu"); lua_pushstring(L, (cpu != NULL) ? cpu : ""); lua_setfield(L, -2, "cpu");
lua_pushstring(L, SDL_GetRendererName(_global.renderer)); lua_setfield(L, -2, "renderer"); lua_pushstring(L, SDL_GetRendererName(_global.renderer)); lua_setfield(L, -2, "renderer");
lua_pushstring(L, (_global.device != NULL) ? SDL_GetGPUDeviceDriver(_global.device) : "none (3D unavailable)"); lua_setfield(L, -2, "gpu"); lua_pushstring(L, (_global.device != NULL) ? rgpuGetDeviceDriver(_global.device) : "none (3D unavailable)"); lua_setfield(L, -2, "gpu");
lua_pushstring(L, videoGetDecoderDescription()); lua_setfield(L, -2, "decoder"); lua_pushstring(L, videoGetDecoderDescription()); lua_setfield(L, -2, "decoder");
lua_pushstring(L, (audio != NULL) ? audio : ""); lua_setfield(L, -2, "audio"); lua_pushstring(L, (audio != NULL) ? audio : ""); lua_setfield(L, -2, "audio");
lua_pushstring(L, midiSoundfont()); lua_setfield(L, -2, "soundFont"); lua_pushstring(L, midiSoundfont()); lua_setfield(L, -2, "soundFont");