singe/src/render.h
2026-09-12 19:24:35 -05:00

193 lines
15 KiB
C

/*
*
* 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