singe/src/render.c
2026-09-22 18:32:07 -05:00

361 lines
12 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.
*
*/
// 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();
}
}
void renderSelect(RenderApiT api, const RenderBackendT *backend, SDL_Renderer *renderer) {
_api = backend != NULL ? api : RENDER_NONE;
_backend = backend;
_renderer = renderer;
utilTrace("Render: %s", renderApiName());
}
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;
}
// 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) {
// A format of our own naming (ETC2) is the GLES backend's alone; SDL's function would index
// its tables past the end with it.
if ((format >= RGPU_TEXTUREFORMAT_PRIVATE_FIRST) && (_api != RENDER_GLES)) {
return false;
}
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);
}