/* * * Singe 3 * Copyright (C) 2006-2026 Scott Duensing * * 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 #include #include #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